-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
mouseSwiped = YES;
UITouch *touch = [touches anyObject];
CGPoint currentPoint = [touch locationInView:scrollView];
//[self drawLine:lastPoint.x :lastPoint.y :currentPoint.x :currentPoint.y];
tempShapeLayer = nil;
CGMutablePathRef linePath = nil;
linePath = CGPathCreateMutable();
tempShapeLayer = [CAShapeLayer layer];
tempShapeLayer.lineWidth = 4.0f;
tempShapeLayer.lineCap = kCALineJoinMiter;
tempShapeLayer.strokeColor = [[UIColor redColor] CGColor];
CGPathMoveToPoint(linePath, NULL, lastPoint.x, lastPoint.y);
CGPathAddLineToPoint(linePath, NULL, currentPoint.x, currentPoint.y);
tempShapeLayer.path = linePath;
CGPathRelease(linePath);
[scrollView.layer addSublayer:tempShapeLayer];
lastPoint = currentPoint;
}
i am drawing line by this code . but i want this line remain straight. means when i move my touch the line should not become smooth.line will be remain straight.
i think i have to do something like
if (currentPoint.x > lastPoint.x){
currentPoint.y = lastPoint.y;
}
else if (currentPoint.x < lastPoint.x) {
currentPoint.x = lastPoint.x;
}
if you are moving subpixels you will get unwanted antialiasing. I suggest floor/ceil to a non float. This obviously gets more complicated when working with retina vs non retina.
Related
I want to draw a straight line on an iOS map using MKmapkit without the map moving around.
For example, if the user draws a straight line between two places on the map without removing the finger off the map, I don't want the map view to move around while the user is drawing the line.
I've searched on google, but didn't find an answer/solution.
Please can somebody help me?
Disable the mapkit scroll
_mapView.scrollEnabled=NO;
And then draw line with the help of core graphics
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [touches anyObject];
if (drawImage==nil) {
drawImage=[[UIImageView alloc]initWithFrame:CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height)];
[self.view addSubview:drawImage];
}
lastPoint = [touch locationInView:self.view];
lastPoint.y -= 20;
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [touches anyObject];
CGPoint currentPoint = [touch locationInView:drawImage];
currentPoint.y -= 20;
UIGraphicsBeginImageContext(drawImage.frame.size);
[drawImage.image drawInRect:CGRectMake(0, 0, drawImage.frame.size.width,drawImage.frame.size.height)];
CGContextSetLineCap(UIGraphicsGetCurrentContext(), kCGLineCapRound);
CGContextSetLineWidth(UIGraphicsGetCurrentContext(), 5.0);
CGContextSetRGBStrokeColor(UIGraphicsGetCurrentContext(), 0.0, 0.5, 0.6, 1.0);
CGContextBeginPath(UIGraphicsGetCurrentContext());
CGContextMoveToPoint(UIGraphicsGetCurrentContext(), lastPoint.x, lastPoint.y);
CGContextAddLineToPoint(UIGraphicsGetCurrentContext(), currentPoint.x, currentPoint.y);
CGContextStrokePath(UIGraphicsGetCurrentContext());
drawImage.image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
lastPoint = currentPoint;
}
You can draw poly line between two or more points/ coordinates.
Here is a question which is answered. Source: Draw a polyline between two coordinates in Xcode
You have to use MKOverlayView like below :
In Interface file
MKPolyline* _routeLine;
MKPolylineView* _routeLineView;
In Implementation file
Store all the coordinates in
NSMutablrArray *routeLatitudes
then
MKMapPoint* pointArr = malloc(sizeof(CLLocationCoordinate2D) * [routeLatitudes count]);
for(int idx = 0; idx < [routeLatitudes count]; idx++)
{
CLLocationCoordinate2D workingCoordinate;
workingCoordinate.latitude=[[routeLatitudes objectAtIndex:idx] doubleValue];
workingCoordinate.longitude=[[routeLongitudes objectAtIndex:idx] doubleValue];
MKMapPoint point = MKMapPointForCoordinate(workingCoordinate);
pointArr[idx] = point;
}
// create the polyline based on the array of points.
routeLine = [MKPolyline polylineWithPoints:pointArr count:[routeLatitudes count]];
[mapViewHome addOverlay:self.routeLine];
free(pointArr);
and Overlay delegate
- (MKOverlayView *)mapView:(MKMapView *)mapView viewForOverlay:(id <MKOverlay>)overlay
{
MKOverlayView* overlayView = nil;
if(overlay == routeLine)
{
routeLineView = [[[MKPolylineView alloc] initWithPolyline:self.routeLine] autorelease];
routeLineView.fillColor = [UIColor colorWithRed:0.945 green:0.027 blue:0.957 alpha:1];
routeLineView.strokeColor = [UIColor colorWithRed:0.945 green:0.027 blue:0.957 alpha:1];
routeLineView.lineWidth = 4;
overlayView = routeLineView;
}
return overlayView;
}
Using core graphics , I am trying to draw a line, in that when I try to add shadow is creates lin and bubbles inside the line, see the image, I am not able to solve the issue. please find my code below
-(void)drawRect:(CGRect)rect
{
[curImage drawAtPoint:CGPointMake(0, 0)];
CGPoint mid1 = midPoint(previousPoint1, previousPoint2);
CGPoint mid2 = midPoint(currentPoint, previousPoint1);
CGContextRef context = UIGraphicsGetCurrentContext();
[self.layer renderInContext:context];
CGContextMoveToPoint(context, mid1.x, mid1.y);
CGContextAddQuadCurveToPoint(context, previousPoint1.x, previousPoint1.y, mid2.x, mid2.y);
CGContextSetLineCap(context, kCGLineCapRound);
CGContextSetLineWidth(context, self.lineWidth);
CGContextSetStrokeColorWithColor(context, self.lineColor.CGColor);
CGContextSetShadow(context, CGSizeMake(-16.00, -5.0f), 5.0f);
CGContextStrokePath(context);
[super drawRect:rect];
[curImage release];
}
Below is the touchesMoved method.
-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [touches anyObject];
previousPoint2 = previousPoint1;
previousPoint1 = [touch previousLocationInView:self];
currentPoint = [touch locationInView:self];
// calculate mid point
CGPoint mid1 = midPoint(previousPoint1, previousPoint2);
CGPoint mid2 = midPoint(currentPoint, previousPoint1);
CGMutablePathRef path = CGPathCreateMutable();
CGPathMoveToPoint(path, NULL, mid1.x, mid1.y);
CGPathAddQuadCurveToPoint(path, NULL, previousPoint1.x, previousPoint1.y, mid2.x, mid2.y);
CGRect bounds = CGPathGetBoundingBox(path);
CGPathRelease(path);
CGRect drawBox = bounds;
//Pad our values so the bounding box respects our line width
drawBox.origin.x -= self.lineWidth * 2;
drawBox.origin.y -= self.lineWidth * 2;
drawBox.size.width += self.lineWidth * 4;
drawBox.size.height += self.lineWidth * 4;
UIGraphicsBeginImageContext(drawBox.size);
[self.layer renderInContext:UIGraphicsGetCurrentContext()];
curImage = UIGraphicsGetImageFromCurrentImageContext();
[curImage retain];
UIGraphicsEndImageContext();
[self setNeedsDisplayInRect:drawBox];
}
CGPoint midPoint(CGPoint p1, CGPoint p2)
{
return CGPointMake((p1.x + p2.x) * 0.5, (p1.y + p2.y) * 0.5);
}
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [touches anyObject];
previousPoint1 = [touch previousLocationInView:self];
previousPoint2 = [touch previousLocationInView:self];
currentPoint = [touch locationInView:self];
[self touchesMoved:touches withEvent:event];
}
Here the issue arrives when I try to add the line
CGContextSetShadow(context, CGSizeMake(-16.00, -5.0f), 5.0f);
Please help me out from the issue, thanks in advance. please see editing question with methods .
It would appear you are drawing a series of segments as quad curves. The bubbles would be where the previous segment and the current segment overlap. Since your segment line is a solid red, you don't notice the overlap, but it shows up in the shadows as darker regions.
Try setting your stroke color to have and alpha of 0.5 (or some such translucency). I think (know) you will see the overlapping segments will also show a similar effect as the the shadow.
To solve it, you will want to draw the segment as a continuous path for each line. I'm guessing you are using touchesBegan:withEvent: / touchesMoved:withEvent / touchesEnded:withEvent: to obtain the points of the lines?
In that case, when touchesBegan:withEvent: is called, start your new path. During touchesMoved:withEvent render the new path over the current image. Commit the path to a UIImage in touchesEnded:withEvent:. At this point you may discard the path until touchesBegan:withEvent: is called again with the next stroke.
Update
In your code snippets you end up rendering the whole view three times every run loop. That's a lot of drawing overhead. I quickly put together some code that demonstrates what I am talking about above. It is not tested code, but it should be mostly right:
Update 2
I had a bit of time to write and test some code that will do what you are seeking based on my latest two comments. This code is from the view controller instead of the view itself, but with a little modification, you can move in the view if you like. As such, I added an UIImageView as a subview to the generic UIView to hold and display the image (and maintained a reference to in the view controller), and made the view controller the first responder in order to receive the touch events. Lots of hard coded values that you can clean up when integrating into your own project:
static
UIImage *CreateImage( CGRect rect, NSArray *paths )
{
UIGraphicsBeginImageContextWithOptions(rect.size, NO, 1.0);
CGContextRef context = UIGraphicsGetCurrentContext();
CGFloat colorComponents[4] = {0.0,0.0,0.0,0.0};
CGContextSetFillColor(context, colorComponents);
CGContextFillRect(context, rect);
for ( id obj in paths ) {
CGPathRef path = (CGPathRef)obj;
CGContextAddPath(context, path);
}
CGContextSetStrokeColorWithColor(context, [[UIColor redColor] CGColor]);
CGContextSetLineCap(context, kCGLineCapRound);
CGContextSetLineWidth(context, 5.0);
CGContextSetShadow(context, (CGSize){-8.0,40}, 5.0);
CGContextBeginTransparencyLayer(context, NULL);
CGContextDrawPath(context, kCGPathStroke);
CGContextEndTransparencyLayer(context);
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return image;
}
static
CGPoint MidPoint( CGPoint pt1, CGPoint pt2 )
{
return (CGPoint){(pt1.x+pt2.x)/2.0,(pt1.y+pt2.y)/2.0};
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
[super touchesBegan:touches withEvent:event];
CGPoint location = [[touches anyObject] locationInView:self.view];
myPath = CGPathCreateMutable();
CGPathMoveToPoint(myPath, NULL, location.x, location.y);
[self.paths addObject:(id)myPath];
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
[super touchesMoved:touches withEvent:event];
UITouch *touch = [touches anyObject];
CGPoint previousLocation = [touch previousLocationInView:self.view];
CGPoint location = [touch locationInView:self.view];
CGPoint midPoint = MidPoint(previousLocation, location);
CGPathAddQuadCurveToPoint(myPath, NULL, midPoint.x, midPoint.y, location.x, location.y);
self.imageView.image = CreateImage(self.view.bounds, self.paths);
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
[super touchesEnded:touches withEvent:event];
UITouch *touch = [touches anyObject];
CGPoint previousLocation = [touch previousLocationInView:self.view];
CGPoint location = [touch locationInView:self.view];
CGPoint midPoint = MidPoint(previousLocation, location);
CGPathAddQuadCurveToPoint(myPath, NULL, midPoint.x, midPoint.y, location.x, location.y);
self.imageView.image = CreateImage(self.view.bounds, self.paths);
CGPathRelease(myPath);
myPath = nil;
}
the problem is you have multiple lines which lie above each other. So the shadows of both overlap and you get a darker color.
For something that easy like your line drawings you can simply copy the whole drawing code and redraw with a different color and a view pixel offset (instead of the shadow). E.g. you save the curves into an array and draw them separately later.
EDIT:
ok #gschandler describes the problem great too.
But you may also like to have a look at the Transparency Layer documentation. Group the elements together and apply a shadow for the group.
Another way round to this is to create an array of points on touch move events, and then draw them..
Create an array of points :
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
[pointArray release]; pointArray = nil;
pointArray=[[NSMutableArray alloc] init];
UITouch *touch = [touches anyObject];
[bufferArray removeAllObjects];
previousPoint1 = [touch previousLocationInView:self];
previousPoint2 = [touch previousLocationInView:self];
currentPoint = [touch locationInView:self];
}
-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [touches anyObject];
previousPoint2 = previousPoint1;
previousPoint1 = [touch previousLocationInView:self];
currentPoint = [touch locationInView:self];
[pointArray addObject: [NSValue valueWithCGPoint:previousPoint2]];
[pointArray addObject:[NSValue valueWithCGPoint:previousPoint1]];
[pointArray addObject:[NSValue valueWithCGPoint:currentPoint]];
[self setNeedsDisplay];
}
-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
}
-(void)drawRect:(CGRect)rect
{
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetLineCap(context, kCGLineCapRound);
CGContextSetLineWidth(context, self.lineWidth);
CGContextSetStrokeColorWithColor(context, self.lineColor.CGColor);
CGContextSetShadow(context, CGSizeMake(-16.00, -5.0f), 5.0f);
for(int i = 1 ; i < [pointArray count]-1;i++)
{
CGPoint mid1 = midPoint([pointArray objectAtIndex:i+1], [pointArray objectAtIndex:i]);
CGPoint mid2 = midPoint([pointArray objectAtIndex:i+2], [pointArray objectAtIndex:i+1]);
CGContextMoveToPoint(context, mid1.x, mid1.y);
CGContextAddQuadCurveToPoint(context, previousPoint1.x, previousPoint1.y, mid2.x, mid2.y);
i+=2;
}
CGContextStrokePath(context);
}
You can manage multiple lines with multiple dimensional array :)
I am building an iOS app for drawing first time. Now i am able to draw lines and curves using core graphics but unable UNDO the drawing. I am saving all the points when drawing and try trying to reuse them for UNDO and REDO, but with no success. Can anyone help to know what i am doing wrong?
Any help is highly appreciated.
Here is my code
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
[tempPathArray removeAllObjects];
UITouch *touch = [touches anyObject];
previousPoint1 = [touch previousLocationInView:self];
previousPoint2 = [touch previousLocationInView:self];
currentPoint = [touch locationInView:self];
}
-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [touches anyObject];
previousPoint2 = previousPoint1;
previousPoint1 = [touch previousLocationInView:self];
currentPoint = [touch locationInView:self];
// calculate mid point
CGPoint mid1 = midPoint(previousPoint1, previousPoint2);
CGPoint mid2 = midPoint(currentPoint, previousPoint1);
CGMutablePathRef path = CGPathCreateMutable();
CGPathMoveToPoint(path, NULL, mid1.x, mid1.y);
CGPathAddQuadCurveToPoint(path, NULL, previousPoint1.x, previousPoint1.y, mid2.x, mid2.y);
CGRect bounds = CGPathGetBoundingBox(path);
CGPathRelease(path);
drawBox = bounds;
//Pad our values so the bounding box respects our line width
drawBox.origin.x -= self.lineWidth * 2;
drawBox.origin.y -= self.lineWidth * 2;
drawBox.size.width += self.lineWidth * 4;
drawBox.size.height += self.lineWidth * 4;
UIGraphicsBeginImageContext(drawBox.size);
[self.layer renderInContext:UIGraphicsGetCurrentContext()];
curImage = UIGraphicsGetImageFromCurrentImageContext();
[curImage retain];
UIGraphicsEndImageContext();
[tempPathArray addObject:[NSValue valueWithCGRect:drawBox]];
[self setNeedsDisplayInRect:drawBox];
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
[pathArray addObject:tempPathArray];
NSLog(#"path array count %d", [pathArray count]);
}
- (void)drawRect:(CGRect)rect
{
NSLog(#"draw rect");
[curImage drawAtPoint:CGPointMake(0, 0)];
CGPoint mid1 = midPoint(previousPoint1, previousPoint2);
CGPoint mid2 = midPoint(currentPoint, previousPoint1);
context = UIGraphicsGetCurrentContext();
[self.layer renderInContext:context];
CGContextMoveToPoint(context, mid1.x, mid1.y);
// Use QuadCurve is the key
CGContextAddQuadCurveToPoint(context, previousPoint1.x, previousPoint1.y, mid2.x, mid2.y);
CGContextSetLineCap(context, kCGLineCapRound);
CGContextSetLineWidth(context, self.lineWidth);
CGContextSetStrokeColorWithColor(context, self.lineColor.CGColor);
CGContextStrokePath(context);
[super drawRect:rect];
}
-(void)undoButtonClicked
{
KidsPhotoBookAppDelegate *appDelegate = (KidsPhotoBookAppDelegate*)[[UIApplication sharedApplication]delegate];
if([pathArray count]>0){
[bufferArray addObject:[pathArray lastObject]];
[pathArray removeLastObject];
for (NSMutableArray *tempArray in pathArray) {
for (int i = 0; i < [tempArray count]; i++) {
CGRect draw = [[tempArray objectAtIndex:i] CGRectValue];
[self setNeedsDisplayInRect:draw];
}
}
}
}
Try this:
Draw a line.
Draw another line back over the first line.
Undo.
I suspect you'll see it mostly workâthe part of the second line that overlapped the first line will disappear, and only the part that didn't will remain.
The problem is that you're setting needs display on the portions of the drawing that you still have, but not the portion that you just vacated. This is exactly the inverse of what you should be doing: The portions you still have don't need to be redrawn, because they haven't changed, whereas the portion you vacated does need to be redrawn because it has changed.
So, set needs display in the area of the path you're removing, just before you remove it. You shouldn't need to set needs display of anything else.
I am new to iphone development and can use the quartz2D using the code below to draw a line.
I want to realize the function that draws a line above a picture (image), for example I have a map image the name is: map.jpg (using the imageview to add).
Can you tell me the detailed code or method? Thanks in advance!
Code:
- (void)drawRect:(CGRect)rect
Use this code to draw a line.
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
mouseSwiped = NO;
UITouch *touch = [touches anyObject];
if ([touch tapCount] == 2) {
drawImage.image = nil;
return;
}
lastPoint = [touch locationInView:self.view];
lastPoint.y -= 20;
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
mouseSwiped = YES;
UITouch *touch = [touches anyObject];
CGPoint currentPoint = [touch locationInView:self.view];
currentPoint.y -= 20;
UIGraphicsBeginImageContext(self.view.frame.size);
[drawImage.image drawInRect:CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height)];
CGContextSetLineCap(UIGraphicsGetCurrentContext(), kCGLineCapRound);
CGContextSetLineWidth(UIGraphicsGetCurrentContext(), 5.0);
CGContextSetRGBStrokeColor(UIGraphicsGetCurrentContext(), 0, 0, 0, 1);
CGContextBeginPath(UIGraphicsGetCurrentContext());
CGContextMoveToPoint(UIGraphicsGetCurrentContext(), lastPoint.x, lastPoint.y);
CGContextAddLineToPoint(UIGraphicsGetCurrentContext(), currentPoint.x, currentPoint.y);
CGContextStrokePath(UIGraphicsGetCurrentContext());
drawImage.image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
lastPoint = currentPoint;
mouseMoved++;
if (mouseMoved == 10) {
mouseMoved = 0;
}
}
I am a beginner working on paint application on iphone.
adding new tool for my iphone App called airbrush...
which will spray on UIImageView. can any one help me out how to work with it.
Logic for air brush.........
- (UIBezierPath *)pathFromPoint:(CGPoint)start toPoint:(CGPoint)end {
CGFloat lineWidth=10;
redrawRect = CGRectMake(end.x-lineWidth,end.y-lineWidth,lineWidth*2,lineWidth*2);
UIBezierPath *bezierPath = [UIBezierPath bezierPath];
UIBezierPath *circle = [UIBezierPath bezierPathWithOvalInRect:redrawRect];
NSInteger i, x, y;
NSInteger modNumber =4*(int)lineWidth;
for (i = 0; i < (lineWidth*lineWidth)/2; i++) {
do {
x = (random() % modNumber)+end.x - 2*lineWidth;
y = (random() % modNumber)+end.y - 2*lineWidth;
} while (![circle containsPoint:CGPointMake(x,y)]);
[bezierPath appendPath:[UIBezierPath bezierPathWithRect:CGRectMake(x,y,0.5,0.5)]];
}
return bezierPath;
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [touches anyObject];
currentPoint = [touch locationInView:self.view];
currentPoint.y -=20;
[self drawCircle];
}
-(void)drawCircle{
UIGraphicsBeginImageContext(self.drawImage.frame.size);
[drawImage.image drawInRect:CGRectMake(0,0, drawImage.frame.size.width, drawImage.frame.size.height)]; //originally self.frame.size.width, self.frame.size.height)];
CGContextSetLineWidth(UIGraphicsGetCurrentContext(),10);
CGContextSetRGBStrokeColor(UIGraphicsGetCurrentContext(), 0.0, 0.0, 0.0, 1.0);
UIBezierPath *path=[self pathFromPoint:currentPoint
toPoint:currentPoint];
[path stroke];
lastPoint = currentPoint;
CGContextStrokePath(UIGraphicsGetCurrentContext());
CGContextFlush(UIGraphicsGetCurrentContext());
drawImage.image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
}
I think you might be looking for CGContextBeginPath and its related functions. I am not quite sure how to define a new stroke but I imagine it can be handled with something like [UIColor colorFromImage:myImage]. You should look into Quartz 2D, try looking here.
/Thomas