# 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 來源:簡書
相關文章
相關標籤/搜索