【SwiftUI模块】0018、SwiftUI搭建一个类似支付宝中的余额宝余额数字动画效果

SwiftUI模块系列 - 已更新18篇
SwiftUI项目 - 已更新1个项目
往期Demo源码下载

技术:SwiftUI、SwiftUI3.0、支付宝、余额宝、数字动画
运行环境:
SwiftUI3.0 + Xcode13.4.1 + MacOS12.5 + iPhone Simulator iPhone 13 Pro Max

概述

SwiftUI搭建一个类似支付宝中的余额宝余额数字动画效果

详细

一、运行效果

请添加图片描述

二、项目结构图

在这里插入图片描述

三、程序实现 - 过程

思路

  1. 先看一下原理图

在这里插入图片描述
2. 其实就是在视图里面创建一排数字比如 0~9 垂直排列展示 然后将其他部分进行切割
3. 拿到当前的数字 和 之前的数字进行每一个位数进行比较
如果是当前数字位数大于之前位数 就向上滚动。
如果当前位数小数之前的尾数就向下滚动

1.创建一个项目命名为 RollingCounter

在这里插入图片描述
在这里插入图片描述

1.1.引入资源文件和颜色

2. 创建一个虚拟文件New Group 命名为 View

在这里插入图片描述
在这里插入图片描述

2. 创建一个文件New File 选择SwiftUI View类型 命名为RollingText

主要是:处理数字的动画效果

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

Code

ContentView - 主窗口

主要是展示主窗口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()
    }
}

Home - 主页

思路

  1. 先看一下原理图

在这里插入图片描述
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()
    }
}

猜你喜欢

转载自blog.csdn.net/qq_42816425/article/details/126593800
今日推荐