Undo/Redo for Drawing in iOS

Undo/Redo for drawing in iOS

I have found a solution for this, we need to create a array of array of DrawingPaths:

- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
// Do the above code, then
[m_undoArray addObject:self.currentPath];
}

- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
[m_parentUndoArray addObject:[NSArray arrayWithArray:m_undoArray]];
}

and then stroke the path in DrawRect.

Undo/Redo for iOS Painting App

I have decided to try a hybrid approach, where I save a bitmap snapshot every 10-15 actions, and use command lines to restore individual actions past the snapshots. A more in depth answer is offered here: https://stackoverflow.com/a/3944758/2303367

How to Undo & Redo a smooth line in iPhone?

Use the NSUndoManager. However, if you are painting lines on the canvas, you will also need to keep their representation around as well (so you can pop them off).

So, whether you collect them as a UIBezierPath, or use shape layers, or your own "array of points" you undo in the same manner.

So, while drawing the line, keep a record of the points you used in your drawing. When the drawing is done (e.g., touchesEnded), you want to "push" your drawing, and tell the undo manager how to undo it. Simply, it would be something like this almost-code...

- (void)pushDrawing:(Drawing*)drawing
{
[self.stack push:drawing];
[self.undoManager registerUndoWithTarget: self
selector: @selector(popDrawing)
object: nil];
}

- (void)popDrawing:(Drawing*)drawing
{
Drawing *drawing = [self.stack pop];
[self.undoManager registerUndoWithTarget: self
selector: @selector(pushDrawing:)
object: drawing];
}

If you are using one canvas, you may have to redraw the entire thing, especially when popping a drawing off. If you are using views or layers, you may not...

Look at the docs for NSUndoManager... it's available on iOS, and has good examples. It "remembers" if you you undo-ing or redo-ing, and will do the right thing, so the above could be implemented as one function (but it's easier to understand at first with one function going each direction).

How to undo and redo operations in a drawing application

I put together a little sketching app in Flash a few years ago and implemented undo/redo functionality. I don't know that this is the right way of doing it (probably not), but I can give you a high level view of what I did.

The basic concept is this (like Andy mentioned): Have a stack of recently executed actions. If you can, have your "undo" action truly undo the last action. Put this "undo" action into a different stack, so that you can provide "redo" functionality. When someone clicks "redo", do the same thing but in reverse -- execute the action in the "redo" stack and put that into the other stack. Once someone does something new (i.e., not "redo" or "undo"), get rid of the "redo" stack and start adding to the main stack.

What was interesting for me was that I couldn't figure out a way to truly "undo" an action (i.e., I didn't know how to undraw a stroke). What I did instead was I drew all the strokes up to the last action. As you can imagine, that's not very efficient. I made it more efficient by having stacks of stacks. That way, the latest "undo" action would only have to undo the last stack's worth of actions, instead of the entire history of all the actions.

Hope this helps!

How to undo a line stroke ios

UIViewController's superclass UIResponder has a NSUndoManager property, which can be used here. The above code performs a setting image action for the imageView, to undo the action, you have to keep a reference of the previous image that was set to the imageView. You can make an instance variable called previousImage, in viewDidLoad, set

previousImage = self.tempImage.image;

And here is the function to set image for UIImageView.

- (void)setImage:(UIImage*)currentImage fromImage:(UIImage*)preImage
{
// Prepare undo-redo
[[self.undoManager prepareWithInvocationTarget:self] setImage:preImage fromImage:currentImage];
self.mainImage.image = currentImage;
self.tempImage.image = currentImage;
previousImage = currentImage;
}

In touchesEnded.

UIImage *currentImage = UIGraphicsGetImageFromCurrentImageContext();
[self setImage:currentImage fromImage:previousImage];

When you click a button for undoing.

- (IBAction)btnClicked:(id)sender {
[self.undoManager undo];
}

Edit

The above method will cause memory issues, don't save images in undoManager, save the line path points instead, here is a sample project with undo and redo capabilities.

Undo with multitouch drawing in iOS

m_redoArray appears to be the big daddy, the one you draw from. I don't understand why you empty this out out in 'touchesBegan...', surely one of these arrays must carry through touchesBegan unaltered, or you'll be dropping stuff from the start of your drawing all the way through, no?

It appears to me that this is how you dropped the 'Hell' in your example here..



Related Topics



Leave a reply



Submit