Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Difficulty implementing NSUndoManager redo function

I'm trying to implement an NSUndoManager in my iOS app. I got the undo functionality to work, but not the redo part. I'm quite new to iOS development and this is the first time that I've used NSUndoManager so it's probably something trivial.

My app is a painting/note taking app, I have a undo/redo stack with the last ten UIImages (I don't know if this is the most efficient way) in an array. When the user makes changes to the current image, the old image is pushed onto the stack, and the first image in the array is removed if the array already has ten objects. I have a int instance variable that I use to keep track of objects in the array and make sure that the correct image is displayed. My code looks like this:

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {

    if (oldImagesArrays.count >= 10) {
        [oldImagesArrays removeObjectAtIndex:0];
    }
    UIImage * currentImage = pageView.canvas.image;
    if (currentImage != nil) {
        [oldImagesArrays addObject:currentImage];
        undoRedoStackIndex = oldImagesArrays.count -1;
    }
    [...]
}

- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {

    UIImage * currentImage = [oldImagesArrays lastObject];
    if (currentImage != pageView.canvas.image) {
        [undoManager registerUndoWithTarget:self selector:@selector(resetImage)  
        object:currentImage];
    }
}

// Gets called when the undo button is clicked
- (void)undoDrawing
{
    [undoManager undo];
    [undoManager registerUndoWithTarget:self 
                           selector:@selector(resetImage)
                             object:pageView.canvas.image];
    undoRedoStackIndex--;
}

// Gets called when the redo button is clicked
- (void)redoDrawing
{
    [undoManager redo];
    undoRedoStackIndex++;
}

- (void)resetImage
{
    NSLog(@"Hello"); // This NSLog message only appears when I click undo.
    pageView.canvas.image = [oldImagesArrays objectAtIndex:undoRedoStackIndex];
}

When I click the undo or redo buttons resetImage should get called, and set the current image to the next or previous object in my image stack (the current value of undoRedoStackIndex), this only happens when I click undo, but not redo.

Solutions && || better ways to do it would be appreciated.

like image 903
Anders Avatar asked Nov 23 '11 21:11

Anders


Video Answer


1 Answers

You do not need to keep track of the changes, this is what the undo manager is for.

Make an undoable method like this:

- (void)setImage:(UIImage*)image
{
    if (_image != image)
    {
        [[_undoManager prepareWithInvocationTarget:self] setImage:_image]; // Here we let know the undo managed what image was used before
        [_image release];
        _image = [image retain];

        // post notifications to update UI
    }
}

This is it. To undo the change just call [_undoManager undo], to redo call [_undoManager redo]. When you tell the undo manager to undo it will call this method with the old image. If you use custom buttons for Undo operation you can validate it using [NSUndoManager canUndo], etc.

There is no limit for the number of undo operations. If you need to clean the undo stack at some point just call removeAllActions method.

like image 179
Davyd Geyl Avatar answered Oct 30 '22 19:10

Davyd Geyl