撤消/重做菜单项从不启用
Posted
技术标签:
【中文标题】撤消/重做菜单项从不启用【英文标题】:Undo/Redo Menu Items Never Enabled 【发布时间】:2020-01-20 18:58:18 【问题描述】:我有一个核心数据应用程序,其 NSTableView 绑定到 NSArrayController。我使用数组控制器管理添加和删除对象。我正在尝试添加撤消/重做支持,因此当一个人使用菜单项从表视图中删除对象时,他们可以撤消删除。
我的删除方法是:
- (IBAction)removeHost:(id)sender
NSInteger row = [bookmarkList selectedRow];
// Get the object so we can get to the attributes of the host
NSArray *a = [bookmarksController arrangedObjects];
NSManagedObject *object = [a objectAtIndex:row];
if (!object) return;
NSManagedObjectContext *managedObjectContext = [self managedObjectContext];
NSUndoManager *undoManager = [managedObjectContext undoManager];
if (managedObjectContext.undoManager == nil)
NSLog(@"No undo manager in app controller!");
else
NSLog(@"We've got an undo manager in app controller!");
[undoManager registerUndoWithTarget:self selector:@selector(addBookmarkObject:) object:object];
[bookmarksController removeObject:object];
[undoManager setActionName:@"Bookmark Delete"];
删除对象可以正常工作,但撤消不能。 Command-Z 菜单项永远不会启用。我设置了一个临时菜单项和操作来测试 undoManager,
- (IBAction)stupidUndoRemoveHost:(id)sender
NSManagedObjectContext *managedObjectContext = [self managedObjectContext];
NSUndoManager *undoer = [managedObjectContext undoManager];
NSLog(@"canUndo? %hhd", [undoer canUndo]);
NSLog(@"canRedo? %hhd", [undoer canRedo]);
NSLog(@"isUndoRegistrationEnabled? %hhd", [undoer isUndoRegistrationEnabled]);
NSLog(@"undoMenuItemTitle = %@", [undoer undoMenuItemTitle]);
NSLog(@"redoMenuItemTitle = %@", [undoer redoMenuItemTitle]);
[undoer undo];
使用这个 IBAction 我可以撤消(嗯,有点,它添加了两次对象,很明显这里还有更多错误),但我只能做一次。如果我删除另一个对象 canUndo 返回 0,而愚蠢的UndoRemoveHost 什么也不做。
我知道我不明白这里的某些内容。我在这里阅读的帖子数不胜数,还有几篇博客文章和Apple documentation。我以前也这样做过,但就像十年前一样,所以我的技能有点生疏。非常感谢任何正确方向的帮助或指示。
更新:这里是 addBookmarkObject 方法:
- (void)addBookmarkObject: (NSManagedObject *)object
[bookmarksController addObject:object];
这是来自 AppDelegate 的windowWillReturnUndoManager
:
- (NSUndoManager *)windowWillReturnUndoManager:(NSWindow *)window
// Returns the NSUndoManager for the application. In this case, the manager returned is that of the managed object context for the application.
NSUndoManager *undoManager = [[NSUndoManager alloc] init];
self.persistentContainer.viewContext.undoManager = undoManager;
if (self.persistentContainer.viewContext.undoManager == nil)
NSLog(@"No undo manager!");
else
NSLog(@"We've got an undo manager!");
return self.persistentContainer.viewContext.undoManager;
【问题讨论】:
显示 addBookmarkObject 的代码。我没有看到 removeHost 中的代码有任何问题。 谢谢,我已经用更多代码更新了这个问题。 托管对象上下文的撤消管理器应该处理撤消,您不必自己实现它。为什么要替换windowWillReturnUndoManager:
中的撤消管理器?这是一个基于文档的应用程序吗?
它不是基于文档的,我在之前的故障排除步骤中添加了它。我将删除自定义撤消管理器并重试。
@Willeke 如果我没有在 App Delegate 的 windowWillReturnUndoManager
中初始化 undoManager,则没有撤消管理器。登录我的 App Controller 的 stupidUndoRemoveHost
显示没有撤消管理器,在委托中也是如此。
【参考方案1】:
windowWillReturnUndoManager:
会在每次 Appkit 想要注册撤销操作以及想要启用/禁用 Undo 菜单项时调用。如果windowWillReturnUndoManager:
返回一个新的撤消管理器,则撤消堆栈为空并且撤消菜单项被禁用。
当一个对象被移除时,Core Data 会注册一个撤销操作,removeHost:
不应该注册一个额外的撤销操作。
- (IBAction)removeHost:(id)sender
[bookmarksController remove:sender];
[undoManager setActionName:@"Bookmark Delete"];
Xcode macOS Cocoa App with Core Data 模板存在一些缺陷。
NSWindowDelegate
方法 windowWillReturnUndoManager:
未被调用,因为在 xib 中,窗口的委托未连接到应用委托。修复:将窗口的委托连接到委托。
self.persistentContainer.viewContext.undoManager
是 nil
。修复:创建持久化容器时创建一次撤消管理器。
- (NSPersistentContainer *)persistentContainer
// The persistent container for the application. This implementation creates and returns a container, having loaded the store for the application to it.
@synchronized (self)
if (_persistentContainer == nil)
_persistentContainer = [[NSPersistentContainer alloc] initWithName:@"TestCDUndo"];
[_persistentContainer loadPersistentStoresWithCompletionHandler:^(NSPersistentStoreDescription *storeDescription, NSError *error)
if (error != nil)
…
abort();
self->_persistentContainer.viewContext.undoManager = [[NSUndoManager alloc] init];
];
return _persistentContainer;
【讨论】:
关于 undomanager 是 nil。这个答案解决了这个问题。然而,关于 OSX 上下文的文档状态提供了它自己的 undomanager,这令人困惑。 developer.apple.com/documentation/coredata/… 文档指出“在 macOS 中,上下文默认提供撤消管理器;在 ios 中,撤消管理器默认为 nil。”但显然NSPersistentContainer.viewContext
默认不提供撤消管理器。
试过了(上下文的新实例),它仍然为零。
@MarekH 请使用minimal reproducible example 发布一个新问题。
有点可笑。你有一个班轮 - >***.com/questions/64539712/…以上是关于撤消/重做菜单项从不启用的主要内容,如果未能解决你的问题,请参考以下文章
在 WinForms 中为上下文菜单动态选择菜单项的正确方法是啥?