基于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
相关产品推荐
相关产品推荐

