【发布时间】:2013-02-25 18:37:24
【问题描述】:
我已经以标准方式 (NSUndoManager) 实现了撤消/重做,但无法弄清楚当我的应用处于特定状态时如何禁用撤消/重做。
用户在我的应用中绘制内容,当他们绘制的内容正在上传时,我禁用 UI,当然不希望用户能够撤消/重做。
我使用 NSView 的撤消管理器,所以我想一种方法可能是让该视图退出第一响应者。还有其他方法吗?
【问题讨论】:
标签: cocoa undo nsundomanager
我已经以标准方式 (NSUndoManager) 实现了撤消/重做,但无法弄清楚当我的应用处于特定状态时如何禁用撤消/重做。
用户在我的应用中绘制内容,当他们绘制的内容正在上传时,我禁用 UI,当然不希望用户能够撤消/重做。
我使用 NSView 的撤消管理器,所以我想一种方法可能是让该视图退出第一响应者。还有其他方法吗?
【问题讨论】:
标签: cocoa undo nsundomanager
如果视图是第一响应者,你可以实现validateMenuItem:协议根据你当前的状态禁用或启用菜单项。
- (BOOL)validateMenuItem:(NSMenuItem *)menuItem {
SEL action = menuItem.action;
if (action == @selector(undo:) ||
action == @selector(redo:)) {
return !uploadingImage;
}
return YES;
}
【讨论】:
您可以使用
完成撤消和重做 - (void) removeAllActions;
或删除针对特定目标的操作
- (void) removeAllActionsWithTarget: (id) target;
如果您只是想暂时禁用任何操作,保持撤消堆栈不变,只需使用 NSMenuValidationProtocol 禁用撤消/重做菜单项
- (BOOL)validateMenuItem:(NSMenuItem *)menuItem;
【讨论】:
我能想到的最佳方法是让视图的 -undoManager 方法在上传期间返回 nil,这会将其从响应者链中移除,并导致该视图的撤消/重做选项被禁用。
(我没有对此进行测试,但我有 99% 的把握,当菜单验证菜单选项时,它会向您的视图询问撤消管理器。)
【讨论】:
我有类似的情况,我想在应用处于特定状态时有条件地禁用某些撤消/重做操作(同时仍允许撤消/重做其他操作)。
在视图上实现- (BOOL)validateMenuItem:(NSMenuItem *)item 的方法对我不起作用(我在 10.12 上有一个基于文档的应用程序)。根据https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/MenuList/Articles/EnablingMenuItems.html 的文档:
如果响应者链中有一个对象实现了项目的操作,NSMenu 会检查该对象是否实现了 validateMenuItem: 或 validateUserInterfaceItem: 方法。如果没有,则启用菜单项。如果是,则菜单项的启用状态由方法的返回值确定。
视图必须添加一个撤消方法,它也能做正确的事情。
当我探测响应者链时,我发现我的 NSWindow 是响应 undo: 的对象(尽管它不是文档化接口的一部分),所以我目前的计划是使用自定义 NSWindow 子类来实现validateMenuItem,大致如下:
#import "Window.h"
@implementation SBXWindow
- (instancetype)initWithContentRect:(NSRect)contentRect styleMask:(NSWindowStyleMask)style backing:(NSBackingStoreType)bufferingType defer:(BOOL)flag screen:(NSScreen *)screen
{
self = [super initWithContentRect:contentRect styleMask:style backing:bufferingType defer:flag screen:screen];
return self;
}
- (BOOL)validateMenuItem:(NSMenuItem *)item
{
// Call super imeplementation as it appears to update the menu item title (and potentially other stuff)
BOOL result = [super validateMenuItem:item];
if (result == NO) {
return NO;
}
if (item.action == @selector(undo:) || item.action == @selector(redo:)) {
// Add custom logic here
}
return result;
}
@end
但是,有警告指出 undo: redo: 方法未实现。这些可以通过在 NSWindow 上创建一个类别来消除,例如:
@interface NSWindow (SBXUndoable)
- (void)undo:(id)sender;
- (void)redo:(id)sender;
@end
不确定这样做是否有任何问题(我没有注意到任何问题),但它确实消除了警告。从那以后,我将该类更改为 Swift 类,它没有任何警告要处理。
【讨论】:
文档是您的朋友。 NSUndoManager 的disableUndoRegistration 方法的名称中有“禁用”。由您应用的控制器决定何时适合禁用和重新启用撤消注册。
【讨论】: