# iOS基础 # 经常使用动画的实现方式整理

1、CoreAnimation(核心动画)

1.什么是核心动画

Core Animation能够用在 Mac OS X 和 iOS平台。Core Animation的动画执行过程是在后台操做的.不会阻塞主线程。要注意的是, Core Animation是直接做用在CALayer上的,并不是UIView。css

整体来讲核心动画的优势有:

一、性能强大,使用硬件加速,能够同时向多个图层添加不一样的动画效果html

二、接口易用,只须要少许的代码就能够实现复杂的动画效果。ios

三、运行在后台线程中,在动画过程当中能够响应交互事件(UIView动画默认动画过程当中不响应交互事件)git

四、只有在发生改变的时候才重绘内容,消除了动画的帧速率上的运行代码,提升应用性能github

动画操做过程:

一、建立一个CAAnimation对象数组

二、设置一些动画的相关属性缓存

三、给CALayer添加动画(addAnimation:forKey: 方法)微信

四、移除CALayer中的动画(removeAnimationForKey: 方法)app

2.核心动画相关类

CAAnimation是全部动画对象的父类,实现CAMediaTiming协议,负责控制动画的时间、速度和时间曲线等等,是一个抽象类,不能直接使用。框架

CAPropertyAnimation 是CAAnimation的子类,它支持动画地显示图层的keyPath,不直接使用。

CATransition 转场动画

综上,核心动画类中能够直接使用的类有:

1. CABasicAnimation  基础动画

2. CAKeyframeAnimation  关键帧动画

3. CATransition 转场动画

4. CAAnimationGroup 组动画

5. CASpringAnimation 弹性动画 (iOS9.0以后,它实现弹簧效果的动画,是CABasicAnimation的子类。)
复制代码

一、CAAnimation (一部分属性来自 CAMediaTiming协议)

duration:动画的持续时间,默认为0.25秒

speed :速度 speed = 1.0 / duration = 1.0 的动画效果 和 speed = 2.0 / duration = 2.0 的动画效果是如出一辙的,咱们设置的duration可能和动画进行的真实duration不同,这个还依赖于speed。

timeOffset 设置动画线的起始结束时间点

//假定一个3s的动画,它的状态为t0,t1,t2,t3,当没有timeOffset的时候,正常的状态序列应该为:
 //t0->t1->t2->t3
 //当设置timeOffset为1的时候状态序列就变为
 //t1->t2->t3->t0
 //同理当timeOffset为2的时候状态序列就变为:
 //t2->t3->t0->t1
复制代码

autoreverses:是否自动回到动画开始状态

repeatCount:动画的重复次数

repeatDuration:动画的重复时间

removedOnCompletion:默认为YES,表明动画执行完毕后就从图层上移除,图形会恢复到动画执行前的状态。若是想让图层保持显示动画执行后的状态,那就设置为NO,不过还要设置fillMode属性为kCAFillModeForwards。好比进入后台回来动画依然执行,可使用这个属性。

fillMode:决定当前对象在非active时间段的行为。好比动画开始以前,动画结束以后。

beginTime:能够用来设置动画延迟执行时间,若想延迟2s,就设置为CACurrentMediaTime() + 2,CACurrentMediaTime()为图层的当前时间。 CALayer 的beginTime 通常用于动画暂停的使用,CAAnimation 的beginTime通常用于动画延迟执行,但只在使用groupAnimation的时候生效,直接添加在layer上的animation使用会致使动画不执行。

timingFunction:速度控制函数,控制动画运行的节奏

枚举参数:

kCAMediaTimingFunctionLinear  时间曲线函数,匀速
kCAMediaTimingFunctionEaseIn  时间曲线函数,由慢到特别快
kCAMediaTimingFunctionEaseOut  时间曲线函数,由快到慢
kCAMediaTimingFunctionEaseInEaseOut  时间曲线函数,由慢到快
kCAMediaTimingFunctionDefault   系统默认
复制代码

delegate:动画代理,通常设置隐式代理,该代理是NSObject的分类,须要遵照协议CAAnimationDelegate

-(void)animationDidStart:(CAAnimation *)anim; 核心动画开始时执行

-(void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag; 核心动画执行结束后调用
复制代码

二、CAPropertyAnimation

keyPath:经过指定CALayer的一个属性名作为keyPath里的参数(NSString类型),而且对CALayer的这个属性的值进行修改,达到相应的动画效果。好比,指定@”position”为keyPath,就修改CALayer的position属性的值,以达到平移的动画效果。

CAPropertyAnimation *animation = [CAPropertyAnimation animationWithKeyPath:@"position.y"];
[self.view.layer addAnimation:animation forKey:@"position_y"];
复制代码

一些经常使用的animationWithKeyPath值的总结

说明 使用形式
transform.scale 比例转化 @(0.8)
transform.scale.x 宽的比例 @(0.8)
transform.scale.y 高的比例 @(0.8)
transform.rotation.x 围绕x轴旋转 @(M_PI)
transform.rotation.y 围绕y轴旋转 @(M_PI)
transform.rotation.z 围绕z轴旋转 @(M_PI)
cornerRadius 圆角的设置 @(50)
backgroundColor 背景颜色的变化 (id)[UIColor purpleColor].CGColor
bounds 大小,中心不变 [NSValue valueWithCGRect:CGRectMake(0, 0, 200, 200)];
position 位置(中心点的改变) [NSValue valueWithCGPoint:CGPointMake(300, 300)];
contents 内容,好比UIImageView的图片 imageAnima.toValue = (id)[UIImage imageNamed:@"to"].CGImage;
opacity 透明度 @(0.7)
contentsRect.size.width 横向拉伸缩放 @(0.4)最好是0~1之间的

三、CABasicAnimation

fromValue : keyPath相应属性的初始值
toValue : keyPath相应属性的结束值,到某个固定的值(相似transform的make含义)

注意:

随着动画的进行,在长度为duration的持续时间内,keyPath相应属性的值从fromValue渐渐地变为toValue.
若是fillMode = kCAFillModeForwards和removedOnComletion = NO;那么在动画执行完毕后,图层会保持显示动画执行后的状态,但实质上,图层的属性值仍是动画执行前的初始值,并无真正被改变.好比: CALayer的postion初始值为(0,0),CABasicAnimation的fromValue为(10,10),toValue为 (100,100),虽然动画执行完毕后图层保持在(100,100) 这个位置,实质上图层的position仍是为(0,0);
复制代码

byValue:不断进行累加的数值(byvalue 值加上fromValue => tovalue)

CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"transform.rotation.z"];
animation.byValue = @(M_PI * 2);
复制代码

构建一个动画

CABasicAnimation *a1 = [CABasicAnimation animation];
a1.keyPath = @"position";
a1.toValue = [NSValue valueWithCGPoint:CGPointMake(150 + 28, 180 + 28)];
a1.duration = 0.5;
a1.fillMode = kCAFillModeForwards;
a1.removedOnCompletion = NO;
[self.view.layer addAnimation:a1 forKey:@"position"];
复制代码

四、 CAKeyframeAnimation

就和画图同样吗,画三个点,三个点连起来的线就是动画轨迹,每一个点有对应的位置,对应的时间点。

values:NSArray对象,里面的元素称为”关键帧”(NSValue类型),动画对象会在指定的时间(duration)内,依次显示values数组中的每个关键帧( NSValue)

//设置动画属性
CAKeyframeAnimation *animation = [CAKeyframeAnimation animationWithKeyPath:@"position"];
NSValue *p1 = [NSValue valueWithCGPoint:CGPointMake(50, 150)];
NSValue *p2 = [NSValue valueWithCGPoint:CGPointMake(250, 150)];
NSValue *p3 = [NSValue valueWithCGPoint:CGPointMake(50, 550)];
NSValue *p4 = [NSValue valueWithCGPoint:CGPointMake(250, 550)];
animation.values = @[p1, p2, p3, p4];
animation.keyTimes = @[ [NSNumber numberWithFloat:0.0],
                        [NSNumber numberWithFloat:0.4],
                        [NSNumber numberWithFloat:0.8],
                        [NSNumber numberWithFloat:1.0]];
复制代码

keyTimes:能够为对应的关键帧指定对应的时间点,其取值范围为0到1.0,keyTimes中的每个时间值都对应values中的每一帧的时间节点的百分比,当keyTimes没有设置的时候,各个关键帧的时间是平分的

path:能够设置一个CGPathRef、CGMutablePathRef,让层跟着路径移动,path只对CALayer的anchorPoint和position起做用,若是设置了path,那么values、keyTimes将被忽略。

CAKeyframeAnimation * ani = [CAKeyframeAnimation animationWithKeyPath:@"position"];
CGMutablePathRef path = CGPathCreateMutable();
    CGPathAddEllipseInRect(path, NULL, CGRectMake(130, 200, 100, 100));
ani.path = path.CGPath;
复制代码

rotationMode:旋转模式

(1)默认nil

(2)设置为kCAAnimationRotateAuto 或 kCAAnimationRotateAutoReverse 会随着旋转的角度作 ”自转“
animKey.rotationMode = kCAAnimationRotateAuto;

构建一个动画

CAKeyframeAnimation *animation = [CAKeyframeAnimation animationWithKeyPath:@"position"];
animation.duration = 2;
//animation.repeatCount = HUGE_VAL;
NSMutableArray *array = [NSMutableArray array];
[array addObject:[NSValue valueWithCGPoint:CGPointMake(30, 150)]];
[array addObject:[NSValue valueWithCGPoint:CGPointMake(SCREEN_WIDTH / 2.0, 70)]];
[array addObject:[NSValue valueWithCGPoint:CGPointMake(SCREEN_WIDTH - 30, 150)]];
[array addObject:[NSValue valueWithCGPoint:CGPointMake(SCREEN_WIDTH / 2.0, 300 - 50)]];
[array addObject:[NSValue valueWithCGPoint:CGPointMake(30, 150)]];
animation.values = array;
//每个动画的时间节点位置 百分比制 0-1之间
NSMutableArray *time_array = [NSMutableArray array];
[time_array addObject:[NSNumber numberWithFloat:0.0]];
[time_array addObject:[NSNumber numberWithFloat:0.25]];
[time_array addObject:[NSNumber numberWithFloat:0.5]];
[time_array addObject:[NSNumber numberWithFloat:0.75]];
[time_array addObject:[NSNumber numberWithFloat:1.0]];
animation.keyTimes = time_array; //设置关键帧对应的时间点,范围:0-1。若是没有设置该属性,则每一帧的时间平分。

[self.imageView.layer addAnimation:animation forKey:@"CAKeyframeAnimation"];
复制代码

五、 CASpringAnimation

iOS9才引入的动画类,它继承于CABasicAnimation,用于制做弹簧动画

mass 质量 ,影响图层运动时的弹簧惯性,质量越大,弹簧拉伸和压缩的幅度越大

stiffness 刚度系数(劲度系数/弹性系数),刚度系数越大,形变产生的力就越大,运动越快

damping 阻尼系数,阻止弹簧伸缩的系数,阻尼系数越大,中止越快

initialVelocity 初始速率,动画视图的初始速度大小
速率为正数时,速度方向与运动方向一致,速率为负数时,速度方向与运动方向相反
若是把速率改为-20,则动画变成

settlingDuration 结算时间 返回弹簧动画到中止时的估算时间,根据当前的动画参数估算
一般弹簧动画的时间使用结算时间比较准确

构建一个动画

CASpringAnimation *animation = [CASpringAnimation animationWithKeyPath:@"position.y"];
animation.fromValue = @(132);
animation.toValue = @(80);
animation.duration = animation.settlingDuration;
animation.damping = 5;
animation.initialVelocity = 0;
animation.stiffness = 500.0;
animation.mass = 1.0;
[self.imageView.layer addAnimation:animation forKey:@"CASpringAnimation"];
复制代码

六、 CAAnimationGroup

animations:动画组,用来保存一组动画对象的NSArray。默认状况下,一组动画对象是同时运行的,也能够经过设置动画对象的beginTime属性来更改动画的开始时间。

构建动画

// 2. 向组动画中添加各类子动画
// 2.1 旋转
CABasicAnimation *anim1 = [CABasicAnimation animationWithKeyPath:@"transform.rotation.z"];
// anim1.toValue = @(M_PI * 2 * 500);
anim1.byValue = @(M_PI * 2 * 1000);

// 2.2 缩放
CABasicAnimation *anim2 = [CABasicAnimation animationWithKeyPath:@"transform.scale"];
anim2.toValue = @(0.1);

// 2.3 改变位置, 修改position
CAKeyframeAnimation *anim3 = [CAKeyframeAnimation animationWithKeyPath:@"position"];
anim3.path = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(50, 100, 250, 100)].CGPath;

// 把子动画添加到组动画中
CAAnimationGroup *groupAnima = [CAAnimationGroup animation];

groupAnima.animations = @[ anim1, anim2, anim3];

groupAnima.duration = 2.0;
[self.imageView.layer addAnimation:groupAnima forKey:@"animationGroup"];
复制代码

七、 CATransition

转场动画

type:设置动画过渡的类型

kCATransitionFade 交叉淡化过渡

kCATransitionMoveIn 新视图移到旧视图上面

kCATransitionPush 新视图把旧视图推出去

kCATransitionReveal 将旧视图移开,显示下面的新视图
复制代码

下面类型包装成字符串赋值 转场动画过渡效果

subtype:设置动画过渡方向

kCATransitionFromRight

kCATransitionFromLeft

kCATransitionFromTop

kCATransitionFromBottom
复制代码

startProgress:动画起点(在总体动画的百分比)

endProgress:动画终点(在总体动画的百分比)

(IBAction)didRecognizeSwipeGesture:(UISwipeGestureRecognizer *)sender {

  // 1. 建立一个转场动画对象
  CATransition *anim = [[CATransition alloc] init];
  // 设置转场动画的类型
  anim.type = @"suckEffect";
  // 设置转场动画时间
  anim.duration = 1.5;
  anim.delegate = self;
  // 判断方向
  if (sender.direction == UISwipeGestureRecognizerDirectionLeft) {
      // 设置转场动画的子类型
      anim.subtype = kCATransitionFromRight;
      // NSLog(@"left");
      self.index++;
  } else {
      // 设置转场动画的子类型
      anim.subtype = kCATransitionFromLeft;
      // NSLog(@"right");
      self.index--;
  }

  // 判断是否越界
  if (self.index > 4) {
      self.index = 0;
  }

  if (self.index < 0) {
      self.index = 4;
  }

  // 拼接图片名称
  NSString *imgName = [NSString stringWithFormat:@"%d", self.index + 1];
  // 切换图片
  self.imgViewIcon.image = [UIImage imageNamed:imgName];
  // 把转场动画添加到对应的控件上
   [self.imgViewIcon.layer addAnimation:anim forKey:@"anim1"];
}
复制代码

构建动画

CATransition *ani = [CATransition animation];
ani.type = @"rippleEffect"; //水滴入水振动的效果
ani.subtype = kCATransitionFromLeft;
ani.duration = 1.5;
[self.imageView.layer addAnimation:ani forKey:nil];
复制代码

3.CALayer图形绘制

一、CALayer

CALayer是NSObject的子类而非UIResponder的子类,所以图层自己没法响应用户操做事件却拥有着事件响应链类似的判断方法,因此CALayer须要包装成一个UIView容器来完成这一功能。

每个UIView自身存在一个CALayer来显示内容。在后者的属性中咱们能够看到存在着多个和UIView界面属性对应的变量,所以咱们在修改UIView的界面属性的时候实际上是修改了这个UIView对应的layer的属性。

CALayer拥有和UIView同样的树状层级关系,也有相似UIView添加子视图的addSublayer这些相似的方法。

为何有了UIView还要CALayer

CALayer经常使用属性

1.position和anchorPoint

anchorPoint(锚点)是一个x和y值取值范围内在0~1之间CGPoint类型,它决定了当图层发生几何仿射变换时基于的坐标原点。默认状况下为0.5, 0.5,由anchorPoint和frame通过计算得到图层的position这个值。

2.mask和maskToBounds

maskToBounds值为true时表示超出图层范围外的全部子图层都不会进行渲染,当咱们设置UIView的clipsToBounds时实际上就是在修改maskToBounds这个属性。mask这个属性表示一个遮罩图层,在这个遮罩以外的内容不予渲染显示。

3.cornerRadius、borderWidth和borderColor

borderWidth和borderColor设置了图层的边缘线条的颜色以及宽度,正常状况下这两个属性在layer的层次上不怎么使用。后者cornerRadius设置圆角半径,这个半径会影响边缘线条的形状。

4.shadowColor、shadowOpacity、shadowOffset和shadowRadius

这四个属性结合起来能够制做阴影效果。shadowOpacity默认状况下值为0,这意味着即使你设置了其余三个属性,只要不修改这个值,你的阴影效果就是透明的。其次,不要纠结shadowOffset这个决定阴影效果位置偏移的属性为何会是CGSize而不是CGPoint。

注意:

1.隐式属性动画的本质是这些属性的变更默认隐含了CABasicAnimation动画实现。

2.CALayer中不多使用frame属性,由于frame自己不支持动画效果,一般使用bounds和position代替。

3.CALayer中透明度使用opacity表示而不是alpha;中心点使用position表示而不是center。

4.anchorPoint属性是图层的锚点,范围在(0-1,0-1)表示在x、y轴的比例,这个点永远能够同position(中心点)重合,当图层中心点固定后,调整anchorPoint便可达到调整图层显示位置的做用(由于它永远和position重合)
复制代码

为了进一步说明anchorPoint的做用,假设有一个层大小100*100,如今中心点位置(50,50),由此能够得出frame(0,0,100,100)。上面说过anchorPoint默认为(0.5,0.5),同中心点position重合,此时使用图形描述以下图1;当修改anchorPoint为(0,0),此时锚点处于图层左上角,可是中心点poition并不会改变,所以图层会向右下角移动,以下图2;而后修改anchorPoint为(1,1),position仍是保持位置不变,锚点处于图层右下角,此时图层如图3。

二、图形绘制(基础属性、基础方法的使用介绍、使用场景、实例)

CALayer的图形绘制有两种方法:

1.经过图层代理方法drawLayer:inContext进行图形绘制的。

2.使用drawInContext:方法,经过建立图层CALayer来进行自定义图层绘制。

须要注意的是调用这两种方法之后,必须调用setNeedsDisplay方法,不然没法显示内容。setNeedsDisplay方法的做用是移除旧的图层内容(contents),设置新的图层内容。

绘制经常使用属性方法:

CGContextRef  ctx 图形上下文,能够将其理解为一块画布
//ctx 的备份
CGContextSaveGState(ctx);
//线的粗细
CGContextSetLineWidth(ctx, 5);

CGContextSetLineCap(ctx, kCGLineCapRound);//线条两端的样式
CGContextSetLineJoin(ctx, kCGLineJoinRound);//两线条转折相接端的样式

//ctx 的出栈 把 ctx 恢复成默认
CGContextRestoreGState(ctx);
//画笔颜色
CGContextSetRGBStrokeColor(ctx, 1, 0, 0, 1);
//两点成线
CGContextMoveToPoint(ctx, 120, 50);    //起点
CGContextAddLineToPoint(ctx, 270, 50); //画线

CGContextStrokePath(ctx); //根据ctx 线条方式绘制
CGContextFillPath(ctx); //根据ctx 填充方式绘制
复制代码

CALayer经常使用绘制

画实线
CGContextMoveToPoint(ctx, 120, 50);    //起点
CGContextAddLineToPoint(ctx, 270, 50); //画线

画虚线
ctx方式:

CGFloat dash[2] = {3, 1};
CGContextSetLineWidth(ctx, 0.5);
CGContextSetLineCap(ctx, kCGLineCapRound);
CGContextSetLineDash(ctx, 0.0, dash, 2);
CGContextMoveToPoint(ctx, 120, 100);
CGContextAddLineToPoint(ctx, 270, 100);
CGContextStrokePath(ctx);

CAShapeLayer方式:

UIBezierPath *pathFour = [UIBezierPath bezierPath];
pathFour.lineWidth = 3;
[pathFour moveToPoint:CGPointMake(120, 50)];
[pathFour addLineToPoint:CGPointMake(270, 50)];
[pathFour stroke];

CAShapeLayer *layer4 = [CAShapeLayer layer];
layer4.path = pathFour.CGPath;
[layer addSublayer:layer4];
layer4.strokeColor = [[UIColor greenColor] CGColor];
layer4.fillColor = [[UIColor clearColor] CGColor];
//线型模板 这是一个NSNumber的数组,索引从1开始记,奇数位数值表示实线长度,偶数位数值表示空白长度
[layer4 setLineDashPattern:@[ @3, @1, @10, @5 ]];

画矩形
CGContextAddRect(ctx, CGRectMake(150, 50, 50, 50));

画圆、圆弧
CGContextAddArc(ctx, 60, 50, 45, 0, M_PI, 0);

画圆、画椭圆
CGContextAddEllipseInRect(ctx, CGRectMake(150, 40, 90, 50));

部分绘制
CAShapeLayer  的 strokeStart 和 strokeEnd 属性

复制代码

贝塞尔曲线

UIBezierPath对象是CGPathRef数据类型的封装。path若是是基于矢量形状的,都用直线和曲线段去建立。 咱们使用直线段去建立矩形和多边形,使用曲线段去建立弧(arc),圆或者其余复杂的曲线形状。 每一段都包括一个或者多个点,绘图命令定义如何去诠释这些点。每个直线段或者曲线段的结束的地方是下一个的开始的地方。每个链接的直线或者曲线段的集合成为subpath。一个UIBezierPath对象定义一个完整的路径包括一个或者多个subpaths。

建立和使用一个path对象的过程是分开的。建立path是第一步,包含一下步骤:

(1)建立一个Bezier path对象。

(2)使用方法moveToPoint:去设置初始线段的起点。

(3)添加line或者curve去定义一个或者多个subpaths。

(4)改变UIBezierPath对象跟绘图相关的属性。

构建动画

UIBezierPath *path = [UIBezierPath bezierPath];
CAShapeLayer *layer = [CAShapeLayer layer];
[path moveToPoint:CGPointMake(50, 100)];
[path addLineToPoint:CGPointMake(200, 100)];
[path addLineToPoint:CGPointMake(200, 250)];
[path addLineToPoint:CGPointMake(50, 250)];

layer.frame = self.contentViewOne.bounds;
layer.strokeColor = [[UIColor redColor] CGColor];
layer.fillColor = [[UIColor clearColor] CGColor];
layer.lineCap = kCALineCapRound;
layer.lineJoin = kCALineJoinRound;
layer.path = [path CGPath];
layer.lineWidth = 6.0;

[self.contentViewOne.layer addSublayer:layer];

CABasicAnimation *pathAnimation = [CABasicAnimation animationWithKeyPath:@"strokeEnd"];
pathAnimation.duration = 3;
pathAnimation.fromValue = @(0.0);
pathAnimation.toValue = @(1.0);
pathAnimation.autoreverses = YES;
pathAnimation.repeatCount = HUGE;
[layer addAnimation:pathAnimation forKey:nil];
复制代码

3.显示动画和隐式动画

当你改变CALayer的一个可作动画的属性,它并不能马上在屏幕上体现出来。相反,它是从先前的值平滑过渡到新的值。这一切都是默认的行为,你不须要作额外的操做。这其实就是所谓的隐式动画。之因此叫隐式是由于咱们并无指定任何动画的类型。咱们仅仅改变了一个属性,而后Core Animation来决定如何而且什么时候去作动画。

Core Animation在每一个run loop周期中自动开始一次新的事务,即便你不显式的用 [CATransaction begin] 开始一次事务,任何在一次run loop循环中属性的改变都会被集中起来,而后作一次0.25秒的动画 (例如:position的变化)。

注意:只有非rootLayer才有隐式动画

4.CATransaction 事务

核心动画里面存在事务(CATransaction)这样一个概念,它负责协调多个动画原子更新显示操做。

简单来讲事务是核心动画里面的一个基本的单元,动画的产生必然伴随着layer的Animatable属性的变化,而layer属性的变化必须属于某一个事务。所以,核心动画依赖事务。

事务的做用:

保证一个或多个layer的一个或多个属性变化同时进行

事务分为隐式和显式:

1.隐式:没有主动调用事务的方法,由系统自动生成事务。好比直接设置一个layer的position属性,则会在当前线程自动生成一个事务,并在下一个runLoop中自动commit事务。

2.显式:主调用事务的方法:[CATransaction begin] 和 [CATransaction commit]。

事务的可设置属性(会覆盖隐式动画的设置):

animationDuration:动画时间
animationTimingFunction:动画时间曲线
disableActions:是否关闭动画
completionBlock:动画执行完毕的回调
复制代码

事务支持嵌套使用:当最外层的事务commit后动画才会开始。

使用实例:

[CATransaction begin];
[CATransaction setAnimationDuration:2.0];
[CATransaction setAnimationTimingFunction:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]];
// [CATransaction setDisableActions:YES]; //设置为YES就关闭动画
self.subLayer.bounds = self.centerShow.layer.bounds;
[CATransaction commit];
复制代码

2、UIViewAnimation

1.UIViewAnimation介绍

可设置动画属性

1. frame            //大小变化:改变视图框架(frame)和边界。
2. bounds           //拉伸变化:改变视图内容的延展区域。
3. center           //居中显示
4. transform        //旋转:即任何应用到视图上的仿射变换(transform)
5. alpha            //改变透明度:改变视图的alpha值。
6. backgroundColor  //改变背景颜色
7. contentStretch   //拉伸内容
复制代码

参数

1. duration   //为动画持续的时间。 
2. animations //为动画效果的代码块。
3. completion //为动画执行完毕之后执行的代码块
4. options    //为动画执行的选项
5. delay      //为动画开始执行前等待的时间
复制代码

2.核心动画类

UIView 的动画方面扩展有三部分 :

一、UIView(UIViewAnimation)

UIView(UIViewAnimation);
 设置动画ID 方便查询
 + (void)beginAnimations:(nullable NSString *)animationID context:(nullable void *)context;
 提交动画 执行动画
 + (void)commitAnimations;
 设置动画执行时间
 + (void)setAnimationDuration:(NSTimeInterval)duration;
 设置动画执延迟执行时间
 + (void)setAnimationDelay:(NSTimeInterval)delay;
 设置动画代理对象,当动画开始或者结束时会发消息给代理对象
 + (void)setAnimationDelegate:(nullable id)delegate;
 设置动画开始时调用的方法 执行delegate对象的selector,而且把beginAnimations:context:中传入的参数传进selector
 + (void)setAnimationWillStartSelector:(nullable SEL)selector;
 设置动画结束时调用的方法 执行delegate对象的selector,而且把beginAnimations:context:中传入的参数传进selector
 + (void)setAnimationDidStopSelector:(nullable SEL)selector;
 设置动画的开始时间,默认为now
 + (void)setAnimationStartDate:(NSDate *)startDate
 设置视图view的过渡效果, transition指定过渡类型, cache设置YES表明使用视图缓存,性能较好
 + (void)setAnimationTransition:(UIViewAnimationTransition)transition forView:(UIView *)view cache:(BOOL)cache
 设置是否自动恢复执行 YES,表明动画每次重复执行的效果会跟上一次相反
 + (void)setAnimationRepeatAutoreverses:(BOOL)repeatAutoreverses
 设置动画的重复次数
 + (void)setAnimationRepeatCount:(float)repeatCount
 设置动画执行效果
 + (void)setAnimationCurve:(UIViewAnimationCurve)curve
 设置动画是否生效
 + (void)setAnimationsEnabled:(BOOL)enabled; 
复制代码

构建动画

self.animationImageView.frame = CGRectMake(0, 132, 56, 36);
[UIView beginAnimations:@"btnOne" context:nil];
[UIView setAnimationCurve:UIViewAnimationCurveEaseInOut];
[UIView setAnimationDelegate:self];
[UIView setAnimationDuration:1.0];
[UIView setAnimationDelay:0.0];
[UIView setAnimationRepeatCount:1];
[UIView setAnimationRepeatAutoreverses:YES];
[UIView setAnimationDidStopSelector:@selector(AnimationDidStop)];
[UIView setAnimationWillStartSelector:@selector(AnimationWillBegin)];
[UIView setAnimationTransition:UIViewAnimationTransitionFlipFromLeft forView:self.animationImageView cache:YES];

self.animationImageView.frame = CGRectMake(SCREEN_WIDTH - 56, 132, 56, 36);
[UIView commitAnimations];
复制代码

二、UIView(UIViewAnimationWithBlocks)

将动画实现封装在block区域,参数构建在类方法上。

可选动画执行效果,如进出效果等
+ (void)animateWithDuration:(NSTimeInterval)duration delay:(NSTimeInterval)delay options:(UIViewAnimationOptions)options animations:(void (^)(void))animations completion:(void (^ __nullable)(BOOL finished))completion NS_AVAILABLE_IOS(4_0);

带回调block动画,动画执行完成后进入block
+ (void)animateWithDuration:(NSTimeInterval)duration animations:(void (^)(void))animations completion:(void (^ __nullable)(BOOL finished))completion NS_AVAILABLE_IOS(4_0); // delay = 0.0, options = 0

不带回调动画
+ (void)animateWithDuration:(NSTimeInterval)duration animations:(void (^)(void))animations NS_AVAILABLE_IOS(4_0); // delay = 0.0, options = 0, completion = NULL

弹簧动画
+ (void)animateWithDuration:(NSTimeInterval)duration delay:(NSTimeInterval)delay usingSpringWithDamping:(CGFloat)dampingRatio initialSpringVelocity:(CGFloat)velocity options:(UIViewAnimationOptions)options animations:(void (^)(void))animations completion:(void (^ __nullable)(BOOL finished))completion NS_AVAILABLE_IOS(7_0);

view的转场动画
+ (void)transitionWithView:(UIView *)view duration:(NSTimeInterval)duration options:(UIViewAnimationOptions)options animations:(void (^ __nullable)(void))animations completion:(void (^ __nullable)(BOOL finished))completion NS_AVAILABLE_IOS(4_0);

view到另外一个view的转场动画
+ (void)transitionFromView:(UIView *)fromView toView:(UIView *)toView duration:(NSTimeInterval)duration options:(UIViewAnimationOptions)options completion:(void (^ __nullable)(BOOL finished))completion NS_AVAILABLE_IOS(4_0); // toView added to fromView.superview, fromView removed from its superview

+ (void)performSystemAnimation:(UISystemAnimation)animation onViews:(NSArray<__kindof UIView *> *)views options:(UIViewAnimationOptions)options animations:(void (^ __nullable)(void))parallelAnimations completion:(void (^ __nullable)(BOOL finished))completion NS_AVAILABLE_IOS(7_0);
复制代码

构建动画

[UIView animateWithDuration:1.0 delay:0.0 usingSpringWithDamping:5.0 initialSpringVelocity:0 options:UIViewAnimationOptionAutoreverse animations:^{

    self.animationImageView.frame = CGRectMake(SCREEN_WIDTH - 56, 132, 56, 36);

} completion:^(BOOL finished) {
    self.animationImageView.frame = CGRectMake(0, 132, 56, 36);
}];
复制代码

三、UIView (UIViewKeyframeAnimations)

关键帧动画
+ (void)animateKeyframesWithDuration:(NSTimeInterval)duration delay:(NSTimeInterval)delay options:(UIViewKeyframeAnimationOptions)options animations:(void (^)(void))animations completion:(void (^ __nullable)(BOOL finished))completion NS_AVAILABLE_IOS(7_0);

在上面的block中添加关键帧
+ (void)addKeyframeWithRelativeStartTime:(double)frameStartTime relativeDuration:(double)frameDuration animations:(void (^)(void))animations NS_AVAILABLE_IOS(7_0); // start time and duration are values between 0.0 and 1.0 specifying time and duration relative to the overall time of the keyframe animation
复制代码

构建动画

[UIView animateKeyframesWithDuration:2.0 delay:0.0 options:UIViewKeyframeAnimationOptionAutoreverse animations:^{

    [UIView addKeyframeWithRelativeStartTime:0.0 relativeDuration:0.5 animations:^{

        self.animationImageView.frame = CGRectMake(200, 132, 56, 36);
    }];

    [UIView addKeyframeWithRelativeStartTime:0.5 relativeDuration:0.5 animations:^{

        self.animationImageView.frame = CGRectMake(200, 232, 56, 36);
    }];

} completion:^(BOOL finished) {
    self.animationImageView.frame = CGRectMake(0, 132, 56, 36);
}];
复制代码

3、其余动画

1.控制器转场动画

原理:UIViewControllerAnimatedTransitioning (过渡协调器)

iOS7之后UIViewControllerAnimatedTransitioning 或者 UIViewControllerContextTransitioning这些协议已经能够比较方便的自定义ViewController之间的动画了,好比修改UINavigationController的动画,下面举个例子来看一看如何作一个自定义的NavigationController的Push和Pop非交互动画。

1.首先 咱们定义一个类 TransitionOneManager 基于NSObject 实现 UIViewControllerAnimatedTransitioning 协议。

2.实现下面两个协议方法

//定义转场动画时间
- (NSTimeInterval)transitionDuration:(nullable id<UIViewControllerContextTransitioning>)transitionContext
//定义转场动画效果
- (void)animateTransition:(id<UIViewControllerContextTransitioning>)transitionContext 
复制代码

3.定义两个控制器 分别是 ONE 和 TWO ONE push 到TWO

在TWO中 实现 UINavigationControllerDelegate 实现下面方法

//这里返回的就是navigationController push 要使用的动画效果
- (id<UIViewControllerAnimatedTransitioning>)navigationController:(UINavigationController *)navigationController animationControllerForOperation:(UINavigationControllerOperation)operation fromViewController:(UIViewController *)fromVC toViewController:(UIViewController *)toVC
{
  //在这里把 TransitionOneManager 实现的动画效果返回回去
}
复制代码

4.在ONE 中 把 navigationController 的代理挂到TWO控制器上面,由于咱们须要使用TWO中实现的push效果

self.navigationController.delegate = vc;
[self.navigationController pushViewController:vc animated:YES];
复制代码

2.动力学

UIDynamic 是苹果在iOS7以后添加的一套动力学框架,运用它咱们能够极其方便地模拟现实生活中的运动,好比重力,碰撞等等。它是经过添加行为的方式让动力学元素参与运动的。

iOS7.0中提供的动力学行为包括:

UIGravityBehavior:重力行为
UICollisionBehavior:碰撞行为
UIAttachmentBehavior:附着行为
UISnapBehavior:吸附行为
UIPushBehavior:推行为
UIDynamicItemBehavior:动力学元素行为
复制代码

UIDynamic的使用仍是相对简单

1.首先咱们建立一个小方块 boxView 并把它放在self.view的上面部分。(只有遵循了UIDynamicItem协议的对象才能参与仿真模拟,而UIView正遵循了此协议,所以全部视图控件都能参与仿真运动)

2.而后定义一个 UIDynamicAnimator 物理仿真器(凡是要参与运动的对象必须添加到此容器中)

_animator = [[UIDynamicAnimator alloc] initWithReferenceView:self.view]; //refrerence表示 self.view内都算仿真器范围
复制代码

3.再添加一个重力行为 到仿真器,而且 这个行为做用对象是咱们以前定义的boxView

[[UIGravityBehavior alloc] initWithItems:@[ boxView ]];
[_animator addBehavior:_gravity];
复制代码

4.而后启动app,能够发现 放在self.view上半部分的boxView受重力行为影响,往下掉落。可是会掉出self.view范围。

5.为了避免掉出self.view 范围 咱们还须要给boxView添加一个别的行为:碰撞行为,接触到仿真器边界或者其余self.view中得容器会产生碰撞效果。

_collision = [[UICollisionBehavior alloc] initWithItems:@[ _behaviorView, _behaviorViewTwo ]];
_collision.translatesReferenceBoundsIntoBoundary = YES; //边界检测
[_animator addBehavior:_collision];
复制代码

6.这样小方块就不会掉出仿真器范围了,同理,其余行为的使用方式和上面同样,必定要添加到仿真器才能生效。

3.CADisplayLink 逐帧动画

CADisplayLink是一个能让咱们以和屏幕刷新率相同的频率将内容画到屏幕上的定时器。咱们在应用中建立一个新的 CADisplayLink对象,把它添加到一个runloop中,并给它提供一个 target 和 selector 在屏幕刷新的时候调用。

一但 CADisplayLink 以特定的模式注册到runloop以后,每当屏幕须要刷新的时候,runloop就会调用CADisplayLink绑定的target上的selector,这时 target 能够读到 CADisplayLink 的每次调用的时间戳,用来准备下一帧显示须要的数据。例如一个视频应用使用时间戳来计算下一帧要显示的视频数据。在UI作动画的过程当中,须要经过时间戳来计算UI对象在动画的下一帧要更新的大小等等。

在添加进runloop的时候咱们应该选用高一些的优先级,来保证动画的平滑。能够设想一下,咱们在动画的过程当中,runloop被添加进来了一个高优先级的任务,那么,下一次的调用就会被暂停转而先去执行高优先级的任务,而后在接着执行CADisplayLink的调用,从而形成动画过程的卡顿,使动画不流畅。

duration属性

提供了每帧之间的时间,也就是屏幕每次刷新之间的的时间。咱们可使用这个时间来计算出下一帧要显示的UI的数值。可是 duration只是个大概的时间,若是CPU忙于其它计算,就无法保证以相同的频率执行屏幕的绘制操做,这样会跳过几回调用回调方法的机会。

frameInterval属性

是可读可写的NSInteger型值,标识间隔多少帧调用一次selector方法,默认值是1,即每帧都调用一次。若是每帧都调用一次的话,对于iOS设备来讲那刷新频率就是60HZ也就是每秒60次,若是将 frameInterval设为2 那么就会两帧调用一次,也就是变成了每秒刷新30次。

咱们经过pause属性来控制CADisplayLink的运行。当咱们想结束一个CADisplayLink的时候,应该调用

-(void)invalidate

从runloop中删除并删除以前绑定的 target跟selector

CADisplayLink 不能被继承。

CADisplayLink 与 NSTimer 有什么不一样

1、iOS设备的屏幕刷新频率是固定的,CADisplayLink在正常状况下会在每次刷新结束都被调用,精确度至关高。
NSTimer的精确度就显得低了点,好比NSTimer的触发时间到的时候,runloop若是在阻塞状态,触发时间就会推迟到下一个runloop周期。而且 NSTimer新增了tolerance属性,让用户能够设置能够容忍的触发的时间的延迟范围。

2CADisplayLink使用场合相对专注,适合作UI的不停重绘,好比自定义动画引擎或者视频播放的渲染。NSTimer的使用范围要普遍的多,各类须要单次或者循环定时处理的任务均可以使用。在UI相关的动画或者显示内容使用 CADisplayLink比起用NSTimer的好处就是咱们不须要在格外关心屏幕的刷新频率了,由于它自己就是跟屏幕刷新同步的。
复制代码

构建动画

self.displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(animationMethod)];
self.displayLink.frameInterval = 30;
[self.displayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];

animationMethod 完成对应的动画操做
复制代码

4.CAEmitterLayer 粒子动画

在UIKit中,粒子系统由两部分组成:

一个或多个CAEmitterCells:发射器电池能够看做是单个粒子的原型(例如,一个单一的粉扑在一团烟雾)。当散发出一个粒子,UIKit根据这个发射粒子和定义的基础上建立一个随机粒子。此原型包括一些属性来控制粒子的图片,颜色,方向,运动,缩放比例和生命周期。

一个或多个CAEmitterLayers,但一般只有一个:这个发射的层主要控制粒子的形状(例如,一个点,矩形或圆形)和发射的位置(例如,在矩形内,或边缘)。这个层具备全局的乘法器,能够施加到系统内的CAEmitterCells。这些给你一个简单的方法覆盖的全部粒子的变化。

构建动画

_snowEmitter = [CAEmitterLayer layer];

_snowEmitter.emitterPosition = CGPointMake(self.view.bounds.size.width / 2.0, -30);
_snowEmitter.emitterSize = CGSizeMake(self.view.bounds.size.width * 2.0, 0.0);
_snowEmitter.emitterShape = kCAEmitterLayerLine;
_snowEmitter.emitterMode = kCAEmitterLayerOutline;

CAEmitterCell *snowflake = [CAEmitterCell emitterCell];

snowflake.birthRate = 1.0;
snowflake.lifetime = 120.0;
snowflake.velocity = -10;
snowflake.velocityRange = 10;
snowflake.yAcceleration = 2;
snowflake.emissionRange = 0.5 * M_PI;
snowflake.spinRange = 0.25 * M_PI;
snowflake.contents = (id)[[UIImage imageNamed:@"scream"] CGImage];
snowflake.color = [[UIColor colorWithRed:0.600 green:0.658 blue:0.743 alpha:1.000] CGColor];

_snowEmitter.shadowOpacity = 1.0;
_snowEmitter.shadowRadius = 0.0;
_snowEmitter.shadowOffset = CGSizeMake(0.0, 1.0);
_snowEmitter.shadowColor = [[UIColor whiteColor] CGColor];
_snowEmitter.emitterCells = [NSArray arrayWithObject:snowflake];
[self.view.layer insertSublayer:_snowEmitter atIndex:0];
复制代码

5.Facebook POP 动画框架

核心动画类中能够直接使用的类有:

POPSpringAnimation 有弹性效果的动画类
POPBasicAnimation 基本动画类
POPDecayAnimation 衰减动画类
POPCustomAnimation 能够自定义动画的类

能够同时做用于UIView 和 CALayer 能够响应用户事件

4、常见问题

一、 若是当动画正在执行的时候, 将程序退出到后台, 那么当程序再次进入前台的时候就不执行了。

缘由: 由于再次进入前台后动画已经被删除了。

解决: anim.removedOnCompletion = NO;

二、代理形成的循环引用问题

缘由:因为CAAnimation的delegate使用的strong类型,因此在全局变量以下设置时会产生循环引用的状况

self.animation.delegate = self; //可经过复用dealloc方法来验证

解决:使用NSProxy解决,在一个对象中对self 弱引用处理 而后经过类方法把 弱引用处理过的self对象转给delegate (YYWeakProxy)

三、.cornerRadius 属于layer层的参数,没法经过UIView animation来动画变动

CABasicAnimation *basicAnimation = [CABasicAnimation animationWithKeyPath:@"cornerRadius"];
basicAnimation.duration = 0.2;
[self.animationView.layer setCornerRadius:20.0f];
[self.animationView.layer addAnimation:basicAnimation forKey:@"cornerRadius"];
复制代码

四、CGAffineTransformMakeRotation 使用的时候 若是直接frame变动 会致使形变 使用center的变动来变动位置就不会。

五、当UIView remove出父容器 UIView 会自动销毁 layer动画也是 但咱们处理layer销毁的时候最好仍是主动去remove掉动画

六、如何主动中止动画(UIView 动画 / 核心动画 通用)

removeAllAnimations 或者移除某个动画

暂停/恢复:

if (self.imageViewOne.layer.speed == 0.0) {
  CFTimeInterval pausedTime = [self.imageViewOne.layer timeOffset];
  self.imageViewOne.layer.speed = 1.0;
  self.imageViewOne.layer.timeOffset = 0.0;
  self.imageViewOne.layer.beginTime = 0.0;
  CFTimeInterval timeSincePause = [self.imageViewOne.layer convertTime:CACurrentMediaTime() fromLayer:nil] - pausedTime;
  self.imageViewOne.layer.beginTime = timeSincePause;
}else{

CFTimeInterval pausedTime = [self.imageViewOne.layer convertTime:CACurrentMediaTime() fromLayer:nil];
self.imageViewOne.layer.speed = 0.0;
self.imageViewOne.layer.timeOffset = pausedTime;
}
复制代码

在一个动画过程当中插入其余动画 阻塞?

七、在给UIView添加绘图delegate的时候的报错

不能再将某个UIView设置为CALayer的delegate,由于UIView对象已是它内部根层的delegate,再次设置为其余层的delegate就会出问题。ShapeLayer 设置代理也会出错

八、UIView的setNeedsDisplay和setNeedsLayout方法

首先两个方法都是异步执行的。而setNeedsDisplay会调用自动调用drawRect方法,这样能够拿到 UIGraphicsGetCurrentContext,就能够画画了。而setNeedsLayout会默认调用layoutSubViews,
就能够 处理子视图中的一些数据。综上所诉,setNeedsDisplay方便绘图,而layoutSubViews方便出来数据。

layoutSubviews在如下状况下会被调用:

一、init初始化不会触发layoutSubviews。
二、addSubview会触发layoutSubviews。
三、设置view的Frame会触发layoutSubviews,固然前提是frame的值设置先后发生了变化。
四、滚动一个UIScrollView会触发layoutSubviews。
五、旋转Screen会触发父UIView上的layoutSubviews事件。
六、改变一个UIView大小的时候也会触发父UIView上的layoutSubviews事件。
七、直接调用setLayoutSubviews。

drawRect在如下状况下会被调用:

一、若是在UIView初始化时没有设置rect大小,将直接致使drawRect不被自动调用。drawRect调用是在Controller->loadView, Controller->viewDidLoad 两方法以后掉用的.因此不用担忧在控制器中,这些View的drawRect就开始画了.这样能够在控制器中设置一些值给View(若是这些View draw的时候须要用到某些变量值).
二、该方法在调用sizeToFit后被调用,因此能够先调用sizeToFit计算出size。而后系统自动调用drawRect:方法。
三、经过设置contentMode属性值为UIViewContentModeRedraw。那么将在每次设置或更改frame的时候自动调用drawRect:。
四、直接调用setNeedsDisplay,或者setNeedsDisplayInRect:触发drawRect:,可是有个前提条件是rect不能为0。
以上1,2推荐;而3,4不提倡

drawRect方法使用注意点:

一、若使用UIView绘图,只能在drawRect:方法中获取相应的contextRef并绘图。若是在其余方法中获取将获取到一个invalidate的ref而且不能用于画图。drawRect:方法不能手动显示调用,必须经过调用setNeedsDisplay 或者 setNeedsDisplayInRect,让系统自动调该方法。
二、若使用calayer绘图,只能在drawInContext: 中(相似于drawRect)绘制,或者在delegate中的相应方法绘制。一样也是调用setNeedDisplay等间接调用以上方法
三、若要实时画图,不能使用gestureRecognizer,只能使用touchbegan等方法来掉用setNeedsDisplay实时刷新屏幕

九、CALayer上动画的暂停和恢复

暂停CALayer的动画

-(void)pauseLayer:(CALayer*)layer
{
CFTimeInterval pausedTime = [layer convertTime:CACurrentMediaTime() fromLayer:nil];
layer.speed=0.0; // 让CALayer的时间中止走动
layer.timeOffset=pausedTime; // 让CALayer的时间停留在pausedTime这个时刻
}
复制代码

恢复CALayer的动画

-(void)resumeLayer:(CALayer*)layer
{
CFTimeInterval pausedTime =layer.timeOffset;
layer.speed=1.0; // 让CALayer的时间继续行走
layer.timeOffset=0.0; // 取消上次记录的停留时刻
layer.beginTime=0.0; // 取消上次设置的时间

//计算暂停的时间(这里用CACurrentMediaTime()-pausedTime也是同样的)
CFTimeInterval timeSincePause = [layer convertTime:CACurrentMediaTime() fromLayer:nil] - pausedTime;
//设置相对于父坐标系的开始时间(日后退timeSincePause)
layer.beginTime = timeSincePause;
}
复制代码

5、动画性能测试调优

一、影响性能的缘由

对于一些须要优化图像性能的场景,咱们能够检查咱们是否触发了offscreen rendering (离屏渲染)。并用更高效的实现手段来替换。

一、阴影绘制:使用ShadowPath来替代shadowOffset等属性的设置

不使用shadowPath

CALayer *imageViewLayer = cell.imageView.layer;
imageViewLayer.shadowColor = [UIColor blackColor].CGColor;
imageViewLayer.shadowOpacity = 1.0;
imageViewLayer.shadowRadius = 2.0;
imageViewLayer.shadowOffset = CGSizeMake(1.0, 1.0);
复制代码

使用shadowPath

imageViewLayer.shadowPath = CGPathCreateWithRect(imageRect, NULL);
复制代码

咱们能够在下图看到两种方式巨大的性能差异。

shadowPath高效的缘由是使用shadowPath避免了offscreen渲染,由于仅须要直接绘制路径便可,不须要提早读取图像去渲染。

二、裁剪图片为圆

使用CornerRadius:

CALayer *imageViewLayer = cell.imageView.layer;
imageViewLayer.cornerRadius = imageHeight / 2.0;
imageViewLayer.masksToBounds = YES;
复制代码

利用一张中间为透明圆形的图片来进行遮盖,虽然会引发blending,但性能仍然高于offerScreen。

根据苹果测试,第二种方式比第一种方式更高效:

以上举了两个例子阐明了在避免大量的offerscreen渲染后,性能可以获得很是直观有效的提升。

二、关于blending (图层混合)

前面提到了用透明圆形的图片来进行遮盖,会引发blending。blending也会耗费性能。咱们先来认识一下Blending.

  • 什么是Blending?

在iOS的图形处理中,blending主要指的是混合像素颜色的计算。最直观的例子就是,咱们把两个图层叠加在一块儿,若是第一个图层的透明的,则最终像素的颜色计算须要将第二个图层也考虑进来。这一过程即为Blending。

  • 会致使blending的缘由:
    • layer(UIView)的Alpha < 1
    • UIImgaeView的image含有Alpha channel(即便UIImageView的alpha是1,但只要image含透明通道,则仍会致使Blending)
  • 为何Blending会致使性能的损失?

缘由是很直观的,若是一个图层是不透明的,则系统直接显示该图层的颜色便可。而若是图层是透明的,则会引入更多的计算,由于须要把下面的图层也包括进来,进行混合后颜色的计算。

在了解完Blending以后,咱们就知道为何不少优化准则都须要咱们尽可能使用不透明图层了。接下来就是在开发中留意和进行优化了。

三、性能调优

具体的调优 在另外一篇介绍instruments 的文章中有写 问题的调试与处理


给你们推荐一个优秀的iOS交流平台,平台里的伙伴们都是很是优秀的iOS开发人员,咱们专一于技术的分享与技巧的交流,你们能够在平台上讨论技术,交流学习。欢迎你们的加入(想要进入的可加小编微信)


做者:就叫yang 连接:https://www.jianshu.com/p/9aead7675221 来源:简书
相关文章
相关标签/搜索