i want to smooth curve on finger touch draw line and i want solution in UIBezierPath only my code not smoothing line completely.my code here's
#implementation MyLineDrawingView
#synthesize undoSteps;
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// Initialization code
[super setBackgroundColor:[UIColor whiteColor]];
pathArray=[[NSMutableArray alloc]init];
bufferArray=[[NSMutableArray alloc]init];
}
return self;
}
// Only override drawRect: if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
- (void)drawRect:(CGRect)rect
{
[[UIColor blackColor] setStroke];
for (UIBezierPath *_path in pathArray)
[_path strokeWithBlendMode:kCGBlendModeNormal alpha:1.0];
}
#pragma mark - Touch Methods
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
[bufferArray removeAllObjects];
myPath=[[UIBezierPath alloc]init];
myPath.lineWidth=5;
myPath.miterLimit=-10;
myPath.lineCapStyle = kCGLineCapRound;
myPath.flatness = 0.0;
UITouch *mytouch=[[touches allObjects] objectAtIndex:0];
[myPath moveToPoint:[mytouch locationInView:self]];
[pathArray addObject:myPath];
}
-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *mytouch=[[touches allObjects] objectAtIndex:0];
[myPath addLineToPoint:[mytouch locationInView:self]];
[self setNeedsDisplay];
}
-(void)undoButtonClicked
{
if([pathArray count]>0)
{
UIBezierPath *_path=[pathArray lastObject];
[bufferArray addObject:_path];
[pathArray removeLastObject];
[self setNeedsDisplay];
}
}
-(void)redoButtonClicked
{
if([bufferArray count]>0){
UIBezierPath *_path=[bufferArray lastObject];
[pathArray addObject:_path];
[bufferArray removeLastObject];
[self setNeedsDisplay];
}
}
- (void)dealloc
{
[pathArray release];
[bufferArray release];
[super dealloc];
}
#end
and i am getting output using my code like below screen shot:
i want smooth curve output like below screen shot:
can any one help me greatly appreciated!
Thanks in Advance!
The problem is that you're only adding points to the path using addLineToPoint:. That's the equivalent of creating a series of straight lines. You really need to add control points, like the handles you might drag while drawing curved paths in Illustrator or Sketch.
You can use addCurveToPoint:controlPoint1:controlPoint2: to add those; the trick is working out where to put the control points, relative to the points you actually get back from the touches* events.
For a good discussion of possible techniques, I recommend the following question: Drawing Smooth Curves - Methods Needed.
Related
This question already has answers here:
UIBezierPath Multiple Line Colors
(2 answers)
Closed 9 years ago.
I am working on project in which i am using UIBezierPath for drawing. My problem is that when i change the color of the path, whole UIBezierPath color changes. I want to ask is it possible to change the color of UIBezierPath with multiple lines.
Regards
For one single UIBezier path you can't AFAIK
You can do something like this
///// Add this in drawRect
for (NSMutableDictionary *dic in pathArray) {
UIBezierPath *_path = [dic valueForKey:#"path"];
[[dic valueForKey:#"fColor"] setFill];
[[dic valueForKey:#"sColor"] setStroke];
[_path stroke];
}
Populating array in touch events
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
self.currentPath=[[UIBezierPath alloc]init];
self.currentPath.lineWidth=5;
self.currentPath.miterLimit=-10;
self.currentPath.lineCapStyle = kCGLineCapRound;
self.currentPath.flatness = 0.0;
UITouch *mytouch=[[touches allObjects] objectAtIndex:0];
[self.currentPath moveToPoint:[mytouch locationInView:self]];
NSMutableDictionary *dic = [[NSMutableDictionary alloc] init];
[dic setObject:currentfColor forKey:#"fColor"];
[dic setObject:currentsColor forKey:#"sColor"];
[dic setObject:self.currentPath forKey:#"path"];
[pathArray addObject:dic];
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [touches anyObject];
[self.currentPath addLineToPoint:[touch locationInView:self.view]];
[self.view setNeedsDisplay];
}
PS: This is just an example. I havent checked this code, yet it should work may be some alteration needed.
Here is my code for free hand drawing. But when i draw the path, previous path is disappeared. I am not able to figure it out why is it happening so. Can any body help me. Here is my code.
- (void)drawRect:(CGRect)rect
{
for (NSMutableDictionary *dictionary in pathArray) {
UIBezierPath *_path = [dict objectForKey:#"Path"];
UIColor *_colors = [dict objectForKey:#"Colors"];
[_colors setStroke];
_path.lineCapStyle = kCGLineCapRound;
[_path stroke];
}
}
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
isEdited=YES;
myPath=[[UIBezierPath alloc]init];
myPath.lineWidth=lineWidths;
CGPoint touchPoint = [[touches anyObject] locationInView:self];
UITouch *mytouch=[[touches allObjects] objectAtIndex:0];
[myPath moveToPoint:[mytouch locationInView:self]];
[myPath addLineToPoint:CGPointMake(touchPoint.x+1, touchPoint.y+1)];
[dict setObject:myPath forKey:#"Path"];
[dict setObject:brushPattern forKey:#"Colors"];
[pathArray addObject:dict];
[self setNeedsDisplay];
}
-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *mytouch=[[touches allObjects] objectAtIndex:0];
[myPath addLineToPoint:[mytouch locationInView:self]];
[self setNeedsDisplay];
}
You should create myPath and dict localy in touchesBegan: each time it's fired. Ditch their class-wide definitions.
For simpler (faster) performance you can still have class-wide currentPath and currentDict
ivars for usage in touchesMoved:
EDIT: code would look something like this:
//currentPath declared as an iVar of UIBezierPath* type
//currentDict declared as an iVar of NSMutableDictionary* type
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
isEdited=YES;
UIBezierPath *myPath=[[UIBezierPath alloc]init]; //locally created
myPath.lineWidth=lineWidths;
CGPoint touchPoint = [[touches anyObject] locationInView:self];
UITouch *mytouch=[[touches allObjects] objectAtIndex:0];
[myPath moveToPoint:[mytouch locationInView:self]];
[myPath addLineToPoint:CGPointMake(touchPoint.x+1, touchPoint.y+1)];
NSMutableDictionary *dict=[[NSMutableDictionary alloc]init]; //locally created
[dict setObject:myPath forKey:#"Path"];
[dict setObject:brushPattern forKey:#"Colors"];
[pathArray addObject:dict];
[self setNeedsDisplay];
currentPath = myPath;
currentDict = dict;
}
-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *mytouch=[[touches allObjects] objectAtIndex:0];
[currentPath addLineToPoint:[mytouch locationInView:self]];
[self setNeedsDisplay];
}
1) Create a UIImage *currentImage instance variable in your drawing view.
2) in drawRect method of your view put the following line:
[currentImage drawInRect:self.bounds];
3) In touchesEnded method put the following code:
CGRect rect = self.bounds;
UIGraphicsBeginImageContext(rect.size);
[self.layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage *tempImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
currentImage = tempImage;
I am using following code to color the image using UIBezierPath. There is color picker from which user can select color. And with that color bezier path will be drawn over image. But when user fills color at some position in image.after that it selects color from color picker and again fills the color at same position. In this case previously drawn color is there. I want to remove that previously filled color and want to fill new color. This is only if user fills the color at same position. Here I am already getting the points where UIBezierPath is already drawn. There is one method of UIBezierPath removeAllPoints. But it's removing all points from path. I only want to remove only touched point from uibezierpath. How can I achieve this?
#import "MyLineDrawingView.h"
#implementation MyLineDrawingView
#synthesize selImage;
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self)
{
arrBezierPaths=[[NSMutableArray alloc]init ];
globalPath=[[UIBezierPath alloc]init];
myPath=[[UIBezierPath alloc]init];
self.userInteractionEnabled = TRUE;
myPath.lineWidth=30;
brushPattern=[UIColor redColor];
NSLog(#"initWithFrame method called");
NSDictionary *dict=[NSDictionary dictionaryWithObjectsAndKeys:myPath,#"Path",brushPattern,#"Color", nil];
[arrBezierPaths addObject:dict];
}
return self;
}
- (void)drawRect:(CGRect)rect
{
for (int i=0; i<[arrBezierPaths count]; i++)
{
NSDictionary *dict=[arrBezierPaths objectAtIndex:i];
UIColor *tempBrushpatter=[[UIColor alloc]init ];
tempBrushpatter=[dict valueForKey:#"Color"];
globalPath=[dict valueForKey:#"Path"];
[globalPath strokeWithBlendMode:kCGBlendModeOverlay alpha:1.0];
[tempBrushpatter setStroke];
}
}
#pragma mark - Touch Methods
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *mytouch=[[touches allObjects] objectAtIndex:0];
[globalPath moveToPoint:[mytouch locationInView:self]];
}
-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *mytouch=[[touches allObjects] objectAtIndex:0];
CGPoint pointTouched = [mytouch locationInView:self];
for(int i=0;i<[arrBezierPaths count];i++)
{
NSDictionary *dictTemp=[arrBezierPaths objectAtIndex:0];
UIBezierPath *temppath=[dictTemp valueForKey:#"Path"];
if([temppath containsPoint:pointTouched])
{
// [tempPath removeAllPoints];
NSLog(#"This point already contain some path");
}
}
[globalPath addLineToPoint:[mytouch locationInView:self]];
[self setNeedsDisplay];
}
-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
}
-(void)changeColor:(UIColor *)color
{
UIBezierPath *temp=[[UIBezierPath alloc]init];
// self.userInteractionEnabled = TRUE;
temp.lineWidth=30;
UIColor *brushColor=color;
NSDictionary *dict=[NSDictionary dictionaryWithObjectsAndKeys:temp,#"Path",brushColor,#"Color", nil];
[temp release];
[arrBezierPaths addObject:dict];
brushPattern=[color retain];
[self setNeedsDisplay];
}
Need some more details. From my assumption you are drawing a line with desired color, But the next time you just wanted to replace the touch point with another color and not the whole line is it?
Hi I am working on an application where i have to draw something. When I draw with marker stroke it makes some edges, how i can avoid these edges?
Code for marker stroke
- (void)drawRect:(CGRect)rect
{
// KidsPhotoBookAppDelegate *appDelegate = (KidsPhotoBookAppDelegate*)[[UIApplication sharedApplication]delegate];
// brushPattern = (UIColor*)appDelegate.kidsSelectedColor;
DBManager *dbMgr = [DBManager sharedManager];
[dbMgr.c setStroke];
for (UIBezierPath *_path in pathArray)
[_path strokeWithBlendMode:kCGBlendModeNormal alpha:1.0];
}
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
KidsPhotoBookAppDelegate *appDelegate = (KidsPhotoBookAppDelegate*)[[UIApplication sharedApplication]delegate];
myPath=[[UIBezierPath alloc]init];
myPath.lineWidth = appDelegate.kidsBrushSize;
myPath.lineCapStyle = kCGLineCapRound;
brushPattern=appDelegate.kidsSelectedColor;
UITouch *mytouch=[[touches allObjects] objectAtIndex:0];
[myPath moveToPoint:[mytouch locationInView:self]];
[pathArray addObject:myPath];
appDelegate.viewContainsDrawing = YES;
}
-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *mytouch=[[touches allObjects] objectAtIndex:0];
[myPath addLineToPoint:[mytouch locationInView:self]];
[self setNeedsDisplay];
}
You should set line join to kCGLineJoinRound using CGContextSetLineJoin function. See the Quartz 2D Guide for more.
UPDATE
Since you are using UIBezierPath, you should try setting the lineJoinStyle of UIBezierPath to kCGLineJoinRound.
How are you drawing those curves? If you're drawing a sequence of short straight line segments, that's the problem. You should be drawing these sorts of curves with a bezier path. If you're using a bezier path already, you may need to tweak the flatness property.
I achieved this smoothness with the help of this code https://github.com/levinunnink/Smooth-Line-View
i want to create a path.something like i touch the screen and draw line in touchmove event.when line intersect from starting point.fill that path using any colour.
now see in the image i've drawn a line.i just want to detect if line intersects again to start point.then fill that path with my own desired color.also i m using core graphics to draw line but it's very slow on real device.could you tell me a way to improve speed?
Header:
#import <UIKit/UIKit.h>
#interface myView : UIView {
CGMutablePathRef path;
CGPathRef drawingPath;
CGRect start;
BOOL outsideStart;
}
#end
Implementation:
#import "myView.h"
#implementation myView
- (id) init {
if (self = [super init]) {
self.userInteractionEnabled = YES;
self.multipleTouchEnabled = NO;
}
}
- (void) finishPath {
if (drawingPath) {
CGPathRelease(drawingPath);
}
CGPathCloseSubpath(path);
drawingPath = CGPathCreateCopy(path);
CGPathRelease(path);
path = NULL;
[self setNeedsDisplay];
return;
}
- (void) touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
path = CGPathCreateMutable();
UITouch *t = [touches anyObject];
CGPoint p = [t locationInView:self];
start = CGRectZero;
start.origin = p;
start = CGRectInset(start,-10, -10);
outsideStart = NO;
CGPathMoveToPoint(path, NULL, p.x, p.y);
}
- (void) touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
if (!path) {
return;
}
UITouch *t = [touches anyObject];
CGPoint p = [t locationInView:self];
if (CGRectContainsPoint(start,p)) {
if (outsideStart) {
[self finishPath];
}
} else {
outsideStart = YES;
}
CGPathAddLineToPoint(path,NULL,p.x,p.y);
[self setNeedsDisplay];
}
- (void) touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
if (!path) {
return;
}
[self finishPath];
}
- (void) touchesCanceled:(NSSet *)touches withEvent:(UIEvent *)event {
if (!path) {
return;
}
CGPathRelease(path);
path = NULL;
}
- (void) drawInRect:(CGRect)rect {
CGContextRef g = UIGraphicsGetCurrentContext();
if (drawingPath) {
CGContextAddPath(g,drawingPath);
[[UIColor redColor] setFill];
[[UIColor blackColor] setStroke];
CGContextDrawPath(g,kCGPathFillStroke);
}
if (path) {
CGContextAddPath(g,path);
[[UIColor blackColor] setStroke];
CGContextDrawPath(g,kCGPathStroke);
}
}
- (void) dealloc {
if (drawingPath) {
CGPathRelease(drawingPath);
}
if (path) {
CGPathRelease(path);
}
[super dealloc];
}
#end
Note that you will probably want to do something so you aren't actually calling setNeedsDisplay every time the path changes. This can get very slow. Suggestions include having an NSTimer that fires every x milliseconds to check if it needs to redisplay and do so if it does, or only redrawing if the touch has moved a significant distance.
Maybe it can be useful to you link text
Core Graphics should definitely not be using [self setNeedsDisplay] every time the image changes, which is probably why your code is so slow on the device. Drawing is slow. If you use OpenGL ES to draw the lines, it will be much quicker, at the expense of being more difficult to understand.