You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.20 19:31:03