SwiftUI中Bottom Sheet内禁用键盘避让的可行方案?
问题:SwiftUI Bottom Sheet内
.ignoresSafeArea(.keyboard)失效,如何让Sheet不被键盘推起? 在普通SwiftUI视图中,给视图添加.ignoresSafeArea(.keyboard, edges: .bottom)可以禁用TextField的键盘避让行为,让TextField保持原位。但把这套逻辑搬到Bottom Sheet里时,修饰符完全失效——整个Sheet还是会被键盘向上推起。
原因推测是Bottom Sheet内部基于UIHostingController实现,而该控制器并不遵循SwiftUI的.ignoresSafeArea(.keyboard)规则,且我们无法直接修改这个系统内部创建的控制器。
下面提供几种可行的替代方案:
方案1:通过UIKit桥接禁用Sheet的键盘自动调整
创建一个自定义的UIViewControllerRepresentable组件,在其中获取Sheet的宿主控制器,直接禁用它的键盘自动调整行为:
struct DisableKeyboardAdjustment: UIViewControllerRepresentable { func makeUIViewController(context: Context) -> UIViewController { let vc = UIViewController() vc.view.backgroundColor = .clear return vc } func updateUIViewController(_ uiViewController: UIViewController, context: Context) { guard let sheetHost = uiViewController.parent else { return } // 关闭自动调整滚动视图内边距(这是系统Sheet被键盘推起的核心原因) sheetHost.automaticallyAdjustsScrollViewInsets = false // 强制重置安全区域内边距,确保没有额外偏移 sheetHost.additionalSafeAreaInsets = .zero } }
然后在Sheet中把这个组件作为背景层加入即可:
struct ContentView: View { @State var mytext: String = "Some text" @State var isPresented: Bool = true var body: some View { Color.mint .sheet(isPresented: $isPresented) { ZStack { DisableKeyboardAdjustment() VStack { Spacer() TextField("abc", text: $mytext) Spacer() } } .presentationDetents([.fraction(0.33)]) } } }
方案2:监听键盘通知,锁定Sheet的展示高度
通过监听键盘的显示/隐藏通知,强制保持Sheet的presentation detent不变,抵消系统的自动调整:
struct ContentView: View { @State var mytext: String = "Some text" @State var isPresented: Bool = true @State private var sheetDetent: PresentationDetent = .fraction(0.33) var body: some View { Color.mint .sheet(isPresented: $isPresented) { VStack { Spacer() TextField("abc", text: $mytext) Spacer() } .presentationDetents([sheetDetent]) // 键盘弹出时,强制保持原高度 .onReceive(NotificationCenter.default.publisher(for: UIResponder.keyboardWillShowNotification)) { _ in sheetDetent = .fraction(0.33) } // 键盘收起时也重置回原高度 .onReceive(NotificationCenter.default.publisher(for: UIResponder.keyboardWillHideNotification)) { _ in sheetDetent = .fraction(0.33) } } } }
这个方案的可靠性稍弱,因为系统可能仍会有微小偏移,但实现起来更简单。
方案3:完全自定义Bottom Sheet
如果系统Sheet的限制难以绕过,可以自己实现一个自定义Sheet,完全控制它的位置和行为,彻底摆脱系统键盘的影响:
// 自定义Sheet组件 struct CustomSheet<Content: View>: View { @Binding var isPresented: Bool let content: Content let heightFraction: CGFloat init(isPresented: Binding<Bool>, heightFraction: CGFloat, @ViewBuilder content: () -> Content) { self._isPresented = isPresented self.heightFraction = heightFraction self.content = content() } var body: some View { ZStack { if isPresented { // 半透明背景 Color.black.opacity(0.3) .ignoresSafeArea() .onTapGesture { isPresented = false } // Sheet内容 content .frame(height: UIScreen.main.bounds.height * heightFraction) .background(Color.white) .cornerRadius(16) .padding(.horizontal) .alignmentGuide(.bottom) { $0[.bottom] } .transition(.move(edge: .bottom)) } } .animation(.easeInOut, value: isPresented) } } // 使用示例 struct ContentView: View { @State var mytext: String = "Some text" @State var isPresented: Bool = true var body: some View { Color.mint .overlay( CustomSheet(isPresented: $isPresented, heightFraction: 0.33) { VStack { Spacer() TextField("abc", text: $mytext) Spacer() } .padding() } ) } }
自定义Sheet的位置完全由我们控制,键盘弹出时不会对它产生任何影响,适合需要高度自定义Sheet行为的场景。
内容的提问来源于stack exchange,提问作者Dave Robertson
相关产品推荐
相关产品推荐

