CALyer解讀--總有一個點會讓你想起它

花絮:基于上一篇文章又有幾個月沒有寫文章了,剛好最近項目基本完成,開啟了16年制定的目標每兩三個月讀一本書籍,并做好記錄,希望自己可以堅持下去,這次讀的是核心動畫高級技巧,已經把電子檔和最后推薦的軟件上傳之碼云上面,喜歡的朋友可以去下載。

在此聲明:本文章為七秒一個字一個字敲起來的,在敲打的過程中難免有錯別字,如果錯別字對于您由理解的影響,那么請你在下留言,七秒會立即改正。

敲起來

傳送門

1. contentsGravity

表示設置layer上面設置圖片的的拉伸方式。和UIImage上面的contentModel一致,只是它設置的類型是NSString類型

 kCAGravityCenter
 kCAGravityTop
 kCAGravityBottom
 kCAGravityLeft
 kCAGravityRight
 kCAGravityTopLeft
 kCAGravityTopRight
 kCAGravityBottomLeft
 kCAGravityBottomRight
 kCAGravityResize
 kCAGravityResizeAspect
 kCAGravityResizeAspectFill         

2.contentsScale

表示當前layer設置的圖片像素尺寸和試圖大小的比例,可以當前圖片在當下屏幕上面一個點顯示的像素值。
注意:如果設置了contentsGravity的值為改變寄宿圖片拉伸效果會導致設置的contentsScale設置的效果無效

3.contentsRect

表示要在layer區域顯示的寄宿圖片的區域。contentRect設置的單位坐標,默認為(0,0,1,1),如果我們設置為(0,0,0.5,0.5)時,layer上面顯示的圖片就是寄宿圖片左上角四分之一。

4.contentsCenter

定義一個固定的邊框和一個在圖層上面可以拉伸的區域。設置的也是單位坐標。注意:此屬性我們也是可以在XIB里面設置。stretching對應的

5.shadowOpacity 設置layer的陰影

1.需要設置陰影的時候,必須設置shadowOpacity的值是在0.0到1.0之間,0.0表示完全透明,1.0表示完全不透明
2.設置陰影的另外三個必不可少的方法shadowColor、shadowOffest、shadowRadius
shadowColor表示設置的陰影的顏色,一個CGColorRef對象
shadowOffest表示設置陰影的方向和距離。是一個CGSize的值,寬度控制陰影的位移,高度控制著縱向的位移。默認是{0,-3}為何為負數,因為在MAC OS 上面原點在左下角。所以在MAC OS上面是在下面顯示的陰影。
shadowRadius控制著陰影的模糊度,當為0的時候,陰影和layer就會有一個明顯的分界線,當值越來越大的時候,就會越來越自然和模糊。

注意:當設置陰影和裁剪的時候,會把陰影的裁剪掉,因為陰影是在layer以外的,而裁剪就是沿著layer邊框裁剪,一般就需要在l試圖層上面在添加一個試圖,使用低試圖的陰影,使用上試圖的裁剪即可
注意:陰影不是根據邊界和圓角路徑來確認陰影形狀的,而是將寄宿圖(包括子試圖等)考慮在內,然后通過這些來完美搭配圖層形狀從而創建一個陰影(也就是根據寄宿圖形狀來顯示陰影,而不是邊界),這也是有多個子圖層時,計算陰影就會很耗資源的原因

6.layer上面的觸摸判斷hitTest來判斷

我們可以根據觸摸點來轉換坐標(convertPoint:fromLayer)和包含當下坐標點(containsPoint:)判斷是否在當前的layer來響應當下的事件
同樣我們可以使用hitTest :來返回觸摸點的layer來判斷,從而響應事件

 -(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
 UITouch *touch = touches.anyObject;
 CGPoint point = [touch locationInView:self.contentView];
/**使用hitTest 來判斷是否觸摸點在那個layer圖層上面 如果超過點超過了最外層layer上面,則會返回nil  測試下即使更改底部layer的zPosition讓底部layer顯示上面后,也是可以觸摸到的,和書上面顯示的有沖突,不知。。。。*/
 CALayer *layer =  [self.contentView.layer hitTest:point];
 if (layer == self.redLayer) {
     NSLog(@"觸摸紅色的layer");
}else if (layer == self.blueLayer){
     NSLog(@"觸摸藍色layer");
}
}

7.shadowPath設置陰影的圖形

如果設置知道圖層的陰影形狀切圖層層級比較多的時候,我們可以使用shadowPath來設置圖層的陰影;

 UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:contentLayer.bounds cornerRadius:50];
 contentLayer.shadowPath = path.CGPath;

8.mask遮蓋來設置圖層顯示的形狀

`mask`可以理解為遮蓋,蒙版,父試圖(就是設置`mask`的圖層)顯示的區域就是`mask`的區域與父圖層區域相較的部分,超過的部分就會被`mask`剪切掉。
'' CALayer *layer = [CALayer layer];
'' UIImage *sex36m = [UIImage imageNamed:@"sex36m"];
'' layer.frame = contentLayer.bounds;
'' layer.contentsGravity = kCAGravityCenter;
'' layer.contents = (__bridge id _Nullable)sex36m.CGImage;
'' contentLayer.mask = layer;

9.minificationFilter(縮小圖片)和magnificationFilter(放大圖片)壓縮和拉伸過濾

layer三種拉伸過濾模式
kCAFilterLinear 采用雙線性濾波算法,通過對多個像素取樣最終生成新的值,得到一個平滑的表現拉伸。當放大的倍數比較大的時候圖片就會模糊不清。
kCAFilterTrilinearkCAFilterLinear非常相似,采用的是三線性濾波算法存儲多個大小情況下的圖片,并三維取樣,同時結合大圖和小圖的存儲進而得到最后的結果。
kCAFilterNearest 取樣最近的單像素點而不管其他顏色,這樣做非??欤膊粫菆D片模糊,但卻是圖片馬賽克更嚴重。如果圖片顏色分界線比較明顯,那么使用此模式比較好。

image.png

10.layerClass

1.返回一個當前試圖寄宿的圖層類型,當我們想更換試圖的寄宿的圖層類型的時候,我們就需要重寫這個方法返回試圖類型。注意當我們創建試圖后,就無法更改它的主寄宿圖層,可以通過layerClass來返回一個試圖的主寄宿圖當然我們也是可以通過添加圖層到試圖上面,那樣我們就浪費了試圖創建的主試圖,同樣子圖層也是無法跟蹤試圖邊界的大小,所以就需要我們手動更新子圖層邊界,所以我們不建議添加子圖層的方法來改變試圖的顯示圖層。

11.2D變換

1.`view`上面的`transform`是一個`CGAffineTransform`類型,且`layer`上面與之對應的屬性為`affineTransform`,而`layer`上面的`transform`屬性是一個`CGTransform3D `類型。`CGAffineTransform`類型是一個2D平面的操作的,也是`CGTransform3D `類型里面沿著Z軸操作。
2.`CGAffineTransformMakeScale(<#CGFloat sx#>, <#CGFloat sy#>)`更改大小的
`CGAffineTransformMakeTranslation(<#CGFloat tx#>, <#CGFloat ty#>)`更改位置
`CGAffineTransformMakeRotation(<#CGFloat angle#>)` 旋轉角度
3.組合操作
`CGAffineTransformIdentity `創建一個空的`transform`
`CGAffineTransformScale(<#CGAffineTransform t#>, <#CGFloat sx#>, <#CGFloat sy#>)` 基于上一個`transform`在添加改變大小操作
`CGAffineTransformTranslate(<#CGAffineTransform t#>, <#CGFloat tx#>, <#CGFloat ty#>)`基于上一個`transform` 在添加位移操作
`CGAffineTransformRotate(<#CGAffineTransform t#>, <#CGFloat angle#>)` 基于上一個`transform`在添加旋轉操作
`CGAffineTransformConcat(<#CGAffineTransform t1#>, <#CGAffineTransform t2#>)`混合兩個已經存在的操作,**放在前面的先執行**
**注意:當使用混合`transfoem`操作動畫的時候,上一個添加的操作會直接影響到下一個`transform`的,特別是縮放和位移直接按的影響,如:一個view 寬高都是100 ,先執行x,y各縮小50%,再X軸位移100,相對于開始位置實際位移多少?(75)**

12.3D變換

1.3D變換比2D變換多了3d立體感的效果。其實也是使用`透視投影`來實現的
設置`透視投影`的值,就是設置m34的值。一般設置為`-1.0/(500~1000)`看著更想3D的時候,就是你想要的值
2.其實3D和2D的用法基本是一致的,再次我就不啰嗦了。但是還是要介紹兩個屬性`sublayerTransform`和`isDoubleSided`
3.`sublayerTransform`當我們想讓父試圖上面多個子試圖都進行單獨的3D變換的時候,那么我就需要設置各個子試圖的`透視投影`,那樣看起來很麻煩,簡單一點,我就可以設置父試圖的`sublayerTransform`屬性來設置統一的`透視投影`,為什么要設置統一的`透視投影`,是為了讓3D效果更逼真,是讓`滅點`統一。
4.`isDoubleSided`來描述圖層的背面是否會被繪制,當不需要繪制背面的圖像的時候,我們就可以設置為`false`來節約GPU的開銷,默認是`true`**注意:其實背面繪制的圖和正面剛好產生的鏡子效應**
**注意,說的是3d變化,其實并不是一個真正的3D,僅僅是平面繪制的,而是改變了大小加上`透視投影`來產生的一個假的3D效果 想看3DDemo的可以看下[]**
3d各個軸旋轉方向

13,CAGradientLayer 顏色漸變的layer

1.colors用來顯示變化顏色的數組,數組的順序就是顏色漸變的吮吸
2.locations:表示每個顏色開始變化的數組。注意:值只能遞增從0-1,切數組的count必須和colorscount一致
3.startPoint:表示開始顏色開始的位置,和endPoint一起來表示顏色漸變的方向。注意:開始的位置和locations設置的值是相對應的

14.CAReplicatorLayer 高效生成多個相似的layer

1.我可能可以使用CAReplicatorLayer來高效生成多個多個相似的圖層。
2.instanceCount:表示圖層重復多少次
3.instanceTransform:來制定圖層是怎么樣的變換,注意:圖層的變換是基于上一個圖層變換后的位置為基點做變換,而不是第一個圖層
4.instanceGreenOffsetinstanceBlueOffset、instanceRedOffset、instanceAlphaOffset四個屬性,來表示顏色和透明度的變化。

15,隱式動畫

1.隱式動畫:改變圖層(不是試圖上面寄宿的圖層)上面動畫屬性所執行的動畫(個人總結)
2,calyer上面更改屬性(可以做動畫的屬性)的值,默認都是可以做動畫(隱式)改變的,動畫的時間默認0.25s
3.這些動畫的類型和時間都是根據當前的事務來決定的。事務實際上是Core Animation用來包含一系列屬性動畫的集合的機制,注意:任何由事務來指定的做動畫的屬性都不會立馬改變,而是只有當事務提交的時候,才可以改變。
4.在每一個runloop周期里面都自動開啟一次新的事務,這也就是你不需要手動開啟事務的原因。任何一次runloop循環中屬性的改變都被集中起來,然后做一個事務時間動畫
5.事務由CATransaction類來便是,begin表示開啟一個事務,commit提交事務。setAnimationDuration設置事務的時間(也就是動畫的時間)setCompletionBlock表示執行完畢動畫之后,再執行的事件(block里面的事件的事務和外面你設置的事務是不一致的,當執行完畢之后,設置的事務以及移除棧,所以block執行的就是你當前runloop里面的事務)注意:如果你直接設置當前runloop里面事務的時間,那么就會更改這個runloop里面所有動畫的時間

16.隱式動畫實現原理

1.當CALyer的屬性被修改的時后,它會調用-actionForKey:傳遞是屬性名稱
2.圖層首先檢測它是否有委托,并且是否是實現CALayerDelegate協議制定的-actionForlayer:forkey方法,如果有直接調用返回結果。
3.如果沒有委托,或者委托沒有實現-actionForLayer:forkey方法,圖層接著檢查包含屬性名稱對應行為映射的actions字典
4.如果actions字典沒有包含對應的屬性,那么圖層接著在它的style字典接著搜索屬性名稱
5.最后如果在style里面也沒找到對應的行為,那么圖層將會直接調用自定義了每個是屬性的標準行為的-defaultActionForKey:方法

補充:大家都知道,直接改變視圖上layer的屬性是不會做隱式動畫的,那是為何?
每一個視圖UIView對它關聯的圖層都扮演了一個委托,并且 提供了-actionLayer:forKey的實現方法。當不在一個動畫塊的實現中,UIView對所有的圖層行為返回nil,但是在動畫block(動畫block)范圍之內,它就會返回一個非空值,這就是為何我們直接作用在試圖上面的layer不會由隱式動畫

17.呈現圖層:

1.當我們操作layer上面動畫時,我們更改的屬性值是立馬改變的,但是給我們顯示出來的卻是動畫改變。其實我們看到的動畫改變就是一個呈現圖層
2.呈現圖層:其實就是圖層的賦值,并且記錄著圖層在屏幕上當下時刻的屬性值,我們可以使用'-presentationLayer'來訪問。
3.眾所周知,iOS屏幕刷新的頻率是一秒60次,當我們執行動畫的之間大于60分之一秒的時候,Core Animation就會在一次舊值和新值之間,對屏幕上的圖層進行重新組織,更改呈現圖層屬性的值。

補充呈現圖層用處:
4.1,一般我們是不會操作或者訪問圖層的呈現圖層的。
4.2如果創建一個定時動畫,這個時候就需要準確知道某一時刻圖層顯示的位置,這個時候我們可以需要獲取呈現圖層上面屬性的位置,來操作動畫了
4.3,當我們使用-hitTest來響應圖層上面的觸摸的時候,我們可以使用呈現圖層來判斷,例如:如果圖層在移動中,來點擊移動的圖層,那么這個時候呈現試圖就會非常有用

fileprivate func wj_testPresentationLayer(_ touches:Set<UITouch>)
    let  touch  = (touches as NSSet).anyObject() as! UITouch;
    let point =  touch.location(in: self.view);
    if((self.animationLayer.presentation()?.hitTest(point)) != nil){
        self.animationLayer.backgroundColor = UIColor.gray.cgColor;
    }else{
        CATransaction.begin();`
        CATransaction.setAnimationDuration(4.0);`
        CATransaction.setCompletionBlock({`
            self.animationLayer.backgroundColor = UIColor.red.cgColor;
        })
        self.animationLayer.position = point;`
        CATransaction.commit();`
    }
}

18視圖上面由多個動畫的時候,如何區分

1.使用-addAnimation:forKey:根據key我們可以區分不同的動畫。如果是相同的動畫操作不同的圖層呢?
2,我們可以給顯示動畫通過**KVC**的方法設置對應的圖層或者專屬值。
3.在代理里面我們就可以直接可以區分圖層或者動畫了

  animation.setValue("七秒", forKey: "name")
 let anme = anim.value(forKey: "name");
 print(anme);

19 關鍵幀動畫(CAKeyframeAnimation)的rotationMode

1.rotationMode設置為kCAAnimationRotateAuto的時候,我們可以讓圖層自動調整沿著path設置的曲線的切線方向

20設置圖層旋轉360°

    `animation.keyPath = "transform.rotation";`
    animation.byValue = (M_PI*2); `
    `transform.rotation` 稱之為虛擬屬性。

21動畫組,也比較簡單

    `CAAnimationGroup`動畫組有個`animations`數組屬性,來添加多個作用在圖層上面的動畫,添加動畫沒有前后順序,一般我們都是根據動畫的`beginTime`來區分前后順序

22 過渡動畫

1.當我們需要更改圖層的底圖或者文本的時候,想動畫更改那別不能動畫的屬性的時候,這是我們就可以使用過渡動畫。
2,過渡動畫使用CATransition來表示,使用typesubType來表示變換效果,注意是CATransition,而不是事務CATransaction
3.type:表示想要使用那種變化效果,subType:表示效果從那個方向過來。
4.startProgress 開始動畫的地方,默認是0
5.endProgress 結束動畫的地方,默認是1.0

type:
kCATransitionFade :淡入淡出
kCATransitionMoveIn:從頂部滑動進來
kCATransitionPush: 一側滑動進來,把舊圖推送出去
kCATransitionReveal:把舊圖層滑動出去,顯示新的
以下為私有API,目前還可以使用:
    cube    立方體
    suckEffect  吸走的效果  
    oglFlip    前后翻轉效
    rippleEffect  波紋效果 
    pageCurl   翻頁起來  
    pageUnCurl   翻頁下來  
    cameraIrisHollowOpen   鏡頭開  
    cameraIrisHollowClose   鏡頭關
  
subType:
kCATransitionFromRight:     從右側劃入
kCATransitionFromLeft:    從左側滑入
kCATransitionFromTop         往頭部方向滑動
kCATransitionFromBottom   往底部方向滑動

注意:過渡動畫設置動畫的key是一個常量transition,而不是你設置的key,在此我們可以使用kvc來區分動畫

切換tabbar加上過渡動畫

func tabBarController(_ tabBarController: UITabBarController, didSelect viewController: UIViewController) {
    let animation = CATransition.init();
    animation.duration = 4.0;
    animation.type = "oglFlip";
    if viewController.isMember(of: ViewController.self) {
        animation.subtype = kCATransitionFromLeft;
    }else{
        animation.subtype = kCATransitionFromRight;
    }
    self.view.layer.add(animation, forKey: nil);
}
tabbar切換動畫

下面是小魚兒推薦應用環節

mac效率軟件Alfred,讓你慢慢丟失鼠標,讓你工作效率翻倍。密碼為xclient.info

image.png

你懂的

傳送門

你的關注和喜歡是七秒前進的動力

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容