I'm trying to draw some bezier paths in my view's drawRect method, but I keep getting an invalid context error. I don't understand why, because I thought that there was always a context when drawRect is called. Am I missing something? This is the full implementation I have:
- (void)drawRect:(CGRect)rect
{
CGContextRef context = UIGraphicsGetCurrentContext();
CGFloat pattern[] = {5.0, 3.0};
CGContextSetLineDash(context, 0.0, pattern, 2);
// Set line color to be black
[[UIColor blackColor] setStroke];
//Stroke the bezier path for each plane
for(SEPlaneView *plane in self.planes){
// Set line thickness and termination style
[plane.planeBezier setLineWidth:1.0];
[plane.planeBezier setLineCapStyle:kCGLineCapRound];
[plane.planeBezier stroke];
}
CGContextRelease(context);
}
Related
I am trying to draw a Bezier path, an arc of a given angle, in response to a pan gesture. Here's what I have inside the function called by the pan gesture:
UIGraphicsBeginImageContext(self.frame.size);
UIBezierPath *path = [self createArcPath];
[[UIColor blackColor] setStroke];
[[UIColor redColor] setFill];
CGContextRef *ref = UIGraphicsGetCurrentContext();
path.lineWidth = 5;
[path fill];
[path stroke];
UIGraphicsEndImageContext();
And here is createArcPath:
- (UIBezierPath *)createArcPath
{
UIBezierPath *aPath = [UIBezierPath bezierPathWithArcCenter:CGPointMake(150, 150)
radius:75
startAngle:0
endAngle:DEGREES_TO_RADIANS(135)
clockwise:YES];
return aPath;
}
I don't get an error, but I just don't get anything on my screen relating to the arc. What am I missing? This is in a method of a UIView subclass.
I also tried the following method in - (id)initWithFrame:
UIColor *color =[UIColor redColor];
UIGraphicsBeginImageContext(self.frame.size);
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetLineWidth(context, width);
CGContextSetFillColorWithColor(context, color.CGColor);
CGContextSetStrokeColorWithColor(context, color.CGColor);
CGRect myOval = {100, 100, self.radius, self.radius};
CGContextAddEllipseInRect(context, myOval);
UIGraphicsEndImageContext();
I also don't see anything. What am I missing? Can these methods not be called from a UIView?
The UIGraphicsBeginImageContext/UIGraphicsEndImageContext calls create an off-screen context that you can draw into, then extract an image from. Since it's an off-screen context nothing shows on the screen.
What you probably want to do is create a custom subclass of UIView that overrides drawRect. In your code that responds to user gestures, change the data structure(s) that represent your drawing, then call setNeedsDisplay to trigger the system to ask your view to redraw itself by calling your drawRect method.
As an alternative you can set up your custom view to install a CAShapeLayer in the view, then change the path on the layer, then call setNeedsDisplay on the layer.
I am looking for the way how to draw a hollow circle using Core Graphics (CGContext) in iOS. I tried to do it using the code:
- (void)drawRect:(CGRect)rect {
[super drawRect:rect];
CGContextRef ctx = UIGraphicsGetCurrentContext();
CGContextSetLineWidth(ctx, 8.0);
CGContextSetStrokeColor(ctx, CGColorGetComponents([[UIColor redColor] CGColor]));
CGContextSetFillColor(ctx, CGColorGetComponents([[UIColor blueColor] CGColor]));
CGContextFillEllipseInRect(ctx, rect);
CGContextFillPath(ctx);
}
but it draws the filled circle.
Since this question is over discussed here but other examples like this give me only filled circles.
If you want to draw a circle within the rect, using Stroke methods and the rect in parameter you will draw a part of the cercle outside the rect.
You then have two choices, assuming you know the width of the circle you want to draw.
First, you can stroke the line, but you have to draw it in a smaller rect :
-(void)drawRect:(CGRect)rect {
CGContextRef ctx = UIGraphicsGetCurrentContext();
CGFloat innerWidth = 8;
[[UIColor redColor] setStroke];
CGContextSetLineWidth(ctx, innerWidth);
// We add an ellipsis shifted by half the inner Width
CGContextAddEllipseInRect(ctx, CGRectInset(rect, innerWidth, innerWidth));
// Stroke the path
CGContextStrokePath(ctx);
}
You can also fill the circles using the even-odd rule (Apple docs)
-(void)drawRect:(CGRect)rect {
CGContextRef ctx = UIGraphicsGetCurrentContext();
CGFloat innerWidth = 8;
[[UIColor redColor] setStroke];
// Add the outer circle
CGContextAddEllipseInRect(ctx, rect);
// Add the inner circle
CGContextAddEllipseInRect(ctx, CGRectInset(rect, innerWidth, innerWidth));
// Fill the path using the EO rule
CGContextEOFillPath(ctx);
}
If you only want the circle outline, don't fill it.
- (void)drawRect:(CGRect)rect {
[super drawRect:rect];
CGContextRef ctx = UIGraphicsGetCurrentContext();
CGContextSetLineWidth(ctx, 8.0);
[[UIColor redColor] set];
CGContextStrokeEllipseInRect(ctx, rect);
}
Use CGContextStrokeEllipseInRect instead of CGContextFillEllipseInRect. And since you haven't built a path in the current context, get rid of CGContextFillPath.
One way you could do it is draw a filled circle and then draw a slightly smaller circle with CGContextSetBlendMode(UIGraphicsGetCurrentContext(),kCGBlendModeClear)
After your code add something like
CGContextRef ctx = UIGraphicsGetCurrentContext();
CGContextSetLineWidth(ctx, 5.0);
CGContextSetBlendMode(ctx,kCGBlendModeClear)
CGContextSetFillColor(ctx, CGColorGetComponents([[UIColor blueColor] CGColor]));
CGContextFillEllipseInRect(ctx, rect);
CGContextFillPath(ctx);
UIGraphicsEndImageContext();
I use CGContextSetBlendMode(UIGraphicsGetCurrentContext(),kCGBlendModeClear) in my drawing application, which is probably a better use. Stroking the path will probably work for you.
I am overriding drawRect method in order to draw ellipse
- (void)drawRect:(CGRect)rect {
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetLineWidth(context, 2.0);
CGContextSetStrokeColorWithColor(context, [UIColor mainColorYellow].CGColor);
CGRect rectangle = CGRectMake(0.0, 2.0, rect.size.width , rect.size.height - 4.0);
CGContextAddEllipseInRect(context, rectangle);
CGContextStrokePath(context);
}
How can I change fill color on ellipse? I tried using CGContextSetFillColorWithColor(CGContextRef c, CGColorRef color) but without result.
There is two different operations, fill and stroke. Stroke es the border, fill is the inside. You are only doing the border. You can control the fill and border color independently. You have, however, to render both explicitly. For example:
[[UIColor yellowColor] setFill];
[[UIColor blackColor] setStroke];
UIBezierPath *path = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(0.0, 2.0, rect.size.width , rect.size.height - 4.0)];
path.lineWidth = 2.0;
[path fill];
[path stroke];
Notice I wrote the code in a more modern quartz. The context is implicit. Hope it helps.
Just add below:
CGContextFillEllipseInRect(context, rectangle);
I have a UIView that draws different shapes. I can make a hole in my image to have it transparent, working fine, but the hole is only square,
//hole
CGRect holeRectValue = CGRectMake(300, 40, 80, 100);
CGRect holeRectIntersection = CGRectIntersection( holeRectValue, rect );
[[UIColor clearColor] setFill];
UIRectFill(holeRectIntersection);
Now I need to make the hole in the image but not as a rect, as the shape of my drawn figure,
Here is the code:
- (id)initWithFrame:(CGRect)frame forNode0:(CGPoint)node0 forNode1:(CGPoint)node1 fornode2:(CGPoint)node2 fornode3:(CGPoint)node3 fornode4:(CGPoint)node4 fornode5:(CGPoint)node5 fornode6:(CGPoint)node6 fornode7:(CGPoint)node7 fornode8:(CGPoint)node8 fornode9:(CGPoint)node9
{
self = [super initWithFrame:frame];
if (self) {
self.opaque = NO;
self.node0Pos = node0;
self.node1Pos = node1;
self.node2Pos = node2;
self.node3Pos = node3;
self.node4Pos = node4;
self.node5Pos = node5;
}
return self;
}
// Only override drawRect: if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
- (void)drawRect:(CGRect)rect
{
//bgnd
UIGraphicsBeginImageContext(self.frame.size);
[[UIImage imageNamed:#"cat.jpeg"] drawInRect:self.bounds];
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
[[UIColor colorWithPatternImage:image] setFill];
// Drawing code
UIRectFill(rect);
// Drawing code
//1. begin new path
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextBeginPath(context);
//2. move to initial starting point
CGContextMoveToPoint(context, self.node0Pos.x, self.node0Pos.y);
//3. add lines defining shape
CGContextAddLineToPoint(context, self.node1Pos.x, self.node1Pos.y);
CGContextAddLineToPoint(context, self.node2Pos.x, self.node2Pos.y);
CGContextAddLineToPoint(context, self.node3Pos.x, self.node3Pos.y);
CGContextAddLineToPoint(context, self.node4Pos.x, self.node4Pos.y);
CGContextAddLineToPoint(context, self.node5Pos.x, self.node5Pos.y);
//4. optionally close path
CGContextClosePath(context);
CGColorRef color;
//5. draw path
color = [UIColor colorWithRed:1 green:0 blue:228/255.0f alpha:0.5].CGColor;
//CGContextSetFillColor(context, color);
CGContextSetFillColorWithColor(context, color);
CGContextDrawPath(context, kCGPathFill);
//
//hole
CGRect holeRectValue = CGRectMake(300, 40, 80, 100);
CGRect holeRectIntersection = CGRectIntersection( holeRectValue, rect );
[[UIColor clearColor] setFill];
UIRectFill(holeRectIntersection);
}
So how do I make the "hole" with my actual context path?
P.S. I was doing some masking, but it leaves a white line around the hole, so I need to avoid this.
[[UIColor clearColor] setFill];
UIRectFill(holeRectIntersection);
This doesn’t do anything—drawing with a clear color is effectively a no-op. What you most likely want to do is add the rectangle you’re trying to cut out as part of the path you’re creating, i.e. inserting a call to CGContextAddRect before your CGContextClosePath. See Filling a Path in the Quartz 2D Programming Guide.
I believe what you're looking for is the CALayer.mask property. To create a "hole", you would generate a CALayer object with an alpha channel in the shape of the hole you want to make, and then apply it to the view you want to punch the hole in.
In semi-pseudocode:
CALayer *holeMask;
UIView *myView;
//
// Build the holeMask object via whatever means,
// and set myView to the target view that you want
// to punch the hole in...
//
myView.layer.mask = holeMask
I want to fill the remaining part of a CGCntext after drawing a rect? How can I do that? Thank you!
But the thing is, I set the blend mode of the cgcontext kCGBlendModeClear. I want to make the little rect in the context to transparent. If a draw the background first, can I still see the image in the rect?
If you want to fill the context (whose frame is bigRect), except for a rectangle inside it (whose frame is smallRect):
CGContextBeginPath(context);
CGContextAddRect(context, bigRect);
CGContextAddRect(context, smallRect);
CGContextSetFillColorWithColor(context, color);
CGContextEOFillPath(context, bigRect);
Do the background first...
CGRect bounds = [self bounds];
[[UIColor blackColor] set];
UIBezierPath* backgroundPath = [UIBezierPath bezierPathWithRect:bounds];
[backgroundPath fill];
CGRect innerRect = CGRectMake(self.bounds-10,
self.bounds-10,
self.bounds.width-20
self.bounds.height-20);
[[UIColor redColor] set];
UIBezierPath* foregroundPath = [UIBezierPath bezierPathWithRect:innerRect];
[foregroundPath fill];
Draw your background and then use CGContextClearRect to clear the area you want to be transparent.
arbitrary shape:
// do your foreground drawing
CGPathRef arbitraryShape; // asign your arbitrary shape
CGContextBeginPath(ctx);
CGContextAddRect(ctx, bounds); // rect in full size of the context
CGContextAddPath(ctx, arbitraryShape); // set the area you dont want to be drawn on
CGContextClip(ctx);
// do your background drawing