【发布时间】:2012-09-22 12:46:35
【问题描述】:
我正在开发的应用程序允许用户管理一些资产。用户可以在屏幕上创建/删除/编辑/拆分/移动资产。用户需要能够撤消所有这些步骤。
资产使用核心数据进行管理(是的,undoManager 已实例化)。
对于这些操作中的每一个,我都使用这对创建撤消分组:
beginUndoGrouping ... endUndoGrouping
Here's a simple example (sequence 1):
// SPLIT
- (void) menuSplitPiece: (id) sender
{
[self.managedObjectContext.undoManager beginUndoGrouping];
[self.managedObjectContext.undoManager setActionName:@"Split"];
//... do the split
[self.managedObjectContext.undoManager endUndoGrouping];
// if the user cancels the split action, call [self.managedObjectContext.undoManager undo] here;
}
我对编辑做同样的事情:如果用户取消编辑,那么我在 endUndoGrouping 之后立即调用 undo。
一切都很好,除了一个例外:除了我创建的组之外,Core Data 还创建了其他我无法控制的组。这就是我的意思:
我注册接收 NSUndoManagerDidCloseUndoGroupNotification 通知,如下所示:
- (void) registerUndoListener
{
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(didCloseUndoGroup:)
name:NSUndoManagerDidCloseUndoGroupNotification object:nil];
...}
我使用这些通知来刷新撤消按钮并显示由于某些操作而被撤消的操作的名称:例如撤消拆分
然而,didCloseUndoGroup 会为上述每个操作调用/通知两次(例如,第 1 节,在 endUndoGrouping 之后):
在第一次通知时,self.managedObjectContext.undoManager.undoActionName 包含我设置的撤消操作名称,这是我预期的,而第二次undoActionName 是一个空字符串。
作为一种解决方法,我尝试简单地撤消名称为空的操作(假设它们不是我的,我不需要它们),并查看我是否遗漏了什么。
现在,didCloseUndoGroup 看起来像这样
- (void) didCloseUndoGroup: (NSNotification *) notification
{
...
if ([self.managedObjectContext.undoManager.undoActionName isEqualToString:@""]){
[self.managedObjectContext.undoManager undo];
}
[self refreshUndoButton]; // this method displays the name of the undo action on the button
...
}
神奇地它起作用了,我可以使用“撤消”撤消任何命令,任何层数。但这不是它应该工作的方式......
在那之前我尝试过的其他几件事:
- [self.managedObjectContext processPendingChanges] 在打开任何分组之前。它仍在发送两个通知。
- 我尝试的另一件事是 disableUndoRegistration / enableUndoRegistration。这产生了一个异常:“无效状态,使用太多嵌套的撤消组调用撤消”
以上都没有帮助我“隔离”我之前提到的神秘群体。
我不应该收到两次 NSUndoManagerDidCloseUndoGroupNotification 通知。或者,我应该吗?有没有更好的方法来处理这种情况?
更新 这就是最终奏效的方法。以前,我一收到通知就会自动撤消无名组。这就是导致问题的原因。现在,我撤消所有操作,直到到达我的目标组,然后对该组执行最后一次撤消。
“undoManagerHelper”只是一个堆栈管理系统,它为每个压入堆栈的命令生成一个唯一的 ID。我使用这个唯一的 ID 来命名组。
- (BOOL) undoLastAction
{
NSString *lastActionID = [self.undoManagerHelper pop]; // the command I'm looking for
if (lastActionID == nil) return false;
//... undo until there is nothing to undo or self.managedObjectContext.undoManager.undoActionName equals lastActionID
//the actual undo here
if ([currentActionID isEqualToString: lastActionID] && [self.managedObjectContext.undoManager canUndo]){
[self.managedObjectContext.undoManager undo];
}
return true;
}
- (void) beginUndoGroupingWithName: (NSString *) name
{
[self.managedObjectContext processPendingChanges];
[self.managedObjectContext.undoManager beginUndoGrouping];
NSString *actionID = [self.undoManagerHelper push: name];
[self.managedObjectContext.undoManager setActionName:actionID];
}
- (void) closeLastUndoGrouping
{
[self.managedObjectContext.undoManager endUndoGrouping];
[self.managedObjectContext processPendingChanges];
}
【问题讨论】:
-
感谢您格式化帖子,这是我的第一个问题。希望你能理解。
标签: objective-c core-data nsundomanager