Welcome to follow my official account. I will regularly share some solutions to the problems I encounter in the project and some practical skills of iOS. At the present stage, I will mainly sort out some basic knowledge and record it

The post will also be synchronized to my blog: ppsheep.com

About the movie, we see too much in the iOS, now basically every APP will add some animation, more or less in the animated series, I will not realize a lot of cool animations, fancy animations, open source is now there are many, also have a lot of mature, here, I mainly speak some understanding of the animation, For the origin of animation and implementation principle and so on.

The Origin of animation

All views in iOS are derived from the base class of UIView, which can handle touch events, can support Core Graphics-based drawing, and can do simple animations such as rotation, scaling, and other sliding gradients. But actually, this is apple encapsulating a layer for us, and the thing that actually animates is this thing called a CALayer, and everything that UIView does, it’s apple encapsulating from CALayer.

CALayer

The CALayer class is conceptually similar to UIView. It can add sublayers (images, text, etc.) just like a View. It can also manage the position and size of sublayers and so on, just like a View. That’s how animations in iOS animate and transform, and the big difference between CALayer and UIView is that CALayer doesn’t deal with user interaction.

UIView and CALayer

Every UIView has a CALayer instance property, and the UIView’s job is to create and create this layer to make sure that when a subview is created, a sublayer can be created, and when a subview is added and removed, a sublayer can be added and removed. Their relationship is one to one.

In fact, all the views or animations that we see on screen are layers. UIView is just a high-level API that Apple wraps for us

There is a historical reason for this, mainly to use CALayer on macs, but touch on iOS devices is not the same as mouse clicks on Macs. On Macs, the advanced API is called NSView.

Where can I use CALayer

We generally don’t use CALayer for simple animations, so why not if Apple has packaged it for us? But if we need to handle more advanced animations, then UIView may not be enough for us.

No exposed CALayer features:

  • Shadows, rounded corners, colored borders
  • 3 d transform
  • Non-rectangular range
  • Transparent mask
  • Multistage nonlinear animation

Using the layer

Let’s get a feel for the layers

Create a new project

Add a view to the View

UIView *layerView = [[UIView alloc] initWithFrame:CGRectMake(10.10.200.200)];
layerView.backgroundColor = [UIColor redColor];
layerView.center = self.view.center;
[self.view addSubview:layerView];Copy the code

And then we want to add a little blue box in the middle of this little red box. Of course, we know it’s easy to just add a child view, but to do so, we lose the point of learning CALayer.

So what I’m thinking is, since the layer looks like a view, can we put a blue box on top of the layer of the layerView and here’s what we’re going to do

CALayer *blueLayer = [CALayer layer];
blueLayer.frame = CGRectMake(50.50.100.100);
blueLayer.backgroundColor = [UIColor blueColor].CGColor;
[layerView.layer addSublayer:blueLayer];Copy the code

So that’s what it looks like and let’s look at the 3D

And you can see, what happens is, we only see one layer, one view and we don’t have any child views added to the layerView

Of course, we’re just doing a layer introduction here, it’s not like you’re going to add views later and you’re going to add views like this, it’s definitely a mistake, but as we said before, layer can’t handle user interaction, and that’s important.

But under what circumstances do we need to use CALayer this way?

  • Develop cross-platform applications that can also run on macOS
  • Use multiple Calayers and don’t want to create extra UIViews to encapsulate them (more on that later)
  • Do something that requires a lot of performance, but in this case, most of the time we just use OpenGL for drawing

In general, it’s much easier to work with views than layers

We created this example here just to show you how the layer tree is associated with the view.

Boarding figure

What does a boarding plan mean? In fact, the layer contains the image

Contents attributes

There’s a property in CALayer called contents, and the type of the property is defined as ID, and it looks like this property can accept any type of value, and if you give contents any type of value, your APP will compile, but the resulting layer is really a blank layer, and in fact, And this contents, on iOS, requires a CGImage value.

So why is this written as an ID, again, this is a historical reason, obviously this is because of macOS, because under macOS, this is the type that accepts NSImage.

There’s a CGImage property in UIImage, and what it returns is a CGImageRef, and if you assign that directly to contents, it will compile an error. CGImageRef is a Core Foundation object, not a Cocoa object, but it can be transformed by bridged, by applying an image to the layer of the layerView we just created

UIImage *image = [UIImage imageNamed:@"plane"];
layerView.layer.contents = (__bridge id _Nullable)(image.CGImage);Copy the code

So we’re bypassing the UIImageView and we’re just going to set an image to the UIView layer

ContentsGravity properties

But we see that the middle image is obviously stretched out, and we want to show it as it is. How do we do that?

When we’re using UIImageView, we’re dealing with this stretching, usually using a property of UIImageView

imageView.contentModel = UIViewContentModeScaleAspectFit;Copy the code

There is a property in CALayer that is similar to contentMode called contentsGravity, except that it is an NSString.

The types of contentsGravity values include:

  • kCAGravityCenter
  • kCAGravityTop
  • kCAGravityBottom
  • kCAGravityLeft
  • kCAGravityRight
  • kCAGravityTopLeft
  • kCAGravityTopRight
  • kCAGravityBottomLeft
  • kCAGravityBottomRight
  • kCAGravityResize
  • kCAGravityResizeAspect
  • kCAGravityResizeAspectFill

Of this type and contentMode have one-to-one correspondence relationship, including kCAGravityResizeAspect equivalent to view contentMode UIViewContentModeScaleAspectFit type

When we set the layerView’s contentsGravity to kCAGravityResizeAspect

layerView.layer.contentsGravity = kCAGravityResizeAspect;Copy the code

The effect is different

ContentsScale property (mainly for Mac)

ContentsScale defines the ratio of the pixel size to the view size of the host diagram, which by default is a floating point number with a value of 1.0. So how do we use this property in general?

This property is created to support the high resolution screen mechanism, and it is used to determine how much space should be created for the boarding image and how stretched the image should be when drawing layers.

To put it simply, if we set contentsScale to 1.0 then the image created by the host map will be drawn one pixel per point, and if set to 2 it will be drawn two pixels per point. This is what we know as the Retina screen.

When we used contentsGravity = kCAGravityResizeAspect, the default was to scale the image to fit the layer size. However, if we set contentsGravity to kCAGravityCenter, So let’s see, what does that look like?

This is because the kCAGravityCenter property, by default, does not stretch the image, so it shows the original size of the image, and then our contentsScale comes into play

layerView.layer.contentsScale = image.scale;Copy the code

At this point, we see that we now use the correct image to draw

Note that we must manually set the contentsScale when we use code to set the lodging diagram

layerView.layer.contentsScale = [UIScreen mainScreen].scale;Copy the code

That way, our images will look good on retina devices.

MaskToBounds properties

I don’t know if you noticed, in the above image, our image has gone beyond the boundary of the layer, but by default, in UIView, we also draw content or subviews that are beyond the boundary.

In UIView, the property that controls whether the bounds are reached is clipsToBounds. In CALayer, the property that controls masksToBounds is set to yes

To be continued…