iOS-CALayer詳解與應(yīng)用

view與layer的關(guān)系
view與layer的關(guān)系圖.png
CALayer屬性表如下
CALayer屬性表.png
CALayer和UIView的區(qū)別
  • UIView是UIKit的(只能iOS使用)荧嵌,CALayer是QuartzCore的(iOS和mac os通用)
  • UIView繼承UIResponder,CALayer繼承NSObject,UIView比CALayer多了一個(gè)事件處理的功能栅哀,也就是說(shuō)责球,CALayer不能處理用戶的觸摸事件渣刷,而UIView可以
  • UIView來(lái)自CALayer让虐,是CALayer的高層實(shí)現(xiàn)和封裝盅弛,UIView的所有特性來(lái)源于CALayer支持
  • CABasicAnimation驻襟,CAAnimation璧帝,CAKeyframeAnimation等動(dòng)畫類都需要加到CALayer上

其實(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è)層。

當(dāng)UIView需要顯示到屏幕上時(shí)苏潜,會(huì)調(diào)用drawRect:方法進(jìn)行繪圖银萍,并且會(huì)將所有內(nèi)容繪制在自己的圖層上,繪圖完畢后恤左,系統(tǒng)會(huì)將圖層拷貝到屏幕上贴唇,于是就完成了UIView的顯示。換句話說(shuō)飞袋,UIView本身不具備顯示的功能戳气,是它內(nèi)部的層才有顯示功能

以下是具體功能代碼

  • 剪切圖片的一部分
- (void)maskView {
    int width = 80;
    int height = 100;
    int sapce = 3;
    
    for(int i = 0; i < 9; i++) {
        UIView *view = [[UIView alloc] init];
        view.frame = CGRectMake(60 + (width + sapce) * (i%3), 80 + (height + sapce) * (i/3), width, height);
        view.backgroundColor = [UIColor redColor];
        //設(shè)置層的內(nèi)容
        view.layer.contents = (__bridge id _Nullable)([UIImage imageNamed:@"women"].CGImage);
        //設(shè)置圖片剪切的范圍  [0,1]  contentsRect 圖層顯示內(nèi)容的大小和位置
        view.layer.contentsRect = CGRectMake(1.0/3.0 * (i%3), 1.0/3.0 * (i/3), 1.0/3.0, 1.0/3.0);
        [self.view addSubview:view];
        /*
         1:(0,0巧鸭,1/3,1/3)
         2: (1/3,0瓶您,1/3,1/3)
         3: (2/3,0,1/3,1/3)
         */
    }
}
裁剪圖片.png
  • 圖層添加邊框和圓角
  • 剪切超過(guò)父圖層的部分
  • 陰影路徑
- (void)shadowPath {
    CALayer *layer = [CALayer layer];
    layer.frame = CGRectMake(60,60, 100, 100);
    layer.backgroundColor = [UIColor redColor].CGColor;
    [self.view.layer addSublayer:layer];
    //1表明不透明蹄皱,注意:設(shè)置陰影當(dāng)前值不能為0览闰,默認(rèn)是0
    layer.shadowOpacity = 1.0;
    //陰影顏色
    layer.shadowColor = [UIColor yellowColor].CGColor;
    //創(chuàng)建路徑
    CGMutablePathRef path = CGPathCreateMutable();
    //橢圓
    CGPathAddEllipseInRect(path, NULL, CGRectMake(0, 0, 200, 200));
    layer.shadowPath = path;
    CGPathRelease(path);
}
陰影路徑.png
  • 添加陰影
// 添加陰影
- (void)addShadowPath {
    CALayer *layer = [CALayer layer];
    layer.frame = CGRectMake(60, saveH + 60, 100, 100);
    layer.backgroundColor = [UIColor redColor].CGColor;
    [self.view.layer addSublayer:layer];
    
    layer.shadowOpacity = 0.9;
    layer.shadowColor = [UIColor yellowColor].CGColor;
    //陰影偏移 ->x正 ->-x負(fù) ,y同理
    layer.shadowOffset = CGSizeMake(10, -10);
    //陰影的圓角半徑
    layer.shadowRadius = 10;
}
image.png
  • 圖層內(nèi)容和內(nèi)容模式
- (void)addContentMode {
    CALayer *layer = [CALayer layer];
    layer.frame = CGRectMake(20, saveH + 20, 100, 100);
    layer.backgroundColor = [UIColor redColor].CGColor;
    [self.view.layer addSublayer:layer];
    
    //設(shè)置層內(nèi)容
    layer.contents = (__bridge id _Nullable)([UIImage imageNamed:@"logo"].CGImage);
    //內(nèi)容模式巷折,類似于UIImageView的contentMode压鉴。默認(rèn)是填充整個(gè)區(qū)域 kCAGravityResize
    //kCAGravityResizeAspectFill 這個(gè)會(huì)向左邊靠 貼到view的邊邊上
    //kCAGravityResizeAspect 這個(gè)好像就是按比例了 反正是長(zhǎng)方形
    layer.contentsGravity = kCAGravityResizeAspect;
    //設(shè)置控制器視圖的背景圖片 性能很高。 /
    self.view.layer.contents = (__bridge id _Nullable)([UIImage imageNamed:@"logo"].CGImage);
}
三 CALayer的探究應(yīng)用——進(jìn)度條
progress.gif

我們主要通過(guò)自定義Layer實(shí)現(xiàn)該功能锻拘,具體請(qǐng)參考工程中ProgressLayerProgressView實(shí)現(xiàn)油吭。

四 使用CALayer的Mask實(shí)現(xiàn)注水動(dòng)畫效果

Core Animation一直是iOS比較有意思的一個(gè)主題,使用Core Animation可以實(shí)現(xiàn)非常平滑的炫酷動(dòng)畫署拟。Core animtion的API是較高級(jí)的封裝婉宰,使用便捷,使得我們免于自己使用OpenGL實(shí)現(xiàn)動(dòng)畫推穷。
下面主要介紹如何使用CALayer的mask實(shí)現(xiàn)一個(gè)雙向注水動(dòng)畫

indicator.gif
了解CALayer的mask
@property(strong) CALayer *mask;

mask實(shí)際上layer內(nèi)容的一個(gè)遮罩心包。如果我們把mask是透明的,實(shí)際看到的layer是完全透明的馒铃,也就是說(shuō)只有mask的內(nèi)容不透明的部分和layer疊加

實(shí)現(xiàn)思路
  1. flow 在View上重疊放置兩個(gè)UIImageView: grayHead&greenHead蟹腾,默認(rèn)greenHead會(huì)遮擋住grayHead痕惋。
  2. 為greenHead設(shè)置一個(gè)mask,這個(gè)mask不是普通的mask娃殖,它由兩個(gè)subLayer:maskLayerUp maskLayerDown組成值戳。
  3. 默認(rèn)情況下,subLayer都顯示在mask內(nèi)容之外炉爆,此時(shí)mask實(shí)際上透明的堕虹,由此greenHead也是透明的。
  4. 現(xiàn)在我們希望greenHead從左上角和右下角慢慢顯示內(nèi)容芬首,那么我們只需要從兩個(gè)方向?yàn)間reenHead填充內(nèi)容就可以了.

核心代碼如下

  • 創(chuàng)建mask
- (CALayer*)greenHeadMaskLayer {
    CALayer* mask = [CALayer layer];
    mask.frame = self.greenHeadImgView.bounds;
    
    self.maskLayerUp = [CAShapeLayer layer];
    self.maskLayerUp.bounds = CGRectMake(0, 0, 60.0f, 60.f);
    self.maskLayerUp.fillColor = [UIColor greenColor].CGColor;
    self.maskLayerUp.path = [UIBezierPath bezierPathWithArcCenter:CGPointMake(30.0, 30.0) radius:30.0f startAngle:0 endAngle:2*M_PI clockwise:YES].CGPath;
    
    self.maskLayerUp.opacity = 0.8f;
    self.maskLayerUp.position = CGPointMake(-5.0f, -5.0f);
    [mask addSublayer:self.maskLayerUp];
    
    self.maskLayerDown = [CAShapeLayer layer];
    self.maskLayerDown.bounds = CGRectMake(0, 0, 60.f,60.f);
    self.maskLayerDown.fillColor = [UIColor greenColor].CGColor;
    self.maskLayerDown.path = [UIBezierPath bezierPathWithArcCenter:CGPointMake(30.0, 30.0) radius:30.0 startAngle:0 endAngle:2*M_PI clockwise:YES].CGPath;
    self.maskLayerDown.position = CGPointMake(65.f, 65.f);
    [mask addSublayer:self.maskLayerDown];
    
    return mask;
}
  • 做動(dòng)畫
- (void)startGreenHeadAnimation {
    CABasicAnimation* animationDown = [CABasicAnimation animationWithKeyPath:@"position"];
    animationDown.fromValue = [NSValue valueWithCGPoint:CGPointMake(-5.0f, -5.0f)];
    animationDown.toValue = [NSValue valueWithCGPoint:CGPointMake(25.0f, 25.0f)];
    animationDown.duration = duration;
    animationDown.repeatCount = MAXFLOAT;
    [self.maskLayerUp addAnimation:animationDown forKey:@"downAnimation"];
    
    CABasicAnimation* animationUp = [CABasicAnimation animationWithKeyPath:@"position"];
    animationUp.fromValue = [NSValue valueWithCGPoint:CGPointMake(65.f, 65.f)];
    animationUp.toValue = [NSValue valueWithCGPoint:CGPointMake(35.f, 35.f)];
    animationUp.duration = duration;
    animationUp.repeatCount = MAXFLOAT;
    [self.maskLayerDown addAnimation:animationUp forKey:@"upAnimation"];
}
小結(jié)

CALayer提供另外一種操作UI的手段赴捞,雖然它提供的API比UIView較底層,但它能提供更加豐富的功能和更高的性能(CALayer的動(dòng)畫是在專門的線程渲染的)衩辟。涉及到復(fù)雜且性能要求高的UI界面螟炫,CALayer的作用就比較明顯了,比如AsyncDisplayKit艺晴。

通過(guò)本片文章昼钻,我們其實(shí)也能看出CALayer的一個(gè)用處,通常我們處理圓角時(shí)會(huì)直接去修改CALayer的cornerRadius封寞,但這種做法性能比較差然评,尤其是放在列表里的時(shí)候,現(xiàn)在我們有了mask狈究,這樣我們可以直接改變layer的mask碗淌,而不會(huì)影響到圖形渲染的性能。

五 為啥有了CALayer了還要UIView

UIView繼承自UIResponder抖锥,主要特點(diǎn)是可以響應(yīng)觸摸事件亿眠。而CALayer實(shí)際的圖層內(nèi)容管理。大家干的的事情不一樣磅废,是兩個(gè)東西纳像,大家的存在互不影響,理所當(dāng)然拯勉。

分析

所以竟趾,在這份理所當(dāng)然的SDK的背后,蘊(yùn)藏著大牛門幾十年的設(shè)計(jì)智慧宫峦。當(dāng)中應(yīng)該能夠看到很多門道岔帽。這次就UIView和CALayer來(lái)分析,就可以得出一些東西导绷。

  • 機(jī)制與策略分離
  • 更多的不可變
  • 各司其職
  • 漏的更少
5.1 機(jī)制與策略分離

Unix內(nèi)核設(shè)計(jì)的一個(gè)主要思想是——提供(Mechanism)機(jī)制而不是策略(Policy)犀勒。編程問(wèn)題都可以抽離出機(jī)制和策略部分。機(jī)制一旦實(shí)現(xiàn),就會(huì)很少更改贾费,但策略會(huì)經(jīng)常得到優(yōu)化枚碗。例如原子可以看做是機(jī)制,而各種原子的組成就是一種策略铸本。CALayer也可以看做是一種機(jī)制,提供圖層繪制遵堵,你們可以翻開CALayer的頭文件看看箱玷,基本上是沒(méi)怎么變過(guò)的,而UIView可以看做是策略陌宿,變動(dòng)很多锡足。越是底層,越是機(jī)制壳坪,越是穩(wěn)定舶得。機(jī)制與策略分離,可以使得需要修改的代碼更少爽蝴,特別是底層代碼沐批,這樣可以提高系統(tǒng)的穩(wěn)定性。

5.2 更多的不可變

穩(wěn)定給你的是什么感覺(jué)蝎亚?堅(jiān)固九孩?不可形變?穩(wěn)定其實(shí)就是不可變发框。一個(gè)系統(tǒng)不可變的東西越多躺彬,越是穩(wěn)定。所以機(jī)制恰是滿足這個(gè)不可變的因素的梅惯。構(gòu)建一個(gè)系統(tǒng)有一個(gè)指導(dǎo)思想就是盡量抽取不可變的東西和可變的東西分離宪拥。水是成不了萬(wàn)丈高樓的,堅(jiān)固的混凝土才可以铣减。更少的修改她君,意味著更少的bug的幾率。

5.3 各司其職

即使能力再大也不能把說(shuō)有事情都干了徙歼,萬(wàn)一哪一天不行了呢犁河,那就是突然什么都不能干了。所以僅僅是基于分散風(fēng)險(xiǎn)原則也不應(yīng)該出現(xiàn)全能類魄梯。各司其職桨螺,相互合作,把可控粒度降到最低酿秸,這樣也可以是系統(tǒng)更穩(wěn)定灭翔,更易修改。

5.4 漏的更少

接口應(yīng)該面向大眾的,按照八二原則肝箱,其實(shí)20%的接口就可以滿足80%的需求哄褒,剩下的80%應(yīng)該隱藏在背后。因?yàn)槁┑纳倏偸前踩幕驼牛皇菃崮派摹JO碌?0%專家接口可以隱藏與深層次。比如UIView遮蔽了大部分的CALayer接口骏融,抽取構(gòu)造出更易用的frame和動(dòng)畫實(shí)現(xiàn)链嘀,這樣上手更容易。


本文參考zmmzxxxCALayer與iOS動(dòng)畫 講解及使用档玻,非常感謝怀泊。


  • 如有錯(cuò)誤,歡迎指正误趴,多多點(diǎn)贊霹琼,打賞更佳,您的支持是我寫作的動(dòng)力凉当。

項(xiàng)目連接地址 - AnimationDemo

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末枣申,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子纤怒,更是在濱河造成了極大的恐慌糯而,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,277評(píng)論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件泊窘,死亡現(xiàn)場(chǎng)離奇詭異熄驼,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)烘豹,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,689評(píng)論 3 393
  • 文/潘曉璐 我一進(jìn)店門瓜贾,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人携悯,你說(shuō)我怎么就攤上這事祭芦。” “怎么了憔鬼?”我有些...
    開封第一講書人閱讀 163,624評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵龟劲,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我轴或,道長(zhǎng)昌跌,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,356評(píng)論 1 293
  • 正文 為了忘掉前任照雁,我火速辦了婚禮蚕愤,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己萍诱,他們只是感情好悬嗓,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,402評(píng)論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著裕坊,像睡著了一般包竹。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上籍凝,一...
    開封第一講書人閱讀 51,292評(píng)論 1 301
  • 那天映企,我揣著相機(jī)與錄音,去河邊找鬼静浴。 笑死,一個(gè)胖子當(dāng)著我的面吹牛挤渐,可吹牛的內(nèi)容都是我干的苹享。 我是一名探鬼主播,決...
    沈念sama閱讀 40,135評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼浴麻,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼得问!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起软免,我...
    開封第一講書人閱讀 38,992評(píng)論 0 275
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤宫纬,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后膏萧,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體漓骚,經(jīng)...
    沈念sama閱讀 45,429評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,636評(píng)論 3 334
  • 正文 我和宋清朗相戀三年榛泛,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了蝌蹂。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,785評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡曹锨,死狀恐怖孤个,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情沛简,我是刑警寧澤齐鲤,帶...
    沈念sama閱讀 35,492評(píng)論 5 345
  • 正文 年R本政府宣布,位于F島的核電站椒楣,受9級(jí)特大地震影響给郊,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜撒顿,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,092評(píng)論 3 328
  • 文/蒙蒙 一丑罪、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦吩屹、人聲如沸跪另。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,723評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)免绿。三九已至,卻和暖如春擦盾,著一層夾襖步出監(jiān)牢的瞬間嘲驾,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,858評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工迹卢, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留辽故,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,891評(píng)論 2 370
  • 正文 我出身青樓腐碱,卻偏偏與公主長(zhǎng)得像誊垢,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子症见,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,713評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容

  • 1 CALayer IOS SDK詳解之CALayer(一) http://doc.okbase.net/Hell...
    Kevin_Junbaozi閱讀 5,150評(píng)論 3 23
  • 在iOS中隨處都可以看到絢麗的動(dòng)畫效果喂走,實(shí)現(xiàn)這些動(dòng)畫的過(guò)程并不復(fù)雜,今天將帶大家一窺ios動(dòng)畫全貌谋作。在這里你可以看...
    每天刷兩次牙閱讀 8,488評(píng)論 6 30
  • 在iOS中隨處都可以看到絢麗的動(dòng)畫效果芋肠,實(shí)現(xiàn)這些動(dòng)畫的過(guò)程并不復(fù)雜,今天將帶大家一窺iOS動(dòng)畫全貌遵蚜。在這里你可以看...
    F麥子閱讀 5,110評(píng)論 5 13
  • Swift1> Swift和OC的區(qū)別1.1> Swift沒(méi)有地址/指針的概念1.2> 泛型1.3> 類型嚴(yán)謹(jǐn) 對(duì)...
    cosWriter閱讀 11,100評(píng)論 1 32
  • Core Animation其實(shí)是一個(gè)令人誤解的命名帖池。你可能認(rèn)為它只是用來(lái)做動(dòng)畫的,但實(shí)際上它是從一個(gè)叫做Laye...
    小貓仔閱讀 3,708評(píng)論 1 4