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
相关产品推荐
相关产品推荐

