view與layer的關(guān)系
CALayer屬性表如下
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)
*/
}
}
- 圖層添加邊框和圓角
- 剪切超過(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);
}
- 添加陰影
// 添加陰影
- (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;
}
- 圖層內(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)度條
我們主要通過(guò)自定義Layer實(shí)現(xiàn)該功能锻拘,具體請(qǐng)參考工程中ProgressLayer
和ProgressView
實(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)畫
了解CALayer的mask
@property(strong) CALayer *mask;
mask實(shí)際上layer內(nèi)容的一個(gè)遮罩心包。如果我們把mask是透明的,實(shí)際看到的layer是完全透明的馒铃,也就是說(shuō)只有mask的內(nèi)容不透明的部分和layer疊加
實(shí)現(xiàn)思路
- flow 在View上重疊放置兩個(gè)UIImageView: grayHead&greenHead蟹腾,默認(rèn)greenHead會(huì)遮擋住grayHead痕惋。
- 為greenHead設(shè)置一個(gè)mask,這個(gè)mask不是普通的mask娃殖,它由兩個(gè)subLayer:maskLayerUp maskLayerDown組成值戳。
- 默認(rèn)情況下,subLayer都顯示在mask內(nèi)容之外炉爆,此時(shí)mask實(shí)際上透明的堕虹,由此greenHead也是透明的。
- 現(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)链嘀,這樣上手更容易。
本文參考zmmzxxx的CALayer與iOS動(dòng)畫 講解及使用档玻,非常感謝怀泊。
- 如有錯(cuò)誤,歡迎指正误趴,多多點(diǎn)贊霹琼,打賞更佳,您的支持是我寫作的動(dòng)力凉当。