5

I'm using CoreGraphics to draw a line. I have just updated Xcode 10 and iOS 12. In iOS 12, my Application is crashed. Although, it still works well in iOS 11.4.1 and version before. I am not able to solve the issue. please find my code below

-(void)drawRect:(CGRect)rect
{
    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);

    CGContextSetShadow(context, CGSizeMake(-16.00, -5.0f), 5.0f);

    CGContextStrokePath(context);

    [super drawRect:rect];
    [curImage release];
} 

Below is the touchesMoved method.

-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{

    UITouch *touch  = [touches anyObject];

    previousPoint2  = previousPoint1;
    previousPoint1  = [touch previousLocationInView:self];
    currentPoint    = [touch locationInView:self];

    // calculate mid point
    CGPoint mid1    = midPoint(previousPoint1, previousPoint2); 
    CGPoint mid2    = midPoint(currentPoint, previousPoint1);

    CGMutablePathRef path = CGPathCreateMutable();
    CGPathMoveToPoint(path, NULL, mid1.x, mid1.y);
    CGPathAddQuadCurveToPoint(path, NULL, previousPoint1.x, previousPoint1.y, mid2.x, mid2.y);
    CGRect bounds = CGPathGetBoundingBox(path);
    CGPathRelease(path);

    CGRect drawBox = bounds;

    //Pad our values so the bounding box respects our line width
    drawBox.origin.x        -= self.lineWidth * 2;
    drawBox.origin.y        -= self.lineWidth * 2;
    drawBox.size.width      += self.lineWidth * 4;
    drawBox.size.height     += self.lineWidth * 4;

    UIGraphicsBeginImageContext(drawBox.size);
    [self.layer renderInContext:UIGraphicsGetCurrentContext()];
    curImage = UIGraphicsGetImageFromCurrentImageContext();
    [curImage retain];
    UIGraphicsEndImageContext();
   [self setNeedsDisplayInRect:drawBox];

}

CGPoint midPoint(CGPoint p1, CGPoint p2)
{
    return CGPointMake((p1.x + p2.x) * 0.5, (p1.y + p2.y) * 0.5);
}


-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{

    UITouch *touch = [touches anyObject];

    previousPoint1 = [touch previousLocationInView:self];
    previousPoint2 = [touch previousLocationInView:self];
    currentPoint = [touch locationInView:self];

    [self touchesMoved:touches withEvent:event];
}

The issue is code crash at [self.layer renderInContext:context]; of (void)drawRect:(CGRect)rect function. drawRect function is called too much time (About 5000 times) and Overload of Memory (About 20Gb) cause crashing app. Could there be changes in iOS 12 and Xcode 10?

I Fixed this issue. Update my new toucheMoved() function:

- (void) touchesMoved: (NSSet*) touches withEvent: (UIEvent*) event
{    
previousPoint2 = previousPoint;
previousPoint = [touch previousLocationInView:self];
location = [touch locationInView:self];

CGPoint mid1 = midPoint(previousPoint, previousPoint2);
CGPoint mid2 = midPoint(location, previousPoint);

CGMutablePathRef path = CGPathCreateMutable();
CGPathMoveToPoint(path, NULL, mid1.x, mid1.y);
CGPathAddQuadCurveToPoint(path, NULL, previousPoint.x, previousPoint.y, mid2.x, mid2.y);
CGRect bounds = CGPathGetBoundingBox(path);
CGPathRelease(path);

CGRect drawBox = bounds;

drawBox.origin.x -= self.lineWidth * 2;
drawBox.origin.y -= self.lineWidth * 2;
drawBox.size.width += self.lineWidth * 4;
drawBox.size.height += self.lineWidth * 4;

UIGraphicsBeginImageContextWithOptions(totalDrawBox.size, self.opaque, 0.0);
CGContextRef context = UIGraphicsGetCurrentContext();
CGAffineTransform affineMoveLeftTop = CGAffineTransformMakeTranslation(-(int)totalDrawBox.origin.x, -(int)totalDrawBox.origin.y);
CGContextConcatCTM(context, affineMoveLeftTop);
float iOSVersion = [[UIDevice currentDevice].systemVersion floatValue];
if (iOSVersion >= 12) {
    [self setNeedsDisplay];
} else {
    [self.layer renderInContext: context];
}
curImage = UIGraphicsGetImageFromCurrentImageContext();
CGContextStrokePath(context);
UIGraphicsEndImageContext();
}
  • 2
    the same issue with mine. did you fix it? – Versus Sep 25 '18 at 15:40
  • @versus I have not fixed yet. When I comment line `[self.layer renderInContext:context];`, the program can draw, but there are lines with many dashed lines and overlapping. – B2G HCM Trn Minh T Sep 26 '18 at 02:23
  • 1
    Possible duplicate of [drawRect:/renderInContext: issues](https://stackoverflow.com/questions/10129106/drawrect-renderincontext-issues) – trungduc Sep 26 '18 at 04:16
  • 1
    @trungduc It seems they are not the same issues, it works well in my app before iOS12. only crashed in iOS12. that's weird. `renderInContext` will always call drawRect until memory overload, then crash in iOS12. – Versus Oct 09 '18 at 13:48
  • 1
    Hi ! I have just encountered the same issue. Did you fix it ? – Matthieu Oct 23 '18 at 13:41
  • 2
    @Matthieu: I fixed it. In touchMoved() function. I called `[self setNeedsDisplay]` instead of `renderInContext`. I only use `renderInContext` in drawRect() function – B2G HCM Trn Minh T Oct 26 '18 at 07:14
  • @B2GHCMTrnMinhT Can you show me your code? I still crashed in `drawRect` function with `[self setNeedsDisplay]`. thanks in advance – Versus Oct 29 '18 at 15:23
  • @Versus: In drawRect(): `UIGraphicsBeginImageContextWithOptions(self.bounds.size, YES, 0.0); [self.layer renderInContext:context]; ..... CGContextStrokePath(context); UIGraphicsEndImageContext();` I still use renderInContext in drawRect. However, In touchMoved() function I use `[self setNeedsDisplay]` instead of `renderInContext` – B2G HCM Trn Minh T Oct 30 '18 at 01:58
  • @B2GHCMTrnMinhT Yep, I know what you mean, and I think our code logics are the same, both using CoreGraphics to draw lines, I want to know that you just removed these code `UIGraphicsBeginImageContext(drawBox.size); ...`, and replaced with `[self setNeedsDisplay]`? can you edit your `touchMoved` code to let me know what you have changed? – Versus Oct 30 '18 at 02:25
  • @Versus: I added my code. Hope this helps you! – B2G HCM Trn Minh T Oct 30 '18 at 03:24
  • @B2GHCMTrnMinhT Thanks a lot ~ – Versus Oct 30 '18 at 08:48

1 Answers1

3

My work around is

Don't let layer renderInContext recursive call in drawRect

(My code is swift)

var mIsInDrawLoop = false

// -(void)drawRect:(CGRect)rect
override func draw(_ rect: CGRect) {
...
    if(!mIsInDrawLoop) {
        mIsInDrawLoop = true
        layer.render(in: context)
        // [self.layer renderInContext:context];
        mIsInDrawLoop = false
    }
...
}
saranpol
  • 2,177
  • 1
  • 23
  • 22
  • That's a good idea, I think it is a good solution. easy and clear. By the way, I think we are supposed to find the reason why this happens, and when `renderInContext` will be called in `drawRect`. – Versus Oct 25 '18 at 02:45