Search code examples
objective-cmacoscocoacore-animationios5

Core Animation - Using `makeBackingLayer` does not apply Core Image Filter


I am building a CALayer backed NSView that applies a mask filter to the layer and then draws the layer. When I build a new layer and set it on the view manually in -(void)viewDidMoveToSuperview) the view renders correctly, but when I override -(CALayer *)makeBackingLayer the filter does not get applied to the layer.

Here's the code that works (layer host view style):

-(void)viewDidMoveToSuperview{
  [super viewDidMoveToSuperview];
  CIFilter *filter = [CIFilter filterWithName:@"CIBlendWithMask"];
  [filter setValue:[self transparentImage] forKey:@"inputBackgroundImage"];
  [filter setValue:[self maskImage] forKey:@"inputMaskImage"];

  self.layer = [TransparentLayer layer];
  self.layer.backgroundColor = CGColorCreateGenericRGB(0, 1, 1, 1);

  [self.layer setPosition:CGPointMake(0 ,0)];
  [self.layer setAnchorPoint:CGPointMake(0 ,0)];
  [self.layer setBounds:CGRectMake(0 ,0 ,200, 200)];
  self.layer.filters = [NSArray arrayWithObject:filter];
  [self setWantsLayer:YES];
  [self.layer setNeedsDisplay];
  [self setNeedsDisplay:YES];
}

This renders the CALayer and the output looks like this:

Layer Host

But when I do this (Layer backed view style):

-(CALayer *)makeBackingLayer{

  CIFilter *filter = [CIFilter filterWithName:@"CIBlendWithMask"];
  [filter setValue:[self transparentImage] forKey:@"inputBackgroundImage"];
  [filter setValue:[self maskImage] forKey:@"inputMaskImage"];

  TransparentLayer *backingLayer = [TransparentLayer layer];
  backingLayer.backgroundColor = CGColorCreateGenericRGB(0, 1, 1, 1);

  [backingLayer setPosition:CGPointMake(0 ,0)];
  [backingLayer setAnchorPoint:CGPointMake(0 ,0)];
  [backingLayer setBounds:CGRectMake(0 ,0 ,200, 200)];
  backingLayer.filters = [NSArray arrayWithObject:filter];
  return backingLayer;

}

-(void)viewDidMoveToSuperview{
  [super viewDidMoveToSuperview];
  [self setWantsLayer:YES];
}

The view ends up looking like this:

Layer Backed View (Broken)

Two questions:

1 - Why isn't the filter applied on the layer when using makeBackingLayer?

2 - How can I use makeBackingLayer to generate the layer rather than calling setLayer?

EDIT: Changing the -(void)viewDidMoveToSuperView to the following renders the layer like I want it to, but it still doesn't answer my question:

-(void)viewDidMoveToSuperview{
  [super viewDidMoveToSuperview];
  //manually set the layer
  [self setLayer:[self makeBackingLayer]];
  [self setWantsLayer:YES];
}

Solution

  • Since you're accessing properties of view's layer you must make the view to host layer rather than layer backed.

    You cannot use -makeBackingLayer to customize layer of a view. Documentation mentions only one application of this method:

    In your view class, override -makeBackingLayer to return an autoreleased instance of your NSOpenGLLayer subclass.

    1. Because you should use -setContentFilters:
    2. You cannot. You must call setLayer:

    -makeBackingLayer is a wrong place to create your custom layer. You should add new method instead (e.g. -makeHostLayer).