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

UITextView作为响应者时如何检测Return与Shift+Return按键?

解决UITextView编辑状态下检测Return/Shift+Return按键的问题

当UITextView处于编辑状态时,系统会优先处理自身的按键事件,导致你在控制器中定义的UIKeyCommand被忽略。以下是两种可靠的解决方法:

方法一:自定义UITextView子类

通过子类化UITextView并重写keyCommands属性,让按键事件在编辑状态下也能被捕获:

class CustomTextView: UITextView {
    // 自定义代理,传递按键事件
    protocol CustomTextViewDelegate: UITextViewDelegate {
        func textViewDidTapReturn(_ textView: CustomTextView)
        func textViewDidTapShiftReturn(_ textView: CustomTextView)
    }
    
    weak var customDelegate: CustomTextViewDelegate?
    
    override var keyCommands: [UIKeyCommand]? {
        return [
            UIKeyCommand(input: "\r", modifierFlags: [], action: #selector(handleReturn)),
            UIKeyCommand(input: "\r", modifierFlags: .shift, action: #selector(handleShiftReturn))
        ]
    }
    
    @objc private func handleReturn() {
        // 通知代理执行单独Return的操作
        customDelegate?.textViewDidTapReturn(self)
        // 阻止系统默认换行行为
        delegate?.textView?(self, shouldChangeTextIn: .zero, replacementText: "")
    }
    
    @objc private func handleShiftReturn() {
        // 通知代理执行Shift+Return的操作
        customDelegate?.textViewDidTapShiftReturn(self)
        // 手动添加换行并调整光标到末尾
        text.append("\n")
        let endPos = endOfDocument
        selectedTextRange = textRange(from: endPos, to: endPos)
    }
}

使用时,在控制器中设置代理并实现对应方法:

class YourViewController: UIViewController, CustomTextView.CustomTextViewDelegate {
    @IBOutlet weak var textView: CustomTextView!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        textView.customDelegate = self
    }
    
    func textViewDidTapReturn(_ textView: CustomTextView) {
        // 单独按Return的操作,比如提交内容、收起键盘
        textView.resignFirstResponder()
        taskOne()
    }
    
    func textViewDidTapShiftReturn(_ textView: CustomTextView) {
        // Shift+Return的操作,比如执行自定义换行逻辑
        taskTwo()
    }
    
    @objc func taskOne() { /* 你的业务逻辑 */ }
    @objc func taskTwo() { /* 你的业务逻辑 */ }
}

方法二:使用UITextViewDelegate代理方法

无需子类化,直接通过代理方法拦截按键事件:

extension YourViewController: UITextViewDelegate {
    func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
        guard text == "\n" else { return true }
        
        // 检测Shift键是否被按下
        let isShiftPressed = UIApplication.shared.sendAction(#selector(getter: UIResponder.keyboardShiftKey), to: nil, from: nil, for: nil) ?? false
        
        if isShiftPressed {
            // Shift+Return:执行自定义操作,保留换行
            textView.text.append("\n")
            let endPos = textView.endOfDocument
            textView.selectedTextRange = textView.textRange(from: endPos, to: endPos)
            taskTwo()
            return false // 阻止系统默认处理
        } else {
            // 单独Return:执行自定义操作,阻止换行
            textView.resignFirstResponder()
            taskOne()
            return false
        }
    }
    
    @objc func taskOne() { /* 你的业务逻辑 */ }
    @objc func taskTwo() { /* 你的业务逻辑 */ }
}

方法对比

  • 自定义子类:适合需要在多个UITextView实例中复用该功能的场景,扩展性更强。
  • 代理方法:快速实现,无需额外子类,适合单个页面的简单需求。

内容的提问来源于stack exchange,提问作者Pranav Badgi

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.22 18:45:32