CGContextError with custom crosshair on iOS


#1

Hey guys, I have no idea why I’m getting this error lately, but when using a custom crosshair, the CGContext isn’t available to actually draw the crosshair in ‘drawCrosshairLines’.

This is one of the errors:  <Error>: CGContextMoveToPoint: invalid context 0x0. This is a serious error. This application, or a library it uses, is using an invalid context  and is thereby contributing to an overall degradation of system stability and reliability. This notice is a courtesy: please fix this problem. It will become a fatal error in an upcoming update.

Is this something that you guys have seen before? I tried to roll back my project from backups with no luck, I just can’t get rid of the error any more.


#2

Hi Maximilian,

Thanks for getting in touch!

Our crosshair no longer uses CGContexts to draw parts of it’s crosshair due to it causing memory issues.

We now use Shape Layers to draw these parts of the crosshair because of it’s efficiency.

This means the drawCrosshairLines are being called from inside the “drawRect:” method causing the CGContext to be NULL.

To get your sample to draw your custom CGContext add the following overridden method:

-(void)drawRect:(CGRect)rect {
    [self drawCrosshairLines];
}

Then add the following line inside of your “moveToPosition:andDisplayDataPoint:fromSeries:andSeriesDataPoint:” method implementation:

[self setNeedsDisplay];

Let me know if you have any questions.

Kind regards,
Andrew Polkinghorn


#3

Thank you so much Andrew, got it working again with CGContexts using the code you’ve provided.

I haven’t notived any memory issue with our crosshair yet, but I’m thankful for the tip and will eventually switch to using the ShapeLayer as well.

Best regards, Max


#4

On further investigation it shows that this doesn’t completely solve my issue with crosshair drawing.

We’ve got several charts on the screen in a scrollview that are linked by using a global gesture recognizer on the scrollview. For syncing crosshairs we implemented the required delegate methods on each chart so that we can notify the other charts to draw a crosshair as well.

Unfortunately the crosshairs linked charts aren’t able to draw their crosshairs because they don’t have a valid context. 

So when the delegate for any given chart fires “crosshairMovedToXValue”, “moveToFloatingPointPosition” is getting called on all other charts.

And this is where the methodology of calling “setNeedsDisplay” in “moveTo(FloatingPoint)Position” wouldn’t work as the context isn’t available. Touch that chart first and it’ll work.


#5

Hi Max,

In our 2.8.0 release of charts we made a rather substantial change to the crosshair’s drawing in order to drop memory usage from 25MB (on average) to around 5MB. The issue was that we were using CoreGraphics and a large bitmap, which has now been substituted internally for UIBezierPaths instead. There were several other changes which mean that drawCrosshairLines is now no-longer called in a ‘drawRect’ context.

Something like the following may resolve your issue to allow you to draw on the crosshair (in a more efficient manner too):
// a property (or alternatively, ivar)
@property (nonatomic, retain) CAShapeLayer *crosshairLinesLayer;

// somewhere in initialisation:
self.crosshairLinesLayer = [CAShapeLayer layer];
[self.layer insertSublayer:self.crosshairLinesLayer atIndex:0];

// in your drawing code:
UIBezierPath *linePath = [UIBezierPath bezierPath];
[linePath moveToPoint:CGPointMake(crosshairCentrePoint.x, crosshairCentrePoint.y - 2.0f)];
[linePath addLineToPoint:CGPointMake(crosshairCentrePoint.x, 0.0f)];

// Set the CAShapeLayer’s path and copy crosshair line attributes from style object
self.crosshairLinesLayer.path = linePath;
self.crosshairLinesLayer.strokeColor = [super.style.lineColor CGColor];
self.crosshairLinesLayer.lineWidth = [super.style.lineWidth floatValue];

Hope this helps!