Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Drawing woes with CALayer

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.

like image 906
AlvinfromDiaspar Avatar asked Aug 20 '09 04:08

AlvinfromDiaspar


2 Answers

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.

like image 141
Matt Long Avatar answered Nov 13 '22 03:11

Matt Long


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.

like image 33
Brad Larson Avatar answered Nov 13 '22 04:11

Brad Larson