What Are the Differences Between a Uiview and a Calayer

What are the differences between a UIView and a CALayer?

On iOS, every UIView is backed by a Core Animation CALayer, so you are dealing with CALayers when using a UIView, even though you may not realize it. Unlike NSViews on the Mac, which evolved before Core Animation existed, UIViews are intended to be lightweight wrappers around these CALayers.

As I describe in the similar question "When to use CALayer on the Mac/iPhone?", working directly with CALayers doesn't give you significant performance advantages over UIViews. One of the reasons you might want to build a user interface element with CALayers instead of UIViews is that it can be very easily ported to the Mac. UIViews are very different from NSViews, but CALayers are almost identical on the two platforms. This is why the Core Plot framework lays out its graphs using CALayers instead of other UI elements.

One thing UIViews provide over CALayers is built-in support for user interaction. They handle hit-testing on touches and other related actions that you would need to build yourself if managing a hierarchy of CALayers. It's not that hard to implement this yourself, but it is extra code you'd need to write when building a CALayer-only interface.

You will often need to access the underlying layers for a UIView when performing more complex animations than the base UIView class allows. UIView's animation capabilities have grown as the iOS SDK has matured, but there are still a few things that are best done by interacting with the underlying CALayer.

UIView animation vs CALayers

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.

Difference drawing on CALayer and UIView

This is due to the contentsScale property. When you have a custom CALayer the default value of this property is 1.0.

The default value of this property is 1.0. For layers attached to a
view, the view changes the scale factor automatically to a value that
is appropriate for the current screen. For layers you create and
manage yourself, you must set the value of this property yourself
based on the resolution of the screen and the content you are
providing. Core Animation uses the value you specify as a cue to
determine how to render your content. Source.

If you have a retina device and you draw with the contentsScale set to 1.0, it will result in that pixelated look you described. In order to fix this you should set the layer's contentsScale to the one of the screen.

[self.layer setContentsScale:[[UIScreen mainScreen] scale]];

This issue does not happen when you draw the circle in the drawRect method of your UIView since there the default contentsScaleFactor is already the one of the screen.

For views that implement a custom drawRect: method and are associated
with a window, the default value for this property is the scale factor
associated with the screen currently displaying the view. Source.

The relationship between CoreGraphics, UIViews and CALayers

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.

iOS: Should I Add UIViews or CALayers for animation?

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.

What is a CALayer?

A UIView is a aggregate class. It contains "stuff" for the event responder chain, stuff for handling the view hierarchy, etc., as well as stuff regarding what to draw on the display. The CALayer of a UIView is just the stuff regarding what to draw: the image bits, the scale, transform, animation properties, etc.

The Cocoa Touch UI is drawn by compositing layers... views on top of views on top of a window. A CALayer is a layer in the composition stack which goes on top of some layers, and potentially underneath other layers. (e.g. an image in a button in a table cell in a view in a split view, etc.)

If you want to do something special with what a view draws or displays that isn't provided in the stock UIView class methods, it might be possible to do that special something by going directly to the CALayer: maybe swapping layers between views and/or images, drawing stuff off-screen, custom animations, etc.

There's lots more explained in Apple CALayer Class Reference document

what is the relationship between a UIView.frame and CALayer.frame? (both pre and post a CATransform3D)

If you are working with Core Animation and layers, you should focus on the following CALayer properties:

  1. position
  2. bounds
  3. anchorPoint
  4. transform

A quote from Apple Technical Q&A QA1620 available here:

Q: When I try to animate the frame of a CALayer nothing happens. Why?

A: The frame property of a CALayer is a derived property, dependent on
the position, anchorPoint, bounds and transform of the layer. Instead
of animating the frame, you should instead animate the position or
bounds, depending on what effect you are trying to accomplish.

Usage UIView vs. CAShapeLayer (drawing, animating, user interaction)

Quoting from Apple's doc

Layers are not a replacement for your app’s views—that is, you cannot
create a visual interface based solely on layer objects. Layers
provide infrastructure for your views. Specifically, layers make it
easier and more efficient to draw and animate the contents of views
and maintain high frame rates while doing so. However, there are many
things that layers do not do. Layers do not handle events, draw
content, participate in the responder chain, or do many other things.
For this reason, every app must still have one or more views to handle
those kinds of interactions.

In iOS, every view is backed by a corresponding layer object but in OS
X you must decide which views should have layers. In OS X v10.8 and
later, it probably makes sense to add layers to all of your views.
However, you are not required to do so and can still disable layers in
cases where the overhead is unwarranted and unneeded. Layers do
increase your app’s memory overhead somewhat but their benefits often
outweigh the disadvantage, so it is always best to test the
performance of your app before disabling layer support.

When you enable layer support for a view, you create what is referred
to as a layer-backed view. In a layer-backed view, the system is
responsible for creating the underlying layer object and for keeping
that layer in sync with the view. All iOS views are layer-backed and
most views in OS X are as well. However, in OS X, you can also create
a layer-hosting view, which is a view where you supply the layer
object yourself. For a layer-hosting view, AppKit takes a hands off
approach with managing the layer and does not modify it in response to
view changes.

Read : https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/CoreAnimation_guide/CoreAnimationBasics/CoreAnimationBasics.html#//apple_ref/doc/uid/TP40004514-CH2-SW3

Now time for Some QNA

Question 1:

My understanding is that UIView (as part of UIKit) uses a normal
CALayer under the hood to draw its content. If this is correct, then
CAShapeLayer (or CALayer in general) would be the exact same thing,
only without the extras UIKit gives you

Again Quoting Apple doc

Layer-backed views create an instance of the CALayer class by default,
and in most cases you might not need a different type of layer object.
However, Core Animation provides different layer classes, each of
which provides specialized capabilities that you might find useful.
Choosing a different layer class might enable you to improve
performance or support a specific type of content in a simple way

Clearly when you try to draw various shapes using CAShapeLayer is beneficial in terms of performance when compared to CALayer.

Question 2

Is CAShapeLayer faster? Is UIKit more optimised for gesture
recognition or user interaction in general?

Clearly Layers do not handle events, draw content, participate in the responder chain, or do many other things. So none of the layer can recognize user interactions no matter whether its CALayer or CAShapeLayer

Question 3

At this point, I'm not sure whether to use UIViews or CAShapeLayers

As you have specified in your question you want user interactions of red circles and because we are now aware of the fact that CAShapeLayer/CALayer will not respond to user interaction its pretty clear that you have to use UIView rather than layer.

In iOS, what's the difference between UIView Animations and Core Animations?

Apple describe in it's docs:

"In iOS, animations are used extensively to reposition views, change
their size, remove them from view hierarchies, and hide them."

View Programming Guide - Apple

The article continues with a fairly strong emphasis on using Core Animations rather than UIView Animations for more detailed animations.

"In places where you want to perform more sophisticated animations, or
animations not supported by the UIView class, you can use Core
Animation and the view’s underlying layer to create the animation.
Because view and layer objects are intricately linked together,
changes to a view’s layer affect the view itself."

Same Doc - View Programming Guide - Apple

So basically put, you manipulate the view's layer to change the view. I don't think it wise to manipulate the view directly when Core Animations exist for this purpose. So my own answer is no, you can't (and probably shouldn't) do to the UIView with UIAnimations what you can do to the Layer with Core Animations.

More info:
Core Animation Programming Guide by Apple

I hope this helps answer your question and that the resources provided will help further your insight into your question.



Related Topics



Leave a reply



Submit