UIBezierPath undo drawing redraw UIImageView's

2019-05-23 01:33发布

问题:

I'm trying to redraw my UIImageView's image by redrawing all UIBezierPaths and associated UIColors in an NSMutableArray I have created, minus the last path when I tap an undo UIButton.

However, the UIImageView's image doesn't redraw and remove the last path in this case, until the NSMutableArray's count is less than 2, in which it sets the UIImageView's image to nil (this is working correctly)

I'd appreciate any help offered. Note: I'm not including my touchesMoved method because I just want to get undoing 'dots' to work for now.

**

Update: here is the code I ended up using, with the help of the accepted answer below:

**

- (void)undoLast
{
    if (self.drawingPathArray.count < 2)
    {
        self.drawingPathArray = nil;
        self.undoButton.hidden = YES;
        self.drawingImageView.image = nil;
    }
    else
    {
        [self.drawingPathArray removeLastObject];

        self.undoButton.tintColor = [[self.drawingPathArray lastObject] objectAtIndex:0];

        UIGraphicsBeginImageContextWithOptions(self.drawingImageView.bounds.size, NO, 0.0);

        for (NSArray *subArray in self.drawingPathArray)
        {
            CGContextRef context = UIGraphicsGetCurrentContext();
            CGContextSetLineWidth(context, 3.0);
            CGContextSetLineCap(context, kCGLineCapRound);
            CGContextSetStrokeColorWithColor(context, [[subArray objectAtIndex:0] CGColor]);
            CGContextAddPath(context, [[subArray objectAtIndex:1] CGPath]);
            CGContextStrokePath(context);
        }

        self.drawingImageView.image = UIGraphicsGetImageFromCurrentImageContext();
        UIGraphicsEndImageContext();
    }
}

Original question's code:

@property (nonatomic, strong) NSMutableArray *drawingPathArray;
@property (nonatomic, assign) NSInteger ctr;
@property (nonatomic, assign) CGPoint lastPoint;
CGPoint pts[4];

- (void)undoLast
{
    if (self.drawingPathArray.count < 2)
    {
        self.drawingPathArray = nil;
        self.undoButton.hidden = YES;
        self.drawingImageView.image = nil;
    }
    else
    {
        [self.drawingPathArray removeLastObject];

        NSArray *lastArray = [self.drawingPathArray lastObject];

        UIColor *lastColor = [lastArray objectAtIndex:0];

        self.undoButton.tintColor = lastColor;

        for (NSArray *subArray in self.drawingPathArray)
        {
            // These colors and paths are getting set correctly, verified by using NSLog
            UIColor *color = [subArray objectAtIndex:0];
            UIBezierPath *path = [subArray objectAtIndex:1];

            [self drawWithPath:path andColor:color];
        }
    }
}

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{           
    self.ctr = 0;
    UITouch *touch = [touches anyObject];
    pts[0] = [touch locationInView:self.drawingImageView];
}

- (void)drawWithPath:(UIBezierPath *)path andColor:(UIColor *)color
{
    path.lineWidth = 3.0;
    path.lineCapStyle = kCGLineJoinRound;

    dispatch_async(dispatch_get_main_queue(),
    ^{
        UIGraphicsBeginImageContextWithOptions(self.drawingImageView.bounds.size, NO, 0.0);

        [self.drawingImageView.image drawAtPoint:CGPointZero];
        [color setStroke];
        [path stroke];
        self.drawingImageView.image = UIGraphicsGetImageFromCurrentImageContext();
        UIGraphicsEndImageContext();
    });
}

- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
    UIBezierPath *path = [UIBezierPath bezierPath];

    if (self.ctr == 0)
    {
        [path moveToPoint:pts[0]];
        [path addLineToPoint:pts[0]];
    }

    self.undoButton.hidden = NO;
    [self.undoButton setTintColor:self.inkColor];

     if (!self.drawingPathArray)
     {
        self.drawingPathArray = [[NSMutableArray alloc]init];
     }

     [self.drawingPathArray addObject:@[self.inkColor,path]];

     [self drawWithPath:path andColor:self.inkColor];
}

回答1:

You don't clear self.drawingImageView.image before you start doing your redraw of the remaining paths. So, all you're currently doing is redrawing the lines over the old lines.

What you should do is:

  1. Create a single new context and draw all of the lines into it
  2. Don't draw the old image into this new context
  3. Don't use GCD to queue updates to this context, just do it inline (in the background if you want)

then, at the end, get the new image from the context (image = UIGraphicsGetImageFromCurrentImageContext()) and save it in self.drawingImageView.image = image;


Pseudo code:

// create a new bitmap image context
UIGraphicsBeginImageContextWithOptions(...
ctx = UIGraphicsGetCurrentContext();

for (NSArray *subArray in self.drawingPathArray) {
    // get the path and colour
    CGContextSetStrokeColor(ctx, ...
    CGContextAddPath(ctx, ... (path from bezier)
    CGContextStrokePath(ctx);
}

// create image
image = UIGraphicsGetImageFromCurrentImageContext();

self.drawingImageView.image = image;