You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于SwiftUI实现时钟盒子动画效果技术求助

实现时间盒子的切换动画

1. 修正时间管理器的动画触发逻辑

原代码中withAnimation的位置不正确,需在定时器内部更新日期时包裹动画,确保视图能感知到变化并触发过渡:

import SwiftUI

class TimeManager: ObservableObject {
    @Published var date = Date()
    @Published var oldDate = Date()

    func timeString(timeFormat: String, date: Date) -> String {
        let formatter = DateFormatter()
        formatter.dateFormat = timeFormat
        return formatter.string(from: date)
    }
    
    var updateTimer: Timer {
        Timer.scheduledTimer(withTimeInterval: 1, repeats: true) { _ in
            withAnimation(.spring(response: 0.5, dampingFraction: 0.7)) {
                self.oldDate = self.date
                self.date = Date()
            }
        }
    }
}

2. 修改盒子视图,传递新旧时间值

每个盒子需要同时获取当前和上一秒的时间值才能触发独立动画,调整BoxesView逻辑:

struct BoxesView: View {
    @EnvironmentObject var timeManager: TimeManager
    
    var body: some View {
        HStack(spacing: 30) {
            let currentTimeParts = splitTime(date: timeManager.date)
            let oldTimeParts = splitTime(date: timeManager.oldDate)
            
            ForEach(0..<3) { index in
                SingleBox(oldText: oldTimeParts[index], currentText: currentTimeParts[index])
            }
        }
        .ignoresSafeArea()
    }
    
    private func splitTime(date: Date) -> [String] {
        timeManager.timeString(timeFormat: "HH:mm:ss", date: date).components(separatedBy: ":")
    }
}

3. 实现单个盒子的翻转动画

通过3D旋转+透明度变化模拟数字切换的翻转效果,当文本更新时自动触发动画:

struct SingleBox: View {
    let oldText: String
    let currentText: String
    
    @State private var isFlipping = false
    
    var body: some View {
        ZStack {
            // 上一秒的数字(翻转后隐藏)
            Text(oldText)
                .foregroundColor(.white)
                .font(.title)
                .fontWeight(.semibold)
                .monospacedDigit()
                .frame(width: 90, height: 90)
                .background(RoundedRectangle(cornerRadius: 12).fill(.pink))
                .rotation3DEffect(.degrees(isFlipping ? 180 : 0), axis: (x: 1, y: 0, z: 0))
                .opacity(isFlipping ? 0 : 1)
            
            // 当前的数字(翻转后显示)
            Text(currentText)
                .foregroundColor(.white)
                .font(.title)
                .fontWeight(.semibold)
                .monospacedDigit()
                .frame(width: 90, height: 90)
                .background(RoundedRectangle(cornerRadius: 12).fill(.pink))
                .rotation3DEffect(.degrees(isFlipping ? 0 : 180), axis: (x: 1, y: 0, z: 0))
                .opacity(isFlipping ? 1 : 0)
        }
        .onChange(of: currentText) { _ in
            isFlipping = false
            withAnimation(.easeInOut(duration: 0.6)) {
                isFlipping = true
            }
        }
    }
}

4. 启动定时器(根视图中)

在根视图的onAppear方法中启动定时器,让时间开始自动更新:

struct ContentView: View {
    @StateObject private var timeManager = TimeManager()
    
    var body: some View {
        BoxesView()
            .environmentObject(timeManager)
            .onAppear {
                _ = timeManager.updateTimer
            }
    }
}

自定义调整

  • 修改.spring()或.easeInOut()的参数,可以调整动画的弹性和时长,匹配你想要的效果
  • 如果需要其他动画类型(比如缩放、滑入),替换rotation3DEffect为对应修饰符即可

内容的提问来源于stack exchange,提问作者user18908963

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.14 02:10:57