views:

606

answers:

2

My app doesn't use Core Data yet. Is it true that I must use Core Data for undo/redo?

And: How does the user do the undo/redo? I've never seen it in action, and never ever used it. Don't know how I should do it if I wanted to. There's no undo/redo button anywhere. Yet they say it has undo/redo. So how does the user trigger this?

A: 

"shakeToEdit" is the idea on the iPhone to trigger the undo/redo alert view.
Have a look here. You don't need to use Core Data, Core Data just makes stuff easier. If you want to implement it have a look here.

bddckr
+11  A: 

iPhone OS 3.0 brought over the concept of NSUndoManager from the Mac, which is what enables undo on the iPhone. NSUndoManager maintains a stack of NSInvocations which are the opposite actions to any edits or other changes you make. For example,

- (void)observeValueForKeyPath:(NSString*)keyPath
                      ofObject:(id)object
                        change:(NSDictionary*)change
                       context:(void*)context
{
    NSUndoManager *undo = [self undoManager];
    // Grab the old value of the key
    id oldValue = [change objectForKey:NSKeyValueChangeOldKey];
    // Add edit item to the undo stack
    [[undo prepareWithInvocationTarget:self] changeKeyPath:keyPath 
                                                  ofObject:object 
                                                   toValue:oldValue];
    // Set the undo action name in the menu
    [undo setActionName:@"Edit"];
}

can be used to observe changes in properties, creating inverse NSInvocations that will undo edits to those properties.

Core Data is not needed for undo, but it makes it much, much easier. It handles the creation of these undo actions for you every time you edit your data model, including complex actions like a cascading delete down a hierarchy of managed objects.

On the iPhone, to enable undo / redo, you need to set up a few things. First, NSManagedObjectContexts on the iPhone don't have an undo manager by default, so you need to create one:

NSUndoManager *contextUndoManager = [[NSUndoManager alloc] init];
[contextUndoManager setLevelsOfUndo:10];
[managedObjectContext setUndoManager:contextUndoManager];
[contextUndoManager release];       

This code would typically go right after where you would have created your NSManagedObjectContext.

Once an undo manager is provided for your context, you need to enable the default gesture for undo on the iPhone, a shake of the device. To let your application handle this gesture automatically, place the following code within the -applicationDidFinishLaunching: method in your application delegate:

application.applicationSupportsShakeToEdit = YES;

Finally, you will need to set up each view controller that will be capable of handling the shake gesture for undo. These view controllers will need to report back the undo manager to use for that controller by overriding the -undoManager method:

- (NSUndoManager *)undoManager;
{
    return [[[MyDatabaseController sharedDatabaseController] scratchpadContext] undoManager];
}

The view controllers will also need to be able to become the first responder to handle gestures, so the following method is needed:

- (BOOL)canBecomeFirstResponder 
{
    return YES;
}

The view controller will need to become the first responder when it appears onscreen. This can be done by calling [self becomeFirstResponder] in -loadView or -viewDidLoad, but I have found that view controllers which appear onscreen immediately after launch need to have this message delayed a bit in order for it to work:

[self performSelector:@selector(becomeFirstResponder) withObject:nil afterDelay:0.3];

With all this in place, you should get automatic undo and redo support courtesy of Core Data, with a nice animated menu.

Brad Larson
+1 for damn detailed answer.
bddckr