UIView drawRect: is it possible to stroke inside a path? - uiview

With core graphics, is it possible to stroke the inside of a path? As opposed to the line weight being drawn half on the outside and half on the inside of a stroked path?
The reason being it would be easier to control the visible thickness of the stroke if say part of a view is on the screen edge and part is not. The part on the screen edge gets cut off, while the view edge that is fully on screen looks thicker (as both sides if the stroke are visible).

Clip to the path before you stroke it.

This draws no stroke:
- (void)drawRect:(CGRect)rect
{
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetStrokeColorWithColor(context, [UIColor darkGrayColor].CGColor);
CGContextSetLineWidth(context, 14);
CGRect rrect = CGRectMake(CGRectGetMinX(rect), CGRectGetMinY(rect), CGRectGetWidth(rect), CGRectGetHeight(rect));
CGFloat radius = 30;
CGFloat minx = CGRectGetMinX(rrect), midx = CGRectGetMidX(rrect), maxx = CGRectGetMaxX(rrect);
CGFloat miny = CGRectGetMinY(rrect), midy = CGRectGetMidY(rrect), maxy = CGRectGetMaxY(rrect);
CGContextMoveToPoint(context, minx, midy);
CGContextAddArcToPoint(context, minx, miny, midx, miny, radius);
CGContextAddArcToPoint(context, maxx, miny, maxx, midy, radius);
CGContextAddArcToPoint(context, maxx, maxy, midx, maxy, radius);
CGContextAddArcToPoint(context, minx, maxy, minx, midy, radius);
CGContextClosePath(context);
CGContextClip(context);
CGContextDrawPath(context, kCGPathStroke);
}
EDIT: This is what worked (according to the correct answer):
- (void)drawRect:(CGRect)rect
{
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetStrokeColorWithColor(context, [UIColor darkGrayColor].CGColor);
CGContextSetFillColorWithColor(context, [UIColor blackColor].CGColor);
CGContextSetLineWidth(context, 14);
CGRect pathRect = CGRectMake(10, 10, rect.size.width -20, rect.size.height -20);
CGPathRef path = [UIBezierPath bezierPathWithRoundedRect:pathRect cornerRadius:20].CGPath;
CGContextAddPath(context, path);
CGContextClip(context);
CGContextAddPath(context, path);
CGContextDrawPath(context, kCGPathEOFillStroke);
}

Related

DrawRect fits 4" screen, but runs off the bottom of 3.5"

Ive Created a DrawRect that fits the screen perfectly when running on a 4" screen. but when applied to a 3.5" the size changes and cant be constrained. Is there a way i can maintain its shape on both? Below is my code:
- (void)drawRect:(CGRect)rect
{
[super drawRect:rect];
CGContextRef context = UIGraphicsGetCurrentContext();
CGRect rrect = CGRectMake(20, 30, 280, 515);
CGFloat radius = 7;
CGFloat minx = CGRectGetMinX(rrect), midx = CGRectGetMidX(rrect), maxx = CGRectGetMaxX(rrect);
CGFloat miny = CGRectGetMinY(rrect), midy = CGRectGetMidY(rrect), maxy = CGRectGetMaxY(rrect);
// Start at 1
CGContextMoveToPoint(context, minx, midy);
// Add an arc through 2 to 3
CGContextAddArcToPoint(context, minx, miny, midx, miny, radius);
// Add an arc through 4 to 5
CGContextAddArcToPoint(context, maxx, miny, maxx, midy, radius);
// Add an arc through 6 to 7
CGContextAddArcToPoint(context, maxx, maxy, midx, maxy, radius);
// Add an arc through 8 to 9
CGContextAddArcToPoint(context, minx, maxy, minx, midy, radius);
// Close the path
CGContextClosePath(context);
CGContextSetStrokeColorWithColor(context, [[UIColor whiteColor] CGColor]);
CGContextSetRGBFillColor(context, 255, 255, 255, 1.0);
// Fill & stroke the path
CGContextDrawPath(context, kCGPathFillStroke);
}

Core Graphics drawing a square result in rectangle

I am trying to draw a square in my context, but it results in a rectangle with a short and longer side. (in my ipad retina simulator)
This is part of the drawRect method of my view object:
CGContextRef context = UIGraphicsGetCurrentContext();
CGFloat minx = 0;
CGFloat maxx = 45 - 1;
CGFloat miny = 0;
CGFloat maxy = 45 - 1;
CGContextSetLineWidth(context, 1.0);
CGContextSetStrokeColorWithColor(context, [UIColor whiteColor].CGColor);
CGContextMoveToPoint(context, minx, maxy);
CGContextAddLineToPoint(context, minx, miny);
CGContextAddLineToPoint(context, maxx, miny);
CGContextStrokePath(context);
CGContextSetStrokeColorWithColor(context, [UIColor redColor].CGColor);
CGContextMoveToPoint(context, maxx, miny);
CGContextAddLineToPoint(context, maxx, maxy);
CGContextAddLineToPoint(context, minx, maxy);
CGContextStrokePath(context);
What am I doing wrong? Do I need to set an aspect ratio or something?
Edit:
I found out that
CGContextScaleCTM(context, 1, 0.75f);
makes the result square again, but the end result is not spot on.
Does anybody know why I need to correct the vertical scale?
Found it....
Turns out I was modifying the views rect elsewhere.
Disabled that, and now my squares are square again.
Thanks for the hint Ismael !!

Clear remnants of drawRect() in custom UIControl

I create a custom slider with using subclass of UIControl. I draw my background and a button in drawRect() but when I redraw my button, there are remnants of previous draw left.
How can I clear this?
I try to track memory leaks with instruments but I haven't found any leaks. My code is:
- (void)drawRect:(CGRect)rect
{
CGContextRef context = UIGraphicsGetCurrentContext();
CGFloat radius = 10;
// Make sure corner radius isn't larger than half the shorter side
if (radius > self.bounds.size.width/2.0) radius = self.bounds.size.width/2.0;
if (radius > self.bounds.size.height/2.0) radius = self.bounds.size.height/2.0;
CGFloat minx = CGRectGetMinX(self.bounds);
CGFloat midx = CGRectGetMidX(self.bounds);
CGFloat maxx = CGRectGetMaxX(self.bounds);
CGFloat miny = CGRectGetMinY(self.bounds);
CGFloat maxy = CGRectGetMaxY(self.bounds);
CGContextMoveToPoint(context, maxx, miny);
CGContextAddArcToPoint(context, minx, miny+ 30, minx, miny+150, radius);
CGContextAddArcToPoint(context, minx, maxy-30, minx+30, maxy, radius);
CGContextAddLineToPoint (context, maxx, maxy);
CGContextClosePath(context);
CGContextSetRGBFillColor(context,
0.0, 0.0, 0.0, 0.2 );
CGContextSetRGBStrokeColor( context,
0.7, 0.7, 0.7, 0.4 );
CGContextDrawPath(context, kCGPathFill);
CGContextSetLineWidth(context, 10);
CGContextSetLineCap(context, kCGLineCapRound);
CGContextMoveToPoint(context, midx, miny+ 80);
CGContextAddLineToPoint(context, midx, maxy-50);
CGContextStrokePath(context);
[self drawButton: _buttonRect inContext:context];
}
and the drawButton() function:
- (void)drawButton:(CGRect)rect inContext:(CGContextRef) context{
UIGraphicsPushContext(context);
CGFloat radius = 7;
CGFloat minx = CGRectGetMinX(rect);
CGFloat midx = CGRectGetMidX(rect);
CGFloat maxx = CGRectGetMaxX(rect);
CGFloat miny = CGRectGetMinY(rect);
CGFloat midy = CGRectGetMidY(rect);
CGFloat maxy = CGRectGetMaxY(rect);
CGContextMoveToPoint(context, minx, midy);
CGContextAddArcToPoint(context, minx, miny, midx, miny, radius);
CGContextAddArcToPoint(context, maxx, miny, maxx, midy, radius);
CGContextAddArcToPoint(context, maxx, maxy, midx, maxy, radius);
CGContextAddArcToPoint(context, minx, maxy, minx, midy, radius);
CGContextClosePath(context);
CGContextSetRGBFillColor(context,
1.0, 1.0, 1.0, 1.0 );
CGContextDrawPath(context, kCGPathFill);
CGContextSetRGBStrokeColor(context,
1.0, 0.0, 0.0, 1.0 );
//Gradient related variables
size_t locationCount = 3;
CGFloat locationList[] = {0.0,0.5,1.0};
CGFloat colorList[] = {
0.0, 0.0, 0.0, 1.0,
0.5, 0.5, 0.5, 1.0,
1.0, 1.0, 1.0, 1.0
};
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
CGGradientRef gradient = CGGradientCreateWithColorComponents(colorSpace, colorList,
locationList, locationCount);
//Paint a linear gradient
CGPoint startPoint, endPoint;
startPoint.x = midx;
startPoint.y = midy-2.5;
endPoint.x = midx;
endPoint.y = midy+2.5;
CGContextSaveGState(context);
CGContextAddEllipseInRect(context, CGRectMake(midx-2.5, midy-2.5, 5, 5));
CGContextClosePath(context);
CGContextClip(context);
CGContextDrawLinearGradient (context, gradient, startPoint, endPoint, kCGGradientDrawsAfterEndLocation);
CGContextRestoreGState(context);
UIGraphicsPopContext();
}
I tried a lot of things but now I have no idea anymore. Thanks.

How to clip the rectangle around the circle when drawing with quartz2d?

I made of subclass of UIView called Bubble using which i want to draw a solid circle.Here is the drawRect: rect method
- (void)drawRect:(CGRect)rect
{
// Drawing code
CGContextRef ctx = UIGraphicsGetCurrentContext();
// Drawing with a white stroke color
CGContextSetRGBStrokeColor(ctx, 1.0, 1.0, 1.0, 1.0);
// And draw with a blue fill color
CGContextSetRGBFillColor(ctx, 0.0, 0.0, 1.0, 1.0);
// Draw them with a 2.0 stroke width so they are a bit more visible.
CGContextSetLineWidth(ctx, 2.0);
CGRect rrect = CGRectMake(self.bounds.origin.x, self.bounds.origin.y, self.bounds.size.width, self.bounds.size.height);
CGFloat radius = 30.0;
CGFloat minx = CGRectGetMinX(rrect), midx = CGRectGetMidX(rrect), maxx = CGRectGetMaxX(rrect);
CGFloat miny = CGRectGetMinY(rrect), midy = CGRectGetMidY(rrect), maxy = CGRectGetMaxY(rrect);
// Start at 1
CGContextMoveToPoint(ctx, minx, midy);
// Add an arc through 2 to 3
CGContextAddArcToPoint(ctx, minx, miny, midx, miny, radius);
// Add an arc through 4 to 5
CGContextAddArcToPoint(ctx, maxx, miny, maxx, midy, radius);
// Add an arc through 6 to 7
CGContextAddArcToPoint(ctx, maxx, maxy, midx, maxy, radius);
// Add an arc through 8 to 9
CGContextAddArcToPoint(ctx, minx, maxy, minx, midy, radius);
// Close the path
CGContextClosePath(ctx);
// Fill & stroke the path
CGContextDrawPath(ctx, kCGPathFillStroke);
}
I have taken the code from the apple sample Quartzdemo.
My problem is i am drawing circle inside a rectangle,but still i see the corners of the rectangle drawn.
How to clip them so that i see only circle? please help. i am new to quartz 2d
Here is how i call it in my viewDidLoad of my controller
- (void)viewDidLoad
{
[super viewDidLoad];
self.view.backgroundColor = [UIColor colorWithRed:0.5 green:0.5 blue:0.5 alpha:0.5];
Bubble *bub = [[Bubble alloc]initWithFrame:CGRectMake(210.0, 90.0, 60.0, 60.0)];
[self.view addSubview:bub];
[bub release];
}
Put this is viewDidLoad
bub.backgroundColor = [UIColor clearColor];

UIView CGGradient clipped to drawn "frame"

I'm trying to make a more sophisticated drawing of a UILabels (or a UIView, putting the UILabel on top, should that be required) background. Since I want this to resize automatically when the user changes iPhone orientation, I am trying to implement this in a subclasses drawRect function. I would love to be able to tune this all in code, omitting the need for pattern images, if it's possible.
I got some hints from some former posts on the subject:
Gradients on UIView and UILabels On iPhone
and
Make Background of UIView a Gradient Without Sub Classing
Unfortunately they both miss the target, since I wanted to combine gradient with custom drawing. I want the CGGradient to be clipped by the line frame I am drawing (visible at the rounded corners) but I can't accomplish this.
The alternative would be to use the CAGradientLayer, which seem to work quite good, but that would require some resising of the frame at rotation, and the gradient layer seem to draw on top of the text, no matter how I try.
My question therefor is twofold
How do I modify the code below to make the gradient clip to the drawn "frame"
How do I make CAGradientLayer draw behind the text of the UILabel (or do I have to put a UIView behind the UILabel with the CAGradientLayer as background)
Here is my drawrect function:
- (void)drawRect:(CGRect)rect {
// Drawing code
CGContextRef c = UIGraphicsGetCurrentContext();
CGContextSetFillColorWithColor(c, [[UIColor clearColor] CGColor]);
CGContextSetStrokeColorWithColor(c, [strokeColor CGColor]);
CGContextSetLineWidth(c, 1);
CGFloat minx = CGRectGetMinX(rect), midx = CGRectGetMidX(rect), maxx = CGRectGetMaxX(rect) ;
CGFloat miny = CGRectGetMinY(rect), midy = CGRectGetMidY(rect), maxy = CGRectGetMaxY(rect) ;
minx = minx + 1;
miny = miny + 1;
maxx = maxx - 1;
maxy = maxy - 1;
CGGradientRef glossGradient;
CGColorSpaceRef rgbColorspace;
size_t num_locations = 2;
CGFloat locations[2] = { 0.0, 1.0 };
CGFloat components[8] = { 0.6, 0.6, 0.6, 1.0, // Start color
0.3, 0.3, 0.3, 1.0 }; // End color
rgbColorspace = CGColorSpaceCreateDeviceRGB();
glossGradient = CGGradientCreateWithColorComponents(rgbColorspace, components, locations, num_locations);
CGRect currentBounds = [self bounds];
CGPoint topCenter = CGPointMake(CGRectGetMidX(currentBounds), 0.0f);
CGPoint midCenter = CGPointMake(CGRectGetMidX(currentBounds), CGRectGetMidY(currentBounds));
CGPoint lowCenter = CGPointMake(CGRectGetMidX(currentBounds), CGRectGetMidY(currentBounds));
CGContextDrawLinearGradient(c, glossGradient, topCenter, midCenter, 0);
CGGradientRelease(glossGradient);
CGColorSpaceRelease(rgbColorspace);
CGContextMoveToPoint(c, minx, midy);
CGContextAddArcToPoint(c, minx, miny, midx, miny, ROUND_SIZE_INFO);
CGContextAddArcToPoint(c, maxx, miny, maxx, midy, ROUND_SIZE_INFO);
CGContextAddArcToPoint(c, maxx, maxy, midx, maxy, ROUND_SIZE_INFO);
CGContextAddArcToPoint(c, minx, maxy, minx, midy, ROUND_SIZE_INFO);
// Close the path
CGContextClosePath(c);
// Fill & stroke the path
CGContextDrawPath(c, kCGPathFillStroke);
// return;
[super drawRect: rect];
What you are looking for is CGContextClip()
Create your path and gradient as in your code, and then
CGContextClip(c);
CGContextDrawLinearGradient(c, glossGradient, topCenter, midCenter, 0);
CGGradientRelease(glossGradient);
(Remove your old call to CGContextDrawLinearGradient)
Also remember to save and restore your context before and after you do any clipping
If your clipping is inverted from what you want, try CGContextEOClip() (or draw your path in reverse order)

Resources