Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Nested undo group with CoreData

I want to add a undo manager to a coredata backed iphone application. When the user tries to add a new object (by tapping on + button) I load a new modal viewcontroller and start a new undo group in viewDidLoad.

When the user presses Cancel button, I want to rollback the changes in cancelAction callback.

Queries:

  1. Is it possible to start a nested Undo group and persist it through event loop and collate all the changes done in one single undo group? Right now, when I call beginUndoGrouping in cancelAction, I get a missing beginUndoGrouping exception.
  2. What is the significance of groupsByEvent API for NSUndoManager? Do I require to set it to NO to persist an undo group through event loop?
  3. What needs to be done in saveAction callback to make changes permanent (apart from calling save on mangedObjectContext)? How do I tell Undo Manager to stop tracking the changes?

Error Message:

* Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: '_endUndoGroupRemovingIfEmpty:: NSUndoManager 0x75415f0 is in invalid state, endUndoGrouping called with no matching begin

Sample Code:

// RootViewController.m
- (void) addAction {
   // Load Modal View Controller to add new object

}

// AddViewController.m
- (void) viewDidLoad {
   // Start nested undo group
   [self.managedObjectContext processPendingChanges];
   [self.managedObjectContext.undoManager beginUndoGrouping];

   [self createModel];

}

- (void) cancelAction {
    // Revert all changes
    [self.managedObjectContext processPendingChanges];
    [self.managedObjectContext.undoManager endUndoGrouping];

    [self.managedObjectContext.undoManager undoNestedGroup];

    ...
}

- (void) saveAction {

    // Save changes
}
like image 896
siasl Avatar asked Jan 31 '11 16:01

siasl


1 Answers

Beginning with your specific questions - Yes, you can manually define the bounds of an undo operation using beginUndoGrouping and endUndoGrouping.

In this case, the undo operation should work whether or not groupsByEvent is set. This is because all the undo groups that are generated by the event loop are nested under your main open undo grouping started with beginUndoGrouping, and as long as you call undoNestedGroup directly after you call endUndoGrouping, it should work. If you are not using the event loop undo groupings, don't worry about it and set it to NO.

To make your changes permanent, close the undo group with endUndoGrouping and call save on your context. The processPendingChanges calls are not needed, and may cause issues in nested groups. If you want clear the undo operations, call removeAllActions on your undomanager after endUndoGrouping - this guarantees the changes will never be un-did.

Use breakpoints/nslog to make sure your begin/end calls are one for one.

If you want your cancel action to be like an 'undo button', you'll have to do the following:

  • Move beginUndoGrouping to viewWillAppear

  • Call endUndoGrouping in viewWillDisappear

  • re-open undo grouping at the end of your cancel action

  • re-open undo grouping at the end of your save action

Otherwise, if you leave it as is, make sure you close the dialog in your save and cancel actions, to avoid possibility of endUndoGrouping being called multiple times.

If you have any questions, please comment and I'll update.

Good luck!

like image 64
Jacob Jennings Avatar answered Sep 22 '22 11:09

Jacob Jennings