This Demo address: github.com/tanghaitao/… If you want to change frame, Angle, etc. for simple animations, just change the related properties. But if you want to do complex animations, you need to use CoreAnimation. This article will take you through CoreAnimaton in a step-by-step way.

The core class of CoreAnimation is CALayer. We will introduce the difference between CALayer and UIView.

Implicit animation/display animation of Layer

  • Implicit animation: default time 0.25s(position, color, size), must be layer independent (not UIView. layer) to have implicit animation, UIView (root layer) does not have implicit animation
  • Display animation: The animation in the figure below requires the instantiation of the specific class to display the corresponding effect of the painting.

The following structure diagram of CoreAnimaton:

Some concepts of Layer:

KeyPath property Settings:

Initialize the animation object. 2. Set the value of the animation property to be modified. Add the animation to the layer (all animations are added to the layer, not the view). Center: uiView // redView width and height 100,layer.position.y moved to 400, Uiview.frame. y= 400-100/2 = 350 presentationLayer and modelLayer (render layer and modelLayer), property Settings first modify the modelLayer data, Vsync comes when the render layer takes data from the modelLayer, redraw again. Anchor points: 1. 2. Unit coordinate 0-1; Implicit animation: default time: 0.25s(position, color, size), must be layer independent (not UIView. layer), uiView (root layer) display animation:Copy the code

CABasicAnimation

Demo address: github.com/tanghaitao/… Here are some examples:

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
    CABasicAnimation *anim = [CABasicAnimation animation];
    anim.keyPath = @"position.y";        
    anim.toValue = @400;
    [_redView.layer addAnimation:anim forKey:nil];    
}
Copy the code

To illustrate the effect, the animation does not stop at the end position, but returns to the original position.

anim.removedOnCompletion = NO; Anim. fillMode = kcafillmodeforward; // Default is back to the original position. // Returns to the original position by defaultCopy the code

The effect of adding the above two lines of code remains exactly where the animation ended

If you want to do something extra before or at the end of the animation, you need to use a proxy:

@interface ViewController ()<CAAnimationDelegate> anim.delegate = self; - (void)animationDidStart:(CAAnimation *)anim{NSLog(@"start-%@",NSStringFromCGRect(_redview.frame)); } //hittest - (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag{ NSLog(@"stop-%@",NSStringFromCGRect(_redView.frame)); // NSLog(@"stop-%@",NSStringFromCGRect(_redView.layer.presentationLayer.frame)); // NSLog(@"stop-%@",NSStringFromCGRect(_redView.layer.modelLayer.frame)); } start-{{0, 0}, {100, 100}} stop-{{0, 0}, {100, 100}}Copy the code

Why is stop-{{0, 0}, {100, 100}} printed at the end of the animation?

UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tapAction:)]; [_redView addGestureRecognizer:tap]; - (void)tapAction:(UITapGestureRecognizer *)ges{NSLog(@"redview was hit "); }Copy the code

So this program runs and I click On RedView, print RedView is clicked and the animation ends and I click on RedView, don’t print RedView is clicked

//NSLog(@"stop-%@",NSStringFromCGRect(_redView.frame)); // stop-{{0, 0}, {100, 100}} //NSLog(@"stop-%@",NSStringFromCGRect(_redView.layer.frame)); // stop-{{0, 0}, {100, 100}} NSLog(@"stop-%@",NSStringFromCGRect(_redView.layer.presentationLayer.frame)); / / stop - {{0, 349.99860227108002}, {100, 100}} / / NSLog (@ "stop - % @", NSStringFromCGRect (_redView. Layer. ModelLayer. Frame)); // stop-{{0, 0}, {100, 100}}Copy the code

_redView. Layer. PresentationLayer. The location of the frame to present

CAKeyframeAnimation

Demo address: github.com/tanghaitao/…

#define angleToRadians(Angle) ((Angle)/ 180.0 *M_PI)// CAKeyframeAnimation *anim = [CAKeyframeAnimation animation]; anim.keyPath = @"transform.rotation"; anim.values = @[@angleToRadians(-6), @angleToRadians(6)]; anim.repeatCount = MAXFLOAT; anim.duration = 2; // The default is 0.5 secondsCopy the code

The effect is as follows:

As you can see,From beginning to endIt’s normal, butFrom the end to the beginningDuration = “out of order” = “out of order” Solutions:

  • anim.values = @[@angleToRadians(-6),@angleToRadians(6),@angleToRadians(-6)];
  • anim.autoreverses = YES; / / reversal,At the same time set upanim.speed = 2;, the speed will slow down after reversal (This approach is recommended)

The effect is as follows:

2.1 Case Study: Animation of roller coaster

UIBezierPath: Using formulas to plot data into curves there are three key pieces of information to plot: drop point line addline pick pointCopy the code

End result:

Start with the idea:

2.1 Draw bezier curves

-(void)test21{UIBezierPath *path = [UIBezierPath bezierPath]; [path moveToPoint:CGPointMake(50, 200)]; [path addCurveToPoint:CGPointMake(300, 200) controlPoint1:CGPointMake(180, 100) controlPoint2:CGPointMake(200, 300)]; / / [path addCurveToPoint: CGPointMake (300, 200) controlPoint1: CGPointMake (112.5, 100) controlPoint2: CGPointMake (237.5, 300)]; // (100, 100) top (200, 300) bottom // width: 300-50 = 250, 1/2= 125,1/4= 62.5, // Left midpoint 175+1/4 = 175+62.5 = 112.5 // right midpoint 175+1/4 = 175+62.5 = 237.5 // < 200, top, > 200, bottom // Add path to layer CAShapeLayer *shapeLayer = [CAShapeLayer layer]; shapeLayer.path = path.CGPath; / / -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- here [self. View. Layer addSublayer: shapeLayer]; }Copy the code

Effect display:

2.2 Remove the fill color and set the brush color

-(void)test22{UIBezierPath *path = [UIBezierPath bezierPath]; [path moveToPoint:CGPointMake(50, 200)]; [path addCurveToPoint:CGPointMake(300, 200) controlPoint1:CGPointMake(180, 100) controlPoint2:CGPointMake(200, 300)]; CAShapeLayer *shapeLayer = [CAShapeLayer]; shapeLayer.path = path.CGPath; shapeLayer.fillColor = [UIColor clearColor].CGColor; // Fill shapelayer.strokecolor = [UIColor blackColor].cgcolor; [self.view.layer addSublayer:shapeLayer]; }Copy the code

Effect display:

2.3 Drawing roller Coaster pictures

-(void)test23 {UIBezierPath *path = [UIBezierPath bezierPath]; [path moveToPoint:CGPointMake(50, 200)]; [path addCurveToPoint:CGPointMake(300, 200) controlPoint1:CGPointMake(180, 100) controlPoint2:CGPointMake(200, 300)]; Begin CALayer *carLayer = [CALayer layer]; carLayer.frame = CGRectMake(50-30, 200-18, 36, 36); //50-36/2 = 32 carLayer.contents = (__bridge id _Nullable)([UIImage imageNamed:@"car.png"].CGImage); [self.view.layer addSublayer:carLayer]; End // add path to layer CAShapeLayer *shapeLayer = [CAShapeLayer]; shapeLayer.path = path.CGPath; shapeLayer.fillColor = [UIColor clearColor].CGColor; // Fill shapelayer.strokecolor = [UIColor blackColor].cgcolor; [self.view.layer addSublayer:shapeLayer]; }Copy the code

Effect display:

2.4 Add roller coaster animation

-(void)test24 {UIBezierPath *path = [UIBezierPath bezierPath]; [path moveToPoint:CGPointMake(50, 200)]; [path addCurveToPoint:CGPointMake(300, 200) controlPoint1:CGPointMake(180, 100) controlPoint2:CGPointMake(200, 300)]; CALayer *carLayer = [CALayer layer]; carLayer.frame = CGRectMake(50-30, 200-18, 36, 36); //50-36/2 = 32 carLayer.contents = (__bridge id _Nullable)([UIImage imageNamed:@"car.png"].CGImage); [self.view.layer addSublayer:carLayer]; CAShapeLayer *shapeLayer = [CAShapeLayer]; shapeLayer.path = path.CGPath; shapeLayer.fillColor = [UIColor clearColor].CGColor; // Fill shapelayer.strokecolor = [UIColor blackColor].cgcolor; [self.view.layer addSublayer:shapeLayer]; CAKeyframeAnimation *anim = [CAKeyframeAnimation animation]; anim.path = path.CGPath; // path, not values anim.keyPath = @"position"; // Change position im.duration = 5.0f; [carLayer addAnimation:anim forKey:nil]; }Copy the code

Effect display:

2.5 Vehicle Angle does not follow the curve

-(void)test25 {UIBezierPath *path = [UIBezierPath bezierPath]; [path moveToPoint:CGPointMake(50, 200)]; [path addCurveToPoint:CGPointMake(300, 200) controlPoint1:CGPointMake(180, 100) controlPoint2:CGPointMake(200, 300)]; CALayer *carLayer = [CALayer layer]; carLayer.frame = CGRectMake(50-30, 200-18, 36, 36); //50-36/2 = 32 carLayer.contents = (__bridge id _Nullable)([UIImage imageNamed:@"car.png"].CGImage); [self.view.layer addSublayer:carLayer]; CarLayer. AnchorPoint = CGPointMake (0.5, 8). // Add path to layer CAShapeLayer *shapeLayer = [CAShapeLayer layer]; shapeLayer.path = path.CGPath; shapeLayer.fillColor = [UIColor clearColor].CGColor; // Fill shapelayer.strokecolor = [UIColor blackColor].cgcolor; [self.view.layer addSublayer:shapeLayer]; CAKeyframeAnimation *anim = [CAKeyframeAnimation animation]; anim.path = path.CGPath; anim.keyPath = @"position"; Anim. Duration = 5.0 f; anim.rotationMode = kCAAnimationRotateAuto; // --------2 [carLayer addAnimation:anim forKey:nil]; }Copy the code

Effect display:

2.6 Stay in the End position

-(void)test26 {UIBezierPath *path = [UIBezierPath bezierPath]; [path moveToPoint:CGPointMake(50, 200)]; [path addCurveToPoint:CGPointMake(300, 200) controlPoint1:CGPointMake(180, 100) controlPoint2:CGPointMake(200, 300)]; CALayer *carLayer = [CALayer layer]; carLayer.frame = CGRectMake(50-30, 200-18, 36, 36); //50-36/2 = 32 carLayer.contents = (__bridge id _Nullable)([UIImage imageNamed:@"car.png"].CGImage); [self.view.layer addSublayer:carLayer]; CarLayer. AnchorPoint = CGPointMake (0.5, 8). // Add path to layer CAShapeLayer *shapeLayer = [CAShapeLayer layer]; shapeLayer.path = path.CGPath; shapeLayer.fillColor = [UIColor clearColor].CGColor; // Fill shapelayer.strokecolor = [UIColor blackColor].cgcolor; [self.view.layer addSublayer:shapeLayer]; CAKeyframeAnimation *anim = [CAKeyframeAnimation animation]; anim.path = path.CGPath; anim.keyPath = @"position"; Anim. Duration = 5.0 f; anim.rotationMode = kCAAnimationRotateAuto; Anim. removedOnCompletion = NO; anim.fillMode = kCAFillModeForwards; [carLayer addAnimation:anim forKey:nil]; }Copy the code

Effect display:

Above is the whole analysis process, you can analyze each step of the operation according to the effect.

3 transition animation CATransition

For a simple example, when a person sleeps straight, the frame and bounds are the same, but if the person sleeps sideways, the frame gets larger, and the bounds are self-explanatory. Frame ={-1,3,62,64},bounds={0,0,40,50};

Demo address: github.com/tanghaitao/…

Transition type:

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{ _index++; if (_index == 3) { _index = 0; } NSString *imgName = _imgs[_index]; _imgView.image = [UIImage imageNamed:imgName]; CATransition *anim = [CATransition animation]; anim.type = @"cube"; anim.duration = .5; // anim.startProgress = .2; // Anim.endProgress =.5; [_imgView.layer addAnimation:anim forKey:nil]; // Transition end time: End transition animation at half [_imgView.layer addAnimation:anim forKey:nil]; }Copy the code

Effect display:

CAAnimationGroup

As the name implies, multiple animations are executed simultaneously

Demo address: github.com/tanghaitao/… In the test1() method of viewController.m:

- (void)test1{// curve UIBezierPath *path = [UIBezierPath bezierPath]; [path moveToPoint:CGPointMake(50, 200)]; [path addCurveToPoint:CGPointMake(300, 200) controlPoint1:CGPointMake(180, 100) controlPoint2:CGPointMake(200, 300)]; CAShapeLayer *shapeLayer = [CAShapeLayer]; shapeLayer.path = path.CGPath; shapeLayer.fillColor = nil; shapeLayer.strokeColor = [UIColor redColor].CGColor; [self.view.layer addSublayer:shapeLayer]; CALayer *colorLayer = [CALayer layer]; colorLayer.frame = CGRectMake(0, 0, 60, 60); colorLayer.position = CGPointMake(50, 200); colorLayer.backgroundColor = [UIColor blueColor].CGColor; [self.view.layer addSublayer:colorLayer]; CAKeyframeAnimation *anim = [CAKeyframeAnimation animation]; anim.keyPath = @"position"; anim.path = path.CGPath; / / anim. Duration = 4.0 f; // anim.removedOnCompletion = NO; // anim.fillMode = kCAFillModeForwards; // anim.rotationMode = kCAAnimationRotateAuto; // [colorLayer addAnimation:anim forKey:nil]; // Change the size of CABasicAnimation *sizeAnim = [CABasicAnimation animation]; sizeAnim.keyPath = @"transform.scale"; sizeAnim.toValue = @.5; / / sizeAnim. Duration = 4.0; // sizeAnim.fillMode = kCAFillModeForwards; // sizeAnim.removedOnCompletion = NO; // // [colorLayer addAnimation:sizeAnim forKey:nil]; CGFloat red = arc4random()/(CGFloat)INT_MAX; CGFloat green = arc4random() / (CGFloat)INT_MAX; CGFloat blue = arc4random() / (CGFloat)INT_MAX; UIColor *color = [UIColor colorWithRed:red green:green blue:blue alpha:1]; CABasicAnimation *colorAnim = [CABasicAnimation animation]; colorAnim.keyPath = @"backgroundColor"; colorAnim.toValue = (id)color.CGColor; / / colorAnim. Duration = 4.0 f; // colorAnim.fillMode = kCAFillModeForwards; // colorAnim.removedOnCompletion = NO; // [colorLayer addAnimation:colorAnim forKey:nil]; CAAnimationGroup *group = [CAAnimationGroup animation]; group.animations = @[anim, sizeAnim, colorAnim]; // Duplicate code can be placed in the animation group. Group. The duration = 4.0 f; group.fillMode = kCAFillModeForwards; group.removedOnCompletion = NO; [colorLayer addAnimation:group forKey:nil]; }Copy the code

Effect display:

CAKeyframeAnimation *anim = [CAKeyframeAnimation animation]; // Change the size of CABasicAnimation *sizeAnim = [CABasicAnimation animation]; CABasicAnimation *colorAnim = [CABasicAnimation animation]; CAAnimationGroup *group = [CAAnimationGroup animation]; group.animations = @[anim, sizeAnim, colorAnim]; // Duplicate code can be placed in the animation group. Group. The duration = 4.0 f; group.fillMode = kCAFillModeForwards; group.removedOnCompletion = NO; Here three animations are added and changed at the same time, using the animation group CAAnimationGroupCopy the code