Core Data UndoManager sends weird NS

The application I'm working on allows users to manage some assets. The user can create / delete / edit / split / move assets on the screen. Users should be able to undo all of these steps.

Assets are managed using master data (and yes, an undoManager is created).

For each of these actions, I create cancellation groups with this pair:

 beginUndoGrouping ... endUndoGrouping Here 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; } 

I do the same for editing: if the user cancels the editing, I call the cancel immediately after endUndoGrouping .

Everything works beautifully with one exception: in addition to the groups that I create, there are other groups created by Core Data that I cannot control . Here is what I mean:

I registered to receive NSUndoManagerDidCloseUndoGroupNotification notifications as follows:

 - (void) registerUndoListener { [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didCloseUndoGroup:) name:NSUndoManagerDidCloseUndoGroupNotification object:nil]; ...} 

I use these notifications to update the Cancel button and display the name of the action that is canceled as a result of some action: for example. Cancel split

However, didCloseUndoGroup is called / notified twice for each action above (for example, section 1 after endUndoGrouping):

During the first notification, self.managedObjectContext.undoManager.undoActionName contains the specified undo name that I expected, and the second time undoActionName is the empty string.

As a workaround, I tried to just cancel operations that had an empty name (assuming they weren't mine and I didn't need them) and see if anything was missing.

Now didCloseUndoGroup looks like this

 - (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 ... } 

And magically this works, I can undo any command, any number of layers, using undo. But this is not how it should work ...

A few other things I've tried before this:

  • [self.managedObjectContext processPendingChanges] before opening any grouping. He still sent two notifications.
  • Another thing I've tried is disableUndoRegistration / enableUndoRegistration. This threw an exception: "invalid state, undo is called with too many nested undo groups"

None of the above helped me "isolate" the mysterious groups that I mentioned earlier.

I should not receive NSUndoManagerDidCloseUndoGroupNotification notifications twice. Or, right? Is there a better way to handle this situation?

UPDATE This is what finally worked. I used to automatically cancel groups without a name as soon as I received a notification. This was the cause of this problem. Now I cancel everything until I reach my target group, and then make the last cancellation for this group.

"undoManagerHelper" is just a stack management system that generates a unique identifier for each command that is pushed onto the stack. I use this unique identifier to name a group.

 - (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]; } 
+4
source share
1 answer

According to the documentation for beginUndoGrouping - https://developer.apple.com/library/ios/#documentation/Cocoa/Reference/Foundation/Classes/NSUndoManager_Class/Reference/Reference.html - "By default, cancellation groups are automatically started at the beginning of the cycle events, but you can start your own cancellation groups with this method and nest them in other groups. " An unnamed group is the default undo group that contains all the operations, it looks like you should ignore the unnamed group for your situation.

0
source

Source: https://habr.com/ru/post/1437229/


All Articles