Roberto Roberto - 4 months ago 27
iOS Question

Draw and Erase Lines with Touch

I have a background image (ImageBG) and a foreground image (ImageFG) in separate UIImageViews. They are the same size and, as the name implies, ImageBG is behind ImageFG so you can't see ImageBG.

If the user starts to "draw" on them, instead of a line appearing where the user touches the screen, I want that portion of ImageFG to become transparent and reveal ImageBG.

The closest thing I can think of is a scratch-and-win.

I know how to do the drawing on a graphics context, but when I draw a transparent line (alpha = 0), well... I get a transparent line on top of whatever is currently in my graphics context, so basically nothing is drawn. This is what I use to set the stroke color.

CGContextSetRGBStrokeColor(UIGraphicsGetCurrentContext(), 1.0, 0.0, 0.0, 0.0)


What am I doing wrong? I know this is possible because there are apps out there that do it.

Any hints, tips or direction is appreciated.

Answer

So you have a layer that is some image like the grey stuff you want to scratch off, you write this image (in this example scratchImage which is a UIImageView and its UIImage .image )to the graphics context. You then stroke it with the blend mode set to kCGBlendModeClear then you save that image (with the cleared paths). This will reveal an image that you have underneath perhaps in another UIImageView. Note that in this instance, self is a UIView.

So outside touchesMoved create a variable to hold a CGPoint

CGPoint previousPoint;
CGPoint currentPoint;

Then in touchesBegan, set it to the previous point.

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [touches anyObject];

previousPoint = [touch locationInView:self];

}

In touchesMoved, write the image to the context, stroke the image with the clear blend mode, save the image from the context, and set the previous point to the current point.

- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {



UITouch *touch = [touches anyObject];   
currentPoint = [touch locationInView:self];

 UIGraphicsBeginImageContext(self.frame.size);
//This may not be necessary if you are just erasing, in my case I am 
//adding lines repeatedly to an image so you may want to experiment with moving 
//this to touchesBegan or something. Which of course would also require the begin
//graphics context etc.

[scratchImage.image drawInRect:CGRectMake(0, 0, self.frame.size.width, self.frame.size.height)]; 

CGContextSaveGState(UIGraphicsGetCurrentContext());
CGContextSetShouldAntialias(UIGraphicsGetCurrentContext(), YES); 
CGContextSetLineCap(UIGraphicsGetCurrentContext(), kCGLineCapRound);
CGContextSetLineWidth(UIGraphicsGetCurrentContext(), 5.0);
CGContextSetRGBStrokeColor(UIGraphicsGetCurrentContext(), 0.25, 0.25, 0.25, 1.0);
CGMutablePathRef path = CGPathCreateMutable();
CGPathMoveToPoint(path, nil, previousPoint.x, previousPoint.y);
CGPathAddLineToPoint(path, nil, currentPoint.x, currentPoint.y);
CGContextSetBlendMode(UIGraphicsGetCurrentContext(), kCGBlendModeClear);    
CGContextAddPath(UIGraphicsGetCurrentContext(), path);
CGContextStrokePath(UIGraphicsGetCurrentContext());
scratchImage.image = UIGraphicsGetImageFromCurrentImageContext();
CGContextRestoreGState(UIGraphicsGetCurrentContext());
UIGraphicsEndImageContext();
previousPoint = currentPoint;
}