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 UIImage
s (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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With