覆盖 UITextView 的 CMD + Z 键命令

Posted

技术标签:

【中文标题】覆盖 UITextView 的 CMD + Z 键命令【英文标题】:Override UITextView's CMD + Z key command 【发布时间】:2021-05-19 06:22:39 【问题描述】:

是否可以覆盖UITextViewcmd + zcmd + shift + z 的处理?

我试过了

覆盖keyCommand 属性,但永远不会调用选择器.. 覆盖undoManager,这也无济于事
class CustomTextView: UITextView 

    override var keyCommands: [UIKeyCommand]? 
       [
            // cmd + z (doesn't work)
            UIKeyCommand(input: "z", modifierFlags: [.command], action: #selector(undo)),
                
            // cmd + shift + z  (doesn't work)
            UIKeyCommand(input: "z", modifierFlags: [.command, .shift], action: #selector(redo)),
                
            // z (works)
            UIKeyCommand(input: "z", modifierFlags: [], action: #selector(z)),
        ]
    
     
    // this doesn't help   
    override var undoManager: UndoManager?  return nil 
        






    // undo
    @objc private func undo() 
        print("undo")
    

    // redo
    @objc private func redo() 
        print("redo")
    

    // z
    @objc private func z() 
        print("z")
       

【问题讨论】:

反馈提交:FB9111541 【参考方案1】:

您可以使用 UITextView+UIResponder.swift 扩展来做到这一点:

import UIKit

extension UITextView 
    static var myUndoManager = UndoManager()

    fileprivate func handleUndo ()  ... 
    fileprivate func handleRedo ()  ... 

    override var undoManager : UndoManager? 
        return Self.myUndoManager
    

    override func pressesBegan (
        _ presses: Set<UIPress>
      , with event: UIPressesEvent?
    )
    
        for press in presses 
            guard
                let key = press.key
              , key.charactersIgnoringModifiers == "z"
            else  continue 

            switch key.modifierFlags 
                case .command:
                    handleUndo()
                case [ .command, .shift]:
                    handleRedo()
                default:
                    break
            
        
        super.pressesBegan (
            presses
          , with: event
        )
    

相关的 Apple 参考资料:

UIResponder::func pressesBegan(_:) https://developer.apple.com/documentation/uikit/uiresponder

Keyboards & Input::class UIKey https://developer.apple.com/documentation/uikit/uikey

另见:uiresponder https://***.com/questions/tagged/uiresponder

为什么覆盖 keyCommands 不起作用?

UITextView 是不透明的,所以我自己看不到,但我认为它会覆盖 pressesBegan(_:with:) 并且不会为它处理的任何按键调用 super。如果没有对super 的调用,UIPressesEvent 事件永远不会向上传播到响应者链; keyCommands 永远不会看到他们。

【讨论】:

不错的答案!但这将禁用关键命令的重复,有没有办法保留它? 您可以在 pressesBegan 中启动 Timer,然后在 pressesEnded 中使 Timer 无效并停止 @HassanTaleb 我不确定“重复”是什么意思;你的意图是只剥离 UITextView 的撤消/重做职责并保持它处理的所有其他内容不受影响(这不是我对 OP 问题的解释)?如果是这样,那么您需要正确子类化 UITextView,并调整我的答案,使其仅在用户意图执行除 Undo/Redo 之外的 other 操作时调用 super.pressesBegan(_:with:) .如果您发布这样的问题,我很乐意回答。 重复我的意思是按钮 (CMD + z) 保持按下状态,函数“handle Undo()”重复调用@Jason @HassanTaleb youjin 的建议似乎不错:在扩展中添加一个静态 Timer,使用 handleUndo() 中的 Timer.scheduledTimer(withTimeInterval:repeats:block:) 设置它,实现 pressesChanged/Ended/Cancelled 这样他们都使静态计时器无效。

以上是关于覆盖 UITextView 的 CMD + Z 键命令的主要内容,如果未能解决你的问题,请参考以下文章

有没有办法覆盖 UITextView UIKeyInput 以便可以从超级视图输入文本?

按下键盘中的返回键时,UITextView 不记得字体样式

如何允许 uitextview 只接受退格键和删除键

Mac Catalyst 无法使用 pressesBegan 覆盖捕获 .command 键修饰符

如何使用 Swift 中的自动生成键从情节提要中设置的 UITextView 中检索文本?

如何手动启用或禁用键盘上的 Return 键?