iOS動(dòng)畫系列之一:帶時(shí)分秒指針的時(shí)鐘動(dòng)畫(上)

經(jīng)過(guò)幾次實(shí)驗(yàn),發(fā)現(xiàn)如果分享的文章能構(gòu)成系列,效果會(huì)非常好。同時(shí)自己也能收獲很大,能夠整塊整塊的復(fù)習(xí),也能夠幫助自己更深入的塊狀學(xué)習(xí)知識(shí)。對(duì)自己梳理線條,整理知識(shí)體系作用非常大。

所以這次還是打算用這種方式,一起來(lái)分享一下iOS中關(guān)于動(dòng)畫的部分。iOS 的動(dòng)畫渲染簡(jiǎn)直是帥的不要不要的,哈哈。其實(shí)當(dāng)初就是因?yàn)閕OS的動(dòng)畫,還有iOS的對(duì)手勢(shì)的處理深深的打動(dòng)了我,才讓我這樣一個(gè)高齡中年老男人開(kāi)始了iOS這條路。啦啦啦啦~

動(dòng)畫這個(gè)系列大概會(huì)分享五篇文章,但是也有可能因?yàn)樽约和祽谢蛘咂渌壒矢淖円幌?。Hoho~

-----------------------華麗分割線,iOS動(dòng)畫系列全集鏈接-------------------------------------------------
第一篇:iOS動(dòng)畫系列之一:通過(guò)實(shí)戰(zhàn)學(xué)習(xí)CALayer和透視的原理。做一個(gè)帶時(shí)分秒指針的時(shí)鐘動(dòng)畫(上)
第二篇:iOS動(dòng)畫系列之二:通過(guò)實(shí)戰(zhàn)學(xué)習(xí)CALayer和透視的原理。做一個(gè)帶時(shí)分秒指針的時(shí)鐘動(dòng)畫。包含了OC和Swift兩種源代碼(下)
第三篇:iOS動(dòng)畫系列之三:Core Animation。介紹了Core Animation的常用屬性和方法。
第四篇:CABasic Animation。iOS動(dòng)畫系列之四:基礎(chǔ)動(dòng)畫之平移篇
第五篇:CABasic Animation。iOS動(dòng)畫系列之五:基礎(chǔ)動(dòng)畫之縮放篇&旋轉(zhuǎn)篇
第六篇:iOS動(dòng)畫系列之六:利用CABasic Animation完成帶動(dòng)畫特效的登錄界面
第七篇:iOS動(dòng)畫系列之七:實(shí)現(xiàn)類似Twitter的啟動(dòng)動(dòng)畫
第八篇:iOS動(dòng)畫系列之八:使用CAShapeLayer繪畫動(dòng)態(tài)流量圖
第九篇:iOS動(dòng)畫系列之九:實(shí)現(xiàn)點(diǎn)贊的動(dòng)畫及播放起伏指示器
第十篇:實(shí)戰(zhàn)系列:繪制過(guò)山車場(chǎng)景

開(kāi)始咱們的第一篇內(nèi)容。

1. 最終實(shí)現(xiàn)的效果以及思維導(dǎo)圖

實(shí)現(xiàn)的效果。不小心暴露了寫文章的時(shí)間。-_-+++


實(shí)現(xiàn)效果

實(shí)現(xiàn)的步驟思維導(dǎo)圖:


思維導(dǎo)圖.png

2. CALayer

其實(shí)今天分享的主角是CALayer。因?yàn)樗械膭?dòng)畫都是在CALayer上完成的。

  • 在iOS中,看得見(jiàn)摸得著的東西基本上都是UIView,比如一個(gè)按鈕、一個(gè)文本標(biāo)簽、一個(gè)文本輸入框、一個(gè)圖標(biāo)等等,這些都是UIView
  • 其實(shí)UIView之所以能顯示在屏幕上,完全是因?yàn)樗鼉?nèi)部的一個(gè)圖層
  • 在創(chuàng)建UIView對(duì)象時(shí),UIView內(nèi)部會(huì)自動(dòng)創(chuàng)建一個(gè)圖層(即CALayer對(duì)象),通過(guò)UIView的layer屬性可以訪問(wèn)這個(gè)層
    @property(nonatomic,readonly,retain) CALayer *layer;
  • 當(dāng)UIView需要顯示到屏幕上時(shí),會(huì)調(diào)用drawRect:方法進(jìn)行繪圖,并且會(huì)將所有內(nèi)容繪制在自己的圖層上,繪圖完畢后,系統(tǒng)會(huì)將圖層拷貝到屏幕上,于是就完成了UIView的顯示
  • 換句話說(shuō),UIView本身不具備顯示的功能,是它內(nèi)部的層才有顯示功能

2.1 CALayer的基本屬性

屬性類型|屬性名稱|用途
-------|---------
@property CGFloat |borderWidth;|邊寬
@property CGColorRef |borderColor;|邊的顏色
@property CGColorRef |backgroundColor;| 背景顏色
@property float |opacity;|透明度
@property CGColorRef |shadowColor;| 陰影顏色
@property float |shadowOpacity;|陰影透明度,設(shè)置范圍0~1。
@property CGSize |shadowOffset;|陰影的偏移
@property CGFloat |shadowRadius;|陰影的模糊度
@property(strong) id |contents;|內(nèi)容??梢栽O(shè)置為圖片,但是需要橋接。
@property CGFloat| cornerRadius;|圓角
@property CGRect |bounds;| Layer的大小
@property CGPoint |position;| 默認(rèn)情況下相當(dāng)于UIView的center
@property CGPoint |anchorPoint;| position的錨點(diǎn)
@property CATransform3D |transform;| 用來(lái)做形變的,是一個(gè)矩陣??梢岳斫鉃榻Y(jié)構(gòu)體。
@property BOOL |masksToBounds;|超過(guò)部分進(jìn)行裁剪

  • 設(shè)置陰影的時(shí)候,陰影顏色+陰影偏移(或者陰影路徑)+陰影透明度缺一不可。
  • 陰影模糊度如果不設(shè)置,默認(rèn)值就是3.0000。
  • 陰影的路徑:
    • 設(shè)置了陰影的路徑,就不再需要設(shè)置陰影的偏移量了。
    • 設(shè)置了陰影的路徑之后,也不能再設(shè)置masksToBounds。因?yàn)槌^(guò)部分會(huì)被裁減。
      以實(shí)現(xiàn)下圖為例:
Paste_Image.png
    UIImageView *imageView = [[UIImageView alloc] initWithFrame:CGRectMake(100, 100, 100, 100)];
    
    [self.view addSubview:imageView];
    
//    設(shè)置背景顏色。注意UIColor 和 CGColor之間的互換
    imageView.layer.backgroundColor = [UIColor grayColor].CGColor;
    
//    生成一個(gè)path
    UIBezierPath *path = [UIBezierPath bezierPathWithRect:CGRectMake(-10, -10, imageView.bounds.size.width + 20, imageView.bounds.size.height + 20)];
//    設(shè)置陰影path
    imageView.layer.shadowPath = path.CGPath;
    
//    設(shè)置陰影顏色
    imageView.layer.shadowColor = [UIColor lightGrayColor].CGColor;
    
//    設(shè)置陰影透明度
    imageView.layer.shadowOpacity = 0.5;

2.2 手動(dòng)創(chuàng)建一個(gè)CALayer

  • 創(chuàng)建CALayer
  • 在設(shè)置frame的時(shí)候,內(nèi)部同時(shí)設(shè)置了position,bounds.size 都會(huì)發(fā)生改變。
  • 設(shè)置position,就和設(shè)置UIView的center一樣的。
  • 記得要添加到父CALayer上。
 CALayer *layer = [[CALayer alloc] init];
 
 // ------------------- 設(shè)置位置大小 ---------------------
 // 方式一: 直接設(shè)置 frame
 // layer.frame = CGRectMake(50, 50, 200, 200);
 
 
 // 方式二:
 // 先設(shè)置大小
 layer.bounds = CGRectMake(0, 0, 100, 100);
 // 再設(shè)置位置(默認(rèn)情況下 position 指的是 center。)
 layer.position = CGPointMake(150, 150);
 // ------------------- 設(shè)置位置大小 ---------------------
 
 layer.backgroundColor = [UIColor redColor].CGColor;
 layer.opacity = 0.7;
 [self.view.layer addSublayer:layer];
 }

2.3 transform

從這里開(kāi)始,咱們的坐標(biāo)軸就變成了xyz三個(gè)軸向了,因?yàn)閳D案也會(huì)變成立體的了。

  • 從 layer 的中心點(diǎn)到 給定的坐標(biāo)點(diǎn)之間連一條線, 然后以這個(gè)線為中心軸, 開(kāi)始旋轉(zhuǎn)
 self.myLayer.transform = CATransform3DMakeRotation(M_PI_4, 10, 20, 30);

這段代碼的意思就是說(shuō)從{0,0,0}這個(gè)點(diǎn),到{10,20,30}這個(gè)點(diǎn),劃一根線。圖形繞著這根線,旋轉(zhuǎn)M_PI_4度數(shù)。

2.3.1 修改透視

在真實(shí)世界中,當(dāng)物體遠(yuǎn)離我們的時(shí)候,由于視角的原因看起來(lái)會(huì)變小,理論上說(shuō)遠(yuǎn)離我們的視圖的邊要比靠近視角的邊跟短,但實(shí)際上并沒(méi)有發(fā)生,而我們當(dāng)前的視角是等距離的,也就是在3D變換中任然保持平行,和之前提到的仿射變換類似。

“為了做一些修正,我們需要引入投影變換(又稱作z變換)來(lái)對(duì)除了旋轉(zhuǎn)之外的變換矩陣做一些修改,Core Animation并沒(méi)有給我們提供設(shè)置透視變換的函數(shù),因此我們需要手動(dòng)修改矩陣值,幸運(yùn)的是,很簡(jiǎn)單:CATransform3D的透視效果通過(guò)一個(gè)矩陣中一個(gè)很簡(jiǎn)單的元素來(lái)控制:m34。m34用于按比例縮放X和Y的值來(lái)計(jì)算到底要離視角多遠(yuǎn)?!?/p>

*Excerpt From: 鐘聲. “ios核心動(dòng)畫高級(jí)技巧.” iBooks. *

Paste_Image.png
  • 通過(guò)修改transform的m34來(lái)達(dá)到效果
  • transform可以看成是一個(gè)結(jié)構(gòu)體,所以修改的時(shí)候需要通過(guò)一個(gè)中間量才能修改。
  • m34的默認(rèn)值是0,可以通過(guò)設(shè)置m34為-1.0 / d來(lái)應(yīng)用透視效果
  • d代表了想象中視角相機(jī)和屏幕之間的距離,以像素為單位,那應(yīng)該如何計(jì)算這個(gè)距離呢?實(shí)際上并不需要,大概估算一個(gè)就好了?!?/li>
  • “因?yàn)橐暯窍鄼C(jī)實(shí)際上并不存在,所以可以根據(jù)屏幕上的顯示效果自由決定它的防止的位置。通常500-1000就已經(jīng)很好了”

Excerpt From: 鐘聲. “ios核心動(dòng)畫高級(jí)技巧.” iBooks.

struct CATransform3D{  
  CGFloat     m11(x縮放),   m12(y切變),   m13(旋轉(zhuǎn)),   m14();
  CGFloat     m21(x切變),   m22(y縮放),   m23,        m24;
  CGFloat     m31(旋轉(zhuǎn)),    m32,         m33,         m34(透視效果,要有旋轉(zhuǎn)角度才能看出效果);
  CGFloat     m41(x平移),   m42(y平移),   m43(z平移),  m44;
};

 
 
 struct CGAffineTransform {
 CGFloat a, b, c, d;
 CGFloat tx, ty;
 };
//    定義矩陣
    CATransform3D transform = CATransform3DIdentity;
    
    transform.m34 = -1.0 / 800;
    
//    旋轉(zhuǎn)加透視
    transform = CATransform3DRotate(transform, -M_PI_4, 0, 1, 0);
    
    imageView.layer.transform = transform;

2.3.2 縮放

//方式一:
transform = CATransform3DMakeScale(<#CGFloat sx#>, <#CGFloat sy#>, <#CGFloat sz#>)

//方式二:
transform = CATransform3DScale(imageView.layer.transform, <#CGFloat sx#>, <#CGFloat sy#>, <#CGFloat sz#>)

2.4 重要屬性之position和anchorPoint

  • 默認(rèn)情況下position相當(dāng)于UIView 的center
  • position決定了layer在父上的位置。
  • 但是anchorPoint決定了position在自身的位置。
  • anchorPoint的數(shù)值只能是0~1。所以是按照百分比計(jì)算的。

3. 隱式動(dòng)畫

  • 當(dāng)對(duì)非Root Layer的部分屬性進(jìn)行修改時(shí),默認(rèn)會(huì)自動(dòng)產(chǎn)生一些動(dòng)畫效果
  • 所有的非Root Layer,也就是手動(dòng)創(chuàng)建的CALayer對(duì)象,都存在著隱式動(dòng)畫
  • 所有注釋里面寫著有Animatable,這個(gè)屬性就有隱式動(dòng)畫效果。
Paste_Image.png

3.1 幾個(gè)常見(jiàn)的Animatable Properties:

  • bounds:用于設(shè)置CALayer的寬度和高度。修改這個(gè)屬性會(huì)產(chǎn)生縮放動(dòng)畫
  • backgroundColor:用于設(shè)置CALayer的背景色。修改這個(gè)屬性會(huì)產(chǎn)生背景色的漸變動(dòng)畫
  • position:用于設(shè)置CALayer的位置。修改這個(gè)屬性會(huì)產(chǎn)生平移動(dòng)畫

3.2 關(guān)閉隱式動(dòng)畫

  • 可以通過(guò)動(dòng)畫事務(wù)(CATransaction)關(guān)閉默認(rèn)的隱式動(dòng)畫效果
  • 關(guān)閉或者修改隱式動(dòng)畫的步驟:
    • 開(kāi)啟動(dòng)畫事物
    • 關(guān)閉動(dòng)畫效果或者修改動(dòng)畫事件
    • 設(shè)置動(dòng)畫完成后的動(dòng)作(可以不設(shè)置)
    • 修改屬性
      • 提交
//開(kāi)啟
[CATransaction begin];
//關(guān)閉動(dòng)畫
[CATransaction setDisableActions:YES];
//修改屬性
self.myview.layer.position = CGPointMake(10, 10);
//提交
[CATransaction commit];

寶貝兒們,我錯(cuò)了。寫到這里發(fā)現(xiàn)已經(jīng)辣么長(zhǎng)辣么長(zhǎng)了,再寫下去這篇該沒(méi)有人看了。

那么,那么。。。。就臨時(shí)變卦吧,把這篇文章變成上下集吧。哈哈~就這么愉快的自己打自己的臉了,文章開(kāi)始還說(shuō)要五篇~

所以,證明一個(gè)道理。計(jì)劃都只是用來(lái)計(jì)劃的,樹(shù)立一個(gè)目標(biāo),能不能實(shí)現(xiàn)再說(shuō)。哈哈~

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容