I have a custom UIView
that draws something in an overwritten
- (void)drawRect:(CGRect)rect
this works fine and gives sharp results on retina screens.
However, now I would like to make the properties on which the drawing is based animatable. Creating animatable properties seems to be possible only on the CALayer
, so instead of doing the drawing in UIView
, I create a custom CALayer
subclass and do the drawing inside
- (void) drawInContext:(CGContextRef)ctx
I use pretty much the exact same drawing code in this function as I used in the drawRect
function of the custom UIView
.
The result looks the same - however, it's not retina resolution but pixelated (large square pixels)
if I put
self.contentsScale = [UIScreen mainScreen].scale;
To the beginning of my drawInContext
implementation, then instead of a pixelated result, I get a blurry result (as if the rendering is still performed in non-retina resolution and then upscaled to retina resolution).
what's the correct way to render sharp retina paths in CALayers
drawInContext
?
here are some screenshots (the blue line is part of the custom drawing in question. the yellow part is just an image)
Drawn inside custom UIView's drawRect
:
Drawn inside custom CALayer's drawInContext
:
Drawin inside custom CALayer's drawInContext
, with setting self.contentScale
first:
For completeness, here's (a stripped down version of the) drawing code:
//if drawing inside custom UIView sublcass: - (void)drawRect:(CGRect)rect { CGContextRef currenctContext = UIGraphicsGetCurrentContext(); [[UIColor blackColor] set]; CGContextSetLineWidth(currenctContext, _lineWidth); CGContextSetLineJoin(currenctContext,kCGLineJoinRound); CGContextMoveToPoint(currenctContext,x1, y1); CGContextAddLineToPoint(currenctContext,x2, y2); CGContextStrokePath(currenctContext); } //if drawing inside custom CALayer subclass: - (void) drawInContext:(CGContextRef)ctx { { //self.contentsScale = [UIScreen mainScreen].scale; CGContextRef currenctContext = ctx; CGContextSetStrokeColorWithColor(currenctContext, [UIColor blackColor].CGColor); CGContextSetLineWidth(currenctContext, _lineWidth); CGContextSetLineJoin(currenctContext,kCGLineJoinRound); CGContextMoveToPoint(currenctContext,x1, y1); CGContextAddLineToPoint(currenctContext,x2, y2); CGContextStrokePath(currenctContext); }
To restate what I want to achieve: I want to achieve the same crisp retina rendering as in the UIView
approach, but when rendering in CALayer
The issue is most likely to be contentScale here; be aware that if you're assigning this to a custom view by overriding its layerClass
function, the layer's content scale may be reset. There may be some other instances in which this also happens. To be safe, set the content scale only after the layer has been added to a view.
Try assigning the main screen's scale to your custom layer during your custom view's init method. In Swift 3 that looks like this:
layer.contentsScale = UIScreen.mainScreen().scale
Or, in Swift 4:
layer.contentsScale = UIScreen.main.scale
Are you using shouldRasterize = YES
in the layer? Try drawing in the CALayer subclass but set the rasterizationScale
to the screen's scale.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With