CGContextStrokePath triggers EXC_BAD_ACCESS in iOS > 5 [duplicate] - objective-c

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.

Related

Draw lines in UIView not supported for multiple screen sizes?

I want to draw lines in a UIView with based the UIView's height. (i.e) line height should be equal to the UIView's height. For that i'm using the following code.
- (void)testDraw :(void(^)(UIImage *image, UIImage *selectedImage))done
{
CGSize imageSize = CGSizeMake(1000, self.bounds.size.height);
//Begin the animation by checking the screen is retina / ordinary
if ([[UIScreen mainScreen] respondsToSelector:#selector(scale)])
UIGraphicsBeginImageContextWithOptions(imageSize, NO, 0.0f);
else
UIGraphicsBeginImageContext(imageSize);
CGContextRef context = UIGraphicsGetCurrentContext();
for (NSInteger intSample=0; intSample < 1000; intSample++) {
if(intSample % self.samplesPerSecond == 0){
//draw separator line
[self drawLine:context fromPoint:CGPointMake(intSample, 0) toPoint:CGPointMake(intSample, self.frame.size.height) color:self.lineColor lineWidth:2.0];
}
}
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
CGRect drawRect = CGRectMake(0, 0, image.size.width, self.frame.size.height);
[self.progressColor set];
UIRectFillUsingBlendMode(drawRect, kCGBlendModeSourceAtop);
UIImage *tintedImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
done(image, tintedImage);
}
//Draw the line based on the value received
- (void) drawLine:(CGContextRef) context fromPoint:(CGPoint) fromPoint toPoint:(CGPoint) toPoint color:(UIColor*) color lineWidth:(float) lineWidth{
CGContextBeginPath(context);
//add the lines with sizes that u want
CGContextSetAlpha(context,1.0);
CGContextSetLineWidth(context, lineWidth);
CGContextSetStrokeColorWithColor(context, [color CGColor]);
CGContextMoveToPoint(context, fromPoint.x, fromPoint.y);
CGContextAddLineToPoint(context, toPoint.x, toPoint.y);
CGContextStrokePath(context);
}
When i execute the above code with it working fine in 3.5 inch display, It's working fine. But when i run the same code in 4-inch the line height is not increasing based on the UIView. The lines are not working as like auto layouts.
Thanks in advance, Can anyone give your suggestion to fix this issue.

draw a stroke around the shape, core graphic

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

Glowing effect in a UILabel using inner shadow

I am trying to achieve this glowing effect for a UILabel as shown below :
I have subclassed UILabel , and created a custom label class that adds an outer shadow.
Edit : Here's the code i have used for outer shadow/glow in my custom Label class :
- (void)drawTextInRect:(CGRect)rect {
UIColor *insideColor;
UIColor *blurColor;
CGContextRef ctx = UIGraphicsGetCurrentContext();
insideColor =[UIColor colorWithRed:255/255.0 green:255/255.0 blue:191/255.0 alpha:1];
blurColor =[UIColor orangeColor];
CGContextSetFillColorWithColor(ctx, insideColor.CGColor);
CGContextSetShadowWithColor(ctx, CGSizeMake(0, 0), self.glowAmount, blurColor.CGColor);
CGContextSetTextDrawingMode(ctx, kCGTextFillStroke);
[self.text drawInRect:self.bounds withFont:self.font lineBreakMode:self.lineBreakMode alignment:self.textAlignment];
}
But this gives me the following result
As you can see this lacks the desired effect because of the missing inner shadow. Can anyone suggest how to achieve this?
Thanks!
I referred the answer by Steven XM for Inner Shadow in UILabel . It was a great help.
Here's what i have done to achieve the result but i want to know if this can be more optimized?
-(void)setInnerGlowWithColor:(UIColor *)shadowColor fillColor:(UIColor *)insideColor inRect:(CGRect)rect
{
UIFont *font = self.font;
// UIFont *font = [UIFont fontWithName:#"HelveticaNeue-Bold" size:17];
CGSize fontSize = [self.text sizeWithFont:font];
/**** Following are the steps to create an inside shadow ****/
// STEP 1 : Create a image mask of your text.
CGImageRef mask = [self createMaskWithSize:rect.size shape:^{
[[UIColor blackColor] setFill];
CGContextFillRect(UIGraphicsGetCurrentContext(), rect);
[[UIColor whiteColor] setFill];
// custom shape goes here
[self.text drawAtPoint:CGPointMake((self.bounds.size.width/2)-(fontSize.width/2), 0) withFont:font];
}];
// STEP 2 : Invert that mask.
CGImageRef cutoutRef = CGImageCreateWithMask([self blackSquareOfSize:rect.size].CGImage, mask);
CGImageRelease(mask);
UIImage *cutout = [UIImage imageWithCGImage:cutoutRef scale:[[UIScreen mainScreen] scale] orientation:UIImageOrientationUp];
CGImageRelease(cutoutRef);
// STEP 3 : Use this inverted mask to draw a shadow around the inside edges of the text.
CGImageRef shadedMask = [self createMaskWithSize:rect.size shape:^{
[[UIColor whiteColor] setFill];
CGContextFillRect(UIGraphicsGetCurrentContext(), rect);
//****************For inner shadow/glow
NSLog(#"in custom label----> %f",self.glowAmount);
CGContextSetShadowWithColor(UIGraphicsGetCurrentContext(), CGSizeMake(0, 0), self.glowAmount, shadowColor.CGColor);
[cutout drawAtPoint:CGPointZero];
}];
// STEP 4 : Create negative image
UIGraphicsBeginImageContextWithOptions(rect.size, NO, 0);
[shadowColor setFill];
// custom shape goes here
[self.text drawAtPoint:CGPointMake((self.bounds.size.width/2)-(fontSize.width/2), 0) withFont:font];
UIImage *negative = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
// STEP 5 : Create the shadow image
CGImageRef innerShadowRef = CGImageCreateWithMask(negative.CGImage, shadedMask);
CGImageRelease(shadedMask);
UIImage *innerShadow = [UIImage imageWithCGImage:innerShadowRef scale:[[UIScreen mainScreen] scale] orientation:UIImageOrientationUp];
CGImageRelease(innerShadowRef);
// STEP 6 : Draw actual text
[insideColor setFill];
[self.text drawAtPoint:CGPointMake((self.bounds.size.width/2)-(fontSize.width/2), 0) withFont:font];
// STEP 7 : Finally apply the shadow image
[innerShadow drawAtPoint:CGPointZero];
}
- (UIImage*)blackSquareOfSize:(CGSize)size {
UIGraphicsBeginImageContextWithOptions(size, NO, 0);
[[UIColor blackColor] setFill];
CGContextFillRect(UIGraphicsGetCurrentContext(), CGRectMake(0, 0, size.width, size.height));
UIImage *blackSquare = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return blackSquare;
}
- (CGImageRef)createMaskWithSize:(CGSize)size shape:(void (^)(void))block {
UIGraphicsBeginImageContextWithOptions(size, NO, 0);
block();
CGImageRef shape = [UIGraphicsGetImageFromCurrentImageContext() CGImage];
UIGraphicsEndImageContext();
CGImageRef mask = CGImageMaskCreate(CGImageGetWidth(shape),
CGImageGetHeight(shape),
CGImageGetBitsPerComponent(shape),
CGImageGetBitsPerPixel(shape),
CGImageGetBytesPerRow(shape),
CGImageGetDataProvider(shape), NULL, false);
return mask;
}

CoreGraphics FillPath And Stroke Path

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.

Objective C: Draw(Write) on Touch in a UIScrollView

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.