在NSView/NSViewController中配置UndoManager以更新撤销菜单动作名称
macOS自定义文本视图撤销菜单配置方案
核心问题定位
你的问题根源在于UndoManager未被响应链正确识别,导致系统无法自动读取撤销动作的状态(是否可撤销)和名称。重写undo/redo方法会绕过系统的菜单状态判断逻辑,虽然功能可用但无法同步菜单文本。以下是修复步骤:
1. 调整第一响应者逻辑
将第一响应者从TextViewController切换到交互视图TextView——系统会从第一响应者开始遍历响应链查找undoManager,而文本输入的交互主体是TextView:
class TextViewController: NSViewController { let textController = TextController() override func viewDidLoad() { super.viewDidLoad() guard let textView = view as? TextView else { return } textView.textController = textController // 延迟让TextView成为第一响应者(确保视图已加载完成) DispatchQueue.main.async { textView.becomeFirstResponder() } } // 可选:让控制器也暴露undoManager,强化响应链覆盖 override var undoManager: UndoManager? { return textController.undoManager } }
2. 正确重写TextView的undoManager属性
确保TextView的undoManager直接返回TextController的实例,让响应链能正确获取到管理撤销状态的对象:
class TextView: NSView { var textController: TextController! // 必须重写此属性,而非自定义getter override var undoManager: UndoManager? { return textController.undoManager } // 确保TextView允许成为第一响应者 override var acceptsFirstResponder: Bool { return true } // 处理文本输入事件示例 override func keyDown(with event: NSEvent) { guard let char = event.characters?.first else { super.keyDown(with: event) return } textController.insertCharacter(char) // 触发菜单状态更新 NSApp.updateWindows() super.keyDown(with: event) } }
3. 规范撤销动作的注册方式
在TextController中正确注册撤销动作并设置名称,确保系统能读取到动作信息:
class TextController { let undoManager = UndoManager() private var text = "" func insertCharacter(_ char: Character) { let oldText = text text.append(char) // 注册撤销逻辑 undoManager.registerUndo(withTarget: self) { [weak self] target in guard let self = self else { return } self.text = oldText // 可选:如果需要同步UI,这里可以触发TextView刷新 } // 设置动作名称,系统会自动拼接为"Undo Typing"/"Redo Typing" undoManager.setActionName("Typing") } }
4. 移除自定义的undo/redo方法
删除TextView中重写的undo()和redo()方法——系统会通过undoManager的canUndo/canRedo属性自动控制菜单的激活状态,同时读取actionName更新菜单文本。
关键注意事项
- 每次注册撤销动作后调用
NSApp.updateWindows(),强制系统刷新菜单状态。 - 确保响应链的完整性:
TextView的nextResponder默认是TextViewController,控制器的nextResponder会自动连接到窗口,无需额外修改。 - 不要手动修改菜单的标题或状态,完全交由
UndoManager和系统响应链处理。
内容的提问来源于stack exchange,提问作者georgemp
相关产品推荐
相关产品推荐

