Hello I am working on this project to allow user doodle on a UIView. My approach is creating an CGMutablePathRef path and adding new lines to it while TouchMoved.
The codes relevant are listed below, which are in class of UIView.
static CGMutablePathRef path; //create it as static value.
//I got this habit from java coding but actually not sure
//if it's a good way to do it in objective-c.
//draw the path
-(void)drawRect:(CGRect)rect{
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextBeginPath(context);
CGContextAddPath(context, path);
CGContextStrokePath(context);
CGPathRelease(path);
}
//touch began. create the path and add point.
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
path = CGPathCreateMutable();
UITouch *touch = [touches anyObject];
CGPoint point = [touch locationInView:self];
CGPathMoveToPoint(path, NULL, point.x, point.y);
[self setNeedsDisplay];
}
//add points when touch moved
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event{
UITouch *touch = [touches anyObject];
CGPoint point = [touch locationInView:self];
CGPathAddLineToPoint(path, NULL, point.x, point.y);
[self setNeedsDisplay];
}
//last points when touch ends
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event{
UITouch *touch = [touches anyObject];
CGPoint point = [touch locationInView:self];
CGPathAddLineToPoint(path, NULL, point.x, point.y);
[self setNeedsDisplay];
}
However, I got errors and I didn't really understand them... I guess it's something with UIGestureRecognizer, which I saw from other doodle codes. Is it necessary to add UIGestureRecognizer to it? I haven't learned anything about it so I was trying to avoid using it. But if it's a must, I will give a try.
Another approach I am thinking of is to store all the point positions to an mutable array, because I have to store these point position values somewhere. However, I don't know if array is suitable because I haven't find a way to store floating values to the array. It will be greatly helpful if anyone can help me with this. Thank you!
Thanks to guys who offered me help. After looking thru a bunch of codes and approaches, luckily I found the effective way to solve this problem!
Generally, the imageView in my SketchView is updated dynamically when I am drawing. That is, every time I drew more pixel, one line will be added to that new pixel, change the UIImageView and then set it as the background image of UIView.
SketchView.h
#property (assign, nonatomic) CGPoint CurrentPoint;
#property (assign, nonatomic) CGPoint PreviousPoint;
#property (assign, nonatomic) CGPoint InitialPoint;
SketchView.m
#synthesize CurrentPoint;
#synthesize PreviousPoint;
#synthesize InitialPoint;
//begin the touch. store the initial point because I want to connect it to the last
//touch point
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
UITouch *touch = [touches anyObject];
CGPoint point = [touch locationInView:image];
InitialPoint = point;
}
//When touch is moving, draw the image dynamically
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event{
UITouch *touch = [touches anyObject];
PreviousPoint = [touch previousLocationInView:image];
CurrentPoint = [touch locationInView:image];
UIGraphicsBeginImageContext(image.frame.size);
CGContextRef ctx = UIGraphicsGetCurrentContext();
[image.image drawInRect:CGRectMake(0, 0, image.frame.size.width, image.frame.size.height)];
CGContextSetLineCap(ctx, kCGLineCapRound);
CGContextSetLineWidth(ctx, 5.0);
CGContextSetRGBStrokeColor(ctx, 1.0, 0.0, 0.0, 1.0);
CGContextBeginPath(ctx);
CGContextMoveToPoint(ctx, PreviousPoint.x, PreviousPoint.y);
CGContextAddLineToPoint(ctx, CurrentPoint.x, CurrentPoint.y);
CGContextStrokePath(ctx);
image.image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event{
UITouch *touch = [touches anyObject];
PreviousPoint = [touch previousLocationInView:image];
CurrentPoint = [touch locationInView:image];
UIGraphicsBeginImageContext(image.frame.size);
CGContextRef ctx = UIGraphicsGetCurrentContext();
[image.image drawInRect:CGRectMake(0, 0, image.frame.size.width, image.frame.size.height)];
CGContextSetLineCap(ctx, kCGLineCapRound);
CGContextSetLineWidth(ctx, 5.0);
CGContextSetRGBStrokeColor(ctx, 1.0, 0.0, 0.0, 1.0);
CGContextBeginPath(ctx);
CGContextMoveToPoint(ctx, PreviousPoint.x, PreviousPoint.y);
CGContextAddLineToPoint(ctx, CurrentPoint.x, CurrentPoint.y);
//I connected the last point to initial point to make a closed region
CGContextMoveToPoint(ctx, CurrentPoint.x, CurrentPoint.y);
CGContextAddLineToPoint(ctx, InitialPoint.x, InitialPoint.y);
CGContextStrokePath(ctx);
image.image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
}
And it works!
p.s. I found
PreviousPoint = [touch previousLocationInView:image];
very helpful, though it's not mentioned much in the codes I found doing doodling... I hope this helps. :)
Related
I'm trying to implement a freehand drawing tool by which user can draw several shapes dynamically like rectangle,eclipse,circle etc on a PDF using touch begins and touch move methods.
So anyone there who have done this kind of tool or have knowledge how to accomplish this,please help me out.
Thank you in advance.
This is a very broad question to be answered on StackOverflow. Still I am trying to give you a start here.
1) Declare two global variables BOOL mouseSwiped, CGPoint lastPoint in your UIViewController.
2)
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
mouseSwiped = NO;
UITouch *touch = [touches anyObject];
lastPoint = [touch locationInView:self.view];
}
3)
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
mouseSwiped = YES;
UITouch *touch = [touches anyObject];
CGPoint currentPoint = [touch locationInView:self.view];
UIGraphicsBeginImageContext(yourSubview.frame.size);
[yourSubview drawInRect:CGRectMake(0, 0, yourSubview.frame.size.width, yourSubview.frame.size.height)];
CGContextMoveToPoint(UIGraphicsGetCurrentContext(), lastPoint.x, lastPoint.y);
CGContextAddLineToPoint(UIGraphicsGetCurrentContext(), currentPoint.x, currentPoint.y);
CGContextSetLineCap(UIGraphicsGetCurrentContext(), kCGLineCapRound);
CGContextSetLineWidth(UIGraphicsGetCurrentContext(), 3.0 );
CGContextSetRGBStrokeColor(UIGraphicsGetCurrentContext(), red, green, blue, 1.0);
CGContextSetBlendMode(UIGraphicsGetCurrentContext(),kCGBlendModeNormal);
CGContextStrokePath(UIGraphicsGetCurrentContext());
yourSubview = UIGraphicsGetImageFromCurrentImageContext();
[yourSubview setAlpha:1.0];
UIGraphicsEndImageContext();
lastPoint = currentPoint;
}
4)
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
UIGraphicsBeginImageContext(yourSubview.frame.size);
[yourSubview drawInRect:CGRectMake(0, 0, yourSubview.frame.size.width, yourSubview) blendMode:kCGBlendModeNormal alpha:1.0];
yourSubview = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
}
Hope this helps
what i have done is getting image from gallery which should
be erased while moving finger on image using touch.my image is erasing.but problem is app terminates when erasing the image .my code is like this
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
self.sizeSlider.hidden=NO;
UITouch *touch = [touches anyObject];
lastPoint = [touch locationInView:self.view];
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [touches anyObject];
CGPoint currentPoint = [touch locationInView:self.shareView];
UIGraphicsBeginImageContext(self.shareView.frame.size);
[imgForeGround.image drawInRect:CGRectMake(0, 0, self.shareView.frame.size.width,
self.shareView.frame.size.height)];
// I add this
CGContextSetBlendMode(UIGraphicsGetCurrentContext(), kCGBlendModeClear);
CGContextSetLineCap(UIGraphicsGetCurrentContext(), kCGLineCapRound);
CGContextSetLineWidth(UIGraphicsGetCurrentContext(), sizeSlider.value);
CGContextBeginPath(UIGraphicsGetCurrentContext());
CGContextSetBlendMode(UIGraphicsGetCurrentContext(), kCGBlendModeClear);
CGContextMoveToPoint(UIGraphicsGetCurrentContext(), lastPoint.x, lastPoint.y);
CGContextAddLineToPoint(UIGraphicsGetCurrentContext(), currentPoint.x, currentPoint.y);
CGContextStrokePath(UIGraphicsGetCurrentContext());
imgForeGround.image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
lastPoint = currentPoint;
[imgForeGround setNeedsDisplay];
}
please anybody help me to avoid this memory leaks of this problem help is appreciated
There are few more controls easy to implement:
https://github.com/moqod/iOS-Scratch-n-See
I'm trying to add a drawing functionality using touch events on my UIImage (inside a UIImageView). However, for certain images, when I begin drawing, the image starts shrinking and becomes blurier the more i draw on it. I mostly used ray wenderlich's old drawing tutorial for reference, just changed values to match my UI. The two images show you what happens when i draw:
It looks like I'm using pretty standard methods for the drawing. My hunch is that the bounds/rectangle drawing methods are causing some sort of problem.
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
if(_allowDoodling == NO) return;
UITouch *touch = [touches anyObject];
lastPoint = [touch previousLocationInView:self.imageView];
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
if(_allowDoodling == NO) return;
UITouch *touch = [touches anyObject];
CGPoint currentPoint = [touch locationInView:self.imageView];
UIGraphicsBeginImageContextWithOptions(_imageView.frame.size, NO, 2.0f);
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetInterpolationQuality(context, kCGInterpolationHigh);
[_imageView.image drawInRect:_imageView.bounds];
CGContextMoveToPoint(UIGraphicsGetCurrentContext(), lastPoint.x, lastPoint.y);
CGContextAddLineToPoint(context, currentPoint.x, currentPoint.y);
CGContextSetLineCap(context, kCGLineCapRound);
CGContextSetLineWidth(context, 5.0);
CGContextSetStrokeColorWithColor(context, _doodleColor.CGColor);
CGContextStrokePath(UIGraphicsGetCurrentContext());
_imageView.image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
lastPoint = currentPoint;
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
if(_allowDoodling == NO) return;
UIGraphicsBeginImageContextWithOptions(_imageView.frame.size, NO, 2.0f);
[_imageView.image drawInRect:_imageView.bounds];
_imageView.image = UIGraphicsGetImageFromCurrentImageContext();
CGContextFlush(UIGraphicsGetCurrentContext());
UIGraphicsEndImageContext();
}
It posible, that you have to use
UIGraphicsBeginImageContextWithOptions(CGSizeMake(_imageView.frame.size.width * 2, _imageView.frame.size.width * 2), NO, 1.0f);
I have created a drawing app to draw when you touch the screen and it works very smooth in the simulator but when I tested it on my iPad 2 it slows down and it takes very long time to draw I am using these lines of code:
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [[event allTouches] anyObject];
lastPoint = [touch locationInView:self.view];
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
mouseSwiped = YES;
UITouch *touch = [touches anyObject];
currentPoint = [touch locationInView:self.view];
UIGraphicsBeginImageContext(self.view.frame.size);
[drawImage.image drawInRect:self.view.frame];
CGContextSetLineCap(UIGraphicsGetCurrentContext(), kCGLineCapRound);
CGContextSetLineWidth(UIGraphicsGetCurrentContext(), 7.0);
CGContextSetRGBStrokeColor(UIGraphicsGetCurrentContext(), 0, 1, 0, 1);
CGContextBeginPath(UIGraphicsGetCurrentContext());
CGContextMoveToPoint(UIGraphicsGetCurrentContext(), lastPoint.x, lastPoint.y);
CGContextAddLineToPoint(UIGraphicsGetCurrentContext(), currentPoint.x, currentPoint.y);
CGContextStrokePath(UIGraphicsGetCurrentContext());
[drawImage setFrame:self.view.frame];
drawImage.image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
lastPoint = currentPoint;
[self.view addSubview:drawImage];
}
I think the problem is with calling drawInRect: method in touchMoved: method
Build up a UIBezierPath in your touchesMoved and don't do any drawing there.
Call setNeedsDisplay in touchesMoved.
Override drawRect and draw your path there.
OR
Use set the layer backing for your view to a CAShapeLayer and add the points to the layer in touchesMoved.
I working on a drawing app, and I am able to draw with my finger touch, now I am trying to implement, clear ,undo and redo functionality,In my viewcontroller ,I have two IBAction methods for "clearAll" and "Undo", I have created a custom class called drawing.h and .m where I have written functions for handling touch events,below are my functions..
- (void) touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [touches anyObject];
tempArray = [[NSMutableArray alloc]init];
if ([touch tapCount] == 2)
{
self.image = nil;
}
return;
}
currentlocation.location = [touch locationInView:self];
}
- (void) touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [touches anyObject];
PointLocation *currentLocation1 = [[PointLocation alloc] init];
currentLocation1.Location = [touch locationInView:self];
UIGraphicsBeginImageContext(self.frame.size);
CGContextRef ctx = UIGraphicsGetCurrentContext();
[self.image drawInRect:CGRectMake(0, 0, self.frame.size.width, self.frame.size.height)];
CGContextSetLineCap(ctx, kCGLineCapRound);
CGContextSetLineWidth(ctx, 2.0);
CGContextSetRGBStrokeColor(ctx, 1.0, 0.0, 0.0, 1.0);
CGContextBeginPath(ctx);
CGContextMoveToPoint(ctx, currentlocation.Location.x, currentlocation.Location.y);
CGContextAddLineToPoint(ctx, currentLocation1.Location.x, currentLocation1.Location.y);
CGContextStrokePath(ctx);
CGContextSetFlatness(ctx, 0.1);
CGContextSetAllowsAntialiasing(ctx, true);
self.image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
currentlocation = currentLocation1;
**[tempArray addObject:[NSArray arrayWithObjects:currentlocation,currentLocation1, nil]];
NSLog(#"%i",[tempArray count]);**
}
- (void) touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [touches anyObject];
PointLocation *currentLocation1 = [[PointLocation alloc] init];
currentLocation1.Location = [touch locationInView:self];
UIGraphicsBeginImageContext(self.frame.size);
CGContextRef ctx = UIGraphicsGetCurrentContext();
[self.image drawInRect:CGRectMake(0, 0, self.frame.size.width, self.frame.size.height)];
CGContextSetLineCap(ctx, kCGLineCapRound);
CGContextSetLineWidth(ctx, 2.0);
CGContextSetRGBStrokeColor(ctx, 1.0, 0.0, 0.0, 1.0);
CGContextBeginPath(ctx);
CGContextMoveToPoint(ctx, currentlocation.Location.x, currentlocation.Location.y);
CGContextAddLineToPoint(ctx, currentLocation1.Location.x, currentLocation1.Location.y);
CGContextStrokePath(ctx);
self.image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
currentlocation = currentLocation1;
**[tempArray addObject:[NSArray arrayWithObjects:currentlocation,currentLocation1, nil]];
NSLog(#"%i",[tempArray count]);**
}
In the above code I am adding all the CGPoints to an Array,the array is filling with the points, now how shall I perform the undo functionality?
So friends please help me out..
Regards
Ranjit