xcodecore-graphicsnsundomanager

Add an undo/redo method to core graphic draw app


I am trying to implement an undo/redo method using NSUndoManager. I have asked other questions on this, but am still stuck.

Where I am at the moment is as follows:

.h
NSUndoManager *undoManager;
@property(nonatomic,retain) NSUndoManager *undoManager;

.m
@synthesize undoManager;

[undoManager setLevelsOfUndo:99]; viewdidload:

NSNotificationCenter *dnc = [NSNotificationCenter defaultCenter];      
[dnc addObserver:self selector:@selector(undoButtonTapped) name:@"undo" object:nil];   
[dnc addObserver:self selector:@selector(redoButtonTapped) name:@"redo" object:nil];

- (void)resetTheImage:(UIImage*)image
{
    NSLog(@"%s", __FUNCTION__);

   // image = savedImage.image;
    if (image != drawImage.image)
    {
        [[undoManager prepareWithInvocationTarget:self] resetTheImage];
        image = drawImage.image ;        
    } else {
        NSLog(@"That didn't work");
    }
}

- (void)undoButtonTapped {
    NSLog(@"%s", __FUNCTION__);
    [undoManager undo];
}

I get "That didn't work"...

I would appreciate help. I will post the answer to my original question when I figure out what I'm doing wrong.

---EDIT---

I have changed resetTheImage as follows:

- (void)resetTheImage:(UIImage*)image
{
    NSLog(@"%s", __FUNCTION__);

    image = savedImage.image;
    if (image != drawImage.image)
    {
        drawImage.image = image;
        savedImage.image = image;
        NSLog(@"undo image");
        [[self.undoManager prepareWithInvocationTarget:drawImage.image] image];        

    } else {
        NSLog(@"same image");
        savedImage.image = image;
    }
}

However, confusion rains - it may help if someone (Brad?, Justin?) can provide a bullet point list of the steps I need to take to get this working. For example:

. Add notifications in ... . Trigger notifications.... . Have undo /redo buttons point to... . What methods/functions I really need to build.. ....

I wish SO would allow me to give you more points than 1..

(It doesn't help that my "o" key is getting wonky) It does help that I had a baby granddaughter yesterday :))))


Solution

  • 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;
    }