This article is referenced fromAtypical tech geeks are bigAs well asSTzen greatlyTwo articles, the code is also a reference to its code collation, invasion and deletion, we strongly recommend reading the above article, benefit a lot.

### background, we may encounter some iOS animations more or less in the development requirements, but no matter the complexity of animations (which is not very complex) or some other reasons of our own, we have not tried to learn this, feeling it is very undesirable. Therefore, after finishing the work at hand, This week, I slowly learned some content of animation, and share some of my doubts with you.

The demo to see

1 Some attributes about CALayer

Effect:

####CALayer Overview

  • The CALayer class is conceptually similar to UIView in that it is also rectangular blocks that are managed by hierarchical trees and can also contain content (like images, text, or background colors) that manage the position of sublayers. They have methods and properties to animate and transform. The biggest difference with UIView is that CALayer doesn't handle user interaction.
  • For some simple needs, we really don't need to deal with CALayer because Apple has made animation very easy indirectly through UIView's advanced API. But this simplicity inevitably leads to some flexibility. On the defects. If you want to make some low-level changes, or use some interface functionality that Apple doesn't implement on UIView, you have no choice but to step in at the bottom of Core Animation.We’ve proved that layers can’t handle touch events the way views can, so what can they do that views can’t? Here are some features of CALayer that UIView does not expose: ●Shadows, rounded corners, colored borders3 d transformNon-rectangular rangeTransparent maskMultistage nonlinear animation

CALayer attribute (Personal understanding)

  • Contents Layer contents, id type. But it must be of CGImage type, otherwise the resulting layer will be blank.
Clocklayer. contents = (__bridge ID _Nullable)([UIImage imageNamed:@)"clock"].CGImage);

clockLayer.contents = (id)([UIImage imageNamed:@"clock"].CGImage);
Copy the code
  • Position is similar to UIView’s Center (by default, in the default center of the graph)

  • The anchorPoint is determined by the position of positon, and we can determine the position by the anchorPoint. It ranges from 0,0 to 1,1. The following figure

  • On top of the clock, we modified it for anchorPoint. Imagine if we didn’t modify its anchorPoint, it would look like this,

  • If CGPointMake is set to (0.5, 0) or (0.5, 1), the effect is the same.

UIView *hourHandView = [[UIView alloc]init]; hourHandView.backgroundColor = [UIColor blackColor]; hourHandView.bounds = CGRectMake(0, 0, 4, 50); hourHandView.center = self.view.center; HourHandView. Layer. AnchorPoint = CGPointMake (0.5, 1); [self.view addSubview:hourHandView]; self.hourHandView = hourHandView;Copy the code

Animation (Picture from network)

CABasicAnimation

  • An object that provides basic, single-keyframe animation capabilities for a layer property. Inherited from CAPropertyAnimation. We can set some properties of the animation using keyPath. Common keyPath(don’t memorize it, just look at it yourself when you use it)
  • FromValue initial state for examplebasicAnimation.fromValue = [NSValue valueWithCGPoint:CGPointMake(0, 0)];Represents the initial position.
  • ToValue End status For examplebasicAnimation.toValue = [NSValue valueWithCGPoint:CGPointMake(300, 300)];Indicates the end position.
  • CABasicAnimation is a special case of CAKeyFrameAnimation, which does not consider the intermediate transformation process, but only considers the starting point and target point

CAKeyframeAnimation Animation of keyframes

  • Animation is done in two ways@property(nullable, copy) NSArray *values;and@property(nullable) CGPathRef path;
  • @property(nullable, copy) NSArray<NSNumber *> *keyTimes;This property is an array assumed to specify the time of each subpath (AB,BC,CD). If you do not explicitly set keyTimes, the default time for each slip-path is ti=duration/(5-1), that is, each slip-path has the same duration, which is 1\4 of duration. Of course, we can also pass an array to make things merge fast and slow. For example, you can pass in {0.0, 0.1, 0.6, 0.7, 1.0}, the fore and aft must is 0 and 1 respectively, so the tAB 0.1 0 =, tCB = 0.6 0.1, tDC = 0.7 0.6, tED = 1-0.7…
  • @property(nullable, copy) NSArray<CAMediaTimingFunction *> *timingFunctions;Used to specify a function of time, similar to the acceleration of motion

GIF code

//values
-(void)animationWithValues
{
    CALayer *orangeLayer = [CALayer layer];
    orangeLayer.bounds = CGRectMake(0, 0, 100, 100);
    orangeLayer.position = CGPointMake(100, 200);
    orangeLayer.backgroundColor = [UIColor orangeColor].CGColor;
    [self.view.layer addSublayer:orangeLayer];
    
    self.orangeLayer = orangeLayer;
    
    CAKeyframeAnimation *keyframeAni = [CAKeyframeAnimation animationWithKeyPath:@"transform.rotation.z"];
    keyframeAni.values = @[@(-M_PI_4 / 5),@(M_PI_4 / 5),@(-M_PI_4 / 5)];
    keyframeAni.duration = 10;
    keyframeAni.repeatCount = CGFLOAT_MAX;
    
    [self.orangeLayer addAnimation:keyframeAni forKey:@"rotation"];
}
//path
-(void)animationWithPath
{
    CALayer *planeLayer = [CALayer layer];
    planeLayer.bounds = CGRectMake(0, 0, 100, 100);
    planeLayer.contents = (__bridge id _Nullable)([UIImage imageNamed:@"plane"].CGImage);
    [self.view.layer addSublayer:planeLayer];
    self.planLayer = planeLayer;
    //shake
    CAKeyframeAnimation *shakeAni = [CAKeyframeAnimation animationWithKeyPath:@"transform.rotation"]; shakeAni.values = @[@(-M_PI_4 / 5),@(M_PI_4 / 5),@(-M_PI_4 / 5)]; ShakeAni. Duration = 0.25; shakeAni.repeatCount = CGFLOAT_MAX; Oval Path CAKeyframeAnimation *keyframePlaneAni = [CAKeyframeAnimation animationWithKeyPath:@"position"];
    
    UIBezierPath *path = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(100, 100, 200, 500)];
    keyframePlaneAni.path = path.CGPath;
    keyframePlaneAni.duration = 3;
    keyframePlaneAni.repeatCount = CGFLOAT_MAX;
    keyframePlaneAni.calculationMode = kCAAnimationCubic;
    
    [self.planLayer addAnimation:keyframePlaneAni forKey:nil];
    [self.planLayer addAnimation:shakeAni forKey:nil];
}
Copy the code

CAShapeLayer Overview

  • Draws A layer that draws A cubic Bezier spline in its coordinate space
  • @property CGFloat strokeStart; strokeEndPartially draw the start and end values. (You can modify this value to handle animations like implementing progress bars.)
  • @property(nullable) CGPathRef path;Delineated path
  • @property(nullable) CGColorRef fillColor;Fill the path color
  • @property(copy) CAShapeLayerFillRule fillRule;Rules for filling paths with two kinds of values, non-zero and odd and even. The default is non-zero.
  • @property(nullable) CGColorRef strokeColor;Stroke color.
  • Inherited fromCALayer, in general, andUIBezierPathUse together. (High frequency of use)
  • See documentation for other attributes

Progress bar part code

- (IBAction)animationClick:(UIButton *)sender {
    
    if (self.timer) {
        [self.timer fire];
    }
    else{the self. The timer = [NSTimer scheduledTimerWithTimeInterval: 0.04 target: self selector: @ the selector (progressShowNumber) userInfo:nil repeats:YES]; } } -(void)progressShowNumber {if([self getExpectValue] > self.progressValue) { self.progressValue += 1; Self. CircleProgressLayer. StrokeEnd = self. ProgressValue / 100.0; CGFloat end = self.circleProgressLayer.strokeEnd; self.progressLabel.text = [NSString stringWithFormat:@"%.1f%@",end * 100,@"%"];

        if([self getExpectValue] == self.progressValue) { [self.timer invalidate]; self.progressValue = [self getExpectValue]; self.timer = nil; }}else if([self getExpectValue] < self.progressValue) { self.progressValue -= 1; Self. CircleProgressLayer. StrokeEnd = self. ProgressValue / 100.0; CGFloat end = self.circleProgressLayer.strokeEnd; self.progressLabel.text = [NSString stringWithFormat:@"%.1f%@",end * 100,@"%"];

        if ([self getExpectValue] == self.progressValue) {

            [self.timer invalidate];
            self.progressValue = [self getExpectValue];
            self.timer = nil;
        }
    }
}
-(CAShapeLayer *)circleProgressLayer
{
    if(! _circleProgressLayer) { _circleProgressLayer = [CAShapeLayer layer]; _circleProgressLayer.bounds = CGRectMake(0, 0, 240, 240); _circleProgressLayer.position = CGPointMake(240/2, 240/2); UIBezierPath *path = [UIBezierPath bezierPathWithArcCenter:CGPointMake(240/2, 240/2) radius:240/2-10 startAngle:0 endAngle:M_PI * 2 clockwise:YES]; _circleProgressLayer.strokeStart = 0; _circleProgressLayer.strokeEnd = 1; _circleProgressLayer.path = path.CGPath; _circleProgressLayer.lineWidth = 10; _circleProgressLayer.strokeColor = [UIColor redColor].CGColor; _circleProgressLayer.fillColor = [UIColor clearColor].CGColor; }return _circleProgressLayer;
}
Copy the code

CAEmitterLayer,CAEmitterCell

CAEmitterLayer @property(nullable, copy) NSArray<CAEmitterCell *> *emitterCells; // The array @property is used to hold particlesfloatbirthRate; // Particle generation coefficient, default 1.0@propertyfloatlifetime; // Particle life cycle coefficient, default 1.0@property CGPoint emitterPosition; // The center point that determines the emission shape of the particle @property CGFloat emitterZPosition; @property CGSize emitterSize; // The size of the emitter @property CGFloat emitterDepth; @property(copy) NSString *emitterShape; @property(copy) NSString *emitterMode; @property(copy) NSString *renderMode; // preservesDepth @property BOOL preservesDepth; @propertyfloatvelocity; // Particle velocity coefficient, default 1.0 @propertyfloatscale; // Particle scaling factor, default 1.0 @propertyfloatspin; // Particle rotation speed coefficient, default 1.0 @property unsigned int seed; // Random number generatorCopy the code
@property(nullable, copy) NSString *name; @property(getter=isEnabled) BOOL enabled; // The particle name is nil by default (but you can use name to set some of its properties via keyPath). @propertyfloatbirthRate; // Particle generation rate, default 0 @propertyfloatlifetime; // The lifetime of a particle, in seconds. The default 0 @ propertyfloatlifetimeRange; // The range of the particle's life cycle in seconds. Default 0 @property CGFloat emissionLatitude; // Specify latitude. The latitude Angle represents the Angle between the X-axis and the X-axis in the x-z-axis plane coordinate system. Default: 0: @property CGFloat emissionLongitude; // Specify the longitude. The longitude Angle represents the Angle between the x-y axis and the X-axis in the x-y axis plane coordinate system. Default: 0: @property CGFloat emissionRange; // Launch Angle range, default 0, in a cone distribution of open launch Angle. The Angle is in radians. The particles are evenly distributed within this conical range; @property CGFloat velocity; // Speed and speed range, both default 0 @property CGFloat velocityRange; @property CGFloat xAcceleration; @property CGFloat yAcceleration; // The acceleration component in the x,y and z directions is 0 by default. @property CGFloat zAcceleration; @property CGFloat scale; // Scale, default is 1 @property CGFloat scaleRange; // Scale range, default is 0 @property CGFloat scaleSpeed; // The zoom speed during the life cycle, default is 0 @property CGFloat spin; // Average spin speed of particles, default is 0 @property CGFloat spinRange; @property(nullable) CGColorRef color; // Particle color, default white @propertyfloatredRange; // Particle color red,green,blue,alpha can change the range, default 0 @propertyfloat greenRange;
@property float blueRange;
@property float alphaRange;
@property floatredSpeed; // The speed at which the particle colors red,green,blue, and alpha change during their lifetime. The default is 0 @propertyfloat greenSpeed;
@property float blueSpeed;
@property floatalphaSpeed; @property(nullable, strong) id contents; // The content of the particle is the object of CGImageRef @property CGRect contentsRect; @property CGFloat contentsScale; @property(copy) NSString *minificationFilter; @property(copy) NSString *magnificationFilter; @propertyfloatminificationFilterBias; @property(nullable, copy) NSArray<CAEmitterCell *> *emitterCells; @property(nullable, copy) NSDictionary *style;Copy the code

Here’s a brief explanation of emissionLongitude:

EmissionLongitude is the Angle between the vertical longitude and the X axis in the XY coordinate system. The coordinates of velocity are both positive and negative. When the value of Velocity is positive, his coordinate system looks like this

You can see the following code modification in the demo to see the changes, welcome everyone to exchange ~~

///"LoveHeartViewController.h"heartCell.velocity = -120.f; heartCell.velocityRange = 60.f; heartCell.yAcceleration = 20.f; heartCell.emissionLongitude = M_PI_2; Heartcell. emissionRange = M_PI_2 * 0.55; /* heartCell.velocity = -120.f; heartCell.velocityRange = 60.f; heartCell.yAcceleration = 20.f; heartCell.emissionLongitude = 0; heartCell.emissionLongitude = M_PI; Heartcell. emissionRange = M_PI_2 * 0.55; * /Copy the code

The demo to see