SwiftUI:点击按钮不中断动画修改NavigationLink切换颜色
问题描述
我需要实现点击按钮时修改带有持续颜色切换动画的NavigationLink的两种颜色,且要求动画不中断。但直接修改颜色变量后,NavigationLink仅变为目标颜色之一,动画也停止了。
原代码
import SwiftUI struct FirstView: View { @State var animate: Bool = false @State var secondaryColor: Color = Color("SecondaryAccentColor") @State var primaryColor: Color = Color("AccentColor") var body: some View { ScrollView { VStack(spacing: 10) { Text("Click on the Navigate button to go to the next page!") .font(.title) .fontWeight(.semibold) Text("Click on the Color button to change the colors!") .padding(.bottom, 25) NavigationLink( destination: NextPage(), label: { Text("NAVIGATE") .foregroundColor(.white) .font(.title3) .fontWeight(.bold) .frame(height: 60) .frame(maxWidth: .infinity) .background(animate ? secondaryColor : primaryColor) .cornerRadius(15) }) .padding(.horizontal, 70) .shadow( color: animate ? secondaryColor.opacity(0.4) : primaryColor.opacity(0.4), radius: 15, x: 0, y: 30) Spacer() Button(action: { //CODE FOR THE CHANGING COLORS }) { Text("Color") } } .frame(maxWidth: 400) .multilineTextAlignment(.center) .padding(30) .onAppear(perform: addAnimation) } .frame(maxWidth: .infinity, maxHeight: .infinity) } func addAnimation() { guard !animate else { return } DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) { withAnimation( Animation .easeOut(duration: 0.5) .repeatForever() ) { animate.toggle() } } } } struct FirstView_Previews: PreviewProvider { static var previews: some View { NavigationView { FirstView() .navigationTitle("Hi") } } }
尝试的无效代码
Button(action: { primaryColor = Color.red secondaryColor = Color.yellow }) { Text("Color") }
执行后NavigationLink仅变为黄色,动画停止。
解决方案
问题根源在于原代码依赖布尔值animate的toggle来触发颜色切换,修改颜色变量时会导致视图重渲染,中断原有的repeatForever动画。我们可以改用自动反转的颜色插值动画,让颜色在两个目标值之间平滑过渡,修改颜色时只需更新目标值并保持动画运行。
修改后的完整代码:
import SwiftUI struct FirstView: View { @State var primaryColor: Color = Color("AccentColor") @State var secondaryColor: Color = Color("SecondaryAccentColor") // 用于实时显示当前动画中的颜色 @State var currentColor: Color = Color("AccentColor") var body: some View { ScrollView { VStack(spacing: 10) { Text("Click on the Navigate button to go to the next page!") .font(.title) .fontWeight(.semibold) Text("Click on the Color button to change the colors!") .padding(.bottom, 25) NavigationLink( destination: NextPage(), label: { Text("NAVIGATE") .foregroundColor(.white) .font(.title3) .fontWeight(.bold) .frame(height: 60) .frame(maxWidth: .infinity) .background(currentColor) .cornerRadius(15) }) .padding(.horizontal, 70) .shadow( color: currentColor.opacity(0.4), radius: 15, x: 0, y: 30) Spacer() Button(action: { // 更新颜色目标值 primaryColor = .red secondaryColor = .yellow // 重新触发动画,让currentColor在新的颜色间继续过渡 withAnimation(Animation.easeOut(duration: 0.5).repeatForever(autoreverses: true)) { currentColor = secondaryColor } }) { Text("Color") } } .frame(maxWidth: 400) .multilineTextAlignment(.center) .padding(30) .onAppear(perform: addAnimation) } .frame(maxWidth: .infinity, maxHeight: .infinity) } func addAnimation() { DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) { // 使用autoreverses让动画自动在两种颜色间来回切换 withAnimation(Animation.easeOut(duration: 0.5).repeatForever(autoreverses: true)) { currentColor = secondaryColor } } } } struct FirstView_Previews: PreviewProvider { static var previews: some View { NavigationView { FirstView() .navigationTitle("Hi") } } }
关键修改说明
- 新增
currentColor状态变量,用于绑定NavigationLink的背景和阴影颜色,作为动画的实时值 - 动画改为使用
repeatForever(autoreverses: true),让颜色在primaryColor和secondaryColor之间自动来回过渡,无需手动toggle布尔值 - 点击按钮修改颜色时,更新
primaryColor和secondaryColor后,重新触发动画,让currentColor在新的颜色目标间继续平滑过渡,确保动画不中断
内容的提问来源于stack exchange,提问作者Turshucuk
相关产品推荐
相关产品推荐

