Drawing woes with CALayer

14,505

Solution 1

Core Animation does make this sort of thing trivial. Brad's suggestion is right on. The bottom line is that you don't need any of those methods to simply render the layer. In order to cause the layer to render, make sure you've done the following:

  • Set the contents property with:

    [imageLayer setContents:(id)[[UIImage imageNamed@"image.png"] CGImage]];

  • Set the bounds of the layer to the size you want.

    [imageLayer setBounds:CGRectMake(0.0f, 0.0f, 50.0f, 50.0f)];

  • Set the position (x,y location) of the layer to display in the view. The default anchorPoint is the center of the layer. This code centers the layer in your view.

    [imageLayer setPosition:CGPointMake([view bounds].size.width/2, [view bounds].size.height/2)];

  • Add the layer to your view's layer:

    [[[self view] layer] addSublayer:imageLayer];

Incidentally, if you prefer, you can set both bounds and position in one method by calling -setFrame:. I prefer to use the two calls myself as it feels more readable to me, but that's up to your own preference. If you do not set the bounds and position or frame of the layer, however, the layer will not render.

If you would prefer, you can avoid using drawInContext by creating additional layers that draw paths, shapes (see CAShaperLayer), or additional images and add them as sublayers of your image layer or add them as sublayers of your parent layer and give them a zPosition that causes them to display in front of your image layer.

Now, if you want to animate the opacity, you can use implicit animation by simply setting the layer property in the exact manner you described, e.g. [imageLayer setOpacity:0.0f]; This will fade the layer and all of it's child layers over 0.25 seconds.

Just some additional thoughts.

Best Regards.

Solution 2

If all that you want your various layers to do is display one image each, you shouldn't need to subclass them at all. You should just be able to set an image (in CGImageRef form) to the contents property of each layer. The layer will then handle the drawing of that image. You can obtain the Core Graphics image representation of a UIImage using its CGImage read-only property.

You are correct, though, about -drawInContext: being the right place to put more custom drawing code within a CALayer subclass. Without subclassing CALayer, you can have a delegate change the layer's drawing behavior through the -drawLayer:inContext: delegate method.

This is all described in detail within the "Providing Layer Content" section of Apple's Core Animation Programming Guide.

Share:
14,505

Related videos on Youtube

AlvinfromDiaspar
Author by

AlvinfromDiaspar

iOS developer as a hobbyist and a self-proclaimed professional.

Updated on June 04, 2022

Comments

  • AlvinfromDiaspar
    AlvinfromDiaspar almost 2 years

    First of all, im finding the iPhone online docs to be not-so-very thoroughly clear when it comes to the various ways of rendering a layer. I get the jist of it, but im not clear when to use which methods and which requires the layer to be added as a sub layer or not.

    My project started off trivially with me loading images and simply drawing them to a UIView via [image drawAtPoint:] as well as [image drawInRect:]. These work fine using the current graphics context.

    Then today i happened to read this concept of using layers so that animating my various images (implicitly) would in theory be a breeze!

    For the record, i know the docs say subclassing CALayer is unecessary, but i did just that. Now I am incredibly confused about the different ways to supposedly render a layer.

    • drawLayer
    • displayLayer
    • display
    • drawInContext

    Now for all of these methods, is it required to set the layer's frame size? Is it required to add a layer to a view's layer?

    The only method that gives me visible results is the drawinContext method. But if i apply an implicit animation (e.g. image.opacity = 0) nothing happens, which makes me believe my layer is not properly set up.

    Some one please bring order back to this chaos.

  • AlvinfromDiaspar
    AlvinfromDiaspar over 14 years
    What i ended up doing is subclassing (because my objects are complicated), and i set the delegate to by itself. So basically im using displayLayer. All i need to do is call the layer's setNeedsDisplay. So now the question still remains on the usage of drawInContext and drawLayer:inContext. Are these called manually, or are they internally called via SetNeedsDisplay? Just curious now.
  • AlvinfromDiaspar
    AlvinfromDiaspar over 14 years
    Here is a more pertinent follow-up question: I can now render to a layer using drawLayer:(CALayer*)layer. However, if i want to update the rendering, lets say an animation frame, on the layer, if I set the layer's content with an updated image*, the content disappears on the screen. Do i need to remove the layer and then render to it, and then re-add it to the render tree? If this is the case, this blows!
  • Brad Larson
    Brad Larson over 14 years
    To your first question: drawInContext: should never be called directly, but is indirectly triggered by setNeedsDisplay. If you haven't overridden the method, I believe it calls up to the delegate to help display the content via -drawLayer:inContext:
  • Brad Larson
    Brad Larson over 14 years
    For your second question: you should be able to change whatever you want about the layer's content, either via the contents property or -drawInContext:, without removing it and re-adding to the layer hierarchy. There must be something wrong with your rendering methods.