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

如何在不放弃第一响应者的情况下隐藏UITextView键盘并显示格式Sheet?

解决方案

核心思路

  • 点击格式按钮时,先让UITextView放弃第一响应者身份收起键盘,再设置inputView为空视图,随后重新成为第一响应者,以此保持编辑状态且不弹出键盘
  • 格式Sheet关闭时,恢复inputView为nil,确保后续输入能正常唤起原生键盘
  • 修正原代码中UITextView作为struct存储属性的问题,避免值类型复制引发的异常

修改后的完整代码

struct EditorView: UIViewRepresentable {
    @ObservedObject var model: EditorModel
    
    init(model: EditorModel) {
        self.model = model
    }
    
    func makeUIView(context: Context) -> UITextView {
        let textView = UITextView()
        textView.allowsEditingTextAttributes = true
        textView.delegate = context.coordinator
        textView.keyboardDismissMode = .interactive
        textView.alwaysBounceVertical = true
        textView.font = .preferredFont(forTextStyle: .body)
        
        let toolBar = UIToolbar(frame: CGRect(x: 0, y: 0, width: textView.frame.size.width, height: 44))
        let boldButton = UIBarButtonItem(
            title: "Format text",
            image: UIImage(systemName: "textformat"),
            primaryAction: UIAction(title: "Format text", handler: { _ in
                model.isTextFormatSheetVisible = true
                // 先收起键盘,再替换输入视图,重新获取第一响应者身份
                textView.resignFirstResponder()
                textView.inputView = UIView()
                textView.becomeFirstResponder()
            })
        )
        toolBar.setItems([boldButton], animated: true)

        textView.inputAccessoryView = toolBar
        
        return textView
    }
    
    func updateUIView(_ textView: UITextView, context: Context) {
        textView.attributedText = model.attributedText
        textView.selectedRange = model.selectedRange
        
        // Sheet关闭时恢复输入视图,保证后续能正常唤起键盘
        if !model.isTextFormatSheetVisible, textView.inputView != nil {
            textView.inputView = nil
            // 如果当前仍处于编辑状态,重新唤起键盘
            if textView.isFirstResponder {
                textView.resignFirstResponder()
                textView.becomeFirstResponder()
            }
        }
    }
    
    func makeCoordinator() -> Coordinator {
        Coordinator(self)
    }
    
    class Coordinator: NSObject, UITextViewDelegate {
        private var parent: EditorView
        
        init(_ parent: EditorView) {
            self.parent = parent
        }
        
        func textViewDidChange(_ textView: UITextView) {
            parent.model.attributedText = textView.attributedText
            parent.model.selectedRange = textView.selectedRange
        }
        
        func textViewDidChangeSelection(_ textView: UITextView) {
            DispatchQueue.main.async {
                self.parent.model.selectedRange = textView.selectedRange
            }
        }
    }
}

关键细节说明

  • 移除struct存储的UITextView:原代码将UITextView作为struct属性,因Swift struct是值类型,多次复制会导致异常,改为在makeUIView内创建更符合UIViewRepresentable的生命周期规范
  • 切换第一响应者状态:键盘弹出后直接修改inputView无效,必须通过「放弃第一响应者→修改输入视图→重新获取第一响应者」的流程,才能让系统识别到输入视图的变化
  • 恢复输入视图:Sheet关闭后必须将inputView设回nil,否则后续输入无法唤起原生键盘

内容的提问来源于stack exchange,提问作者Nicola D'Aniello

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.18 17:17:34