SwiftUI按钮添加按压动画时如何正确显示跳转视图?
问题:按钮按压动画导致切换视图后控件位置不稳定
场景:点击按钮切换到Level视图时,原本Level里的Back按钮稳定显示在顶部,但给按钮添加按压弹簧动画后,Back按钮位置变得不稳定,有时在顶部有时偏移。移除.animation修饰符后布局恢复正常,希望同时保留按钮按压动画和视图的正确显示。
简化代码
import SwiftUI struct ContentView2: View { @State private var displayScreen = "" var body: some View { ZStack { VStack(spacing: 20) { Group { Button(" Play ") { displayScreen = "Level" } .padding(.top, 50) .buttonStyle(Button2()) } } .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .top) if displayScreen == "Level" { Level(displayedScreen: $displayScreen) } } .background(Color.yellow) } } struct Button2: ButtonStyle { func makeBody(configuration: Configuration) -> some View { configuration.label .padding(6) .background(Color.blue) .scaleEffect(configuration.isPressed ? 0.7 : 1) .animation(.interpolatingSpring(stiffness: 400, damping: 5, initialVelocity: 10), value: configuration.isPressed) // 问题所在 .foregroundColor(.white) } } struct Level: View { @Binding var displayedScreen: String var body: some View { NavigationView { ZStack(alignment: .top) { VStack { Button("Back") { displayedScreen = "" } .padding(.top) Spacer() } .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .top) } .background(Color.green) } } } struct ContentView2_Previews: PreviewProvider { static var previews: some View { ContentView2() } }
解决方案
方案1:固定按钮尺寸,隔离动画对布局的影响
在ButtonStyle中为按钮添加.fixedSize(),让按钮的缩放仅为视觉效果,不改变父视图的布局计算,从而避免干扰Level视图的位置:
struct Button2: ButtonStyle { func makeBody(configuration: Configuration) -> some View { configuration.label .padding(6) .background(Color.blue) .foregroundColor(.white) .fixedSize() // 固定按钮尺寸,防止缩放影响父布局 .scaleEffect(configuration.isPressed ? 0.7 : 1) .animation(.interpolatingSpring(stiffness: 400, damping: 5, initialVelocity: 10), value: configuration.isPressed) } }
方案2:使用Transaction隔离动画传播
通过transaction将动画限定在按钮自身的属性变化上,阻止动画传播到其他视图的布局过渡:
struct Button2: ButtonStyle { func makeBody(configuration: Configuration) -> some View { configuration.label .padding(6) .background(Color.blue) .foregroundColor(.white) .scaleEffect(configuration.isPressed ? 0.7 : 1) .transaction { transaction in // 仅为按钮的scale变化应用动画 transaction.animation = configuration.isPressed ? .interpolatingSpring(stiffness: 400, damping: 5, initialVelocity: 10) : nil } } }
原因说明
原代码中,按钮的动画会被SwiftUI的动画系统传播到父视图(ZStack)的布局变化上,当切换到Level视图时,视图的出现动画会与按钮的按压动画冲突,导致Level视图的布局计算异常。通过固定按钮尺寸或隔离动画传播,可以避免这种跨视图的动画干扰。
内容的提问来源于stack exchange,提问作者Eric
相关产品推荐
相关产品推荐

