undo all changes made in the child view controller
Asked Answered
B

1

9

There are two entities: Author and Book. Author has an attribute authorName and a to-many relationships books. Book has several attributes and a relationship author. There is a view controller (VCAuthor) to edit an Author object. The child view controller(VCBook) is to edit books of the author. There is only one managedobjectcontext. In the VCBook class i group the undomanager as following

-(void)viewDidLoad
{    
    NSUndoManager *anUndoManager = [[NSUndoManager  alloc] init];
    [self.book.managedObjectContext setUndoManager:anUndoManager];
    [anUndoManager release];
    [self.book.managedObjectContext.undoManager beginUndoGrouping];
}

-(void)cancelAction:(id)sender
{
    NSLog(@"%@", self.author.authorName);
    [self.book.managedObjectContext.undoManager endUndoGrouping];
    [self.book.managedObjectContext.undoManager undoNestedGroup];
    self.book.managedObjectContext.undoManager = nil;
    NSLog(@"%@", self.author.authorName);    
    [self dismissModalViewControllerAnimated:YES];  
}

the cancelAction is linked to an cancel button on the VCBook which used to undo all the changes made in the VCBook.

Problems is here: First, in the VCAuthor, I edit the authorName in an UITextfiled authorNameTextField from Obama to Big Obama, and save it to the MOC by author.authorName = authorNameTextField.text in the - (void)viewWillDisappear:(BOOL)animated{} method. Then I went into the child view controller VCBook to edit books of the author and click the cancel button to get back to the VCAuthor. I find the authorName still be Obama, that means the expected change of the authorName has been undo. The change of the authorName is not in the undo group at all, and why could this happen? ps. of course i reloadData when i get back into VCAuthor. I just NSLog the authorName before and after the undo. Before undo the authorName is the changed one Big Obama, and after undo it become Obama

Boresome answered 4/5, 2012 at 6:25 Comment(7)
I think the problem here is not with the reloading, the problem is you are saving the changed name of the author to the entityCalamint
but i did not group the change, how could it be undoed here?Boresome
its undo manager default behaviorCalamint
but i set a new undoManager in the VCBook.Boresome
and if i push a new view controller for editing the authorName and save the changed name to the entity when get back to the VCAuthor. The undo method in the VCBook will not affect at all.Boresome
Thats what i am saying, it is not saving the new name, debug itCalamint
You mean save it to the persistent store? No, I should leave the save action to the save button in VCAuthor. The save in my last comment is just by author.authorName = authorNameTextField.textBoresome
C
9

Several things to consider. First, in a scenario like this, I would use a separate MOC instead of the undo manager. Namely, I'd do something like this (assuming ARC - you can do the mapping if necessary)...

You must have some other code providing the book to the VC through a setter, since you access it in viewDidLoad. I'd change viewDidLoad to something like this...

-(void)viewDidLoad
{
    self.managedObjectContext = [[NSManagedObjectContext alloc] init];
    self.managedObjectContext.parentContext = self.book.managedObjectContext;
    // We are in the main thread, so we can safely access the main MOC
    // Basically, move our object pointer to book into our local MOC.
    NSError * error = nil;
    Book *book = [self.managedObjectContext existingObjectWithID:self.book.objectID error:&error];
    // handle nil return and/or error
    self.book = book;
    // Now, your access to book will be in the local MOC, and any changes
    // you make to the book or book.author will all be confined to the local MOC.
}

Now, all you have to do is call

[self.managedObjectContext save:&error];

in your saveAndDismiss action. If you don't call save, none of the changes will be saved, they will all just automatically disappear.

EDIT

Note, that the above "save" just moves the object state into the parent context. So, the "main" MOC now has the changes from the "child" but none of the changes have been saved to disk yet.

Cosignatory answered 7/5, 2012 at 19:22 Comment(9)
If i use a separate MOC, the book could not be add to the author by [self.author addBookObject self.book]; . I will not save the changes to the persistent store in the VCBook. After finishing all changes in the VCAuthor (such as modifying the author name, age, etc.) and then be saved there.Boresome
I'm sorry, but I do not understand what you are trying to say in this comment. You made three incomplete statements, and I can not understand what problems you are trying to describe.Cosignatory
1st, If i edit the book in a separate MOC, i could not add the book to the author by "[self.author addBookObject self.book];". Because they are not in the same MOC. 2nd, In the VCBook, "[self.managedObjectContext save:&error];" is not what i want for some other reasons. 3rd, All changes will be saved to the persistent store when the save button in the VCAuthor is touched town.Boresome
Did you read the code I posted? First, you only have one view controller active at a time. This particular view controller is, according to what you said earlier, editing an author. Second, in the code I poster, the "current book" object that is being used is in the same MOC. Finally, your assumptions are just pain wrong. Saving the author context just pushes those changes into the parent MOC. They are not saved until that MOC saves.Cosignatory
[self.managedObjectContext save:&error]; this code just save changes to the parentContext right? let me try later, thanks!Boresome
btw. i did not post my "saveAndDismiss action", which mentioned in your answer.Boresome
Right, saving a child context just pushes those changes to the parent context. They do not get saved to the database until the "root" context saves them. Also, I just made up the saveAndDismiss to show that you need to save the context to the parent when done... and that's all because if you do not do that, all changes made in the child context will just "disappear" when that context is deallocated.Cosignatory
If the save fails, how can you undo the partial failed changes if you don't have an undo manager?Burt
I would ask how you do that now. You can have an undo manager, but I have found it unnecessary, and a bit wasteful of resources. The temporary MOC acts as a temporary scratchpad. Let the user change the data in the temporary MOC, or abandon the changes all together, and start over. Again, it depends on the problem you are trying to solve... I've only needed to use an undo manager on a temporary MOC once. Not a big deal when you design your app for these use cases.Cosignatory

© 2022 - 2024 — McMap. All rights reserved.