Doing Undo and Redo with Cglayer Drawing

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.

Add an undo/redo method to core graphic draw app

First, I want to thank everyone for any/all assistance. I solved this finally although I'm not sure if it's the best solution.

I made a UIView called from the UIViewController. The controls (colors and brushes) remain in the VC. The drawing methods move to the View Method.

The View Method calls a Drawing method to actually perform the draw, and the View method controls the undo/redo.

Here are some code snippets:

-(void)undoButtonClicked
{
//NSLog(@"%s", __FUNCTION__);
if ([self.currentArray count] == 0) {
//nothing to undo
return;
}

DrawingPath *undonePath = [self.currentArray lastObject];
[self.currentArray removeLastObject];
[self.redoStack addObject:undonePath];
[self setNeedsDisplay];

}

-(void)redoButtonClicked
{
//NSLog(@"%s", __FUNCTION__);

if ([self.redoStack count] == 0) {
// nothing to redo
return;
}

DrawingPath *redonePath = [self.redoStack lastObject];
[self.redoStack removeLastObject];
[self.currentArray addObject:redonePath];
[self setNeedsDisplay];

}

Let me know if anyone wants clarification. Thanks all again..

UPDATE as requested:

These are some headers:

    DrawingViewController  *mvc;
NSMutableArray *pathArray;
NSMutableArray *colorArray;
NSMutableArray *bufferArray;
NSMutableArray *currentArray;
UIBezierPath *myPath;
NSString *brushSize;
CGPoint lastPoint;
int colorIndex;
NSString *colorKey;

SoundEffect *erasingSound;
SoundEffect *selectSound;

BOOL swiped;
int moved;
UIColor *currentColor;
NSString *result;
}
@property(nonatomic,assign) NSInteger undoSteps;
@property (strong, nonatomic) NSString *result;

@property (strong,nonatomic) UIColor *currentColor;
@property (strong,nonatomic) NSMutableArray *currentArray;
@property (strong,nonatomic) NSMutableArray *bufferArray;
@property (strong,nonatomic) DrawingPath *currentColoredPath;
@property (strong,nonatomic) NSMutableArray *redoStack;
@property (strong, nonatomic) NSString *colorKey;

and here are some more of the methods.. The currentArray then keeps track of points, brush and color in a sort of stack. Undo removes from the stack, and adds into a temp stack that can be used to Redo.

-(void)undoButtonClicked
{
//NSLog(@"%s", __FUNCTION__);
if ([self.currentArray count] == 0) {
//nothing to undo
return;
}

DrawingPath *undonePath = [self.currentArray lastObject];
[self.currentArray removeLastObject];
[self.redoStack addObject:undonePath];
[self setNeedsDisplay];

}

-(void)redoButtonClicked
{
//NSLog(@"%s", __FUNCTION__);

if ([self.redoStack count] == 0) {
// nothing to redo
return;
}

DrawingPath *redonePath = [self.redoStack lastObject];
[self.redoStack removeLastObject];
[self.currentArray addObject:redonePath];
[self setNeedsDisplay];

}

#pragma mark - Touch Methods
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
//NSLog(@"%s", __FUNCTION__);
self.currentColoredPath = [[DrawingPath alloc] init];
[self.currentColoredPath setColor:self.currentColor];
UITouch *touch= [touches anyObject];

[self.currentColoredPath.path moveToPoint:[touch locationInView:self]];
[self.currentArray addObject:self.currentColoredPath];
// Remove all paths from redo stack
[self.redoStack removeAllObjects];

lastPoint = [touch locationInView:self];
lastPoint.y -= 20;

if ([touch tapCount] == 2) {
[self alertOKCancelAction];

return;
}

}

-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
//NSLog(@"%s", __FUNCTION__);
UITouch *touch = [touches anyObject];
[self.currentColoredPath.path addLineToPoint:[touch locationInView:self]];

[self setNeedsDisplay];

CGPoint currentPoint = [touch locationInView:self];
currentPoint.y -= 20;

lastPoint = currentPoint;

}

-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
//NSLog(@"%s", __FUNCTION__);
self.currentColoredPath = nil;
}

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).



Related Topics



Leave a reply



Submit