SwiftUI:如何获取.sheet中视图的固有高度并更新其尺寸?
SwiftUI中让Sheet自适应内容固有高度的解决方案
问题背景
我想创建一个自适应内容固有高度的.sheet,尝试通过readSize方法读取视图高度,再用presentationDetents([.height(sheetHeight)])设置自定义高度,但发现初始sheetHeight为0时,视图被限制为0高度,无法获取真实的固有高度。虽然知道confirmationDialog更适合我的场景,但还是想了解.sheet的解决办法。
解决方案
核心思路是先让Sheet不受高度限制地渲染内容,获取到固有高度后,再动态切换为自定义高度的detent,避免初始高度限制导致无法读取真实尺寸。
修改后的完整代码
import SwiftUI struct ContentView: View { @State private var showBottomSheet = false @State private var sheetHeight = Double.zero @State private var useCustomDetent = false // 控制是否切换到自定义高度 var body: some View { Form { Button("Show bottom sheet") { // 每次弹出前重置状态,避免上次高度影响 sheetHeight = .zero useCustomDetent = false showBottomSheet = true } } .sheet(isPresented: $showBottomSheet) { BottomSheetView() .readSize { newSize in // 只在第一次获取到有效高度时更新 guard sheetHeight == .zero, newSize.height > 0 else { return } sheetHeight = newSize.height // 延迟切换detent,避免布局循环 DispatchQueue.main.async { useCustomDetent = true } } .presentationDetents(useCustomDetent ? [.height(sheetHeight)] : [.automatic]) } } } struct ContentView_Previews: PreviewProvider { static var previews: some View { ContentView() } } struct BottomSheetView: View { var body: some View { Form { Button("Button 1") { /*Perform stuff*/ } Button("Button 2") { /*Perform other stuff*/ } } } } struct SizePreferenceKey: PreferenceKey { static var defaultValue: CGSize = .zero static func reduce(value: inout CGSize, nextValue: () -> CGSize) {} } extension View { func readSize(onChange: @escaping (CGSize) -> Void) -> some View { background( GeometryReader { geometryProxy in Color.clear .preference(key: SizePreferenceKey.self, value: geometryProxy.size) } ) .onPreferenceChange(SizePreferenceKey.self, perform: onChange) } }
关键逻辑说明
- 状态变量
useCustomDetent:初始为false,让Sheet使用.automaticdetent,确保内容能自由布局,不受高度限制 - 高度读取控制:仅在第一次获取到有效高度时更新
sheetHeight,避免重复触发布局更新 - 延迟切换detent:用
DispatchQueue.main.async确保视图完成布局后再切换到自定义高度,防止出现布局循环或视觉闪烁
内容的提问来源于stack exchange,提问作者appfrosch
相关产品推荐
相关产品推荐

