user touch and drag on top of image
i had this code that will give me circle after drawing but how do i space apart them ?
//UIView CircleView
- (void) touchesBegan:(NSSet *) touches withEvent:(UIEvent *) event
{
NSLog(#"started");
// Initialize a new path for the user gesture
self.path = [UIBezierPath bezierPath];
self.path.lineWidth = 4.0f;
self.whiteCircleArray = [[NSMutableArray alloc]init];
UITouch *touch = [touches anyObject];
[self.path moveToPoint:[touch locationInView:self]];
self.touchedEnded = FALSE;
}
- (void) touchesMoved:(NSSet *) touches withEvent:(UIEvent *) event
{
// Add new points to the path
NSLog(#"Moved");
UITouch *touch = [touches anyObject];
//NSLog(#"[touch locationInView:self] %#",NSStringFromCGPoint([touch locationInView:self]));
[self.whiteCircleArray addObject: NSStringFromCGPoint([touch locationInView:self])];
[self.path addLineToPoint:[touch locationInView:self]];
//double startAngle = RADIANS(0);
//double endAngle = RADIANS(360);
// if ([touch locationInView:self].x <=200 )
// {
// [self.path addArcWithCenter:[touch locationInView:self] radius:LARGE_RADIUS startAngle:startAngle endAngle:endAngle clockwise:NO];
// }
// else if ([touch locationInView:self].x >=120 )
// {
// [self.path addArcWithCenter:[touch locationInView:self] radius:SMALL_RADIUS startAngle:startAngle endAngle:endAngle clockwise:NO];
// }
//[self.path addArcWithCenter:[touch locationInView:self] radius:BIG_RADIUS startAngle:startAngle endAngle:endAngle clockwise:NO];
//[self.path addArcWithCenter:[touch locationInView:self] radius:MIDDIUM_RADIUS startAngle:startAngle endAngle:endAngle clockwise:NO];
[self setNeedsDisplay];
}
- (void) touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
NSLog(#"ended");
UITouch *touch = [touches anyObject];
//[self.path addLineToPoint:[touch locationInView:self]];
self.locationOfTouch =[touch locationInView:self];
self.touchedEnded = TRUE;
//NSLog(#"white circle pos %#",self.whiteCircleArray);
for(NSString *p in self.whiteCircleArray)
{
CGPoint point = CGPointFromString(p);
NSLog(#"X=%0.1f Y=%0.1f",point.x,point.x);
}
[self setNeedsDisplay];
}
- (void) touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
{
NSLog(#"cancelled");
[self touchesEnded:touches withEvent:event];
}
-(void)drawTouchCircle
{
CGContextRef ctx= UIGraphicsGetCurrentContext();
CGContextSaveGState(ctx);
CGContextSetLineWidth(ctx,1);
CGContextSetRGBFillColor(ctx, 55.0/255, 255.0/255.0, 5.0/255.0, 1.0);
CGContextSetRGBStrokeColor(ctx,0.0/255,0.0/255,0.0/255,1.0);
for(NSString *p in self.whiteCircleArray)
{
CGPoint point = CGPointFromString(p);
NSLog(#"X=%0.1f Y=%0.1f",point.x,point.x);
CGContextAddArc(ctx, point.x, point.y, LARGE_RADIUS, 0, M_PI*2, YES);
CGContextFillPath(ctx);
CGContextStrokePath(ctx);
}
}
- (void) drawRect:(CGRect)rect
{
// Draw the path
NSLog(#"drawRect");
if (self.touchedEnded)
{
[self drawTouchCircle];
}
else
{
UIColor *strokeColor = [UIColor redColor];
[strokeColor setStroke];
[self.path stroke];
}
// [self.path fill];
//[self.path closePath];
}
Put a distance check in your algo... CGPoint diff = {p2.x - p1.x, p2.y - p1.y} If the diff is greater than , add a new point.
Related
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];
}
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];
}
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];
}
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.
Can someone here please show me how to draw a single dot using UIBezierpath? I am able to draw a line using the UIBezierpath but if I remove my finger and put it back and then remove nothing get drawn on the screen.
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [touches anyObject];
CGPoint p = [touch locationInView:self];
[pPath moveToPoint:p];
[pPath stroke];
[self setNeedsDisplay];
}
- (void)drawRect:(CGRect)rect
{
[pPath stroke];
}
Your path doesn't include any line or curve segments to be stroked.
Try this instead:
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
CGPoint p = [touches.anyObject locationInView:self];
static CGFloat const kRadius = 3;
CGRect rect = CGRectMake(p.x - kRadius, p.y - kRadius, 2 * kRadius, 2 * kRadius);
pPath = [UIBezierPath bezierPathWithOvalInRect:rect];
[self setNeedsDisplay];
}
- (void)drawRect:(CGRect)rect {
[[UIColor blackColor] setFill];
[pPath fill];
}
I used this code:
-(void)handleTap:(UITapGestureRecognizer*)singleTap {
//draw dot on screen
CGPoint tapPoint = [singleTap locationInView:self];
[bezierPath_ moveToPoint:tapPoint];
[bezierPath_ addLineToPoint:tapPoint];
[self setNeedsDisplay];
}