技术:SwiftUI、SwiftUI3.0、支付宝、余额宝、数字动画
运行环境:
SwiftUI3.0 + Xcode13.4.1 + MacOS12.5 + iPhone Simulator iPhone 13 Pro Max
SwiftUI搭建一个类似支付宝中的
余额宝
余额数字动画效果
思路
- 先看一下原理图
2. 其实就是在视图里面创建一排数字比如 0~9 垂直排列展示 然后将其他部分进行切割
3. 拿到当前的数字 和 之前的数字进行每一个位数进行比较
如果是当前数字位数大于之前位数 就向上滚动。
如果当前位数小数之前的尾数就向下滚动
RollingCounter
无
New Group
命名为 View
New File
选择SwiftUI View
类型 命名为RollingText
主要是:处理数字的动画效果
主要是展示主窗口
Home
和手动改变随机数
//
// ContentView.swift
// Shared
//
// Created by lyh on 2022/8/29.
//
import SwiftUI
struct ContentView: View {
@State var value :Int = 0
var body: some View {
NavigationView{
VStack(spacing:25){
RollingText(font: .system(size: 55), weight: .black, value: $value)
Button("change Value"){
value = .random(in: 100...2999)
}
}
.padding()
.navigationTitle("RollingCounter")
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
思路
- 先看一下原理图
2. 其实就是在视图里面创建一排数字比如 0~9 垂直排列展示 然后将其他部分进行切割
3. 拿到当前的数字 和 之前的数字进行每一个位数进行比较
如果是当前数字位数大于之前位数 就向上滚动。
如果当前位数小数之前的尾数就向下滚动
//
// RollingText.swift
// RollingCounter (iOS)
//
// Created by lyh on 2022/8/30.
//
import SwiftUI
struct RollingText: View {
// 文本属性
var font : Font = .largeTitle
var weight : Font.Weight = .regular
@Binding var value : Int
// 动画属性
@State var animationRange: [Int] = []
var body: some View {
HStack(spacing:0){
ForEach(0..<animationRange.count,id: \.self){index in
//查找给定字体的文本大小
Text("8")
.font(font)
.fontWeight(weight)
.opacity(0)
.overlay{
GeometryReader{ proxy in
let size = proxy.size
VStack(spacing:0){
// MARK:因为它的个人价值
//我们需要从0到9
ForEach(0...9,id: \.self){number in
Text("\(number)")
.font(font)
.fontWeight(weight)
.frame(width:size.width,height:size.height,alignment: .top)
}
}
// 设置偏移量
.offset(y:-CGFloat(animationRange[index]) * size.height)
}
.clipped()
}
}
}
.onAppear{
// 加载范围
animationRange = Array(repeating: 0, count: "\(value)".count)
DispatchQueue.main.asyncAfter(deadline: .now() + 0.06) {
updateText()
}
}
.onChange(of: value) { newValue in
// 处理额外价值的添加/移除
let extra = "\(value)".count - animationRange.count
if extra > 0{
// 添加额外的范围
for _ in 0..<extra {
withAnimation(.easeIn(duration:0.1)){ animationRange.append(0)
}
}
}
else
{
for _ in 0..<(-extra) {
withAnimation(.easeIn(duration:0.1)){ animationRange.removeLast()
}
}
}
updateText()
}
}
func updateText(){
let stringValue = "\(value)"
for(index,value) in zip(0..<stringValue.count, stringValue){
//如果First Value = 1
//然后Offset将被应用为-1
//所以文本将向上移动显示1Value
// 基于指标值的阻尼分数
var fraction = Double(index) * 0.15
// Max = 0.5
// Total = 1.5
fraction = (fraction > 0.5 ? 0.5 : fraction)
withAnimation(.interactiveSpring(response: 0.8, dampingFraction: 1, blendDuration: 1 + fraction)){
animationRange[index] = (String(value) as NSString).integerValue
}
}
}
}
struct RollingText_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}