1. The iOS animation

In general, from the form of classes involved, iOS animation includes: affine deformation animation based on UIView, animation based on CAAnimation and its subclasses, animation based on CG. This article focuses on the first two animations.

2. UIView animation

There are two properties commonly used to set UIView deformation animations,.frame and.transform, so some people might call them respectively:

  • (1) frame animation
  • (2) the transform of the animation

These two animations simply need to be set in the appropriate place in the animation syntax, based on UIView and CALayer properties. This animation does not need to call the special classes and apis in the core animation CAAnimation.

Among them, the frame animation setting mode is limited, and the frame before and after the deformation must be accurately formulated. The translation is ok, especially when the rotation, the new frame can only be calculated by mathematical knowledge. This is not worth the cost. So, more often than not, a transform can be used instead of a frame when some frame, bounds, or center change or deformation is involved.

2.1 Set the two syntax forms of UIView animation

  • begin — commit
UIView beginAnimations:@"move" context:nil];  
  [UIView setAnimationDuration:2];  
  [UIView setAnimationDelegate:self];  
  imageContainView.frame = CGRectMake(80, 80, 200, 200);  
  [label1 setBackgroundColor:[UIColor yellowColor]];  
  [label1 setTextColor:[UIColor redColor]];  
  [UIView commitAnimations]; 
Copy the code
  • animations block
/ view/zoom animation. The transform = CGAffineTransformIdentity; [UIView animateWithDuration: 1.0 f animations: ^ {the transform = CGAffineTransformMakeScale (2.0 f to 2.0 f);}].Copy the code

2.2 Setting the two types of property deformation animation

  • UIView CGAffineTransform properties: animatedView**. Transform ** is generally the View’s rotation, stretch and move properties, etc. It is two-dimensional and usually uses classes with the prefix CGAffineTransform.
CGAffineTransform transform = CGAffineTransformScale (imageContainView. The transform, 1.2, 1.2); [UIView beginAnimations: @"scale"context: nil]; 
[UIView setAnimationDuration: 2];
[UIView setAnimationDelegate: self];
[imageView setTransform: transform]; 
[UIView commitAnimations];
Copy the code
  • The CALayerCATransform3DType properties: animaView**.layer.transform** Pass.layer.transformChanges can be made under 3D mode, usually using classes prefixed with CATransform3D.
imageView.layer.transform = CATransform3DIdentity; [UIView animateWithDuration: 1.0 f animations: ^ {imageView. Layer. The transform = CATransform3DMakeScale (2.0, 2.0, 1.0);}].Copy the code

2.3 Animation-related properties

2.3.1 UIView animation-related properties — corresponding to CGAffineTransform

Here are some properties of UIView

@property(nonatomic) CGRect            frame;
@property(nonatomic) CGRect            bounds;      // default bounds is zero origin, frame size. animatable
@property(nonatomic) CGPoint           center;      // center is center of frame. animatable
@property(nonatomic) CGAffineTransform transform;   // default is CGAffineTransformIdentity. animatable
@property(nonatomic) CGFloat           contentScaleFactor NS_AVAILABLE_IOS(4_0);

@property(nonatomic,getter=isMultipleTouchEnabled) BOOL multipleTouchEnabled __TVOS_PROHIBITED;   // default is NO
@property(nonatomic,getter=isExclusiveTouch) BOOL       exclusiveTouch __TVOS_PROHIBITED;         // default is NO
Copy the code

In actual development, use scenarios:

  • (1) A transform can be used instead of a frame when some frame, bounds, or center changes or deforms are involved.

  • (2) Generally, translation, rotation and scaling are used in combination in actual development.

2.3.2 CALayer’s animation-related properties — corresponding to CATransform3D

Here are some of CALayer’s properties

// Width and height @property CGRect bounds; // Position (the default is the midpoint, which is determined by anchorPoint) @property CGPoint position; @property CGPoint anchorPoint; @property CGPoint anchorPoint; @property CGPoint anchorPoint; // backgroundColor (CGColorRef type) @property CGColorRef backgroundColor; // @property CATransform3D transform; // borderColor (CGColorRef type) @property CGColorRef borderColor; @property CGFloat borderWidth; // property CGFloat cornerRadius; // Contents (e.g. CGImageRef) @property(retain) id contents;Copy the code

2.4 Encapsulation classes for managing 2d and 3D deformation: CGAffineTransform and CATransform3D

2.4.1 CGAffineTransform operation API
  • CGAffineTransform structure definition
struct CGAffineTransform {
  CGFloat a, b, c, d;
  CGFloat tx, ty;
};
Copy the code

It’s actually a matrix:

Since the last column is always (0,0,1), the useful information is the first two columns. Making an affine change to a view is equivalent to multiplying every point on the view, resulting in:

A represents the horizontal scaling of x, tx represents the horizontal scaling of X, D represents the vertical scaling of y, and ty represents the vertical scaling of y. If b and C are not zero, then the view must be rotated. The rotation Angle is calculated as follows: Tan (Angle) = B/A

If so:

This is what it looked like in the beginning without change.

  • CGAffineTransform API operation
/ / reduction CGAffineTransformIdentity / / displacement affine - understood as translation CGAffineTransformMakeTranslation (CGFloat tx, CGFloat ty) Rotate affine - understood as CGAffineTransformTranslate / / (CGFloat Angle) CGAffineTransformMakeRotation CGAffineTransformRotate / / scaling of affine - understand zooming (CGFloat sx, CGFloat sy) CGAffineTransformMakeScale CGAffineTransformScaleCopy the code

The mathematical nature of CGAffineTransform operations

  • CGPoint conversion formula

  • Demonstration of matrix multiplication operation principle

2.4.2 CATransform3D operation API
// Restore CATransform3DIdentity // shift 3D affine ==> (CGFloat tx, CGFloat ty, CGFloat tz) CATransform3DMakeTranslation CATransform3DTranslation / / rotate the 3 d affine = = > (CGFloat Angle, CGFloat x, CGFloat y, CGFloat z) CATransform3DMakeRotation CATransform3DRotation / / 3 d affine scaling = = > (CGFloat Angle, CGFloat x, CGFloat y, CGFloat z) CATransform3DMakeScale CATransform3DScale // Add 3D affine effect CATransform3DConcat // Affine basic 3D method, CGAffineTransformMake (sx, SHX,shy, SY,tx, TY) == (CATransform3D t) CATransform3DIsIdentity (transform) / / check whether the two 3 d affine effect is the same CATransform3DEqualToTransform (transform1 transform2) / / 3 d affine effect reversed (the opposite effect, For example, if you expand, you shrink.Copy the code
2.4.3 API for exchanging CATransform3D and CGAffineTransform
Converting a CGAffinrTransform / / CATransform3D CATransform3D CATransform3DMakeAffineTransform CGAffineTransform (m); // Determine whether a CATransform3D can be converted to CAAffineTransformbool CATransform3DIsAffine (CATransform3D T); / / converts CATransform3D CGAffineTransform CGAffineTransform CATransform3DGetAffineTransform CATransform3D (t);Copy the code

2.5 “Combination Animation” and CGAffineTransformConcat

2.5.1 Connection Set multiple properties to combine into one animation

If you connect an animation that sets more than two properties, you can call the API with formMake first and then the API with form only. For example, like this:

alertView.transform = CGAffineTransformMakeScale(.25, .25);
alertView.transform = CGAffineTransformTranslate(alertView.transform, 0, 600);
Copy the code
2.5.2 Set the combined animation with CGAffineTransformConcat

In addition, you can directly use CGAffineTransformConcat to combine various deformation apis that contain formMake.

CGAffineTransform viewTransform = CGAffineTransformConcat(CGAffineTransformMakeScale(.25, .25), CGAffineTransformMakeTranslation(0, 600));
alertView.transform = viewTransform;
Copy the code

There is also an API for combining 3D deformations, CATransform3DConcat, which will be covered in the next article on 3D deformations.

  • CGAffineTransformConcat combines small examples of various deformation animations
- (void)viewDidAppear: (BOOL) Animated {CGFloat offset = label1.frame.size. Height * 0.5; label1.transform = CGAffineTransformConcat( CGAffineTransformMakeScale(0, 0), CGAffineTransformTranslate(0, -offset) ); label1.alpha = 0; [UIView animateWithDuration: 3. animations: ^ {/ / / reduction label1 state of transformation and deformation and offset label2 label1. Transform = CGAffineTransformIdentifier;  label1.transform = CGAffineTransformConcat( CGAffineTransformMakeScale(0, 0), CGAffineTransformTranslate(0, offset) );  label1.alpha = 1; label2.alpha = 0; }]; }Copy the code
  • The mathematical essence of CGAffineTransformConcat is to multiply the coefficient matrices of several transformations represented in parentheses.

  • Another combinatorial transform appends a new CGAffineTransform continuously based on the existing CGAffineTransform:

CGAffineTransform transform = CGAffineTransformIdentity; Transform = CGAffineTransformScale(transform, 0.5f, 0.5f); Transform = CGAffineTransformRotate (transform, f / 180.0 30.0 f * M_PI); Transform = CGAffineTransformTranslate (transform, 200.0 f, 0.0 f); layerView.layer.affineTransform = transform;Copy the code

2.6 Restore the properties after animation

If we need to restore the default state when changing a view.transform property or view.layer.transform, remember to reset them to:

view.transform = CGAffineTransformIdentity;
view.layer.transform = CATransform3DIdentity;
Copy the code

2.7 Attention: Effect of Transform on frame

The transform property is documented as follows:

Changes to this property can be animated. However, if the transform property contains a non-identity transform, the value of the frame property is undefined and should not be modified. In that case, you can reposition the view using the center property and adjust the size using the bounds property instead.

If you change the transform of a control in your program, do not use the control’s frame to calculate the layout of the child control; use bounds+ Center instead.

CAAnimation core animation

CAAnimation – The parent class of all animation objects

3.1 Setting a syntax form for animation

  • addAnimation
*/ -(void)shakeAnimation{CAKeyframeAnimation *anima = [CAKeyframeAnimation animationWithKeyPath:@"transform.rotation"]; // Here at @"transform.rotation"= = @"transform.rotation.z"
    NSValue *value1 = [NSNumber numberWithFloat:-M_PI/180*4];
    NSValue *value2 = [NSNumber numberWithFloat:M_PI/180*4];
    NSValue *value3 = [NSNumber numberWithFloat:-M_PI/180*4];
    anima.values = @[value1,value2,value3];
    anima.repeatCount = MAXFLOAT;
    
    [_demoView.layer addAnimation:anima forKey:@"shakeAnimation"];
}
Copy the code

3.2 CAAnimation Inheritance structure

CAAnimation{
     CAPropertyAnimation{
            CABasicAnimation{
                    CASpringAnimation
            }
            CAKeyframeAnimation
     }
     CATransition   
     CAAnimationGroup
}
Copy the code

Is the parent class of all animation objects, responsible for controlling the duration and speed of the animation, is an abstract class, should not be used directly, should use its concrete subclass

3.3 Properties of CAAnimation class

* indicates attributes from the CAMediaTiming protocol.)

  • *duration: indicates the duration of the animation
  • *repeatCount: number of repetitions, infinite loop can be set to HUGE_VALF or MAXFLOAT
  • *repeatDuration: indicates the repeatDuration
  • RemovedOnCompletion: Default is YES, which means that the animation will be removed from the layer after completion and the image will be restored to its original state. Set it to NO if you want the layer to look like it did after the animation was executed, but also set fillMode to kcafillmodeforward
  • *fillMode: determines the behavior of the current object when it is not active. Like before the animation starts or after the animation ends
  • *beginTime: can be used to set the animation delay. If you want to delay the animation for 2s, set it to CACurrentMediaTime()+2, CACurrentMediaTime() is the current time of the layer
  • TimingFunction: a speed control function that controls the tempo of the animation
  • Delegate: Animation delegate

3.4 Several important attribute values

RemovedOnCompletion attribute values

CAAnimation — Animation fill mode

The default value is YES, which means the animation is removed from the layer and the image is restored to its original state. Set it to NO if you want the layer to look like it did after the animation was executed, but also set fillMode to kcafillmodeforward

FillMode property values

CAAnimation – Control reverts to before animation execution

For fillMode to work, it is best to set removedOnCompletion = NO

  • KCAFillModeRemoved This is the default value, which means that before and after the animation, the animation has no effect on the layer, after the animation is finished, the layer will revert to its previous state
  • Kcafillmodeforward After the animation ends, the layer will keep the final state of the animation
  • KCAFillModeBackwards Before animation can start, all you need to do is add animation to a layer and the layer will go to the initial state of animation and wait for animation to start.
  • KCAFillModeBoth is actually a combination of these two. After the animation is added, the layer is in the initial state of the animation until it starts, and after the animation is over, the layer remains in the last state of the animation

If fillMode = kcafillmodeforward and removedOnComletion = NO, then after the animation is finished, the layer will keep displaying the animation state. But in essence, the layer properties are the same as they were before the animation was executed and haven’t really changed.

TimingFunction attribute values

CAAnimation – Animation speed control function

  • KCAMediaTimingFunctionLinear (linear) : uniform, give you a relatively static feeling
  • KCAMediaTimingFunctionEaseIn (gradual) : enter slowly, then accelerates to leave
  • KCAMediaTimingFunctionEaseOut (gradually) : enter at full speed, and then slow to arrive
  • KCAMediaTimingFunctionEaseInEaseOut (gradual fading out) : enter slow, intermediate speeds up, and then slow down to arrive at the destination. This is the default animation behavior.

4. CABasicAnimation

Basic animation, a subclass of CAPropertyAnimation

4.1 Special Attribute Description:

  • KeyPath: Name of the property to change (pass string)
  • FromValue: keyPath Specifies the initial value of the corresponding attribute
  • ToValue: keyPath End value of the corresponding attribute

4.2 What values can keyPath be

X transform.rotation. Y transform.rotation. Z //scale scale transform.scale.x Transform.scale.y transform.scale.z //translation translation transform.translation.x transform.translation.y transform.translation.z } CGPoint{ position position.x position.y } CGRect{ bounds bounds.size bounds.size.width bounds.size.height bounds.origin bounds.origin.x bounds.origin.y } property{ opacity backgroundColor cornerRadius borderWidth contents Shadow{ shadowColor shadowOffset shadowOpacity shadowRadius } }Copy the code

For example, 4.3

  • Scale animation — transform.scale
CABasicAnimation *scaleAnimation = [CABasicAnimation animationWithKeyPath:@"transform.scale"]; // Select keyPath to scale scaleAnimation. FromValue = [NSNumber numberWithDouble:0.5]; ScaleAnimation. ToValue = [NSNumber numberWithDouble:1.5]; scaleAnimation. ToValue = [NSNumber numberWithDouble:1.5]; Duration = 1; scaleAnimation. Duration = 1; / / set time scaleAnimation. RepeatCount = MAXFLOAT; / / repetitions [_heartImageView layer addAnimation: scaleAnimationforKey:@"CQScale"]; // Add animationCopy the code
  • Rotate animation — transform.rotation.z
// CABasicAnimation *rotationAnimation = [CABasicAnimation animationWithKeyPath:@"transform.rotation.z"];
    rotationAnimation.fromValue = [NSNumber numberWithDouble:0.f];
    rotationAnimation.toValue = [NSNumber numberWithDouble:2 * M_PI];
    rotationAnimation.duration = 2.f;
    rotationAnimation.repeatCount = MAXFLOAT;
    [_fengcheImageView.layer addAnimation:rotationAnimation forKey:@"CQRotation"];
Copy the code
  • Pan animation — Position. x/ Position. y
CABasicAnimation *positionAnimation = [CABasicAnimation animationWithKeyPath:@"position.x"];
    positionAnimation.fromValue = [NSNumber numberWithDouble:0.f];
    positionAnimation.toValue = [NSNumber numberWithDouble:SCREEN_WIDTH];
    positionAnimation.duration = 2;
    positionAnimation.repeatCount = MAXFLOAT;
    [_arrowImageView.layer addAnimation:positionAnimation forKey:@"CQPosition"];
Copy the code

CAKeyframeAnimation 5. CAKeyframeAnimation

5.1 Parameter Array Format

CAKeyframeAnimation *catKeyAnimation = [CAKeyframeAnimation animationWithKeyPath:@"position"];
    CGPoint originalPoint = self.catImageView.layer.frame.origin;
    CGFloat distance =  50;
    NSValue *value1 = [NSValue valueWithCGPoint:CGPointMake(originalPoint.x + distance, originalPoint.y + distance)];
    NSValue *value2 = [NSValue valueWithCGPoint:CGPointMake(originalPoint.x + 2 * distance, originalPoint.y + distance)];
    NSValue *value3 = [NSValue valueWithCGPoint:CGPointMake(originalPoint.x + 2 * distance, originalPoint.y +  2 * distance)];
    NSValue *value4 = [NSValue valueWithCGPoint:originalPoint];
    catKeyAnimation.values = @[value4, value1, value2, value3, value4];
    catKeyAnimation.duration = 2;
    catKeyAnimation.repeatCount = MAXFLOAT;
    catKeyAnimation.removedOnCompletion = NO;
    [self.catImageView.layer addAnimation:catKeyAnimation forKey:nil];
Copy the code

5.2 the path form

/ / specified path UIBezierPath * path = [UIBezierPath bezierPathWithOvalInRect: CGRectMake (0, 200, 200, 200)]. UIBezierPath *path2 = [UIBezierPath bezierPath]; [path2 moveToPoint:CGPointMake(100, 100)]; [path2 addLineToPoint:CGPointMake(100, 200)]; [path2 addLineToPoint:CGPointMake(200, 200)]; [path2 addLineToPoint:CGPointMake(200, 100)]; [path2 addLineToPoint:CGPointMake(100, 100)]; CAKeyframeAnimation *penguinAnimation = [CAKeyframeAnimation animationWithKeyPath:@"position"];
    penguinAnimation.path = path2.CGPath;
    penguinAnimation.duration = 2;
    penguinAnimation.repeatCount = MAXFLOAT;
    penguinAnimation.removedOnCompletion = NO;
    [self.penguinImageView.layer addAnimation:penguinAnimation forKey:nil];
Copy the code

6. Set of animations

6.1 group animation

The single animation situation above is actually less common in real life development, and more often involves composing animations: create different types of animated objects, set their parameters, and then store these animated objects in an array, passing them to the animations property of the group of animated objects.

6.2 the sample

CAAnimationGroup *animationGroup = [CAAnimationGroup animation]; animationGroup.duration = 3; animationGroup.repeatCount = MAXFLOAT; animationGroup.removedOnCompletion = NO; /* beginTime can be set separately for each animation to control the trigger time of each animation in the group, the time can not exceed the animation time, CAKeyframeAnimation *animation1 = [CAKeyframeAnimation animationWithKeyPath:@"transform.scale"]; Animation1. values = @[[NSNumber numberWithFloat:1.0],[NSNumber numberWithFloat:0.5],[NSNumber NumberWithFloat: 1.5], [NSNumber numberWithFloat: 1.0]]. animation1.beginTime = 0.f; CAKeyframeAnimation *animation2 = [CAKeyframeAnimation animationWithKeyPath:@"position"]; UIBezierPath *bezierPath = [UIBezierPath bezierPath]; [bezierPath moveToPoint:CGPointMake(300, 200)]; [bezierPath addQuadCurveToPoint:CGPointMake(200, 300) controlPoint:CGPointMake(300, 300)]; [bezierPath addQuadCurveToPoint:CGPointMake(100, 200) controlPoint:CGPointMake(100, 300)]; [bezierPath addQuadCurveToPoint:CGPointMake(200, 100) controlPoint:CGPointMake(100, 100)]; [bezierPath addQuadCurveToPoint:CGPointMake(300, 200) controlPoint:CGPointMake(300, 100)]; animation2.path = bezierPath.CGPath; animation2.beginTime = 0.f; // CABasicAnimation *animation3 = [CABasicAnimation animationWithKeyPath:@"opacity"]; Animation3. FromValue = [NSNumber numberWithDouble: 0.0]; Animation3. ToValue = [NSNumber numberWithDouble: 1.0]; animation3.beginTime = 0.f; / / add group animation animationGroup. Animations = @ [animation1, animation2 animation3]; [_penguinImageView.layer addAnimation:animationGroupforKey:nil];
Copy the code

7. Bezier curve

Bezier curve is mentioned in the previous chapter of keyframe animation. This curve is very useful. In iOS development, there are two forms available: CGMutablePathRef and UIBezierPath. Here’s an example of both:

7.1 CGMutablePathRef

  • Uniquely identified by key point curve connection
CGMutablePathRef path = CGPathCreateMutable(); CGPathMoveToPoint(path, NULL, 200, 200); // Start point CGPathAddCurveToPoint(path, NULL, 100, 300, 300, 500, 200, 600); // CGPathAddCurveToPoint(path, NULL, control point 1.x, control point 1.y, control point 2.x, control point 2.y, endpoint.x, endpoint.y); // Set the path property keyframeAnimation. Path = path; CGPathRelease(path); / / set the other properties keyframeAnimation. Duration = 4; keyframeAnimation.beginTime = CACurrentMediaTime() + 1; // Set the execution delay to 2 seconds. If this attribute is not set, the execution will be directly executed by default. The animation is added to the layer and automatically execute [_layer addAnimation: keyframeAnimationforKey:@"GGKeyframeAnimation"];
Copy the code

7.2 UIBezierPath

  • Uniquely determined by a rectangular incut ellipse
    CAKeyframeAnimation *anima = [CAKeyframeAnimation animationWithKeyPath:@"position"]; UIBezierPath *path = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(SCREEN_WIDTH/2-100, SCREEN_HEIGHT/2-100, 200, 200)]; anima.path = path.CGPath; Anima. Duration = 2.0 f; [_demoView.layer addAnimation:animaforKey:@"pathAnimation"];
Copy the code
  • Uniquely identified by key point linear connection
-(void)pathAnimation2{// Create path UIBezierPath *path = [UIBezierPath bezierPath]; // Set path.lineWidth = 3; // Line corner path.lineCapStyle = kCGLineCapRound; // Path. lineJoinStyle = kCGLineJoinRound; [path moveToPoint (CGPoint){0, SCREEN_HEIGHT/2-50}]; [path addLineToPoint:(CGPoint){SCREEN_WIDTH/3, SCREEN_HEIGHT/2-50}]; [path addLineToPoint:(CGPoint){SCREEN_WIDTH/3, SCREEN_HEIGHT/2+50}]; [path addLineToPoint:(CGPoint){SCREEN_WIDTH*2/3, SCREEN_HEIGHT/2+50}]; [path addLineToPoint:(CGPoint){SCREEN_WIDTH*2/3, SCREEN_HEIGHT/2-50}]; [path addLineToPoint:(CGPoint){SCREEN_WIDTH, SCREEN_HEIGHT/2-50}]; // [path closePath]; CAKeyframeAnimation *anima = [CAKeyframeAnimation animationWithKeyPath:@"position"]; anima.path = path.CGPath; Anima. Duration = 2.0 f; anima.fillMode = kCAFillModeForwards; anima.removedOnCompletion = NO; anima.timingFunction = [CAMediaTimingFunctionfunctionWithName:kCAMediaTimingFunctionEaseInEaseOut];
    [_demoView.layer addAnimation:anima forKey:@"pathAnimation"];
}
Copy the code