SwiftUI视图位置动画异常:首次跳转与按钮延迟问题
Sheet弹出时背景视图动画异常修复
问题
- 首次触发Sheet弹出时,背景视图直接跳转到目标位置,无过渡动画
- Button视图相对Text视图存在明显的动画延迟
问题原因
- 首次无动画:初始高度值为0,Sheet弹出时通过异步方法更新高度,此时
show的动画已经启动,高度变化滞后导致视图直接跳变 - 动画不同步:自定义的
Geo类异步传递高度,加上外层动画作用范围的问题,导致子视图动画时序不一致
修复方案1(SwiftUI原生实现)
import SwiftUI struct ContentView: View { @State private var show = false @State private var sheetHeight: CGFloat = 0 var body: some View { VStack{ VStack{ Button("show"){ show.toggle() } .foregroundColor(.black) Text("Test") } .padding() .border(Color.black) .offset(y: show ? -sheetHeight * 0.5 : 0) } .frame(maxWidth: .infinity, maxHeight: .infinity) .background(Color.green) .animation(.easeInOut, value: show) .sheet(isPresented: $show) { SheetContent(height: $sheetHeight) .presentationDetents([.fraction(0.4)]) .presentationBackgroundInteraction(.enabled(upThrough: .fraction(0.4))) } } } struct SheetContent: View { @Binding var height: CGFloat var body: some View { Text("Showed") .padding() .background( GeometryReader { geo in Color.clear .onAppear { // 同步获取Sheet高度,无需异步 height = geo.size.height } .onDisappear { height = 0 } } ) } }
修复方案2(UIKit辅助实现)
如果原生方案有适配问题,也可以直接通过UIKit获取Sheet尺寸:
import SwiftUI struct ContentView: View { @State private var show = false @State private var sheetHeight: CGFloat = 0 var body: some View { VStack{ VStack{ Button("show"){ show.toggle() } .foregroundColor(.black) Text("Test") } .padding() .border(Color.black) .offset(y: show ? -sheetHeight * 0.5 : 0) } .frame(maxWidth: .infinity, maxHeight: .infinity) .background(Color.green) .animation(.easeInOut, value: show) .sheet(isPresented: $show) { VStack{ Text("Showed") .padding() } .presentationDetents([.fraction(0.4)]) .presentationBackgroundInteraction(.enabled(upThrough: .fraction(0.4))) .onAppear { // 通过UIKit获取Sheet视图高度 guard let windowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene, let sheetVC = windowScene.windows.first?.rootViewController?.presentedViewController as? UIHostingController<AnyView> else { return } sheetHeight = sheetVC.view.frame.height } .onDisappear { sheetHeight = 0 } } } }
关键修复点
- 移除异步更新高度的逻辑,确保Sheet弹出时高度值已就绪,让
show状态驱动的动画能正常过渡 - 用
@Binding直接传递Sheet高度,替代自定义的ObservableObject,减少状态传递的延迟 - 统一动画触发源为
show,确保Button和Text的动画同步执行
内容的提问来源于stack exchange,提问作者sheldor
相关产品推荐
相关产品推荐

