如何让SwiftUI Sheet高度精确匹配内部内容?
实现Sheet高度与内部内容完全匹配的SwiftUI方案
要让Sheet的高度自动适配内部VStack的实际高度,核心是通过PreferenceKey捕获视图高度,再动态传递给presentationDetents修饰符。具体实现步骤如下:
1. 定义PreferenceKey用于传递高度
先创建一个自定义的PreferenceKey,用来在视图层级间传递VStack的高度值:
struct ViewHeightKey: PreferenceKey { static var defaultValue: CGFloat = 0 static func reduce(value: inout CGFloat, nextValue: () -> CGFloat) { value = nextValue() } }
2. 捕获内部视图的高度
在目标VStack中添加透明背景的GeometryReader,通过preference将视图高度传递出去:
VStack { Text("Title") Text("Some very long text ...") } .padding() .background( GeometryReader { geo in Color.clear .preference(key: ViewHeightKey.self, value: geo.size.height) } )
3. 监听高度变化并动态设置Sheet高度
在父视图中用@State变量存储高度,通过onPreferenceChange监听高度更新,最后将高度传入presentationDetents:
完整示例代码:
struct ContentView: View { @State private var showSheet = false @State private var contentHeight: CGFloat = 250 // 初始 fallback 高度 var body: some View { Button("Show Sheet") { showSheet = true } .sheet(isPresented: $showSheet) { VStack { Text("Title") Text("Some very long text ...") } .padding() .background( GeometryReader { geo in Color.clear .preference(key: ViewHeightKey.self, value: geo.size.height) } ) .onPreferenceChange(ViewHeightKey.self) { newHeight in contentHeight = newHeight } .presentationDetents([.height(contentHeight)]) } } } struct ViewHeightKey: PreferenceKey { static var defaultValue: CGFloat = 0 static func reduce(value: inout CGFloat, nextValue: () -> CGFloat) { value = nextValue() } }
关键说明
GeometryReader放在background中不会影响原视图布局,透明背景对用户无感知。- 初始设置
contentHeight为250是为了避免Sheet初始加载时高度为0的异常情况,实际内容渲染后会自动更新为真实高度。 - 如果内部内容动态变化(比如文本长度改变),
onPreferenceChange会自动捕获新高度并更新Sheet尺寸。
内容的提问来源于stack exchange,提问作者Rahul Bir
相关产品推荐
相关产品推荐

