本文為讀書筆記: iOS Core Animation: Advanced Techniques
1.實際上layer
才是真正用來在屏幕上顯示和做動畫,UIView
僅僅是對它的一個封裝,提供了一些iOS
類似于處理觸摸的具體功能,以及Core Animation
底層方法的高級接口。
2.我們已經證實了圖層不能像視圖那樣處理觸摸事件,那么他能做哪些視圖不能做的呢?這里有一些UIView
沒有暴露出來的CALayer
的功能:
- 陰影 、圓角、帶顏色的邊框
- 3D變換
- 非矩形范圍
- 透明遮罩
- 多級非線性動畫
CALayer 屬性 |
屬性類型 | 作用 |
---|---|---|
contents |
id |
設置圖片 |
contentsGravity |
NSString |
設置圖片展示,類似于view 的contentMode
|
3.contents
id
意味著給contents
賦任何值編譯都可以通過,但是,在實踐中,如果你給contents
賦的不是CGImage
,那么你得到的圖層將是空白的。contents
這個奇怪的表現是由Mac OS
的歷史原因造成的。它之所以被定義為id
類型,是因為在Mac OS
系統上,這個屬性對CGImage
和NSImage
類型的值都起作用。
使用 :layer.contents = (__bridge id)image.CGImage;
4.contentsGravity
UIView
的contentMode
,對這些屬性的操作其實是對對應圖層的操作,contentsGravity
就是圖層的contentMode
/** Layer `contentsGravity' values. **/
CA_EXTERN NSString * const kCAGravityCenter
CA_EXTERN NSString * const kCAGravityTop
CA_EXTERN NSString * const kCAGravityBottom
CA_EXTERN NSString * const kCAGravityLeft
CA_EXTERN NSString * const kCAGravityRight
CA_EXTERN NSString * const kCAGravityTopLeft
CA_EXTERN NSString * const kCAGravityTopRight
CA_EXTERN NSString * const kCAGravityBottomLeft
CA_EXTERN NSString * const kCAGravityBottomRight
CA_EXTERN NSString * const kCAGravityResize
CA_EXTERN NSString * const kCAGravityResizeAspect
CA_EXTERN NSString * const kCAGravityResizeAspectFill
5.contentsScale
這個設置圖片的每個點1個像素或者兩個像素
使用 :
layer.contentsScale = image.scale;
或者
layer.contentsScale = [UIScreen mainScreen].scale;
6.contentsRect
可以用來作拼合圖片(一般APP估計不會使用)
7.CALayer
給不同坐標系之間的圖層轉換提供了一些工具類方法:
- (CGPoint)convertPoint:(CGPoint)point fromLayer:(CALayer *)layer;
- (CGPoint)convertPoint:(CGPoint)point toLayer:(CALayer *)layer;
- (CGRect)convertRect:(CGRect)rect fromLayer:(CALayer *)layer;
- (CGRect)convertRect:(CGRect)rect toLayer:(CALayer *)layer;
8.陰影效果
shadowOpacity
: 是一個必須在0.0(不可見)和1.0(完全不透明)之間的浮點數。shadowColor
: 屬性控制著陰影的顏色,和borderColor
和backgroundColor
一樣,它的類型也是CGColorRef。陰影默認是黑色。shadowOffset
屬性控制著陰影的方向和距離。它是一個CGSize
的值,寬度控制這陰影橫向的位移,高度控制著縱向的位移。shadowOffset
的默認值是 {0, -3},意即陰影相對于Y軸有3個點的向上位移(以為shadow最早出現在MacOS,在MacOS上坐標軸和iOS是相反的)。shadowPath
: 如果事先知道陰影的形狀,可以設置shadowPath
來提高性能。
9.opacity
相當于view
的alpha
10.layer.shouldRasterize
這是由透明度的混合疊加造成的,當你顯示一個50%透明度的圖層時,圖層的每個像素都會一半顯示自己的顏色,另一半顯示圖層下面的顏色。這是正常的透明度的表現。但是如果圖層包含一個同樣顯示50%透明的子圖層時,你所看到的視圖,50%來自子視圖,25%來了圖層本身的顏色,另外的25%則來自背景色。
在我們的示例中,按鈕和表情都是白色背景。雖然他們都是50%的可見度,但是合起來的可見度是75%,所以標簽所在的區域看上去就沒有周圍的部分那么透明。所以看上去子視圖就高亮了,使得這個顯示效果都糟透了。
解決辦法:
bottomView.layer.shouldRasterize = YES;
bottomView.layer.rasterizationScale = [UIScreen mainScreen].scale;
11.CGAffineTransform
仿射
的意思是無論變換矩陣用什么值,圖層中平行的兩條線在變換之后任然保持平行- 如果需要混合兩個已經存在的變換矩陣,就可以使用如下方法,在兩個變換的基礎上創建一個新的變換:
CGAffineTransformConcat(CGAffineTransform t1, CGAffineTransform t2);
12.CATransform3D
的m34
元素,用來做透視
詳見
- (void)viewDidLoad { [super viewDidLoad];
//create a new transform
CATransform3D transform = CATransform3DIdentity;
//apply perspective
transform.m34 = - 1.0 / 500.0;
//rotate by 45 degrees along the Y axis
transform = CATransform3DRotate(transform, M_PI_4, 0, 1, 0);
//apply to layer
self.layerView.layer.transform = transform; }
-
創建一個3D的正方體](https://zsisme.gitbooks.io/ios-/content/chapter5/solid-objects.html))
14.Core Graphics
直接向原始的CALyer
的內容中繪制一個路徑,相比直下,使用CAShapeLayer
有以下一些優點:
- 渲染快速。CAShapeLayer使用了硬件加速,繪制同一圖形會比用Core Graphics快很多。
- 高效使用內存。一個CAShapeLayer不需要像普通CALayer一樣創建一個寄宿圖形,所以無論有多大,都不會占用太多的內存。
- 不會被圖層邊界剪裁掉。一個CAShapeLayer可以在邊界之外繪制。你的圖層路徑不會像在使用Core Graphics的普通CALayer一樣被剪裁掉(如我們在第二章所見)。
- 不會出現像素化。當你給CAShapeLayer做3D變換時,它不像一個有寄宿圖的普通圖層一樣變得像素化。
15.UILabel
: 借助圖層代理直接將字符串使用Core Graphics
寫入圖層的內容
16.宿主圖層
: 指的是view
的layer
,既self.view.layer
,可以通過下面的方法改變
+ (Class)layerClass
{
return [CATextLayer class];
}
17.CATextLayer
替換 UILabal
](https://zsisme.gitbooks.io/ios-/content/chapter6/CATextLayer.html))
18.CATransformLayer
:創建一個立方體圖層
19.CAGradientLayer
colors
: 保存的各種顏色locations
: 顏色的作用范圍,這個范圍數組需要個顏色數組保持相同 , 當只有兩個顏色的時候設置gradientLayer.locations = @[@0,@1];
是均分兩個視圖startPoint
,開始的點,(0 , 0)
代表左上角endPoint
,結束的店,(1 , 1)
代表右下角
20.CATiledLayer
: 把一個大圖裁剪成小圖并加載,一般用在地圖,或者加載大的圖片.
-
Transactions(事務)
: 實際上是Core Animation用來包含一系列屬性動畫集合的機制,任何用指定事務去改變可以做動畫的圖層屬性都不會立刻發生變化,而是當事務一旦提交的時候開始用一個動畫過渡到新值。
begin
: 開始commit
: 結束+ (void)setCompletionBlock:(nullable void (^)(void))block;
: 代碼完成塊
例1
- (void)viewDidLoad
{
[super viewDidLoad];
self.colorLayer = [CALayer layer];
self.colorLayer.frame = CGRectMake(0.0f, 0.0f, 150.0f, 150.0f);
self.colorLayer.backgroundColor = [UIColor blueColor].CGColor;
//add it to our view
[self.containerView.layer addSublayer:self.colorLayer];
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
//begin a new transaction
[CATransaction begin];
//set the animation duration to 1 second
[CATransaction setAnimationDuration:5.0];
//randomize the layer background color
CGFloat red = arc4random() / (CGFloat)INT_MAX;
CGFloat green = arc4random() / (CGFloat)INT_MAX;
CGFloat blue = arc4random() / (CGFloat)INT_MAX;
self.colorLayer.backgroundColor = [UIColor colorWithRed:red green:green blue:blue alpha:1.0].CGColor;
//commit the transaction
[CATransaction commit];
}
例2
- (void)viewDidLoad{ [super viewDidLoad]; //create sublayer self.colorLayer = [CALayer layer]; self.colorLayer.frame = CGRectMake(50.0f, 50.0f, 100.0f, 100.0f); self.colorLayer.backgroundColor = [UIColor blueColor].CGColor; //add a custom action CATransition *transition = [CATransition animation]; transition.type = kCATransitionPush; transition.subtype = kCATransitionFromLeft; self.colorLayer.actions = @{@"backgroundColor": transition}; //add it to our view [self.layerView.layer addSublayer:self.colorLayer];}- (IBAction)changeColor{ //randomize the layer background color CGFloat red = arc4random() / (CGFloat)INT_MAX; CGFloat green = arc4random() / (CGFloat)INT_MAX; CGFloat blue = arc4random() / (CGFloat)INT_MAX; self.colorLayer.backgroundColor = [UIColor colorWithRed:red green:green blue:blue alpha:1.0].CGColor;}
22.隱式動畫是如何實現的
- 圖層首先檢測它是否有委托,并且是否實現CALayerDelegate
協議指定的-actionForLayer:forKey
方法。如果有,直接調用并返回結果。- 如果沒有委托,或者委托沒有實現-actionForLayer:forKey
方法,圖層接著檢查包含屬性名稱對應行為映射的actions
字典。- 如果actions字典
沒有包含對應的屬性,那么圖層接著在它的style
字典接著搜索屬性名。- 最后,如果在style
里面也找不到對應的行為,那么圖層將會直接調用定義了每個屬性的標準行為的-defaultActionForKey:
方法。
23.CATransition
響應CAAction
協議,并且可以當做一個圖層行為.行為通常是一個被Core Animation隱式調用的顯式動畫對象。
24.presentationLayer
: 呈現圖層 ,用戶真正看到的圖層(相當于View)
modelLayer
: 圖層模型 (相當于Model)
Core Animation
: 相當于控制器
大多數情況下,你不需要直接訪問呈現圖層,你可以通過和模型圖層的交互,來讓Core Animation更新顯示。兩種情況下呈現圖層會變得很有用,一個是同步動畫,一個是處理用戶交互。
- 如果你在實現一個基于定時器的動畫(見第11章“基于定時器的動畫”),而不僅僅是基于事務的動畫,這個時候準確地知道在某一時刻圖層顯示在什么位置就會對正確擺放圖層很有用了。
- 如果你想讓你做動畫的圖層響應用戶輸入,你可以使用-hitTest:方法(見第三章“圖層幾何學”)來判斷指定圖層是否被觸摸,這時候對呈現圖層而不是模型圖層調用-hitTest:會顯得更有意義,因為呈現圖層代表了用戶當前看到的圖層位置,而不是當前動畫結束之后的位置。
25.緩沖函數CAMediaTimingFunction
自定義緩沖函數
+ (instancetype)functionWithControlPoints:(float)c1x :(float)c1y :(float)c2x :(float)c2y;
- (instancetype)initWithControlPoints:(float)c1x :(float)c1y :(float)c2x :(float)c2y;
- 那么該如何使用緩沖方程式呢?
首先需要設置CAAnimation的timingFunction
屬性,是CAMediaTimingFunction
類的一個對象。如果想改變隱式動畫的計時函數,同樣也可以使用CATransaction
的+setAnimationTimingFunction:
方法。
26.RunLoop
NSDefaultRunLoopMode
- 標準優先級
NSRunLoopCommonModes
- 高優先級
UITrackingRunLoopMode
- 用于UIScrollView和別的控件的動畫
CADisplayLink
: 屏幕更新執行一次刷新一次
self.timer = [CADisplayLink displayLinkWithTarget:self selector:@selector(step:)];
[self.timer addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];
[self.timer addToRunLoop:[NSRunLoop mainRunLoop] forMode:UITrackingRunLoopMode];
NSTimer
:標準的時間
self.timer = [NSTimer timerWithTimeInterval:1/60.0 target:self selector:@selector(step:) userInfo:nil repeats:YES];[[NSRunLoop mainRunLoop] addTimer:self.timer forMode:NSRunLoopCommonModes];