iOS16中Toolbar.principal位置自定义TextField侧滑返回时键盘不消失问题
iOS16 Toolbar Principal位置自定义TextField侧滑返回键盘无法消失的解决方案
问题场景
在iOS16环境下,将基于UIViewRepresentable实现的自定义TextField放置在Toolbar的.principal位置时,侧滑返回(BackSwipe)过程中键盘无法正常消失。调试发现:侧滑时UIResponder.keyboardWillHideNotification会触发,但随后textFieldDidBeginEditing被莫名调用,导致键盘重新弹出,直到页面完全关闭才消失。将TextField移出NavigationBar则无此问题。
解决方案
方案1:监听导航控制器侧滑手势,拦截编辑触发
通过监听UINavigationController的interactivePopGestureRecognizer,在侧滑开始时强制TextField resign第一响应者,并在滑动过程中禁止编辑:
struct ToolbarSearchView: UIViewRepresentable { @Binding var text: String let keyboardAppearanceDelay: Double let cancelAction: () -> Void func makeUIView(context: Context) -> UITextField { let textField = UITextField() textField.delegate = context.coordinator // 自定义TextField配置(如placeholder、样式等) textField.placeholder = "搜索" textField.borderStyle = .roundedRect // 绑定侧滑手势监听 if let navController = textField.window?.rootViewController as? UINavigationController { navController.interactivePopGestureRecognizer?.addTarget( context.coordinator, action: #selector(context.coordinator.handlePopGesture) ) } return textField } func updateUIView(_ uiView: UITextField, context: Context) { uiView.text = text } func makeCoordinator() -> Coordinator { Coordinator(parent: self) } class Coordinator: NSObject, UITextFieldDelegate { let parent: ToolbarSearchView private var isPopGestureActive = false init(parent: ToolbarSearchView) { self.parent = parent } @objc func handlePopGesture(_ gesture: UIGestureRecognizer) { switch gesture.state { case .began: isPopGestureActive = true gesture.view?.endEditing(true) // 强制隐藏键盘 case .ended, .cancelled, .failed: isPopGestureActive = false default: break } } // 侧滑过程中禁止TextField进入编辑状态 func textFieldShouldBeginEditing(_ textField: UITextField) -> Bool { return !isPopGestureActive } // 原有delegate方法(如textFieldDidChange、cancelAction绑定等) func textFieldDidChangeSelection(_ textField: UITextField) { parent.text = textField.text ?? "" } func textFieldShouldReturn(_ textField: UITextField) -> Bool { textField.endEditing(true) parent.cancelAction() return true } } }
方案2:利用NavigationStack路径变化监听隐藏键盘
如果项目使用NavigationStack,可以通过环境变量监听导航路径变化,在页面即将返回时强制隐藏键盘:
struct ToolbarSearchView: UIViewRepresentable { @Binding var text: String let keyboardAppearanceDelay: Double let cancelAction: () -> Void @Environment(\.navigationPath) private var navPath @State private var lastPathCount = 0 func makeUIView(context: Context) -> UITextField { let textField = UITextField() textField.delegate = context.coordinator // 自定义配置 textField.placeholder = "搜索" textField.borderStyle = .roundedRect return textField } func updateUIView(_ uiView: UITextField, context: Context) { uiView.text = text // 路径长度减少时说明正在返回,隐藏键盘 if navPath.count < lastPathCount { uiView.endEditing(true) } lastPathCount = navPath.count } // Coordinator实现同方案1(可去掉手势监听部分) func makeCoordinator() -> Coordinator { Coordinator(parent: self) } class Coordinator: NSObject, UITextFieldDelegate { let parent: ToolbarSearchView init(parent: ToolbarSearchView) { self.parent = parent } func textFieldDidChangeSelection(_ textField: UITextField) { parent.text = textField.text ?? "" } func textFieldShouldReturn(_ textField: UITextField) -> Bool { textField.endEditing(true) parent.cancelAction() return true } } }
方案3:替换为SwiftUI原生TextField(优先推荐)
如果自定义UITextField的需求可以通过原生组件满足,直接使用SwiftUI的TextField放在Toolbar中,系统会自动处理侧滑返回时的键盘隐藏逻辑:
func navigationBarWithSearch(searchText: Binding<String>, cancelAction: @escaping () -> Void) -> some View { toolbar { ToolbarItem(placement: .principal) { TextField("搜索", text: searchText) .textFieldStyle(.roundedBorder) .submitLabel(.done) .onSubmit { cancelAction() } } ToolbarItem(placement: .navigationBarTrailing) { Button("取消", action: cancelAction) } } }
方案选择建议
- 若必须保留自定义UITextField,优先使用方案1,能精准拦截侧滑过程中的意外编辑触发;
- 使用
NavigationStack的项目可尝试方案2,更符合SwiftUI声明式风格; - 原生TextField能满足需求时,优先用方案3,避免自定义组件带来的适配问题。
内容的提问来源于stack exchange,提问作者Simimi_Dot
相关产品推荐
相关产品推荐

