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
Xcode 7 Supporting Watch Os1 and Os2
Button Action in Mkannotation View Not Working
Custom Class Clusters in Swift
Tableview Cell How Do We Resize Cell in Swift Along with Image and Label
Creating Thumbnail from Local Video in Swift
Dial a Phone Number with an Access Code Programmatically in iOS
Tracking Multiple (20+) Locations with iOS Geofencing
Set Uitableview's Height to the Height of Its Content with Auto Layout
Add Glowing Effect to an Skspritenode
How to Disable Afnetworking Cache
Split View Controller Must Be Root View Controller
Delivery Report of Sending Remote Push Notifications Using Apns - iOS
How to Get the Incoming Call Number by Using Callkit
How to Add Older Version of iOS Sdk in Xcode 4.5
Uicollectionview Insert Cells Above Maintaining Position (Like Messages.App)