SwiftUI自适应高度Sheet添加fixedSize后按钮动画异常问题
为什么SwiftUI Sheet添加fixedSize后按钮点击出现跳变动画?
问题原因
你的代码里出现跳变动画的核心原因是高度更新的时机和方式不符合SwiftUI的状态流转逻辑:
fixedSize(vertical: true)强制视图优先使用内容的固有高度,让Sheet内容的高度在布局阶段快速确定,但你通过GeometryReader直接同步修改绑定的height值,会立刻触发presentationDetents的更新。- 初始
sheetHeight设为0,Sheet呈现时先以这个高度初始化,随后GeometryReader读取到实际内容高度后瞬间修改height,导致Sheet从0高度(或系统 fallback 的最小高度)突然跳转到目标高度,没有平滑过渡,视觉上就出现了跳变。 - 直接在
GeometryReader闭包里修改绑定值,会触发视图的同步重渲染,这个过程和Sheet的自带呈现动画冲突,进一步加剧了跳变效果。
解决方法
通过PreferenceKey规范高度传递逻辑,并给高度更新添加动画,同时设置合理初始高度:
1. 定义高度偏好键
用SwiftUI标准的PreferenceKey传递布局高度,替代直接修改绑定值的方式:
struct ContentHeightPreferenceKey: PreferenceKey { static var defaultValue: CGFloat = 0 static func reduce(value: inout CGFloat, nextValue: () -> CGFloat) { value = nextValue() } }
2. 修改SheetHeightModifier
通过偏好键获取高度,并在更新时添加平滑动画:
struct SheetHeightModifier: ViewModifier { @Binding var height: CGFloat func body(content: Content) -> some View { content .fixedSize(horizontal: false, vertical: true) .background( GeometryReader { reader in Color.clear .preference(key: ContentHeightPreferenceKey.self, value: reader.size.height) } ) .onPreferenceChange(ContentHeightPreferenceKey.self) { newHeight in withAnimation(.easeInOut) { height = newHeight } } } }
3. 设置合理初始高度
避免初始高度为0,给sheetHeight设置一个接近内容最小高度的初始值,减少初始状态和目标状态的差距:
struct ContentView: View { @State var showSheet = false @State var sheetHeight: CGFloat = 100 // 设置初始高度 var body: some View { VStack { Button("Present Sheet") { showSheet = true } } .sheet(isPresented: $showSheet) { VStack { Text("Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.") } .padding() .flexiblePresentationDetents(height: $sheetHeight) } .padding() } }
补充说明
fixedSize(vertical: true)本身是正确的自适应高度方案,问题出在高度更新的同步性和缺少动画过渡。- 使用
PreferenceKey是SwiftUI中传递布局信息的标准实践,比直接修改绑定值更稳定,能避免不必要的重渲染冲突。
内容的提问来源于stack exchange,提问作者Berry Blue
相关产品推荐
相关产品推荐

