SwiftUI中如何让Sheet高度适配内容+键盘高度?
解决方案
要实现Sheet默认适配内容高度、键盘弹出时自动扩展高度的效果,你可以通过捕获内容真实高度+监听键盘高度的组合方式动态调整presentationDetents,具体实现如下:
1. 定义PreferenceKey捕获内容高度
先创建一个PreferenceKey,用来收集Sheet内内容的实际高度:
struct ContentHeightKey: PreferenceKey { static var defaultValue: CGFloat = 0 static func reduce(value: inout CGFloat, nextValue: () -> CGFloat) { value = max(value, nextValue()) } }
2. 修改Sheet视图实现动态高度
在Sheet的内容视图中,通过background里的GeometryReader获取内容高度,同时监听键盘弹出/收起事件更新键盘高度,最终动态计算Sheet总高度:
struct TestView: View { @State private var showSheet: Bool = false @State private var textfieldInput: String = "" @State private var contentHeight: CGFloat = 0 @State private var keyboardHeight: CGFloat = 0 var body: some View { VStack { Button("Show Sheet") { showSheet.toggle() } .sheet(isPresented: $showSheet) { VStack(alignment: .leading) { TextField("This is a textfield", text: $textfieldInput) .bold() .font(.title3) Text("More (variable) content underneath the textfield") } .padding(20) .border(Color.red) // 捕获内容真实高度 .background( GeometryReader { geo in Color.clear .preference(key: ContentHeightKey.self, value: geo.size.height) } ) .onPreferenceChange(ContentHeightKey.self) { newValue in contentHeight = newValue } // 动态设置Sheet高度 .presentationDetents([.height(contentHeight + keyboardHeight)]) // 监听键盘弹出并同步动画 .onReceive(NotificationCenter.default.publisher(for: UIResponder.keyboardWillShowNotification)) { notification in guard let frame = notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? CGRect else { return } guard let duration = notification.userInfo?[UIResponder.keyboardAnimationDurationUserInfoKey] as? TimeInterval else { return } withAnimation(.easeOut(duration: duration)) { keyboardHeight = frame.height } } // 监听键盘收起并同步动画 .onReceive(NotificationCenter.default.publisher(for: UIResponder.keyboardWillHideNotification)) { notification in guard let duration = notification.userInfo?[UIResponder.keyboardAnimationDurationUserInfoKey] as? TimeInterval else { return } withAnimation(.easeOut(duration: duration)) { keyboardHeight = 0 } } } } } }
关键说明
- 你之前用根视图
GeometryReader失效的原因:根视图的GeometryReader获取的是Sheet初始分配的可用空间,而非内容的真实高度,导致Sheet高度不足。 - 动画优化:通过键盘通知里的动画时长参数,让Sheet高度变化和键盘弹出/收起动画同步,提升流畅度。
- 兼容性:该方案适配iOS 15+,如果是iOS 16+,也可以结合
.presentationDetents([.height(contentHeight), .dynamic])简化,但动态计算高度的方式兼容性更强。
内容的提问来源于stack exchange,提问作者Konrad
相关产品推荐
相关产品推荐

