I need to draw an hexagon and fill it with a color build with an Image as pattern.
I did:
CGContextSaveGState(context);
CGContextSetLineCap(context, kCGLineCapRound);
CGContextSetFillColorWithColor(context, [[UIColor colorWithPatternImage:[UIImage imageNamed:#"patternerba.png"]] CGColor]);
CGContextSetStrokeColorWithColor(context, [[UIColor blackColor] CGColor]);
CGContextSetLineWidth(context, 3.0);
// drawing hexagon here...
CGContextStrokePath(context);
CGContextFillPath(context);
[[NSString stringWithFormat:#"Foo"] drawAtPoint:innerRect.origin withFont:[UIFont fontWithName:#"Helvetica" size:16]];
CGContextRestoreGState(context);
But depending from the order of CGContextStrokePath and CGContextFillPath, I get an hexagon bordered but not filled or filled but not bordered. How can I fix this?
Try
CGContextDrawPath(context, kCGPathFillStroke);
Instead of
CGContextStrokePath(context);
CGContextFillPath(context);
Either use
CGContextDrawPath(context, kCGPathFillStroke);
as sch suggested, or draw the hexagon again before calling FillPath. StrokePath and FillPath remove the path that you have added to the context, thus the next call will silently fail without a path.
CGPathRef path = /* drawing hexagon here */;
CGContextAddPath(context, path);
CGContextStrokePath(context);
CGContextAddPath(context, path);
CGContextFillPath(context);
Note: the two segments of code are not equivalent and gives different result.
Related
How can I draw a rectangle with a border in one line?
There are separate methods like:
CGContextStrokeRect(context, someRectangle);
and
CGContextFillRect(context, someRectangle);
but is there something that does both in one?
- (void)drawRect:(CGRect)rect {
CGContextRef context = UIGraphicsGetCurrentContext();
CGPathRef path = CGPathCreateWithRect(rect, NULL);
[[UIColor redColor] setFill];
[[UIColor greenColor] setStroke];
CGContextAddPath(context, path);
CGContextDrawPath(context, kCGPathFillStroke);
CGPathRelease(path);
}
Although, I can't say it's any less verbose than stroke & fill in separate calls...
If you're just looking to save line space, you could define your own method to make two calls and place it in a utility class.
void strokeAndFill(CGContextRef c, CGRect rect)
{
CGContextFillRect(c, rect);
CGContextStrokeRect(c, rect);
}
CGContextDrawPath does it one go if you set fill and stroke color beforehand.
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetStrokeColor(context, a);
CGContextSetFillColor(context, b);
CGContextDrawPath(context, rect)
I have a square image 40x40 that I want to make round via clipping, but also put a black 5 pixel border around the image.
I have the following which is masking the square image so its now round
UIImage *image = self.imageView.image;
CGSize imageSize = image.size;
CGRect imageRect = CGRectMake(0, 0, imageSize.width, imageSize.height);
UIGraphicsBeginImageContextWithOptions(imageSize, NO, 0.0);
// Create the clipping path and add it
UIBezierPath *path = [UIBezierPath bezierPathWithOvalInRect:imageRect];
[path addClip];
[image drawInRect:imageRect];
UIImage *roundedImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
self.imageView.image = roundedImage;
But now I need to also add a round border around it. Do I need a new path or can I just tack onto the one one in the code above?
Add the following three lines in your code (with whatever color and stroke width you want):
CGContextSetStrokeColorWithColor(ctx, [[UIColor greenColor] CGColor]);
[path setLineWidth:50.0f];
[path stroke];
So it becomes:
UIGraphicsBeginImageContextWithOptions(imageSize, NO, 0.0);
CGContextRef ctx = UIGraphicsGetCurrentContext();
// Create the clipping path and add it
UIBezierPath *path = [UIBezierPath bezierPathWithOvalInRect:imageRect];
[path addClip];
[image drawInRect:imageRect];
CGContextSetStrokeColorWithColor(ctx, [[UIColor greenColor] CGColor]);
[path setLineWidth:50.0f];
[path stroke];
UIImage *roundedImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
self.imageView.image = roundedImage;
I am creating a drawing application like this, but I have one issue. The problem is that when I draw a line with one color and then draw a line with another color, it gives me combination of both colors where the lines intersect.
- (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);
CGContextStrokePath(context);
[super drawRect:rect];
[curImage release];
}
The rest of the project is available on GitHub.
Maybe have a look at blend modes.
https://developer.apple.com/library/mac/#documentation/graphicsimaging/Reference/CGContext/Reference/reference.html#//apple_ref/c/tdef/CGBlendMode
This question already has answers here:
Assigning an existing CGColor to a CGColor property works in iOS Simulator, not iOS device. Why?
(3 answers)
Closed 9 years ago.
I have a custom UIButton class that adds a gradient and a gloss effect to the UIButton
the code works perfectly in iOS 4 and on iOS5 simulator but when i run it on iOS 5 devices it gives me the exception EXC_BAD_ACCESS , the exception is triggered by the line :
CGContextStrokePath(context);
any help is really appreciated ,
here's my code
- (void)drawRect:(CGRect)rect {
CGContextRef context = UIGraphicsGetCurrentContext();
CGFloat actualBrightness = _brightness;
if (self.selected) {
actualBrightness -= 0.10;
}
CGColorRef blackColor = [UIColor colorWithRed:0.0 green:0.0 blue:0.0 alpha:1.0].CGColor;
CGColorRef highlightStart = [UIColor colorWithRed:1.0 green:1.0 blue:1.0 alpha:0.7].CGColor;
CGColorRef highlightStop = [UIColor colorWithRed:0.0 green:0.0 blue:0.0 alpha:0.0].CGColor;
CGColorRef outerTop = [UIColor colorWithHue:_hue saturation:_saturation brightness:1.0*actualBrightness alpha:1.0].CGColor;
CGColorRef outerBottom = [UIColor colorWithHue:_hue saturation:_saturation brightness:0.80*actualBrightness alpha:1.0].CGColor;
CGFloat outerMargin = 7.5f;
CGRect outerRect = CGRectInset(self.bounds, outerMargin, outerMargin);
CGMutablePathRef outerPath = createRoundedRectForRect(outerRect, 6.0);
// Draw gradient for outer path
CGContextSaveGState(context);
CGContextAddPath(context, outerPath);
CGContextClip(context);
drawLinearGradient(context, outerRect, outerTop, outerBottom);
CGContextRestoreGState(context);
if (!self.selected) {
CGRect highlightRect = CGRectInset(outerRect, 1.0f, 1.0f);
CGMutablePathRef highlightPath = createRoundedRectForRect(highlightRect, 6.0);
CGContextSaveGState(context);
CGContextAddPath(context, outerPath);
CGContextAddPath(context, highlightPath);
CGContextEOClip(context);
drawLinearGradient(context, CGRectMake(outerRect.origin.x, outerRect.origin.y, outerRect.size.width, outerRect.size.height/3), highlightStart, highlightStop);
CGContextRestoreGState(context);
drawCurvedGloss(context, outerRect, 180);
CFRelease(highlightPath);
}
else {
//reverse non-curved gradient when pressed
CGContextSaveGState(context);
CGContextAddPath(context, outerPath);
CGContextClip(context);
drawLinearGloss(context, outerRect, TRUE);
CGContextRestoreGState(context);
}
if (!_toggled) {
//bottom highlight
CGRect highlightRect2 = CGRectInset(self.bounds, 6.5f, 6.5f);
CGMutablePathRef highlightPath2 = createRoundedRectForRect(highlightRect2, 6.0);
CGContextSaveGState(context);
CGContextSetLineWidth(context, 0.5);
CGContextAddPath(context, highlightPath2);
CGContextAddPath(context, outerPath);
CGContextEOClip(context);
drawLinearGradient(context, CGRectMake(self.bounds.origin.x, self.bounds.size.height-self.bounds.size.height/3, self.bounds.size.width, self.bounds.size.height/3), highlightStop, highlightStart);
CGContextRestoreGState(context);
CFRelease(highlightPath2);
}
else {
//toggle marker
CGRect toggleRect= CGRectInset(self.bounds, 5.0f, 5.0f);
CGMutablePathRef togglePath= createRoundedRectForRect(toggleRect, 8.0);
CGContextSaveGState(context);
CGContextSetLineWidth(context, 3.5);
CGContextSetStrokeColorWithColor(context, [UIColor whiteColor].CGColor);
CGContextAddPath(context, togglePath);
CGContextStrokePath(context);
CGContextRestoreGState(context);
CFRelease(togglePath);
}
// Stroke outer path
CGContextSaveGState(context);
CGContextSetLineWidth(context, 0.5);
CGContextSetStrokeColorWithColor(context, blackColor);
CGContextAddPath(context, outerPath);
CGContextStrokePath(context);
CGContextRestoreGState(context);
CFRelease(outerPath);
}
what I really wanna now is that is it really related to iOS 5 or am i doing something else wrong ?
The crash happens because the UIColors are already deallocated when you access the CGColorRefs.
An easy way to avoid this is to use
UIColor* blackColor = [UIColor blackColor];
CGContextSetStrokeColorWithColor(context, [blackColor CGColor]);
instead of
CGColorRef* blackColor = [[UIColor blackColor] CGColor];
CGContextSetStrokeColorWithColor(context, blackColor);
so ARC doesn't get the chance to deallocate the UIColor objects early.
I've been using the code below to draw on an UIView
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetStrokeColorWithColor(context, [UIColor blueColor].CGColor);
CGContextSetRGBFillColor(context, 0.0, 0.0, 1.0, 1.0);
CGContextSetAlpha(context, 0.5);
CGContextSetLineWidth(context, 10.0);
CGContextMoveToPoint(context, point.x, point.y);
CGContextAddLineToPoint(context, point.x, point.y);
CGContextStrokePath(context);
So that part works.
Now how do I clear my drawing? Most of the example just shows the drawing. Having a hardtime finding keyword to google.
Thanks,
Tee
Have you tried CGContextClearRect?
[self setNeedsDisplay];
it sends the message drawRect: to the view.