Search code examples
objective-ciosimagecalayer

UIImage content disappearing (and reappearing) when using drawLayer:inContext


I'm using the delegate method drawLayer:inContext to change the background opacity for a small box as it moves around the screen. The layer had a UIImage set as the content when it was initialized.

Every time drawLayer:inContext is called the image will either disappear or reappear (toggling back and forth) making for a stutter-y looking movement on screen.

I have the image data as a local ivar in the delegate class so that it doesn't need to reload every time.

I'm not sure what's causing this. Any ideas? Here's the code I'm using:

- (id)init
{
    self = [super init];
    if(self) {
        UIImage *layerImage = [UIImage imageNamed:@"Hypno.png"];
        image = layerImage.CGImage;
    }
    return self;
}

- (void)drawLayer:(CALayer *)layer inContext:(CGContextRef)ctx
{
    float alpha = layer.position.y / layer.superlayer.bounds.size.height;
    UIColor *reddish = [UIColor colorWithRed:1.0 green:0.0 blue:0.0 alpha:alpha];
    CGColorRef cgReddish = reddish.CGColor;
    layer.backgroundColor = cgReddish;
    [layer setContents:(id)image];
}

Solution

  • Don't use -drawLayer:inContext:. That method is for drawing into a CG context which will then become the layer's contents, using Core Graphics or UIKit methods. You don't want to change the layer's contents at all -- you want to set it to be your image, once, and then never touch it again.

    Your image is appearing and disappearing, because sometimes the layer displays the image, and other times the layer displays what you drew into the context in -drawLayer:inContext: -- namely, nothing.

    (Also: in order for -drawLayer:inContext: to be called, you must be calling [layer setNeedsDisplay], or you have the layer's needsDisplayOnBoundsChange turned on, or something similar. Don't do any of that.)

    Instead, try using the CALayer layout machinery. For instance, in your layer's delegate:

    - (void)layoutSublayersOfLayer:(CALayer *)layer
    {
        float alpha = layer.position.y / layer.superlayer.bounds.size.height;
        UIColor *reddish = [UIColor colorWithRed:1.0 green:0.0 blue:0.0 alpha:alpha];
        CGColorRef cgReddish = reddish.CGColor;
        layer.backgroundColor = cgReddish;
    }
    

    This will get called as part of the display process, anytime the layer or any of its superlayers have had their -setNeedsLayout method called. Often this happens automatically, but depending on exactly what causes the layer to move, you may need to do it manually. Try it and see.

    (Alternatively: Since you already have code that changes the layer's position, why not change the layer's background color at the same time?)