What is the difference between CGContextDrawLinearGradient, called in a UIView's drawRect method, and a CAGradientLayer? How do they compare performance wise? What is the best practice for creating gradient views? I'd really like a nice explanation of how they relate to each other and why one is better performance than the other.
Thanks.
If you just want a box with a gradient in it then performance isn't really an issue. You should go with whatever is simplest to implement for your particular requirements.
Adding a CAGradientLayer means you don't have to create a view subclass, you can just add the layer to an existing view. The setup is also slightly easier, since you don't need to worry about frame sizes or any c-style core graphics functions. You can also add rounded corners, shadows etc without too much effort.
However, if you want more than one gradient view, a subclass might be a good idea, so you can just instatiate new ones.
So, unfortunately, there isn't a clear cut answer to your question. Neither is definitively better. If you are concerned with performance, implement both and test using instruments.
Related
I have to create UIView with custom shape, such as triangular, half-rect, etc.
I used to crop special image to that form and set as a background of my view.
Although it is a popular solution, I am not sure whether it is the most efficient one in terms of sustainability.
On the other hand, I found useful way of solving this problem with CAShapeLayer()
Could you please provide pros and cons of both approaches?
CAShapeLayer all the way!
With little effort you can achieve the same result with less memory (RAM) and less maintenance time (if you want to make the triangle thicker for example, you'll need a new image however a small change in code and you have it!). Moreover your app size will be relatively smaller and you don't have to worry about the resolution like the images.
Hope this helps!
I always used CoreGraphics and CoreAnimation, I understand how each of them works on their own, but not those edge cases when one have to talk with the other. I also understand that UIViews are a nice wrapper for CALayer, where CALayer does all the heavy lifting of rendering, and the UIView adds the touch-based responsiveness.
But, all the questions I have seen thus far, attack the problem from one side or the other, not the interplay between them, specially between CoreGraphics and CALayer.
Anyway, my question is ...
How does CoreGraphics relate to CALayer?
My understanding is that a CALayer wraps the CoreGraphics methods to draw itself, but does it once, and can live with the snapshot of itself until invalidated. But, how these drawing methods interplay with the sublayers of that layer? Are they exclusive?
For example, what happens when I have a UIView that has sub-views, and I overload the drawRect method? How does that affect the drawing of its sublayers?
Is it even a good idea to intermix the two inside the same function?
Also, I'm asking only about iOS, I understand that Mac is a different beast (and also have those fancy CIFilters, bastards!).
Prior Research
Here's some related questions I've researched beforehand:
confusion regarding quartz2d, core graphics, core animation, core images. This question asks the differences between each other, and the chosen answer indeed delivers, but it answers for each individual library as if the other didn't exist.
To Drawrect or not to Drawrect. Another great question, but it addresses only the subject of drawing CoreGraphics vs handing the problem to UIKit, but anyway, the chosen answer delivers parts of the puzzle.
Animating Pie Slices with Custom CALayer. Must be one of the most valuable tutorials I've seen in this subject, it's the only one that has guided me through to drawing a CALayer
What is different between CoreGraphics and CoreAnimation Absolutely disappointed on how quick the asker accepted the answer, I feel that there's a whole lot more going in here.
Various WWDC videos, but I haven't seen one that explains in detail the general scope. If anyone replies with a WWDC video that does, I'll consider that a valid answer.
I'll try to answer your question at a conceptual, 20,000ft level. I will try to disclaim my points where I'm over-generalizing, but I'll attempt to hit the common case.
Perhaps the easiest way to think about it is this: In the GPU's memory you have textures which, for the purposes of this discussion, are bitmap images. A CALayer might have a texture associated with it, or it might not. These cases would correspond to a layer with a -drawRect: method, and a layer that exists solely to contain sublayers, respectively. Conceptually, each layer that has a texture associated with it has a different texture all it's own (there are some details and optimizations that make this not strictly/universally true, but in the general, abstract case, it can help to think of it this way.) With that in mind, a superlayer's -drawRect: method has no effect on any of its sublayers' -drawRect: methods, and (again, in the general case) a sublayer's -drawRect: method has no effect on its superlayer's -drawRect: method. Each draws into its own texture (also called a "backing store") and then, based on the layer tree and the associated geometries and transforms, the GPU composites all these textures together into what you see on the screen. When one of the layers is invalidated, directly or indirectly (via -setNeedsDisplayInRect:), then when CA goes to display the next frame on screen, the invalid layers will be redrawn by virtue of having their -drawRect: methods called. That will update the associated texture, and once all the invalid layers' textures are updated, the GPU will composite them, generating the final bitmap that you see on-screen.
So to answer your first question: In the general case, no, there is no interplay between the -drawRect: methods of distinct CALayers.
As to your second question: For the purposes of this discussion you can think of UIViews as being the same as CALayers. The interrelationship with respect to drawing and textures is largely unchanged from that of non-UIView CALayers.
To your third question: Is it a good idea to intermix UIViews and CALayers? Every UIView has a CALayer backing it (all views in UIKit are layer-backed, which is not normally the case on OSX.) So at some level, they're "intermixed" whether you want them to be or not. It is perfectly fine to add CALayer sublayers to the layer that backs a UIView, although that layer will not have all the added functionality that UIView brings to the party. If that layer's purpose is just to generate an image for display, then that's fine. If you want the sub-layer/view to be a first class participant in touch handling, or to be positioned/sized using AutoLayout, then it will need to be a UIView. It's probably best to think of a UIView as a CALayer with a bunch of extra functionality added to it. If you need that functionality, use a UIView. If you don't, use a CALayer.
In sum: CoreGraphics is an API for drawing 2D graphics. One common use of CG (but not the only one) is to draw bitmap images. CoreAnimation is an API providing an organized framework for manipulating bitmaps on-screen. You could meaningfully use CoreAnimation without ever calling a CoreGraphics drawing primitive, for example, if all your textures were backed by images that were compiled into your application at build time.
Hopefully this is helpful. Leave comments if you need clarification, and I'll edit to oblige.
Suppose we need to decorate a UIButton, UIView, etc. in iOS we use the following methods,
use the CALayer properties like borderWidth, CATransform, CAGradientLayer, etc.
use the drawRect: method of UIView and perform our decoration drawing via CG.
use just an already prepared image to be drawn on the UIView's or its layer's background.
Now, my question is out of the above 3 which one takes less processing time and give more speed (performance boost) ?
As I have noticed that on views which are constantly getting updates (visually) like a tableview; even if the layer.shouldRasterise = YES is applied, the performance is still very poor. And if the cornerRadius is active, then the performance becomes more poor.
Thanks all.
That depends on situation, if you don't care about memory consumption - images should work best, on the other hand if you want to minimize memory footprint - CALayer+drawing does the job for me.
As of cornerRadius and other similar properties - you can easily get the same job done by drawing, that would be more efficient.
Also check latest WWDC sessions, there was one (I don't remember the name) about UIKit/CoreGraphics performance tuning.
Let's say I want to add 50 images to a view for the purpose of animating them. And let's suppose I'm planning on using Core Animation (e.g., CABasicAnimation) rather than "UIView" animation.
Am I better off implementing this by adding 50 subviews or 50 sublayers? Does it make a difference?
Thanks.
As I describe here, I've used both UIViews and CALayers in animations and found a negligible performance difference between them. UIViews are very lightweight wrappers around the layers. Also, any layer-based animations you need can be applied to a UIView's backing layer easily.
I've used CALayers directly in situations where I wanted to create cross-platform (Mac / iOS) UI elements, because CALayers are almost identical in their implementation on both platforms (unlike the significantly different NSViews and UIViews). CALayers don't have any touch-handling routines out of the box, but you can add that capability if you need to.
There are also some edge cases where you might want to work directly with layers, like when trying to do limited 3-D manipulation of the layers (as in a CoverFlow effect) or when using a CAReplicatorLayer to produce particle effects.
UIViews contain sublayers, so they are heavier weight, and contain stuff you probably don't need for all 50 images, such as event and touch handlers/variables. So using layers would probably be slightly more efficient and use a bit less memory than using views for each image.
The difference for such a small number of images is negligible. Use what's most convenient.
I've not done animation (yet :-), but the stuff I remember reading about it suggests to create one image with all 50 tiled on it and then just offset to the correct image when drawing. That way you only need one layer or UIImage or whatever to display it. I don't know about speed, but I'd guess it would save memory and would probably be easier to manage and code.
I'm struggling with conceptualizing animations with a CALayer as opposed to UIView's own animation methods. Throw "Core Animation" into this and, well, maybe someone can articulate these concepts from a high level so I can better visualize what's happening and why I'd want to migrate UIView animations (which I'm quite familiar with now) to CALayer animations on the iPhone. Every view in Cocoa-Touch automatically gets a layer. And, it seems, you can animate one and/or the other?!? Even mix them together?!? But why? Where's the line? What's the pro/con to each?
The Core Animation Programming Guide immediately jumps into Layer & Timing Classes and I think need to take a step back and understand why these varied pieces exist and how relate to each other.
Use views for control and layers for eye candy. Layers don't receive events so it's easier to use a view for those cases, but when you want to animate a sprite or backgrounds, etc., layers make sense. Events pass right through layers to the backing view so you can have a pretty visual representation without messing up your events. Try to overlay a view that you're just using for visual representation and you'll have to pass tap events through to the underlying view yourself.
An UIView is always rendered to a CALayer. When you use UIView methods to animate a view, you are effectively manipulating the underlying CALayer.
If you need to do simple things, use the UIView methods. For more complex situatins, or if you want layers not associated with any view in particular, use CALayers.
I've done a bunch of apps in the past year. Here's my rule of thumb:
Use UIView until it doesn't do what you want.
Then move to CoreAnimation. But before you get into it too much...
If you write more than a few animations, use Cocos2D.
UIView transforms are only 2D and are restricted to that, LAyer transforms however can be 3D and you should use those if you want to do 3D stuff, UIView animation will work if you change either the UIView transform or the CALayer transform. So at a basic level, you can do a lot more manipulation when you are working with a Layer rather than the View.
I am not sure if I am misunderstanding Chris' response to "What's Cocos2D doing better? Don't you have other problems then, regarding the touch event handling and many other stuff that misses in openGL ES?"
It sounds like the answer suggests Cocos2D is not based on the OpenGL ES framework when in fact it actual is. While it is a great 2D game engine it does implement OpenGL for much of it's rendering - attached to a physics library it allows for a lot of very interesting possibilities for animation - and Chris is correct - it is a lot less coding indeed.