How to subclass CALayer for use as another CALayer mask?

core-animation, core-graphics, ios, objective-c, uiview

Solution

Call `-setNeedsDisplay` on your layer subclass before setting it to the mask and you'll only need to override `-drawInContext:` of the `CALayer` subclass. That will help you get the method to be called (CALayer calls `-drawInContext:` using its default implementation of `-display` which is called after `-setNeedsDisplay`). You may also have to set the frame of the layer:

self.collectionViewTickerMaskLayer.frame = view.layer.bounds;
self.collectionViewTickerMaskLayer.autoresizingMask = kCALayerWidthSizable | kCALayerHeightSizable;

You have a logic issue in this code. You are using an endpoint which is CGPointZero, so even if the methods were called, no gradient would even be drawn to mask your layer.

Problem

I' m trying to subclass `CALayer` for use as a mask on another layer. I want to use my `CALayer` subclass in place of a `CAGradientLayer` and use it for a gradient mask as explained here. However I wish to use a custom `CALayer` that uses an internal `CGGradient` for drawing instead, as this should produce smoother results than `CAGradientLayer` (see here). I don't care about performance, I want better gradient quality. I'm following this example for creating my `CGGradient` and storing it on the `CALayer`.. However, I cannot get the mask to draw. I don't know where to put the draw code: `CALayer`'s `display` nor `drawInContext:(CGContextRef)ctx` nor `drawInContext:(CGContextRef)ctx` seem to be called when it is used as a mask. Bear with me as I'm new with CoreAnimation. So, how to fix this so my `CALayer` subclass works as a replacement for `CAGradientLayer` but draws using a `CGGradient`? My current code here: ``` @interface CANiceGradientLayer : CALayer @property (nonatomic) CGGradientRef gradient; @property (atomic) CGPoint startPoint; @property (atomic) CGPoint endPoint; @end @implementation CANiceGradientLayer - (instancetype)initWithGradientRef:(CGGradientRef)gradient startPoint:(CGPoint)startPoint endPoint:(CGPoint)endPoint { if ( !(self = [super init]) ) { return nil; } self.gradient = CGGradientRetain(gradient); self.startPoint = startPoint; self.endPoint = endPoint; return self; } - (void)dealloc { CGGradientRelease(self.gradient); } - (void)display { NSLog(@"display"); } - (void)drawInContext:(CGContextRef)ctx { NSLog(@"drawInContext:"); CGContextDrawLinearGradient(ctx, self.gradient, self.startPoint, self.endPoint, kCGGradientDrawsBeforeStartLocation | kCGGradientDrawsAfterEndLocation);; } - (void)renderInContext:(CGContextRef)ctx { NSLog(@"renderInContext:"); } @end ``` And here is how I create it: ``` size_t num_locations = 2; CGFloat locations[2] = { 0.0, 1.0 }; CGFloat components[8] = { 1.0, 1.0, 1.0, 1.0, // Start color 1.0, 1.0, 1.0, 0.0 }; // End color CGColorSpaceRef rgbColorspace = CGColorSpaceCreateDeviceRGB(); CGGradientRef gradient = CGGradientCreateWithColorComponents(rgbColorspace, components, locations, num_locations); self.collectionViewTickerMaskLayer = [[CANiceGradientLayer alloc] initWithGradientRef:gradient startPoint:CGPointZero endPoint:CGPointZero]; self.collectionViewTickerMaskLayer.anchorPoint = CGPointZero; view.layer.mask = self.collectionViewTickerMaskLayer; ``` If I use a `CAGradientLayer` instead, it works fine (but the gradient looks bad).

Original source

Related problems