Clear UIView of drawing? - ios

I am using a subclass of UIView to draw, this subclassed view is used to get your signature on a view controller. There is a clear button which is supposed to clear the UIView except it doesn't work. Here is what I have tried.
subclass.h
#implementation subclassed uiview
{
UIBezierPath *path;
UIImage *incrementalImage; // (1)
}
- (id)initWithCoder:(NSCoder *)aDecoder {
if (self = [super initWithCoder:aDecoder]) {
[self setMultipleTouchEnabled:NO];
[self setBackgroundColor:[UIColor clearColor]];
path = [UIBezierPath bezierPath];
[path setLineWidth:2.0];
}
return self;
}
- (void)drawRect:(CGRect)rect {
[incrementalImage drawInRect:rect]; // (3)
[path stroke];
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [touches anyObject];
CGPoint p = [touch locationInView:self];
[path moveToPoint:p];
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [touches anyObject];
CGPoint p = [touch locationInView:self];
[path addLineToPoint:p];
[self setNeedsDisplay];
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event // (2)
{
UITouch *touch = [touches anyObject];
CGPoint p = [touch locationInView:self];
[path addLineToPoint:p];
[self drawBitmap]; // (3)
[self setNeedsDisplay];
[path removeAllPoints]; //(4)
}
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event {
[self touchesEnded:touches withEvent:event];
}
- (void)drawBitmap // (3)
{
UIGraphicsBeginImageContextWithOptions(self.bounds.size, NO, 0.0);
//[[UIColor blackColor] setStroke];
if (!incrementalImage) { // first draw;
UIBezierPath *rectpath = [UIBezierPath bezierPathWithRect:self.bounds]; // enclosing bitmap by a rectangle defined by another UIBezierPath object
[[UIColor clearColor] setFill];
[rectpath fill]; // fill it
}
[incrementalImage drawAtPoint:CGPointZero];
[path stroke];
incrementalImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
}
#end
view controller.m
- (IBAction)clearTapped:(id)sender {
self.subclassedView.backgroundColor = [UIColor clearColor];
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetFillColorWithColor(context, [UIColor clearColor].CGColor);
CGContextFillRect(context,self.subclassedView.bounds);
CGContextClearRect(context, self.subclassedView.bounds);
CGContextFlush(context);
[self.subclassedView setNeedsDisplay];
}
Can anyone tell me why this doesn't work, and what I should do instead?

Reinitialise the path variable and force the view to draw with this newly created empty path. This gives impression of erasing the previous drawn path. In your subclass include this function,
- (void)erase {
path = nil; //Set current path nil
path = [UIBezierPath bezierPath]; //Create new path
[self setNeedsDisplay];
}
Declare this method in the .h file of your subclass. From your view controller class you need to call this method whenever you need to erase the view,
- (IBAction)clearTapped:(id)sender {
[self.subclassedView erase];
}
Try it out.
Hope that helps!

IT will surely work.. guaranteed...
addition to answer by #Amar and comment by #Josue... thanx to you both
- (void)erase {
path = nil; //Set current path nil
path = [UIBezierPath bezierPath]; //Create new path
incrementalImage = nil;
[self setNeedsDisplay];
}

Related

How to remove UIBezierPath from UIView on button click?

I am using following code to create a digital signature on UIView:
#import "Signature.h"
#implementation Signature {
UIBezierPath *path;
UIImage *incrementalImage;
CGPoint pts[5]; // we now need to keep track of the four points of a Bezier segment and the first control point of the next segment
uint ctr;
}
- (id)initWithCoder:(NSCoder *)aDecoder
{
if (self = [super initWithCoder:aDecoder])
{
[self setMultipleTouchEnabled:NO];
[self setBackgroundColor:[UIColor greenColor]];
path = [UIBezierPath bezierPath];
[path setLineWidth:2.0];
}
return self;
}
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
[self setMultipleTouchEnabled:NO];
path = [UIBezierPath bezierPath];
[path setLineWidth:2.0];
}
return self;
}
- (void)drawRect:(CGRect)rect
{
[incrementalImage drawInRect:rect];
[path stroke];
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
ctr = 0;
UITouch *touch = [touches anyObject];
pts[0] = [touch locationInView:self];
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [touches anyObject];
CGPoint p = [touch locationInView:self];
ctr++;
pts[ctr] = p;
if (ctr == 4)
{
pts[3] = CGPointMake((pts[2].x + pts[4].x)/2.0, (pts[2].y + pts[4].y)/2.0); // move the endpoint to the middle of the line joining the second control point of the first Bezier segment and the first control point of the second Bezier segment
[path moveToPoint:pts[0]];
[path addCurveToPoint:pts[3] controlPoint1:pts[1] controlPoint2:pts[2]]; // add a cubic Bezier from pt[0] to pt[3], with control points pt[1] and pt[2]
[self setNeedsDisplay];
// replace points and get ready to handle the next segment
pts[0] = pts[3];
pts[1] = pts[4];
ctr = 1;
}
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
[self drawBitmap];
[self setNeedsDisplay];
[path removeAllPoints];
ctr = 0;
}
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
{
NSLog(#"hello");
[self touchesEnded:touches withEvent:event];
}
- (void)drawBitmap
{
UIGraphicsBeginImageContextWithOptions(self.bounds.size, YES, 0.0);
if (!incrementalImage) // first time; paint background white
{
UIBezierPath *rectpath = [UIBezierPath bezierPathWithRect:self.bounds];
[[UIColor greenColor] setFill];
[rectpath fill];
}
[incrementalImage drawAtPoint:CGPointZero];
[[UIColor blackColor] setStroke];
[path stroke];
incrementalImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
}
- (void)erase {
NSLog(#"Erase Testing...");
// self->path = nil; //Set current path nil
//path = [UIBezierPath bezierPath];
// [self setNeedsDisplay];
}
I am calling erase method on button click but it's not working for me. I don't know why it's not responding.
sandeep tomar,
Trying to remove the already drawn UIBezierPath from UIView can be bit cumbersome though absolutely possible.
But I believe the simplest solution would be not trying to remove it and just make it invisible :)
Assume you are drawing on a white canvas then when you want to remove the existing UIBezierPath just stroke it again but this time with white color :)
You can never see a white line drawn on white canvas can you ??? :)
[[UIColor whiteColor] setStroke];
[path stroke];
EDIT
If you want to clear everything in one shot then this might help you :)
- (void)erase {
incrementalImage = nil;
path = nil; //Set current path nil
path = [UIBezierPath bezierPath];
[self setNeedsDisplay];
}
Try this and lemme know if you still have issues.
Hope it helps :)
try this
- (void)erase {
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextClearRect(context, self.bounds);
}

How will i remove UIBezierPath from the uiview on button click.?

I am working on digital signature on uiview. i create it normaly by this code, but i am not able to remove bezier path on button click.i am sharing my code PLease look at my code.
#import "Signature.h"
#implementation Signature {
UIBezierPath *path;
UIImage *incrementalImage;
CGPoint pts[5]; // we now need to keep track of the four points of a Bezier segment and the first control point of the next segment
uint ctr;
}
- (id)initWithCoder:(NSCoder *)aDecoder
{
if (self = [super initWithCoder:aDecoder])
{
[self setMultipleTouchEnabled:NO];
[self setBackgroundColor:[UIColor greenColor]];
path = [UIBezierPath bezierPath];
[path setLineWidth:2.0];
}
return self;
}
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
[self setMultipleTouchEnabled:NO];
path = [UIBezierPath bezierPath];
[path setLineWidth:2.0];
}
return self;
}
- (void)drawRect:(CGRect)rect
{
[incrementalImage drawInRect:rect];
[path stroke];
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
ctr = 0;
UITouch *touch = [touches anyObject];
pts[0] = [touch locationInView:self];
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [touches anyObject];
CGPoint p = [touch locationInView:self];
ctr++;
pts[ctr] = p;
if (ctr == 4)
{
pts[3] = CGPointMake((pts[2].x + pts[4].x)/2.0, (pts[2].y + pts[4].y)/2.0); // move the endpoint to the middle of the line joining the second control point of the first Bezier segment and the first control point of the second Bezier segment
[path moveToPoint:pts[0]];
[path addCurveToPoint:pts[3] controlPoint1:pts[1] controlPoint2:pts[2]]; // add a cubic Bezier from pt[0] to pt[3], with control points pt[1] and pt[2]
[self setNeedsDisplay];
// replace points and get ready to handle the next segment
pts[0] = pts[3];
pts[1] = pts[4];
ctr = 1;
}
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
[self drawBitmap];
[self setNeedsDisplay];
[path removeAllPoints];
ctr = 0;
}
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
{
NSLog(#"hello");
[self touchesEnded:touches withEvent:event];
}
- (void)drawBitmap
{
UIGraphicsBeginImageContextWithOptions(self.bounds.size, YES, 0.0);
if (!incrementalImage) // first time; paint background white
{
UIBezierPath *rectpath = [UIBezierPath bezierPathWithRect:self.bounds];
[[UIColor greenColor] setFill];
[rectpath fill];
}
[incrementalImage drawAtPoint:CGPointZero];
[[UIColor blackColor] setStroke];
[path stroke];
incrementalImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
}
- (void)erase {
NSLog(#"Erase Testing...");
// self->path = nil; //Set current path nil
//path = [UIBezierPath bezierPath];
// [self setNeedsDisplay];
}
I call erase method on unbutton click from another class, but I am not able to remove uibezeripath.
Try using this to remove the UIBezierPath,
[path removeAllPoints];
If you use CAShapeLayer *rectLayer = [[CAShapeLayer alloc] init]; aswell then call this line too,
[rectLayer removeFromSuperlayer];
Check how i am using this in one of my project if it can help,
- (void)drawRect:(CGRect)rect {
[_path stroke];
}
- (id)initWithFrame:(CGRect)frame{
self = [super initWithFrame: frame];
if (self) {
// set Multiple touches Enabled to false for allow only single touch.
[self setMultipleTouchEnabled: NO];
_path = [UIBezierPath bezierPath];
// set Line width.
[_path setLineWidth:2.0];
}
return self;
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
isMouseswipped = NO; //for touches eneded to make dot
ctr = 0;
UITouch *myTouch = [touches anyObject];
pts[0] = [myTouch locationInView: self];
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
isMouseswipped = YES; //for touches eneded to make dot
CustomSignatureViewController *csvc = [[CustomSignatureViewController alloc]init];
UITouch *touch = [touches anyObject];
CGPoint p = [touch locationInView: self];
ctr++;
pts[ctr] = p;
if (ctr == 4) {
pts[3] = CGPointMake((pts[2].x + pts[4].x)/2.0, (pts[2].y + pts[4].y)/2.0);
[_path moveToPoint: pts[0]];
[_path addCurveToPoint: pts[3] controlPoint1:pts[1] controlPoint2:pts[2]];
[self setNeedsDisplay];
pts[0] = pts[3];
pts[1] = pts[4];
ctr = 1;
csvc.status = 1;
self.status = 1;
}
}
- (void)erase {
_path = nil;
_path = [UIBezierPath bezierPath];
[_path setLineWidth:2.0];
[self setNeedsDisplay];
}
This is custom class which returns uiview to fraw signature. It is subclass of UIView.
So when required signature, i import this class and instantiate it and got uiview object on which i can draw. then capture that view as image using graphics beginimage context!
Update :
In viewcontroller i am calling method like,
-(void)clear : (id)sender{
[signView erase];
signView.status = 0;
}
signView is signature class(as mentioned above)'s object. and erase is public method declared in .h file of signature class. (note : signature class is subclass of uiview so intance of it returns view onject so signView is uiview object)
Hope this will help :)
use this code it will help you
- (void)resetPath {
path = nil;
path = [UIBezierPath bezierPath];
[self setNeedsDisplay];
}
create this method in .h file of your subclass. From your UIViewController class you call this method whenever you need it
- (IBAction)youraction:(id)sender
{
[self.subclassedView resetPath];
}

How we erase Line

I want to make erase button on click we erase line on moving the line . But I am Unable to do it. I am successfully implemented of code of create d Line. But stuck how to erase Line. I am newer in iOS. Here is my code. Please help
#implementation Line
{
UIBezierPath *path;
UIImage *incrementalImage; // (1)
}
- (id)initWithCoder:(NSCoder *)aDecoder
{
if (self = [super initWithCoder:aDecoder])
{
[self setMultipleTouchEnabled:NO];
[self setBackgroundColor:[UIColor whiteColor]];
path = [UIBezierPath bezierPath];
[path setLineWidth:2.0];
}
return self;
}
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
[self setMultipleTouchEnabled:NO];
path = [UIBezierPath bezierPath];
[path setLineWidth:1.0];
self.backgroundColor=[UIColor clearColor];
_button = [UIButton buttonWithType:UIButtonTypeCustom];
[_button setTitle:#"" forState:UIControlStateNormal];
_button.userInteractionEnabled=YES;
_button.frame =CGRectMake(100, 130, 100, 100);
_button.backgroundColor =[ UIColor redColor];
[_button addTarget:self
action:#selector(erase)
forControlEvents:UIControlEventTouchUpInside];
[self addSubview:_button];
}
return self;
}
- (void)drawRect:(CGRect)rect
{
[incrementalImage drawInRect:rect]; // (3)
[path stroke];
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [touches anyObject];
CGPoint p = [touch locationInView:self];
[path moveToPoint:p];
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [touches anyObject];
CGPoint p = [touch locationInView:self];
[path addLineToPoint:p];
[self setNeedsDisplay];
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event // (2)
{
UITouch *touch = [touches anyObject];
CGPoint p = [touch locationInView:self];
[path addLineToPoint:p];
[self drawBitmap]; // (3)
[self setNeedsDisplay];
[path removeAllPoints]; //(4)
}
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
{
[self touchesEnded:touches withEvent:event];
}
- (void)drawBitmap // (3)
{
UIGraphicsBeginImageContextWithOptions(self.bounds.size, YES, 0.0);
[[UIColor blackColor] setStroke];
if (!incrementalImage) // first draw; paint background white by ...
{
UIBezierPath *rectpath = [UIBezierPath bezierPathWithRect:self.bounds]; // enclosing bitmap by a rectangle defined by another UIBezierPath object
[[UIColor whiteColor] setFill];
[rectpath fill]; // filling it with white
}
[incrementalImage drawAtPoint:CGPointZero];
[path stroke];
incrementalImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
}
-(void)erase
{
NSLog(#"erasingLIne");
}
Take a BOOL value for erase. BOOL isErase;
when erase button tapped
if button tapped
{
isErase =Yes;
}else
{
isErase = NO;
}
[path strokeWithBlendMode:isErase?kCGBlendModeClear:kCGBlendModeNormal
alpha:1.0f];
Hope it works for you.

How to create two strokes of different color using one UIBezierPath

I am trying to draw lines on a UIView with finger and it worked fine with one color. If I try to change the color and draw again, the previous UIBezierPath color is also changed to new color. So, I am unable to draw a different color line keeping the previous color line on the UIView
I set all the properties(path,linecolor) as nonatomic and strong in my UIView
For reference:
my first draw:
my second draw:
my third draw:
After I choose the color, I change the stroke color of my UIView in color picker delegate method:
#pragma mark - FCColorPickerViewControllerDelegate Methods
-(void)colorPickerViewController:(FCColorPickerViewController *)colorPicker didSelectColor:(UIColor *)color {
self.drawView.lineColor = color; //this works fine
// self.drawView.path=[UIBezierPath bezierPath]; tried this to create new bezier path with new color, but this erases the olde bezier path and return new
// [self.drawView.lineColor setStroke]; tried this
// [self.drawView.lineColor setFill]; tried this
[self dismissViewControllerAnimated:YES completion:nil]; //dismiss the color picker
}
Here are my view methods for drawing:
- (id)initWithCoder:(NSCoder *)aDecoder
{
if (self = [super initWithCoder:aDecoder])
{
[self setBackgroundColor:[UIColor whiteColor]];
path = [UIBezierPath bezierPath];
[path setLineWidth:self.lineWidth];
}
return self;
}
- (void)drawRect:(CGRect)frame
{
[self.lineColor setStroke];
[path stroke];
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
dispatch_async(dispatch_get_main_queue(), ^{
UITouch *touch = [touches anyObject];
CGPoint p = [touch locationInView:self];
[path moveToPoint:p];
});
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
dispatch_async(dispatch_get_main_queue(), ^{
UITouch *touch = [touches anyObject];
CGPoint p = [touch locationInView:self];
[path addLineToPoint:p];
[self setNeedsDisplay];
});
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
[self touchesMoved:touches withEvent:event];
}
I tried this: adding the old bezier paths to the array and redraw them, but it didnt work , this time I cant make another bezier path with new color.:
- (void)drawRect:(CGRect)frame // (5)
{
//load the path from array
for (int i = 0; i < [pathArray count]; i++){
NSLog(#"Path: %#",[pathArray objectAtIndex:0]);
NSLog(#"Color: %#",[pathArray objectAtIndex:1]);
UIBezierPath *oldpath = [pathArray objectAtIndex:0];
//color
[[pathArray objectAtIndex:1] setStroke];
//path
[oldpath stroke];
}
UIBezierPath *newPath = [self pathForCurrentLine];
if (newPath)
{
// set the width, color, etc, too, if you want
[lineColor setStroke];
[newPath stroke];
}
}
- (UIBezierPath*)pathForCurrentLine {
if (CGPointEqualToPoint(startPoint, CGPointZero) && CGPointEqualToPoint(endPoint, CGPointZero)){
return nil;
}
UIBezierPath *newpath = [UIBezierPath bezierPath];
[newpath moveToPoint:startPoint];
[newpath addLineToPoint:endPoint];
return newpath;
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
dispatch_async(dispatch_get_main_queue(), ^{
UITouch *touch = [touches anyObject];
CGPoint p = [touch locationInView:self];
[path moveToPoint:p];
startPoint=p;
});
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
dispatch_async(dispatch_get_main_queue(), ^{
UITouch *touch = [touches anyObject];
CGPoint p = [touch locationInView:self];
[path addLineToPoint:p]; // (4)
endPoint=p;
[self setNeedsDisplay];
});
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
[self touchesMoved:touches withEvent:event];
[pathArray addObject:path];
[pathArray addObject:lineColor];
}
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
{
[self touchesEnded:touches withEvent:event];
}
You actually can't stroke different parts of the same path in different colors.
You can think of the drawing context as a state machine. Each time you issue a draw command (such as calling stroke on a UIBezierPath) it will check the current stroke/fill color and use that to perform the draw. To create multiple strokes in different colors you would need to have multiple paths to stroke, and set the stroke color in between each invocation of stroke.
- (void)drawRect:(CGRect)frame {
UIBezierPath* pathOne = // create path one
UIBezierPath* pathTwo = // create path two
UIBezierPath* pathThree = // create path three
[[UIColor redColor] setStroke];
[pathOne stroke];
[[UIColor greenColor] setStroke];
[pathTwo stroke];
[[UIColor blueColor] setStroke];
[pathThree stroke];
}

Black background in UIView?

I followed a tutorial online to draw in a subclassed UIView. The tutorial showed a UIView with a white background, I fixed this by simply changing the super's bg color. The problem is, when touches end, the background does not remain clear. I have no idea. I simply tried setting the fill color to [uicolor clearcolor]; unsuccessfully. Here is the code I am using:
#implementation CachedLIView
{
UIBezierPath *path;
UIImage *incrementalImage; // (1)
}
- (id)initWithCoder:(NSCoder *)aDecoder
{
if (self = [super initWithCoder:aDecoder])
{
[self setMultipleTouchEnabled:NO];
[self setBackgroundColor:[UIColor whiteColor]];
path = [UIBezierPath bezierPath];
[path setLineWidth:2.0];
}
return self;
}
- (void)drawRect:(CGRect)rect
{
[incrementalImage drawInRect:rect]; // (3)
[path stroke];
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [touches anyObject];
CGPoint p = [touch locationInView:self];
[path moveToPoint:p];
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [touches anyObject];
CGPoint p = [touch locationInView:self];
[path addLineToPoint:p];
[self setNeedsDisplay];
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event // (2)
{
UITouch *touch = [touches anyObject];
CGPoint p = [touch locationInView:self];
[path addLineToPoint:p];
[self drawBitmap]; // (3)
[self setNeedsDisplay];
[path removeAllPoints]; //(4)
}
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
{
[self touchesEnded:touches withEvent:event];
}
- (void)drawBitmap // (3)
{
UIGraphicsBeginImageContextWithOptions(self.bounds.size, NO, 0.0);
[[UIColor blackColor] setStroke];
if (!incrementalImage) // first draw; paint background white by ...
{
UIBezierPath *rectpath = [UIBezierPath bezierPathWithRect:self.bounds]; // enclosing bitmap by a rectangle defined by another UIBezierPath object
[[UIColor clearColor] setFill];
[rectpath fill]; // filling it with white
}
[incrementalImage drawAtPoint:CGPointZero];
[path stroke];
incrementalImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
}
Put the following in your view -(id) initWithFrame method:
self = [super initWithFrame:frame];
if (self) {
self.backgroundColor = [UIColor clearColor];
}
return self;
That way your view subclass will have a transparent background = no black when drawing using bezier, CGContext… methods or anything else.

Resources