一.簡介:
CALayer類在概念上和UIView類似,同樣也是一些被層級關系樹管理的矩形塊湃缎,同樣也可以包含一些內容(像圖片嗓违,文本或者背景色)蹂季,管理子圖層的位置乏盐。它們有一些方法和屬性用來做動畫和變換父能。和UIView最大的不同是CALayer不處理用戶的交互何吝。
CALayer只是UIView 的一個基本架構手负,layer使繪制和動畫視圖內容和保持高幀速率的內容變得更加容易和高效熬拒。layer上不具備處理事件苗踪,繪制內容棵帽,響應鏈(responder chain)等
Layer也和View一樣存在著一個層級樹狀結構,稱之為圖層樹(Layer Tree),直接創(chuàng)建的或者通過UIView獲得的(view.layer)用于顯示的圖層樹,稱之為模型樹(Model Tree),模型樹的背后還存在兩份圖層樹的拷貝,一個是呈現(xiàn)樹(Presentation Tree),一個是渲染樹(Render Tree). 呈現(xiàn)樹可以通過普通layer(其實就是模型樹)的layer.presentationLayer獲得,而模型樹則可以通過modelLayer屬性獲得.模型樹的屬性在其被修改的時候就變成了新的值,這個是可以用代碼直接操控的部分;呈現(xiàn)樹的屬性值和動畫運行過程中界面上看到的是一致的.而渲染樹是私有的,你無法訪問到,渲染樹是對呈現(xiàn)樹的數(shù)據(jù)進行渲染,為了不阻塞主線程,渲染的過程是在單獨的進程或線程中進行的,所以你會發(fā)現(xiàn)Animation的動畫并不會阻塞主線程.
呈現(xiàn)樹通過圖層樹中所有圖層的呈現(xiàn)圖層所形成。注意呈現(xiàn)圖層僅僅當圖層首次被提交(就是首次第一次在屏幕上顯示)的時候創(chuàng)建逾苫,所以在那之前調用-presentationLayer將會返回nil铅搓。
layer.presentationLayer的運用狸吞,如果你要在一個運動的layer中判斷蹋偏,一個點是否在layer中威始,如果直接用layer可能出現(xiàn)黎棠,時間在現(xiàn)實中是在layer上脓斩,但是判斷上并沒有随静,因為這個時候layer的幾何屬性位置是和layer的位置有差別的燎猛,應該用layer.presentationLayer來判斷重绷。
具備的功能:
陰影昭卓,圓角葬凳,帶顏色的邊框
3D變換
非矩形范圍
透明遮罩
多級非線性動畫
二.CALayer使用
1.基本方法
1.1 初始化的3種方法
/*
+ (instancetype)layer;
- (instancetype)init;
- (instancetype)initWithLayer:(id)layer; 復制一個圖層
*/
CALayer *layer = [CALayer layer];
// 設置layer的大小
layer.bounds = CGRectMake(0, 0, 100, 100);
// 設置位置居中
layer.position = self.view.center;
// 添加視圖
[self.view.layer addSublayer:layer];
// 這里直接設置了一個圖片到圖層
UIImage *image2 = [UIImage imageNamed:@"2.jpg"];
layer.contents = (__bridge id _Nullable)(image2.CGImage);
效果圖
1.2 層的添加,移除昌简,插入,替換
sublayers:子layers
superlayer:父layer
- (void)addSublayer:(CALayer *)layer;
- (void)insertSublayer:(CALayer *)layer atIndex:(unsigned)idx;
- (void)insertSublayer:(CALayer *)layer below:(nullable CALayer *)sibling;
- (void)insertSublayer:(CALayer *)layer above:(nullable CALayer *)sibling;
- (void)replaceSublayer:(CALayer *)layer with:(CALayer *)layer2;
1.3相關層對象
呈現(xiàn)樹通過圖層樹中所有圖層的呈現(xiàn)圖層所形成。注意呈現(xiàn)圖層僅僅當圖層首次被提交(就是首次第一次在屏幕上顯示)的時候創(chuàng)建犬金,所以在那之前調用-presentationLayer將會返回nil晚顷。
- (nullable instancetype)presentationLayer; 得到層的副本该默。得到的是當前層的副本,也就是說在動畫的時候presentationLayer是不斷改變的栓袖。當屏幕沒有開始顯示layer的時候裹刮,為nil
- (instancetype)modelLayer; 返回模型層捧弃。如果是-presentationLayer返回的呈現(xiàn)層調用則返回所依賴的CALayer塔橡;如果不是-presentationLayer返回的層調用則返回self葛家。只有當涉及到表示層的更改時户辞,該方法才會返回一個值。如果沒有正在進行的事務癞谒,調用此方法的結果是未定義的底燎。
1.4 渲染
當更新層,改變不能立即顯示在屏幕上弹砚。當所有的層都準備好時双仍,可以調用setNeedsDisplay方法來重繪顯示桌吃。
[layer setNeedsDisplay];
若要重繪部分屏幕區(qū)域朱沃,請使用setNeedsDisplayInRect:方法,通過在CGRect結構的區(qū)域更新:
[layer setNeedsDisplayInRect:CGRectMake(0.0,0.0,50.0,75.0)];
如果是用的Core Graphics框架來執(zhí)行渲染的話茅诱,可以直接渲染Core Graphics的內容逗物。用renderInContext:來做這個事。
[layer renderInContext:UIGraphicsGetCurrentContext()];
當它的邊界矩形變化時瑟俭,層內容是否必須更新
layer.needsDisplayOnBoundsChange = NO;
正常更新周期之外強制更新您的層的內容,一般不使用翎卓。首選方法是調用setNeedsDisplay,讓系統(tǒng)在下一次周期中更新層
[layer displayIfNeeded];
指示該層是否被標記為需要更新
[layer needsDisplay];
返回一個布爾值摆寄,表示對指定鍵的更改是否要求重新顯示該層失暴。
BOOL neetd_display = [CALayer needsDisplayForKey:@"position"];
NSLog(@"%d", neetd_display);
1.5 動畫的添加坯门,移除
- (void)addAnimation:(CAAnimation *)anim forKey:(nullable NSString *)key;
- (void)removeAllAnimations;
- (void)removeAnimationForKey:(NSString *)key;
- (nullable NSArray<NSString *> *)animationKeys;
- (nullable CAAnimation *)animationForKey:(NSString *)key;
1.6 層調整和布局
指示層的sublayers的布局已經更改并且必須更新,當該layer的邊界發(fā)生變化,系統(tǒng)通常會自動調用該方法。
- (void)setNeedsLayout;
子類可以覆蓋此方法來給sublayer布局逗扒,詳情可以看9田盈。
- (void)layoutSublayers;
遍歷層的超級層,直到找到不需要布局的父層缴阎,然后在祖先的整個層上執(zhí)行布局允瞧。
- (void)layoutIfNeeded;
返回一個布爾值,指示該層是否被標記為需要一個布局更新蛮拔。
- (BOOL)needsLayout;
最適合的尺寸
- (CGSize)preferredFrameSize;
1.7 Scrolling
visibleRect - 返回layer在屏幕顯示的內容的位置
滾動到某一點述暂,只有CAScrollLayer有效
- (void)scrollPoint:(CGPoint)p;
滾動到某一個區(qū)域可見,只有CAScrollLayer有效
- (void)scrollRectToVisible:(CGRect)r;
1.8 時間轉換方法:用來得到layer之間通過speed建炫,timeOffset等參數(shù)因數(shù)在內的時間轉換后的時間
- (CFTimeInterval)convertTime:(CFTimeInterval)t fromLayer:(CALayer *)l;
- (CFTimeInterval)convertTime:(CFTimeInterval)t toLayer:(CALayer *)l;
2.contents屬性
contents給CALayer賦予內容畦韭,類型為id類型,意味著它可以是任何類型的對象肛跌。但是在ios中你只能把CGImageRef賦值給它艺配,如果不是CGImage得到的內容會是空。賦值方法和效果可以參考上面1的例子衍慎。
這里主要講如何給layer賦內容转唉,主要有3種方法:
a.圖像對象直接賦給層對象的內容屬性(這種技術最適合用于不需要或很少更改的層內容。)
b.使用CALayerDelegate委托繪制層的內容稳捆。(此技術最適合于可能周期性變化的層內容赠法,可以由外部對象提供,例如view)
c.定義一個layer子類乔夯,并覆蓋它的繪圖方法之一砖织,以自己提供layer內容。(如果你需要創(chuàng)建一個自定義layer子類末荐,或者你想改變層的基本繪圖行為侧纯,這個技巧是合適的。)
2.1第一種方法
UIImage *image = [UIImage imageNamed:@"123.jpg"];
self.view.layer.contents = (__bridge id _Nullable)(image.CGImage);
2.2第二種方法
// 說明:對于UIView應該使用drawRect:來繪圖甲脏,不要使用這個代理眶熬。UIView自動使它自己成為layer的委托,并實現(xiàn)所需的委托方法
CALayer *layer = [CALayer layer];
layer.bounds = CGRectMake(0, 0, 100, 100);
layer.position = self.view.center;
[self.view.layer addSublayer:layer];
layer.delegate = self;
// 告訴layer需要更新顯示剃幌,不適用這個不會調用委托的方法
[layer setNeedsDisplay];
#pragma mark - contents屬性方法2 <CALayerDelegate>
// CALayerDelegate必須實現(xiàn)displayLayer:或drawLayer:inContext:方法聋涨。如果CALayerDelegate實現(xiàn)了displayLayer和drawLayer:inContext:方法,層只調用displayLayer:方法负乡。
// 該實現(xiàn)負責創(chuàng)建位圖并將其分配到該層的內容屬性
- (void)displayLayer:(CALayer *)layer {
UIImage *image = [UIImage imageNamed:@"2.jpg"];
layer.contents = (__bridge id _Nullable)(image.CGImage);
}
// 創(chuàng)建一個圖形上下文來繪制位圖,然后調用你的委托方法來填充位圖
- (void)drawLayer:(CALayer *)layer inContext:(CGContextRef)ctx {
CGMutablePathRef thePath = CGPathCreateMutable();
CGPathMoveToPoint(thePath,NULL,15.0f,15.f);
CGPathAddCurveToPoint(thePath,
NULL,
15.f,250.0f,
295.0f,250.0f,
295.0f,15.0f);
CGContextBeginPath(ctx);
CGContextAddPath(ctx, thePath);
CGContextSetLineWidth(ctx, 5);
CGContextStrokePath(ctx);
// Release the path
CFRelease(thePath);
}
2.3第三種方法
#import "CACustomLayer.h"
@implementation CACustomLayer
// 第一種
// 第一種實現(xiàn)了不會調用第二種
- (void)display {
UIImage *image = [UIImage imageNamed:@"2.jpg"];
self.contents = (__bridge id _Nullable)(image.CGImage);
}
// 第二種
- (void)drawInContext:(CGContextRef)ctx {
CGMutablePathRef thePath = CGPathCreateMutable();
CGPathMoveToPoint(thePath,NULL,15.0f,15.f);
CGPathAddCurveToPoint(thePath,
NULL,
15.f,250.0f,
295.0f,250.0f,
295.0f,15.0f);
CGContextBeginPath(ctx);
CGContextAddPath(ctx, thePath);
CGContextSetLineWidth(ctx, 5);
CGContextStrokePath(ctx);
// Release the path
CFRelease(thePath);
}
@end
CACustomLayer *layer = [[CACustomLayer alloc] init];
layer.bounds = CGRectMake(0, 0, 100, 100);
layer.position = CGPointMake(CGRectGetMidX(self.view.frame), CGRectGetMidY(self.view.frame));
[self.view.layer addSublayer:layer];
// 告訴layer需要更新顯示脊凰,不使用這個不會layer重寫的方法
[layer setNeedsDisplay];
3.CALayer的幾何學
這里我使用同一個layer的介紹下面的內容
// 創(chuàng)建一個layer
UIImage *background_image = [UIImage imageNamed:@"123.jpg"];
self.view.layer.contents = (__bridge id)background_image.CGImage;
CALayer *layer = [CALayer layer];
UIImage *image = [UIImage imageNamed:@"2.jpg"];
layer.contents = (__bridge id)image.CGImage;
[self.view.layer addSublayer:layer];
3.1 CALayer布局
// frame 和view中的frame類似抖棘,表示在父layer里面的位置
// bounds 和view中的bounds類似茂腥,表示自己的大小
// position 和view中的center類似,表示在父layer里的中心位置
// 注:這些賦值參數(shù)盡量使用整數(shù)
layer.bounds = CGRectMake(0, 0, 100, 100);
layer.position = CGPointMake(CGRectGetMidX(self.view.bounds), CGRectGetMidY(self.view.bounds));
// 這里設置frame和設置bounds切省,position一致
// layer.frame = CGRectMake(CGRectGetMidX(self.view.bounds) - 50, CGRectGetMidY(self.view.bounds) - 50, 100, 100);
3.2 錨點anchorPoint
這種瞄點可以改變視圖的位置最岗,默認值是[0.5,0.5],如果設置為[0.0,0.0]朝捆,視圖就會往右下移般渡,顯示的開始位置變成了原先顯示的中點位置。簡單的說芙盘,position驯用,bounds,anchorPoint決定了視圖的frame儒老。
錨點在ios中和mac os 一點不同蝴乔,了解具體的內容可以自行查閱資料,也可以看官方文檔驮樊。
例子:
// 設置layer的位置在父layer中心讓左上分別偏移50的位置
layer.position = CGPointMake(CGRectGetMidX(self.view.bounds) - 50, CGRectGetMidY(self.view.bounds) - 50);
// 這里設置layer又顯示到了中心位置薇正。
layer.anchorPoint = CGPointMake(0.0, 0.0);
3.3 不同坐標系之間的圖層轉換方法
- (CGPoint)convertPoint:(CGPoint)p fromLayer:(nullable CALayer *)l;
- (CGPoint)convertPoint:(CGPoint)p toLayer:(nullable CALayer *)l;
- (CGRect)convertRect:(CGRect)r fromLayer:(nullable CALayer *)l;
- (CGRect)convertRect:(CGRect)r toLayer:(nullable CALayer *)l;
調用的layer和l必須共享同一個父layer。
l可以為nil,如果您為l參數(shù)指定nil囚衔,這個方法將返回從該層的原點減去的原始點挖腰。
例子:
CALayer *layer2 = [CALayer layer];
layer2.backgroundColor = [UIColor yellowColor].CGColor;
layer2.bounds = CGRectMake(0, 0, 100, 100);
layer2.position = CGPointMake(CGRectGetMidX(self.view.bounds) + 50, CGRectGetMidY(self.view.bounds) + 50);
[self.view.layer insertSublayer:layer2 below:layer];
// 得到layer的CGPointMake(0, 0)在屏幕顯示點,在_displayLayer.layer2為坐標參考對應的坐標
CGPoint point1 = [layer convertPoint:CGPointMake(0, 0) fromLayer:layer2];
NSLog(@"%@", NSStringFromCGPoint(point1));
CGPoint point2 = [layer2 convertPoint:CGPointMake(0, 0) toLayer:layer];
NSLog(@"%@", NSStringFromCGPoint(point2));
// 得到self.view.layer的CGRectMake(0, 0, 1, 1)在屏幕顯示點练湿,在layer為坐標參考對應的坐標
NSLog(@"%@", NSStringFromCGRect(layer.frame));
CGRect rect1 = [layer convertRect:CGRectMake(0, 0, 1, 1) fromLayer:self.view.layer];
NSLog(@"%@", NSStringFromCGRect(rect1));
CGRect rect2 = [self.view.layer convertRect:CGRectMake(0, 0, 1, 1) toLayer:layer];
NSLog(@"%@", NSStringFromCGRect(rect2));
3.4 hitTest
返回p點曙聂,在層層次結構最底層的layer
- (CALayer *)hitTest:(CGPoint)p;
返回是否包含指定的點。
- (BOOL)containsPoint:(CGPoint)p;
3.5 內容垂直翻轉鞠鲜。不影響內容宁脊,只影響layer的幾何屬性。
layer.geometryFlipped = NO;
4.顯示效果調整屬性
新建一個layer來說明:
// 創(chuàng)建一個layer
UIImage *background_image = [UIImage imageNamed:@"123.jpg"];
self.view.layer.contents = (__bridge id)background_image.CGImage;
CALayer *layer = [CALayer layer];
UIImage *image = [UIImage imageNamed:@"2.jpg"];
layer.contents = (__bridge id)image.CGImage;
layer.bounds = CGRectMake(0, 0, 100, 100);
layer.position = CGPointMake(CGRectGetMidX(self.view.bounds), CGRectGetMidY(self.view.bounds));
[self.view.layer addSublayer:layer];
4.1 圓角和邊界裁剪 cornerRadius masksToBounds
// 設置圓角為寬度的一半
// 邊界裁剪默認為no贤姆,所有設置圓角需要邊界裁剪設置為yes
layer.cornerRadius = 50;
layer.masksToBounds = YES;
4.2 背景顏色
layer.backgroundColor = [UIColor whiteColor].CGColor;
4.3 邊框榆苞, 邊框的設置在設置圓角的時候,會受圓角影響
// 邊框顏色
layer.borderColor = [UIColor orangeColor].CGColor;
// 邊框寬度
layer.borderWidth = 1;
效果圖:
4.4 陰影效果
shadowOpacity 控制陰影的透明度霞捡,默認為0坐漏,完全透明
shadowColor 控制陰影的顏色,默認是黑色
shadowOffset 控制陰影的方向和距離碧信,它是一個CGSize的值赊琳,寬度控制這陰影橫向的位移,高度控制著縱向的位移砰碴,默認值是 {0, -3}躏筏,意即陰影相對于Y軸有3個點的向上位移(主要是mac os 里面的坐標系統(tǒng),所以默認是向上的呈枉,ios中趁尼,自己設置就可以了)埃碱。
shadowPath 陰影效果顯示,可以由UIBezierPath來控制路徑酥泞,比如設置一個圓的陰影效果
由于陰影效果是在layer外顯示的砚殿,所以masksToBounds為yes的時候,會被裁剪芝囤,不會顯示似炎。解決這種方法通常在外面套一個layer實現(xiàn)陰影效果
// 創(chuàng)建一個顯示陰影的layer
CALayer *shadowLayer = [CALayer layer];
shadowLayer.bounds = CGRectMake(0, 0, 100, 100);
shadowLayer.position = CGPointMake(CGRectGetMidX(self.view.bounds), CGRectGetMidY(self.view.bounds));
[self.view.layer addSublayer:layer];
// 設置陰影效果
shadowLayer.shadowOpacity = 0.6;
shadowLayer.shadowColor = [UIColor orangeColor].CGColor;
shadowLayer.shadowOffset = CGSizeMake(0, 0);
// 添加shadowLayer在layer層的下面
[self.view.layer insertSublayer:shadowLayer below:layer];
// 因為layer被裁剪為圓形,所有陰影效果要保持一直悯姊,通過shadowPath來設置羡藐。
UIBezierPath *bezierPath = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(0, 0, 100, 100) cornerRadius:50];
shadowLayer.shadowPath = bezierPath.CGPath;
效果圖:
4.5 圖層蒙版
mask屬性就像是一個餅干切割機,mask圖層實心的部分會被保留下來挠轴,其他的則會被拋棄
UIImage *maskImage = [UIImage imageNamed:@"t"];
CALayer *maskLayer = [CALayer layer];
maskLayer.frame = layer.bounds;
maskLayer.contents = (__bridge id _Nullable)(maskImage.CGImage);
layer.mask = maskLayer;
// 為了顯示效果暫時把shadowLayer隱藏
shadowLayer.hidden = YES;
// // 去除蒙版,回復原先的顯示效果
// layer.mask = nil;
// shadowLayer.hidden = NO;
效果圖:
4.6 組透明度
類似于UIView的alpha传睹,layer也有一個opacity屬性來設置layer的透明度。
在layer設置opacity的時候岸晦,會影響子layer的透明欧啤,這個主要是由allowsGroupOpacity組不透明度來控制,這個值取決于Info.plist設置启上,在ios7之后默認為yes邢隧,就是說layer設置了opacity會整合子layer的透明度,看起來他們就是一個試圖冈在,透明度一樣倒慧。當然如果設置為no的時候,會產生子試圖的透明度和layer不一致包券。
調整layer:
// 在layer上纫谅,添加一個黑色的subLayer
CALayer *subLayer = [CALayer layer];
subLayer.backgroundColor = [UIColor blackColor].CGColor;
subLayer.frame = CGRectMake(20, 20, 60, 60);
[layer addSublayer:subLayer];
// 為了體現(xiàn)出區(qū)別,我這里設置視圖往下移
layer.position = CGPointMake(CGRectGetMidX(self.view.bounds), CGRectGetMidY(self.view.bounds) + 200);
// 隱藏shadowLayer
shadowLayer.hidden = YES;
總結:
1.設置layer的opacity會對subLayer產生影響
2.設置subLayer的opacity會在layer參數(shù)產生影響的基礎上再產生影響溅固。
3.allowsGroupOpacity決定了subLayer能對layer的opacity參生不同的效果付秕,layer的透視效果?發(fā)送了改變。
4.為了不要對比得復雜侍郭,我這里沒有列舉出全部的例子询吴,權權是啟到一個知識回顧提示作用。
還可以設置CALayer的一個叫做shouldRasterize屬性來實現(xiàn)組透明的效果亮元,如果它被設置為YES猛计,在應用透明度之前,圖層及其子圖層都會被整合成一個整體的圖片爆捞,這樣就沒有透明度混合的問題了
如果你使用了shouldRasterize奉瘤,那么需要通過rasterizationScale屬性去匹配屏幕,以防止出現(xiàn)Retina屏幕像素化的問題
注:當shouldRasterize和UIViewGroupOpacity一起的時候嵌削,會出現(xiàn)性能問題毛好,這個在圖層性能上會提到望艺。
// layer.shouldRasterize = YES;
// layer.rasterizationScale = 2;
4.7 顯示和隱藏
layer.hidden = NO;
4.8 可以通過style屬性給屬性賦值,如果已經給layer屬性賦過值苛秕,那么給這個屬性設置將不起作用肌访。這個屬性一般在對進行自定義屬性,添加隱式動畫的時候使用
NSMutableDictionary *dic = [NSMutableDictionary dictionary];
[dic setObject:(__bridge id)[UIColor redColor].CGColor forKey:@"borderColor"];
[dic setObject:@2 forKey:@"borderWidth"];
layer.style = dic;
4.9 allowsEdgeAntialiasing 允許邊緣抗鋸齒
4.10 edgeAntialiasingMask 使用此屬性禁用對其他層邊緣的反鋸齒艇劫,以消除可能發(fā)生的邊界
4.11 和contents內容設置有關的參數(shù)
// contentsGravity 內容布局方式
layer.contentsGravity = kCAGravityResizeAspectFill;
// contentsScale 內容倍數(shù)
layer.contentsScale = [UIScreen mainScreen].scale;
// contentsRect 屬性允許我們在圖層邊框里顯示寄宿圖的一個子域吼驶, 比如:把圖片按中心分成4個部分,只顯示圖片的后下半部分[0.5, 0.5, 1, 1],前上部分[0.0, 0.0, 0.5, 0.5];
layer.contentsRect = CGRectMake(0.0, 0.0, 1, 1);
// contentsCenter內容中心店煞,可以配合contentsGravity進行內容顯示調整蟹演,他工作起來的效果和UIImage里的-resizableImageWithCapInsets: 方法效果非常類似。簡單的說顷蟀,設置這個會改變圖片的拉伸效果酒请,默認情況下,contentsCenter是{0, 0, 1, 1}鸣个,這意味著如果大行叻础(由conttensGravity決定)改變了,那么寄宿圖將會均勻地拉伸開。但是如果我們增加原點的值并減小尺寸{0.25, 0.25, 0.5, 0.5}囤萤,就只會在這個部分就行拉伸昼窗。
layer.contentsCenter = CGRectMake(0.0, 0.0, 1.0, 1.0);
// contentsAreFlipped 顯示的內容是否翻轉
// opaque 內容是否不透明,只影響由核心動畫管理的備份存儲。如果內容不包含alpha通道涛舍,設置為yes澄惊,可以提高性能。
layer.opaque = NO;
// 是否在后臺繪制內容
layer.drawsAsynchronously = NO;
// contentsFormat 內容顯示的格式,layer 和view 會自動調整適合的格式,一般不需要去設置
// layer.contentsFormat = kCAContentsFormatRGBA8Uint;
5.變換
對layer顯示就行調整富雅,只是改變了顯示效果掸驱,沒有對他的幾何屬性做改變。主要有:旋轉没佑,縮放和平移.
a.仿射變換CGAffineTransform
b.3D變換CATransform3D
6.Key-Value Coding Extensions
核心動畫擴展了NSKeyValueCoding協(xié)議毕贼,因為它屬于CAAnimation和CALayer類。此擴展為一些鍵添加默認值图筹,展開包裝約定帅刀,并為CGPoint、CGRect远剩、CGSize和CATransform3D類型添加關鍵路徑支持扣溺。官方文檔
關于kvc可以自行去了解,這里主要對layer的+ (id)defaultValueForKey:(NSString *)key做一個說明瓜晤。kvc官方文檔
// 返回是否歸檔
- (BOOL)shouldArchiveValueForKey:(NSString *)key;
設置默認值锥余,重寫用
+ (id)defaultValueForKey:(NSString *)key;
#import "CAKeyValueCodingLayer.h"
@implementation CAKeyValueCodingLayer
// NSKeyValueCoding 設置默認返回key對應的值
// 對用設置的值類型
+ (id)defaultValueForKey:(NSString *)key {
if ([key isEqualToString:@"masksToBounds"]) {
return [NSNumber numberWithBool:YES];
}
return [super defaultValueForKey:key];
}
@end
CAKeyValueCodingLayer *layer = [CAKeyValueCodingLayer layer];
NSLog(@"masksToBounds = %@", [layer valueForKey:@"masksToBounds"]);
NSLog(@"someKey1 = %@", [layer valueForKey:@"someKey"]);
// 支持 NSKeyValueCoding Key-Value Coding Extensions
[layer setValue:[NSNumber numberWithInteger:50] forKey:@"someKey"];
NSNumber *someKeyValue = [layer valueForKey:@"someKey"];
NSLog(@"someKey2 = %@", someKeyValue);
masksToBounds默認的值應該為no,上面的代碼把默認值設為了yes痢掠。
7.改變view默認帶有l(wèi)ayer的類型
view默認帶有l(wèi)ayer驱犹,可以通過重寫+ (Class)layerClass改變layer的類型
大多數(shù)view默認會備份這個view嘲恍。一般情況下使用默認就可以。
但是可能需要更改的情況雄驹,列舉幾種情況(創(chuàng)建后佃牛,視圖的圖層對象不能更改。):
a.使用 Metal 和 OpenGL ES医舆,來進行繪制俘侠,使用 CAMetalLayer or CAEAGLLayer
b.使用一個專門的layer類型,來提高性能
c.利用一些專門的核心動畫層類蔬将,例如粒子發(fā)射器或復制器,CAEmitterLayer,CAReplicatorLayer
#import "CAMetalLayerView.h"
@implementation CAMetalLayerView
+ (Class)layerClass {
return [CAMetalLayer class];
}
@end
CAMetalLayerView *view = [[CAMetalLayerView alloc] init];
NSLog(@"%@", NSStringFromClass(view.layer.class));
這里把UIView的layer類型改成了 CAMetalLayer爷速。
8.手動給sublayers布局
手動布局主要有2種方式:
a.實現(xiàn)CALayerDelegate代理- (void)layoutSublayersOfLayer:(CALayer *)layer
b.自定義layer實現(xiàn)覆蓋父類- (void)layoutSublayers;
#import <QuartzCore/QuartzCore.h>
@interface CACustomDisplayLayer : CALayer
@property (nonatomic, strong) CALayer *layer1;
@property (nonatomic, strong) CALayer *layer2;
@property (nonatomic, strong) CALayer *layer3;
@end
#import "CACustomDisplayLayer.h"
#import <UIKit/UIKit.h>
@implementation CACustomDisplayLayer
// 自定義Layer, 覆蓋這個實現(xiàn)霞怀,可以實現(xiàn)手動給sublayer布局惫东。
- (void)layoutSublayers {
[super layoutSublayers];
CGFloat width = floor((CGRectGetWidth(self.bounds) - 24) * 0.5);
CGFloat height = floor((CGRectGetHeight(self.bounds) - 24) * 0.5);
self.layer1.frame = CGRectMake(8, 8, width, height);
self.layer2.frame = CGRectMake(16 + width, 8, width, height);
self.layer3.frame = CGRectMake(8, 16 + height, width, height);
}
- (CALayer *)layer1 {
if (!_layer1) {
_layer1 = [CALayer layer];
_layer1.backgroundColor = [UIColor yellowColor].CGColor;
[self addSublayer:_layer1];
}
return _layer1;
}
- (CALayer *)layer2 {
if (!_layer2) {
_layer2 = [CALayer layer];
_layer2.backgroundColor = [UIColor redColor].CGColor;
[self addSublayer:_layer2];
}
return _layer2;
}
- (CALayer *)layer3 {
if (!_layer3) {
_layer3 = [CALayer layer];
_layer3.backgroundColor = [UIColor blueColor].CGColor;
[self addSublayer:_layer3];
}
return _layer3;
}
@end
// CACustomDisplayLayer 里面只有3一樣,但是顏色不一樣的圖形
CACustomDisplayLayer *displayLayer = [CACustomDisplayLayer layer];
displayLayer.bounds = CGRectMake(0, 0, 200, 200);
displayLayer.position = CGPointMake(CGRectGetMidX(self.view.bounds), CGRectGetMidY(self.view.bounds));
displayLayer.backgroundColor = [UIColor orangeColor].CGColor;
displayLayer.opacity = 0.6;
displayLayer.delegate = self;
[self.view.layer addSublayer:displayLayer];
// 再最后再添加一個顏色不一樣毙石,其他一樣的圖形
CALayer *sub_displayLayer = [CALayer layer];
sub_displayLayer.backgroundColor = [UIColor grayColor].CGColor;
[displayLayer addSublayer:sub_displayLayer];
// 4秒之后改變displayLayer.bounds廉沮,觀察 displayLayer,sublayers是否自定調節(jié)
// dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(4 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
//
// displayLayer.bounds = CGRectMake(0, 0, 150, 150);
// });
這樣做有一個好處胁黑,就是當你改變了layer的大小废封,layer會自動調用相應的方法,對子layer做調整丧蘸。
三.CALayer其他使用
待補充