Clear remnants of drawRect() in custom UIControl - ios

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.

Related

How do I get the last point of CGContext in Objective-C?

I'm working on a custom progress bar and I want to get the last point of CGContext because I want to add an image to current state.
This is my code:
-(void)drawRect:(CGRect)rect {
CGContextRef ctx = UIGraphicsGetCurrentContext();
CGPoint center = CGPointMake(rect.size.width/2, rect.size.height/2);
float minSize = MIN(rect.size.width, rect.size.height);
float lineWidth = _strokeWidth;
if(lineWidth == -1.0) lineWidth = minSize*_strokeWidthRatio;
float radius = (minSize-lineWidth)/2;
float endAngle = M_PI*(self.value*2);
//what should i do here
//_pont.center = CGPointMake
CGContextSaveGState(ctx);
CGContextTranslateCTM(ctx, center.x, center.y);
CGContextRotateCTM(ctx, -M_PI*0.5);
CGContextSetLineWidth(ctx, lineWidth);
CGContextSetLineCap(ctx, kCGLineCapRound);
// "Full" Background Circle:
CGContextBeginPath(ctx);
CGContextAddArc(ctx, 0, 0, radius, 0, 2*M_PI, 0);
CGContextSetStrokeColorWithColor(ctx, [_color colorWithAlphaComponent:0.1].CGColor);
CGContextStrokePath(ctx);
// Progress Arc:
CGContextBeginPath(ctx);
CGContextAddArc(ctx, 0, 0, radius, 0, endAngle, 0);
CGContextSetStrokeColorWithColor(ctx, [_color colorWithAlphaComponent:0.9].CGColor);
CGContextStrokePath(ctx);
CGContextRestoreGState(ctx);
}
You have to use CGContextGetPathCurrentPoint, but you have to call it before you clear the path by calling stroke or fill. So look at the following playground and see what the value returned by CGContextGetPathCurrentPoint return varies by when you call it.
import CoreGraphics
import UIKit
let lineWidth = CGFloat(10)
let rect = CGRectMake(0, 0, 100, 100)
let size = CGSizeMake(100, 100)
UIGraphicsBeginImageContextWithOptions(size, false, 0.0)
let ctx = UIGraphicsGetCurrentContext()
let center = CGPointMake(rect.size.width/2, rect.size.height/2);
let minSize = fmin(rect.size.width, rect.size.height);
let radius = (minSize-10)/2;
let endAngle = CGFloat(M_PI*(20.0*2))
CGContextSaveGState(ctx);
CGContextTranslateCTM(ctx, center.x, center.y);
CGContextRotateCTM(ctx, CGFloat(-M_PI*0.5));
CGContextSetLineWidth(ctx, lineWidth);
CGContextSetLineCap(ctx, CGLineCap.Round);
// "Full" Background Circle:
CGContextBeginPath(ctx);
CGContextAddArc(ctx, 0, 0, radius, 0, CGFloat(2*M_PI), 0);
CGContextSetStrokeColorWithColor(ctx, UIColor.greenColor().CGColor);
var endPoint = CGContextGetPathCurrentPoint(ctx)
CGContextStrokePath(ctx);
endPoint = CGContextGetPathCurrentPoint(ctx)
// Progress Arc:
CGContextBeginPath(ctx);
CGContextAddArc(ctx, 0, 0, radius, 0, endAngle, 0);
endPoint = CGContextGetPathCurrentPoint(ctx)
CGContextSetStrokeColorWithColor(ctx, UIColor.redColor().CGColor);
CGContextStrokePath(ctx);
CGContextRestoreGState(ctx);
let image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
It's all simple geometry.
With context transformation matrix from your drawRect: code, you should use:
CGPoint lastPoint = CGPointMake(radius * cos(endAngle), radius * sin(endAngle));
After reset or before setup CTM, you should use:
//Replace "what should i do here" comment with following code:
CGPoint lastPoint = CGPointZero;
lastPoint.x = center.x + radius * cos(endAngle - M_PI * 0.5);
lastPoint.y = center.y + radius * sin(endAngle - M_PI * 0.5);
Also, second solution will work outside of drawRect: implementation.

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);
}

Is Point Inside Rounded Rectangle?

I have this code to draw rounded rectangles:
void ContextAddRoundedRect(CGContextRef ctx, CGRect rect, CGFloat radius) {
CGFloat minX = CGRectGetMinX(rect);
CGFloat maxX = CGRectGetMaxX(rect);
CGFloat minY = CGRectGetMinY(rect);
CGFloat maxY = CGRectGetMaxY(rect);
CGContextMoveToPoint(ctx, minX + radius, minY);
CGContextAddArcToPoint(ctx, maxX, minY, maxX, minY + radius, radius);
CGContextAddArcToPoint(ctx, maxX, maxY, maxX - radius, maxY, radius);
CGContextAddArcToPoint(ctx, minX, maxY, minX, maxY - radius, radius);
CGContextAddArcToPoint(ctx, minX, minY, minX + radius, minY, radius);
}
how can I figure out if a given CGPoint is inside my rect, considering the rounded corners?
Instead of building it directly into the CGContextRef like this, you can build it instead as a UIBezierPath, and then use -[UIBezierPath containsPoint:].
When you're ready to actually render your path into the context, you can do so via:
CGContextAddPath(ctx, [myBezierPath CGPath]);

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

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);
}

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];

Resources