You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

SwiftUI自适应高度Sheet添加fixedSize后按钮动画异常问题

为什么SwiftUI Sheet添加fixedSize后按钮点击出现跳变动画?

问题原因

你的代码里出现跳变动画的核心原因是高度更新的时机和方式不符合SwiftUI的状态流转逻辑:

  1. fixedSize(vertical: true)强制视图优先使用内容的固有高度,让Sheet内容的高度在布局阶段快速确定,但你通过GeometryReader直接同步修改绑定的height值,会立刻触发presentationDetents的更新。
  2. 初始sheetHeight设为0,Sheet呈现时先以这个高度初始化,随后GeometryReader读取到实际内容高度后瞬间修改height,导致Sheet从0高度(或系统 fallback 的最小高度)突然跳转到目标高度,没有平滑过渡,视觉上就出现了跳变。
  3. 直接在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.19 12:42:02