I have following problem:
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetLineCap(context,kCGLineCapRound);
CGContextSetLineJoin(context, kCGLineJoinRound);
CGContextSetRGBFillColor(context, 1, 0, 0, 1);
for (drawnCurve *line in completeLines) {
CGContextBeginPath(context);
for(int i=0;i<[line.points count]-1;i++)
{
drawnPoint*point=[line.points objectAtIndex:i];
drawnPoint*point2=[line.points objectAtIndex:i+1];
// CGContextSetLineWidth(context, point.r);
[point.color set];
CGContextMoveToPoint(context, point.x,point.y);
CGContextAddLineToPoint(context, point2.x, point2.y);
// CGContextAddCurveToPoint(context, (point.x+point2.x)/2, point2.y, (point.x+point2.x)/2, point2.y, point2.x, point2.y);
//CGContextEOFillPath(context);
}
CGContextClosePath(context);
CGContextFillPath(context);
}
But it's just disappearing and not showing any kind of filled curves.. What is the problem?
A call to CGContextMoveToPoint begins a new subpath. You should call it once before your nested for loop, and then use only CGContextAddLineToPoint to preserve line connectivity:
drawnPoint*pointZero=[line.points objectAtIndex:0];
CGContextMoveToPoint(context, pointZero.x,pointZero.y);
for(int i=1;i<[line.points count];i++)
{
drawnPoint*point=[line.points objectAtIndex:i];
CGContextAddLineToPoint(context, point.x, point.y);
}
CGContextClosePath(context);
CGContextFillPath(context);
Related
I am drawing a shape like following :
- (void)drawRect:(CGRect)rect
{
// Draw a cross rectagle
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSaveGState(context);
CGContextMoveToPoint(context, 190, 0);
CGContextAddLineToPoint(context, 220, 0);
CGContextAddLineToPoint(context, 310, 90);
CGContextAddLineToPoint(context, 310, 120);
CGContextSetFillColorWithColor(context, [UIColor lightGrayColor].CGColor);
CGContextFillPath(context);
CGContextRestoreGState(context);
}
I am getting a light-dark cross flag below
Now I would like to draw a stroke around the cross flag I have just drawn
What should I do to achieve this. Please advice me on this issue.
Thanks.
Surely CGContextDrawPath(context, kCGPathFillStroke); is what you're after
You can adjust the pattern and color using:
CGContextSetStrokePattern
CGContextSetStrokeColor
https://developer.apple.com/library/ios/#documentation/graphicsimaging/reference/CGContext/Reference/reference.html
So, in your case, assuming you want a plain black stroke, you'd have:
- (void)drawRect:(CGRect)rect
{
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSaveGState(context);
CGContextSetLineWidth(context, 2.0);
CGContextSetStrokeColorWithColor(context, [UIColor blackColor].CGColor);
CGContextSetFillColorWithColor(context, [UIColor lightGrayColor].CGColor);
CGContextMoveToPoint(context, 190, 0);
CGContextAddLineToPoint(context, 220, 0);
CGContextAddLineToPoint(context, 310, 90);
CGContextAddLineToPoint(context, 310, 120);
CGContextClosePath(context);
CGContextDrawPath(context, kCGPathFillStroke);
CGContextFillPath(context);
CGContextRestoreGState(context);
}
Produces:
- (void)drawRect:(CGRect)rect
{
// Draw a cross rectagle
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSaveGState(context);
//New
CGContextSetLineWidth(context, 2.0);
CGContextMoveToPoint(context, 190, 0);
CGContextAddLineToPoint(context, 220, 0);
CGContextAddLineToPoint(context, 310, 90);
CGContextAddLineToPoint(context, 310, 120);
//New
CGContextSetStrokeColorWithColor(context, [UIColor blackColor].CGColor);
CGContextSetFillColorWithColor(context, [UIColor lightGrayColor].CGColor);
CGContextFillPath(context);
//New
CGContextStrokePath(context);
CGContextRestoreGState(context);
}
#WDUK :after spending hours to figure it out, I know why your above answer is not working.
The reason is when you do CGContextFillPath first, the path is eventually cleared and then you can no long do CGContextStrokePath on it again.
Therefore in order to do CGContextFillPath and CGContextStrokePath, we have to do
CGContextDrawPath(context, kCGPathFillStroke);
After trying it , I am getting the following
I used this function to draw a path between two points. I get the coordinates from Google's API, and there's no problem with this. My problem is, how can I specify whether a particular point is within the path I draw or not? I'm trying to use CGContextPathContainsPoint(), but with no luck.
-(void)updateRouteView {
CGContextRef context = CGBitmapContextCreate(nil,
routeView.frame.size.width,
routeView.frame.size.height,
8,
4 * routeView.frame.size.width,
CGColorSpaceCreateDeviceRGB(),
kCGImageAlphaPremultipliedLast);
CGContextSetStrokeColorWithColor(context, lineColor.CGColor);
CGContextSetRGBFillColor(context, 0.0, 0.0, 1.0, 1.0);
CGContextSetLineWidth(context, 5.0);
// routes : array of coordinates of the path
for (int i = 0; i < routes.count; i++) {
CLLocation* location = [routes objectAtIndex:i];
CGPoint point = [mapView convertCoordinate:location.coordinate toPointToView:routeView];
if (i == 0)
CGContextMoveToPoint(context, point.x, routeView.frame.size.height - point.y);
else
CGContextAddLineToPoint(context, point.x, routeView.frame.size.height - point.y);
}
CGContextStrokePath(context);
// this is the point I want to check if it is inside the path
CLLocationCoordinate2D checkedLocation;
checkedLocation.latitude = 37.782756;
checkedLocation.longitude = -122.409647;
CGPoint checkedPoint = [mapView convertCoordinate:checkedLocation toPointToView:routeView];
checkedPoint.y = routeView.frame.size.height - checkedPoint.y;
// even if the checkedPoint is inside the path, the result is false
BOOL checking = CGContextPathContainsPoint(context, checkedPoint, kCGPathStroke);
if (checking)
NSLog(#"YES");
else
NSLog(#"NO");
CGImageRef image = CGBitmapContextCreateImage(context);
UIImage* img = [UIImage imageWithCGImage:image];
routeView.image = img;
CGContextRelease(context);
}
Call CGContextStrokePath(context) AFTER you check for your point. Quartz will clear the current path once it's struck.
so i need to draw 2 different lines. through another posting, i figured out how to redraw a line. my question is, if i want to draw 2 lines do i need to have 2 code segments to do it? or is there a way to draw n lines?
- (void)drawRect:(CGRect)rect
{
CGContextRef context = UIGraphicsGetCurrentContext();
NSLog(#"drawRect called");
CGContextSetLineWidth(context, self.lineWidth);
CGContextSetStrokeColorWithColor(context, self.lineColor.CGColor);
CGContextMoveToPoint(context, self.startPoint.x, self.startPoint.y);
CGContextAddLineToPoint(context, self.endPoint.x, self.endPoint.y);
CGContextStrokePath(context);
}
implementation
draw2D *myCustomView = [[draw2D alloc] init];
myCustomView.startPoint = CGPointMake(0, 0);
myCustomView.endPoint = CGPointMake(300, 300);
myCustomView.lineWidth = 5;
myCustomView.lineColor = [UIColor redColor];
myCustomView.frame = CGRectMake(0, 0, 500, 500);
[myCustomView setBackgroundColor:[UIColor blueColor]];
[self.view addSubview:myCustomView];
[myCustomView setNeedsDisplay];
myCustomView.endPoint = CGPointMake(100, 100);
myCustomView.lineColor = [UIColor orangeColor];
[myCustomView setNeedsDisplay];
this will just redraw the line. do i repeat the drawRect block for each line i want? so if i want two lines, do i have to do:
- (void)drawRect:(CGRect)rect
{
//line1
CGContextRef context1 = UIGraphicsGetCurrentContext();
NSLog(#"drawRect called");
CGContextSetLineWidth(context1, self.lineWidth1);
CGContextSetStrokeColorWithColor(context1, self.lineColor1.CGColor);
CGContextMoveToPoint(context1, self.startPoint1.x, self.startPoint1.y);
CGContextAddLineToPoint(context1, self.endPoint1.x, self.endPoint1.y);
CGContextStrokePath(context1);
//line 2
CGContextRef context2 = UIGraphicsGetCurrentContext();
NSLog(#"drawRect called");
CGContextSetLineWidth(context2, self.lineWidth2);
CGContextSetStrokeColorWithColor(context2, self.lineColor2.CGColor);
CGContextMoveToPoint(context2, self.startPoint2.x, self.startPoint2.y);
CGContextAddLineToPoint(context2, self.endPoint2.x, self.endPoint2.y);
CGContextStrokePath(context2);
}
or is there a better way to handle drawing my lines?
EDIT: the end goal is to use it with a UIView with labels and textboxes i'm using as a form. i want lines to break up the form.
i'm wondering if i should just use these custom draw2D UIViews as lines only and place them on top of the UIView form, then i can just create multiple "draw2D" UIViews?
SOLUTION:
so here is the code i figured out that would work:
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// Initialization code
NSMutableArray *tempArray = [[NSMutableArray alloc] init];
NSMutableDictionary *tempDict = [[NSMutableDictionary alloc] init];
[tempDict setValue:[NSValue valueWithCGPoint:CGPointMake(10, 10)] forKey:#"start"];
[tempDict setValue:[NSValue valueWithCGPoint:CGPointMake(500, 500)] forKey:#"end"];
[tempArray addObject:tempDict];
NSMutableDictionary *tempDict2 = [[NSMutableDictionary alloc] init];
[tempDict2 setValue:[NSValue valueWithCGPoint:CGPointMake(400, 10)] forKey:#"start"];
[tempDict2 setValue:[NSValue valueWithCGPoint:CGPointMake(500, 500)] forKey:#"end"];
[tempArray addObject:tempDict2];
self.pointArray = [NSArray arrayWithArray:tempArray];
}
return self;
}
// Only override drawRect: if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
- (void)drawRect:(CGRect)rect
{
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetLineWidth(context, self.lineWidth);
CGContextSetStrokeColorWithColor(context, self.lineColor.CGColor);
for (int i = 0; i < [self.pointArray count]; i++)
{
CGPoint tempPoint = [[[self.pointArray objectAtIndex:i] objectForKey:#"start"] CGPointValue];
CGPoint tempPoint2 = [[[self.pointArray objectAtIndex:i] objectForKey:#"end"] CGPointValue];
CGContextMoveToPoint(context, tempPoint.x, tempPoint.y);
CGContextAddLineToPoint(context, tempPoint2.x, tempPoint2.y);
}
CGContextStrokePath(context);
}
i included my initWithFrame: method on creating the array that i used for testing (CGPoint's are not objects, so had to figure out how to include them in an dictionary).
so this will loop through and create n lines.
You can just repeat this bit as many times as you need (with different coordinates each time obviously).
CGContextMoveToPoint(context, self.startPoint.x, self.startPoint.y);
CGContextAddLineToPoint(context, self.endPoint.x, self.endPoint.y);
The first line of code sets the start point of the line, the second line of code draws it. You don't need to repeat all the rest of the code.
In other words, you can draw multiple lines like this:
//set up context
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetLineWidth(context, self.lineWidth1);
CGContextSetStrokeColorWithColor(context, self.lineColor1.CGColor);
//line1
CGContextMoveToPoint(context, self.startPoint1.x, self.startPoint1.y);
CGContextAddLineToPoint(context, self.endPoint1.x, self.endPoint1.y);
//line 2
CGContextMoveToPoint(context, self.startPoint2.x, self.startPoint2.y);
CGContextAddLineToPoint(context, self.endPoint2.x, self.endPoint2.y);
//line 3
etc...
//finished drawing
CGContextStrokePath(context);
You don't need to copy the whole lot each time you draw a line, you can just repeat those two lines of code.
Well, you could refactor out the duplicate code into another method and pass in the variables it needs to draw a line, i.e. the context, colour, start and end points, width
the better and universal way is:
- (void)drawRect:(CGRect)rect:(long)lineWidth:(CGColor)color:(CGPoint)pStart:(CGPoint)pEnd
{
//line1
CGContextRef context = UIGraphicsGetCurrentContext();
NSLog(#"drawRect called");
CGContextSetLineWidth(context, lineWidth);
CGContextSetStrokeColorWithColor(context, color);
CGContextMoveToPoint(context, pStart.x, pStart.y);
CGContextAddLineToPoint(context, pEnd.x, pEnd.y);
CGContextStrokePath(context);
}
...
[self drawRect:rect:self.lineWidth1:self.lineColor1.CGColor:self.startPoint1:self.endPoint1];
[self drawRect:rect:self.lineWidth2:self.lineColor2.CGColor:self.startPoint2:self.endPoint2];
...
I have this code on my touchesMoved but like the others its not working on UIScrollView
Here it is, on my touchesMoved:
touchSwiped = YES;
currentTouch = [touch locationInView:self.view];
currentTouch.y -= 5;
UIGraphicsBeginImageContext(self.view.frame.size);
[writeView.image drawInRect:CGRectMake(0, 0, 768, 1024)];
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetLineCap(context, kCGLineCapRound);
CGContextSetLineWidth(context, 15);
CGContextSetRGBStrokeColor(context, 0, 0, 0, 1);
CGContextSetBlendMode(context, kCGBlendModeNormal);
CGContextMoveToPoint(context, endPoint.x, endPoint.y);
CGContextAddLineToPoint(context, currentTouch.x, currentTouch.y);
CGContextStrokePath(context);
writeView.image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
endPoint = currentTouch;
touchMoved++;
if (touchMoved == 10) {
touchMoved = 0;
}
so, i transferred it using Gesture recognizer but still it is not working.
i used the PanGestureRecognizer
here:
- (void) writePan:(UIPanGestureRecognizer *)writingRecognizerP {
switch (writingRecognizerP.state)
{
case UIGestureRecognizerStateChanged:
[scrollView setScrollEnabled:NO];
[scrollView setUserInteractionEnabled:NO];
touchSwiped = YES;
currentTouch = [writingRecognizerP locationInView:scrollView];
currentTouch.y -= 5;
UIGraphicsBeginImageContext(self.view.frame.size);
[writeView.image drawInRect:CGRectMake(0, 0, 768, 1024)];
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetLineCap(context, kCGLineCapRound);
CGContextSetLineWidth(context, 15);
CGContextSetRGBStrokeColor(context, 0, 0, 0, 1);
CGContextSetBlendMode(context, kCGBlendModeNormal);
CGContextMoveToPoint(context, endPoint.x, endPoint.y);
CGContextAddLineToPoint(context, currentTouch.x, currentTouch.y);
CGContextStrokePath(context);
writeView.image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
endPoint = currentTouch;
touchMoved++;
if (touchMoved == 10) {
touchMoved = 0;
}
break;
case UIGestureRecognizerStateEnded:
[scrollView setScrollEnabled:YES];
[scrollView setUserInteractionEnabled:YES];
break;
}
}
anyone who has an idea how will i able to write on touch??
it'll be much appreciated! :)
You should do all your drawing in -drawRect:, and when something changes send your view -setNeedsDisplay or setNeedsDisplayInRect: message.
I try to Draw a pdf with CoreGraphics, everything work fine but in instrument there is a 100% leak on : CGContextDrawPDFPage(ctx, page2);
I release with CGPDFDocumentRelease(); everytime i use CGPDFDocumentCreateWithURL();
There is any solution to release : CGContextDrawPDFPage ?
- (void)drawRect:(CGRect)rect
{
if (state == 0) {
CGContextRef ctx = UIGraphicsGetCurrentContext();
CGContextSaveGState(ctx);
[[UIColor whiteColor] set];
CGContextFillRect(ctx, CGRectMake(0, 0, rect.size.width, rect.size.height));
CGContextGetCTM(ctx);
CGContextScaleCTM(ctx, 1, -1);
CGContextTranslateCTM(ctx, 45, -rect.size.height);
CGPDFDocumentRef pdf = CGPDFDocumentCreateWithURL(((CFURLRef)url));
CGPDFPageRef page = CGPDFDocumentGetPage(pdf, currentPage);
CGRect mediaRect = CGPDFPageGetBoxRect(page, kCGPDFCropBox);
CGContextScaleCTM(ctx, (678) / mediaRect.size.width,
(rect.size.height ) / (mediaRect.size.height));
CGContextSetInterpolationQuality(ctx, kCGInterpolationHigh);
CGContextSetRenderingIntent(ctx, kCGRenderingIntentDefault);
CGContextDrawPDFPage(ctx, page);
CGPDFDocumentRelease(pdf);
CGContextRestoreGState(ctx);
}
else
{
CGContextRef ctx = UIGraphicsGetCurrentContext();
CGContextSaveGState(ctx);
[[UIColor whiteColor] set];
CGContextFillRect(ctx, CGRectMake(0, 0, rect.size.width, rect.size.height));
CGContextGetCTM(ctx);
CGContextScaleCTM(ctx, 1, -1);
CGContextTranslateCTM(ctx, 6, -rect.size.height);
CGPDFDocumentRef pdf = CGPDFDocumentCreateWithURL(((CFURLRef)url));
CGPDFPageRef page = CGPDFDocumentGetPage(pdf, currentPage);
CGRect mediaRect = CGPDFPageGetBoxRect(page, kCGPDFCropBox);
CGContextScaleCTM(ctx, (497) / mediaRect.size.width,
(rect.size.height ) / (mediaRect.size.height));
// draw it
CGContextSetInterpolationQuality(ctx, kCGInterpolationHigh);
CGContextSetRenderingIntent(ctx, kCGRenderingIntentDefault);
CGContextDrawPDFPage(ctx, page);
CGPDFDocumentRelease(pdf);
CGContextRestoreGState(ctx);
//
CGContextSaveGState(ctx);
CGContextGetCTM(ctx);
CGContextScaleCTM(ctx, 1, -1);
CGContextTranslateCTM(ctx, 506, -rect.size.height);
CGPDFDocumentRef pdf2 = CGPDFDocumentCreateWithURL(((CFURLRef)url));
CGPDFPageRef page2 = CGPDFDocumentGetPage(pdf2, (currentPage + 1));
CGContextScaleCTM(ctx, (497) / mediaRect.size.width,
(rect.size.height ) / (mediaRect.size.height));
CGContextSetInterpolationQuality(ctx, kCGInterpolationHigh);
CGContextSetRenderingIntent(ctx, kCGRenderingIntentDefault);
//Leak 100%
CGContextDrawPDFPage(ctx, page2);
CGPDFDocumentRelease(pdf2);
CGContextRestoreGState(ctx);
}
}
And I don't know why. Any idea?
This is the only leak of my app :(
I don't see where the leak come from :s
PS : state = 0 = portrait orientation
state = 1 = landscape so I draw 2 pages in landscape orientation.
Looks like you're not releasing the CGPDFPageRef instances you're getting from CGPDFDocumentGetPage.
CGPDFPageRelease(page);
Add that for page and page2 at the appropriate locations.