The Layer Tree(圖層樹)
一、圖層與視圖的差異:
二、關於視圖層級、圖層樹、呈現樹、渲染樹
因此以上四層。每一層都負責不一樣的任務,其數據流是視圖-》圖層-》呈現樹-》渲染樹。
並且繪製週期60FPS也是會隨着系統的狀態而變化的,若系統資源超載,而會經過下降繪製次數來確保系統能穩定執行。html
三、視圖動畫
視圖動畫是顯式,因爲UIView默認把CALayer的隱式動畫禁止掉了。因此要經過動畫Block的形式才幹取消動畫的限制。(動畫block實際上就是曾經的動畫棧)。
並且視圖能實現的動畫僅僅是圖層所開放的一部分,都是2D動畫,因此視圖動畫的效果會稍稍簡單。
四、圖層動畫
圖層動畫是隱性動畫。僅僅要改動圖層的屬性。其變化都會以默認的漸變形式呈現出來(詳細原理見《隱式動畫》)。並且圖層動畫支持不少其它視圖動畫沒法實現的效果。如:
五、更適宜使用CAlayer呈現內容的場景
因爲UIView與CALayer都可以呈現內容。儘管CALayer不能直接實現事件響應。但開發人員也可以經過hit-test機制的原理來本身實現事件響應,那什麼場景更加適合用CALayer而不是UIView呢?例如如下所看到的:
|
The Backing Image(寄宿層)
一、寄宿層&Contents屬性
CALayer有一個名曰Contents的屬性,這個屬性是與寄宿層相相應了,而contents屬性指向的對象必須爲CGImageRef類型(一個指向CGImage結構的指針),因此寄宿層是用來展現圖片所用的。例如如下狀況會調用寄宿層:
core Graphics可以實現本身定義繪製,但一般不建議那麼作,因爲core Graphics繪製會默認生成一個繪製專用的存儲空間,而這空間有十多M那麼多。會佔用大量內存,因此Apple不建議實現空的core Graphics更不建議在core Graphics實現不屬於該方法的代碼。
二、contentGravity屬性
與UIView的contentMode屬性相相應,用於調整圖片的佈局,支持一下常量值:
三、contentsScale
contentsScale屬性定義了寄宿層的像素尺寸和視圖大小比例,默認是1.0(一個點一個像素),在retina屏幕得設置在2.0(一個點兩個像素)。在plus設備上得設備爲3.0(一個點3個像素)。
但若contentGravity設置瞭如kCAGravityResizeAspectFill本身主動適配大小的屬性後,contentsScale會不起做用。因此不能依靠contentsScale來作縮放的操做,縮放仍是得交給transform或者affineTransform。
三、maskToBounds
該屬性與UIView的clipsToBounds屬性相相應。都是應用於將超出圖層邊界的子圖層的內容進行裁剪。
這裏需要一點需要注意的,但咱們實現radiusCorner時,實際上就是經過設置背景顏色來實現的。而與maskToBounds結合纔是真正把邊角內容裁剪掉,但radiusCorner+maskToBounds結合使用會引起離屏渲染。因此在radiusCorner知足要求。就不要再調用maskToBounds。
四、contentsRect
CALayer的contentsRect贊成咱們在圖層邊框內顯示寄宿圖的一個子域,而contentsRect是一個相對值屬性,如{0,0。1,1}表示整個寄宿圖,這樣的座標系統也叫單位。例如如下簡述iOS下的三種座標系統單位:
contentsRect有一種經典的使用方法——image sprites(圖片拼接,常用於遊戲),這種使用方法就是用來實現一次性載入多張圖片的。因爲每一張圖片的使用前都要通過,載入——》壓縮——》呈現,的一個過程中載入和壓縮是十分耗時的,載入消耗IO,壓縮算法的複雜會添加CPU的消耗,因此想遊戲這種App需要載入解壓大量的圖片時,可以把所有圖片整合成一張圖片來載入解壓,而後經過contentsRect裁剪出子圖,並顯示,可以優化載入解壓的過程。
五、contentsCenter
contentsCenter的名字事實上有一點的誤導性,事實上際上與UIView的Stretching屬性相相應。用來實現寄宿層的縮放時的呈現效果。也是一個相對值單位。
六、customs drwaing
除了經過CGImage設置到contents的方式來實現寄宿層。還可以經過Core Graphics來直接繪製寄宿層,但這樣的方式十分消耗內存不建議使用。
CALayer有一個可選的Delegate屬性。實現了CALayerDelegate,但CALayer需要又一次繪製,則調用它的displayLayer方法(與UIView的setNeedDispaly相相應),就會調用其代理方法drawLayer:inContext(與UIView的drawRect相相應)。
若在視圖層中,僅僅要用戶實現了drawRect那麼僅僅要屏幕需要又一次繪製那麼該方法都會被默認調用。git |
Layer Geometry(圖層幾何學)
一、佈局
UIView的三個重要佈局屬性:frame、bounds、center
CALayer的三個重要的佈局屬性:frame、bounds、position
可以看出UIView與CALayer的佈局屬性是一一相應的,而惟一有啥區別的就是錨點的命名,UIView爲center,而CALayer爲position,但錨點最開始都是默以爲圖形的中心。
當中frame實際上就是一個虛擬屬性,其由bounds與center/position計算所得。因此改動bounds與center/position也會影響到frame的數值。
還有無論是視圖仍是圖層。在屏幕上無論怎麼變形,本質上其仍是一個矩形。但在旋轉的狀況下。frame的數值是會事實變更的,例如如下圖:
![]() ![]() ![]() ![]()
二、錨點
錨點屬性是用來指定圖層相對於父圖層的位置。也可以理解爲利用圖層的句柄。默以爲圖層的中心但也可以改動。在圖層中改動了錨點會產生移動(隱性動畫),相同的錨點也是一個相對值。
例如如下。爲錨點的實驗效果圖:
圖一:時分秒針直接以圖層的中點爲錨點,因爲旋轉時也以其爲中心,效果例如如下:
![]() ![]()
圖二:將錨點改動成指針的尾端
![]() ![]()
圖三:改動錨點後的旋轉效果。
![]() ![]()
三、座標系
因爲每個圖層都有本身的座標系,因此CALayer也提供了一系列的方法用於一個點在不一樣的座標系之間轉換,這些方法特別適合於子圖層與父圖層之間的座標轉換。
![]()
事實上UIView與CALayer都有zPosition。但由於UIView只支持2D變換,因此UIView的zPosition只適合於用來做圖層的調整,但更加建議用UIView提供的視圖數組管理方法來調整視圖的繪製順序來調整。而CALayer的zPosition是一個重要的三維座標系。
四、Hit Testing
Hit Testing是iOS中一個十分重要的機制,用於檢索點擊了的目標圖標,與響應鏈條相互搭配的話。就會將終於目標圖標設置爲第一響應者。Hit Testing提供了兩個核心的方法:
五、本身主動佈局
對於UIView。經過UIView暴露出來的UIViewAutoresizingMask和NSLayoutConstraint來實現本身主動佈局。
但對於CALayer,則需要手動來操做實現,當中最爲簡單的方法就是實現CALayerDelegate的例如如下函數:
- (void)layoutSublayersOfLayer:(CALayer *)layer;
在該方法內依據當前layer的屬性來計算調整來實現本身主動佈局。
因爲CALayer實現本身主動佈局不方便,因此這也是爲何更加建議使用UIView來實現界面的構建。
|
Visual Effects(視覺效果)——本節探討能夠經過CALayer實現的附加視覺效果
一、圓角
CALayer有一個叫作cornerRadius的屬性控制圖層角的曲率,這個曲率僅僅影響背景顏色而不影響圖片或者子圖片。圓角的計算原理就是一個以CALayer的中點,曲率所設的半徑的原與矩形的交集就是目標圖形。
另外一個名曰maskToBounds屬性,就是實現將圖層裏面邊界外的圖形全部分割丟棄。
因此經過cornerRadius+maskToBounds組合可以實現圓角,但同一時候會引起離屏渲染,如果小量的離屏渲染還好,但如滾動欄的場景,開會有大量的離屏渲染的任務產生,就會嚴重影響性能,這一點也是注意優化的。詳細效果例如如下圖所看到的:
![]() ![]()
二、圖層邊框
經過設置CALayer的borderWidth與borderColor兩個屬性可以改動邊框的效果。
效果例如如下:算法 ![]() ![]()
三、陰影
陰影是一種能達到圖層深度暗示效果的裝飾。
CALayer提供下面參數來定製陰影的效果:數據庫
shadowRadius的設置效果圖例如如下:
![]() ![]()
有一點需要注意的,就是陰影的繪製是屬於離屏繪製。是比較效果資源的。因此必定要下降同一屏幕進行大量陰影繪製的狀況。
四、陰影裁剪
圖層的陰影不少其它是繼承於內容的外形,而不是依據邊界和角半徑來肯定。爲了計算出陰影的形狀。core animation會將寄宿層也考慮在內。
但若直接maskToBounds來裁剪,會把陰影效果也裁減掉。編程
爲了解決以上問題。可以經過專門引入一個陰影圖層來解決,詳細效果例如如下:
圖一:直接maskToBounds裁剪會把陰影效果一併裁剪掉。
![]() ![]()
圖二:經過加入一個另外的陰影圖層在如下,然它來實現陰影。而詳細的內容圖層就直接maskToBounds裁剪。
![]() ![]()
圖三:終於既能實現裁剪,又能實現陰影。
![]() ![]()
PS:無論是陰影仍是圓角裁剪都會引起離屏渲染,大量的該類型的圖形需要渲染的話。會減低幀率。
五、shadowPath 屬性
陰影並必定是方形的。咱們也可以經過shadowPath來定製本身想要的陰影形狀。詳細效果例如如下所看到的:
![]() ![]()
六、圖層蒙版
經過masksToBounds屬性。咱們可以實現邊界裁剪,但咱們還需要更加靈活的裁剪需求的時候就可以經過圖層蒙版來實現。CALayer提供一個mask的屬性來解決一個問題。而mask原本就是指向還有一個圖層的指針。其詳細原理例如如下圖所看到的:
![]() ![]() ![]() ![]()
從上圖效果可以知道mask舒心僅僅關心形狀的交集,而顏色仍是由原來的圖形的顏色決定。
另外一點需要注意的是圖層蒙版也會引起離屏渲染,會帶來性能問題,因此使用的時候也得多加註意。
七、拉伸過濾
當咱們視圖顯示一個圖片的時候。都應該以正確的比例,正確的1:1像素顯示在屏幕上。緣由例如如下:
但有時候咱們就需要縮略圖。所專門另外存儲縮略圖這對內存來講。
這時就可以經過CALayer的minificationFilter(縮小濾波器)和magnificationFilter(方法濾波器)屬性實現圖形的縮放算法。當中提供下面三種默認的縮放算法:數組
對於大圖的縮放,採用kCAFilterLinear、kCAFilterTrilinear,效果會比較好。
對於小圖、較少斜線的圖的縮放,。採用kCAFilterNearest效果比較好。
圖一:採用kCAFilterLinear的方法濾波器算法的效果。
![]() ![]()
圖二:採用kCAFilterNearest的放大濾波器算法的效果:
![]() ![]()
八、透明組
UIView有一個alpha屬性類肯定視圖的透明度。而CALayer有一個與之相應的屬性——opacity。這兩個屬性都會影響到子圖層。例如如下圖所看到的:
當咱們將view2的alpha數值設置爲0.5,這時候,對於label所處的點的顏色計算公式爲:50%label + 25%view + 25%background,因此就是灰色偏白的顏色。
若想整個圖層的色調保持一致,可以經過將shouldRasterize屬性設置爲YES,那麼圖層及其子圖層將被整合成一個整體的圖片。效果例如如下:
圖一:shouldRasterize屬性設置爲默認值NO的效果。
![]() ![]()
圖二:shouldRasterize屬性設置爲默認值YES的效果。
![]() ![]() |
Transforms(變換)
一、仿射變換
UIView相應的transfrom屬性是一個CGAffineTransfrom類型。用於實現二維空間旋轉、平移、縮放、斜切。
而CALayer的transform屬性是一個CATransforms3D。能應用3維空間進行旋轉、平移、縮放、斜切等效果。
當中,仿射變化的數學原理例如如下:
![]() ![]()
只是iOS爲了運算的方便。已經提供了三個標準方法分別用於實現旋轉、縮放。平移,而斜切得靠原生運算來實現。
![]() ![]()
當中有一點需要注意的,就是旋轉的angle是以弧度製爲單位的。
iOS系統提供下面方法來實現混合變換,能同一下面的組合函數實現複雜的變形模式:
圖一:建立一個空的變換結構體。
![]()
圖二:不斷疊加變換結構體
![]() ![]()
圖三:直接將兩個變換結構體合併
![]() ![]()
剪切變換的效果與事實上現代碼:
![]() ![]() ![]() ![]()
二、3D變化
3D變化與2D變化是十分類似的,不一樣的僅僅是3D變換是3個維度的,加入了Z軸。其數學原理例如如下:
![]() ![]()
這樣的矩陣運算仍是挺麻煩的。因此iOS系統提供給咱們便捷的方法:
![]() ![]()
相同也會有變換疊加的方法,當中旋轉的時候必定要注意當前的旋轉的參考軸,順時針旋轉爲正值,逆時針旋轉爲負值。
![]() ![]()
三、透視投影
在真實的世界中,當物體遠離咱們。由於視角的緣由,其會變小。因此爲了讓3D效果更佳真實,需要引入投影變換,儘管core animation並無提供給咱們,但十分簡單,其數學實現例如如下:
![]() ![]()
僅僅要將m34 = -1/d, d= 500~1000,(d越小越失真,d越大透視效果越弱)。
圖3.1:沒實現透視投影的旋轉效果。
![]() ![]()
圖3.2:實現了透視投影的旋轉效果。
![]() ![]()
四、滅點
在人的視覺中。當物體離人越遠則會變得越小,當接近無窮遠的時候就會匯聚成一個不可見的點,這個點就叫滅點。現實生活中,這個點一般就是中心點,因此在程序中咱們也要讓圖形的滅點集中在屏幕的中點,例如如下所看到的:
![]() ![]()
而實現方式則是。首先加入圖形的時候先以屏幕中點爲錨點加入,而後經過transfrom來說圖層移動到恰當的位置。
五、sublayerTransform
sublayerTransform是一種將所有對應的配置統一設置到該圖層的所有子圖層的便捷屬性。
六、3D座標下的圖層背面
在3D座標下,圖層的背面也是會繪製的,而且是正面的鏡像。
咱們可以經過CALayer的doubleSided屬性來決定是否運行雙面繪製。
七、扁平化圖層
假設父圖層的座標發生變換,那麼子圖層也會隨着父圖層而進行相同的變換,如7.1圖所看到的。但有時咱們僅僅想變換父圖層並不想變換子圖層。這時就得作相對變換來抵消掉父圖層的變換。讓視覺效果看起來子圖層是精巧的。例如如下圖所看到的:
圖7.1:2D相對旋轉理論圖
![]() ![]()
圖7.2:2D相對旋轉效果圖
![]() ![]()
圖7.3:3D相對旋轉理論圖
![]() ![]()
圖7.4:3D相對旋轉實際效果圖
![]() ![]()
咱們可以看出,在2D座標系下。相對變換是合理的。但在3D座標系下,相對變換的結果並不如預想的同樣,爲何呢?
這是因爲core Animation圖層儘管存在於3D空間以內,但並不是每一個圖層都存在於同一個3D空間之間。
而用戶是以當前window所在的3D座標系爲參考的,這時7.4圖的3D座標系與window的已經不重合的,因此在咱們的視覺裏面,就是看不到預想的效果,這就是圖層扁平化。緩存
圖層扁平化讓core animation建立複雜的3D場景變得十分困難,因爲很是難使用圖層樹去建立一個3D結構的層級關係——將所有的場景下的3D都保持一樣的3D座標系。
只是CALayer提供一個專用圖層——CATransformLayer來解決問題,所有加入到該圖層內的3D圖形都共享一個標準座標系。
八、3D場景下的光亮和陰影
略。臨時不懂。
九、3D場景下的點擊事件
在處理點擊事件,必定要注意點擊響應是由UIView中的圖層結構來決定的,而不是CALayer在屏幕上的呈現效果,因此得注意事件攔截的順序。
可以經過設置userInteractionEnabled以及簡單的視圖覆蓋來實現點擊事件正確的傳遞。
|
Specialized Layers(專有圖層)
一、CAShapeLayer
二、CATextLayer
三、CATransformLayer
四、CAGradientLayer
五、CAReplicatorLayer
六、CAScrollLayer
七、CATiledLayer
八、CAEmitterLayer
九、CAEAGLlayer
十、AVPlayerLayer
|
Implicit Animations(隱式動畫)
一、隱式動畫&事務
Core Animation基於一個若是,就是屏幕上的所有圖形都可以作動畫,而且這樣的動畫不需要開發人員去設置,會本身主動實現。這也是爲何直接改動CALayer的屬性會以一種漸變的形式來更新到一個新的值。
這樣的僅僅需要改動圖層的數值就會引起動畫的形式來更新到新數值的動畫稱爲隱式動畫。
而這樣的定義了動畫的詳細呈現效果的機制,就稱爲事務。
由於CALayer是默認開啓了隱式動畫的。而若咱們在CALayer上調用顯式動畫的話,就考慮到隱式動畫對效果的影響,進而決定是否需要取消隱式動畫。
二、動畫Block
UIView封裝了CALayer。但UIView禁止了隱性動畫,因此開發人員需要在UIView上實現顯性動畫。
在iOS4.0以前,UIView採用動畫棧來管理顯示。後來提供了動畫Block這個更加便捷的方式。但原理都是同樣的。性能優化
三、圖層行爲
咱們把改變CALayer屬性時本身主動應用的動態稱爲行爲。一組行爲的運行步驟例如如下所看到的:
通過以上的整個流程,要麼actionForKey返回nil則無動畫效果,要麼就是返回CAAction,而後CALayer利用它實現動畫。
所以可以推進出UIView是怎樣禁止隱式動畫的,就是講CALayer的delegate設置爲自身。而後在actionForLayer:forKey方法中返回nil。
四、呈現與模型
在iOS中。屏幕每秒鐘又一次刷新屏幕60次。
因此Core Animaiton就需要在設置一次新值和新值生效之間,對屏幕上的圖層進行又一次組織。微信
正是覺得上面的機制的存在。因此纔會有iOS纔會有圖層以及呈現層之間的關係,圖層更像是model,而呈現層就是view,Core Animation就是Controller,因此在一個繪製週期內,圖層負責收集與保存用戶對屬性的改動,到繪製時刻時。就將圖層的數據覆蓋到呈現樹。
咱們可以經過CALayer的 -presentationLayer方法來獲取正在屏幕上顯示的呈現樹的數據,儘管通常不需要。但在下面狀況下會比較有做用
|
Explicit Animations(顯式動畫)
一、Core Animation的類圖架構
![]() ![]()
二、基礎屬性動畫
基礎動畫主要由CABasicAnimation實現,由上圖可知道。而CABasicAnimation繼承於CAPropertyAnimation屬性動畫,經過設置下面三個數值以及其它的選項,就能夠實現本身主動動畫:
當中toValue與byValue不能同一時候使用,toVaule是絕對值,byValue是相對值。
屬性動畫都是針對關鍵幀的動畫。也就是說僅僅關心出發點與結束點,中間的過渡可以本身生成也可以默認。網絡
三、關鍵幀動畫
CAKeyFrameAnimation用於實現關鍵幀動畫,能經過設置其animations數組來定義每個關鍵幀的位置,也可以直接經過path屬性來定義運動的路徑。
四、affineTransform屬性
若咱們讓一個圖形沿着曲線運動。可以經過設置affineTransform。讓其能沿着曲線的切線運動從而讓運動更加真實。先後效果圖例如如下圖所看到的:
![]() ![]() ![]() ![]()
五、虛擬屬性
屬性動畫其實是針對關鍵路徑而不是一個鍵的。這就意味着可以對子屬性甚至虛擬屬性作動畫。
如若咱們想實現旋轉的效果,原本咱們需要在keyPath上設置「transform」,
若使用虛擬屬性。可以直接在keyPath上直接設置「transfrom.rotation」,這樣在設置formValue與toValue時就可以直接設置弧度叫的值。
事實上transfrom.rotation這個屬性是並不存在的,而是core aniamiton本身主動依據CAValueFunction來又一次計算transform的數值所得,正因爲如此才稱之爲虛擬屬性。
六、動畫組
CAAnimationGroup是一種組合動畫的解決方式。經過CAAnimationGroup加入沿曲線運動與顏色變化的動畫。
![]() ![]()
七、過渡
略。
八、在動畫的過程當中取消動畫
Core Animation提供了一部分的方法來實現動畫加入以及移除。例如如下圖所看到的:
- (CAAnimation *)animationForKey:(NSString *)key; //實現動畫的加入,當中key不只可以用來訪問動畫,還可以用來移除動畫
- (void)removeAnimationForKey:(NSString *)key; //移除Key指定的動畫
- (void)removeAllAnimations; //移動CALayer已經加入的所有動畫
|
Layer Time(圖層時間)
一、CAMediaTiming協議
CAMediaTiming協議定義了在一段動畫內控制逝去時間的屬性集合。
CALayer和CAAnimaition都實現了該協議,因此時間可以被隨意圖層或者一段動畫的類所控制。
CAMediaTiming協議下定義的一些核心屬性:
可以經過將repeatCount或者repeatDuration設置爲INFINITY來實現動畫無限循環播放。但不能同一時候使用這兩個屬性。
二、相對時間
在Core Animation中,時間是相對的,每個動畫都有本身的描寫敘述時間,可以獨立的加速、延遲或者偏移,當中有下面關鍵屬性:
三、fillMode
當一個圖層產生動畫,實際上就是呈現層在運行動畫。那麼。當呈現層運行完動畫是否要講當前屬性的值覆蓋會圖層。這樣的行爲就稱爲fill mode,這個由開發人員決定:
四、全局時間與本地時間
對
CALayer
或者
CAGroupAnimation
調整
duration
和
repeatCount
/
repeatDuration
屬性並不會影響到子動畫。但是
beginTime
。
timeOffset
和
speed
屬性將會影響到子動畫。
每個
CALayer
和
CAAnimation
實例都有本身
本地
時間的概念。是依據父圖層/動畫層級關係中的
beginTime
。
timeOffset
和
speed
屬性計算。
CoreAnimation有一個
全局時間
的概念。也就是所謂的
馬赫時間
(「馬赫」其實是iOS和Mac OS系統內核的命名)。
用
CACurrentMediaTime
函數來訪問馬赫時間:
CFTimeInterval time = CACurrentMediaTime();
該值返回的是一個相對值,與現實的時間無關。但可以經過它來比對不一樣動畫之間的時間差,iOS提供下面方法來進行不一樣圖層之間本地時間的轉化:
- (CFTimeInterval)convertTime:(CFTimeInterval)t fromLayer:(CALayer *)l;
- (CFTimeInterval)convertTime:(CFTimeInterval)t toLayer:(CALayer *)l;
經過以上知識可以實現同步不一樣圖層的speed,timeOffset、beginTime的動畫。
五、暫停、倒回和快進的實現
設置動畫的
speed
屬性爲0可以暫停動畫。但不能再加入後再改動。不然會報錯。
但咱們可以經過下面的設置來實現一些調試:
self.window.layer.speed = 100;
六、手動動畫
將speed設置爲0讓動畫中止播放。而後改動timeOffset來切換不時間的動畫序列。從而實現手動切花動畫的效果。
|
Easing(緩衝)——讓動畫更加平滑天然
一、CALayer、Animation的緩衝動畫與CAMediaTimingFucntion
首先需要設置
CAAnimation
的
timingFunction
屬性,是
CAMediaTimingFunction
類的一個對象。假設想改變隱式動畫的計時函數,相同也可以使用
CATransaction
的
+setAnimationTimingFunction:
方法。來實現緩衝函數。當中有下面默認的緩衝函數:
kCAMediaTimingFunctionLinear //線性
kCAMediaTimingFunctionEaseIn //緩進
kCAMediaTimingFunctionEaseOut //緩出
kCAMediaTimingFunctionEaseInEaseOut //
緩進
緩出
kCAMediaTimingFunctionDefault //默認的效果與
kCAMediaTimingFunctionEaseInEaseOut相似,但效果更佳不明顯
二、UIView的緩衝動畫
經過設置UIView的options參數加入例如如下常量也可以實現緩衝動畫的效果:
UIViewAnimationOptionCurveEaseInOut
三、緩衝和關鍵幀動畫
CAKeyframeAnimation
有一個
NSArray
類型的
timingFunctions
屬性。咱們可以用它來對每次動畫的步驟指定不一樣的計時函數。但是指定函數的個數必定要等於
keyframes
數組的元素個數
減一
,因爲它是描寫敘述每一幀之間動畫速度的函數。
四、本身定義緩衝函數
臨時不討論。
|
Timer-Based Animation(基於定時器的動畫)——咱們可以經過事務來實現動畫。設置關鍵幀,讓中間的過渡本身主動計算得出。也可以基於定時器來設置每一個繪製週期的變換來實現動畫。
iOS提供一下兩種形式來實現基於定時器的動畫:
但無論是NSTimer仍是CADispaly本質上都是基於runloop,NSTImer把每個時間觸發的事件註冊到runloop裏面。以待制定。
而CADisplay則把任務直接嵌套進繪製操做以前。 |
Tuning for Speed(性能調優)
一、CPU & GPU
CPU(中央處理器)和GPU(圖形處理器)都是能用來處理。
總的來講。咱們可以用軟件(使用CPU)作不論什麼事情,但是對於圖像處理,通常用硬件會更快,因爲GPU使用圖像對高度並行浮點運算作了優化。
因爲某些緣由,咱們想盡量把屏幕渲染的工做交給硬件去處理。問題在於GPU並無無限制處理性能。而且一旦資源用完的話,性能就會開始降低了(即便CPU並無全然佔用)。
性能優化的本質就會合理地利用CPU與GPU,使他們不會超出負荷。
Core Animation處於iOS的核心地位,無論是應用內仍是應用外都會用到它。因此iOS特別設計了一個進程來運行渲染相關的任務,也叫渲染服務。渲染服務管理動畫和屏幕上組合的圖層。
當執行一段動畫的時候。這個過程會被切分爲六個階段,包含應用內的四個階段,與應用外的2個階段。當中應用內的階段例如如下:
當數據被打包到渲染服務進程。會將其反序列化造成還有一個渲染樹。
使用這個渲染樹對動畫的每一幀作出例如如下工做:
減小GPU圖層繪製的部分場景:
減小CPU圖層繪製的部分場景:
二、Instruments實現App性能優化,操做順序一般例如如下:
|
Efficient Drawing(高效畫圖)
一、軟件繪製
軟件畫圖意爲不借助GPU的圖形繪製。在iOS中一般就是由Core Graphics來實現。但其對照Core Animation和OpenGL,Core Graphics要慢很多,而且也十分消耗內存。
軟件畫圖不只效率低,還會消耗可觀的內存。
CALayer
僅僅需要一些與本身相關的內存:僅僅有它的寄宿圖會消耗必定的內存空間。即便直接賦給
contents
屬性一張圖片,也不需要添加額外的照片存儲大小。假設一樣的一張圖片被多個圖層做爲
contents
屬性,那麼他們將會共用同一塊內存,而不是複製內存塊。
但是一旦你實現了
CALayerDelegate
協議中的
-drawLayer:inContext:
方法或者
UIView
中的
-drawRect:
方法(事實上就是前者的包裝方法),圖層就建立了一個繪製上下文,這個上下文需要的大小的內存可從這個算式得出:圖層寬*圖層高*4字節。寬高的單位均爲像素。對於一個在Retina iPad上的全屏圖層來講。這個內存量就是 2048*1526*4字節,至關於12MB內存,圖層每次重繪的時候都需要又一次抹掉內存而後又一次分配。
軟件畫圖的代價昂貴,除非絕對必要。你應該避免重繪你的視圖。
提升繪製性能的祕訣就在於儘可能避免去繪製。
二、矢量圖形
咱們用Core Graphics來畫圖的一個一般緣由就是僅僅是用圖片或是圖層效果不能輕易地繪製出矢量圖形。矢量畫圖包括一下這些:
文章中實現了例如如下效果的demo:
![]() ![]()
事實上現思路就是每當有touch event發生,則向UIBezierPath中加入一條直線。但在對於屏幕中每當有一個touch event事件發生,意味着整個屏幕都要又一次繪製一遍,當需要又一次繪製的內容愈來愈多,則會引發幀率的降低。
這時候咱們可以經過髒矩陣來對這個問題進行優化。
三、髒矩形
髒矩形是一個能制定又一次繪製區域的機制。
在程序中則是經過用setNeedDispalyInRect來替代setNeedDispaly方法來實現繪製詳細的區域,詳細實現見文章。
四、異步繪製
UIKit的單線程天性意味着寄宿圖一般要在主線程上更新,這意味着繪製會打斷用戶交互,甚至讓整個app看起來處於無響應狀態。咱們對此無能爲力,但是假設能避免用戶等待繪製完畢就好多了。
針對這個問題,core Animation提供了一下兩種方案來實現異步繪製,提升界面的響應效率:
|
Image IO(圖像IO)——研究怎樣優化從閃存或者網絡中載入和顯示圖片
一、載入與潛伏
畫圖實際消耗的時間一般並不是影響性能的因素,而且若把圖片直接存儲在內存,會損耗大量的資源,
因此需要在應用執行的時候週期性地載入和卸載圖片。
圖片文件的載入速度同一時候受到CPU及IO(輸入/輸出)延遲的影響。
iOS設備中的閃存已經比傳統硬盤快很是多了。但仍然比RAM慢將近200倍左右,這就需要慎重地管理載入。以免延遲。
有時候圖片也需要從遠程網絡鏈接中下載,這將會比從磁盤載入要消耗不少其它的時間,甚至可能由於鏈接問題而載入失敗(在幾秒鐘嘗試以後)。你不能在主線程中載入網絡,並在屏幕凍結期間指望用戶去等待它。因此需要後臺線程。
二、異步線程載入
由於圖片的載入是十分耗時間的,若把該操做放置在主線程中運行會減小CPU的利用率甚至拖慢幀率。可以經過GCD或者NSOperationQueue來實現異步載入最後再在主線程中同步發起渲染就能夠。
三、延遲解壓
一旦圖片文件被載入使用。就必須要通過解碼(解壓)的過程,解碼過程是一個至關複雜的任務。耗時長也佔大量的內存。
對於PNG:載入相對長。文件相對更大,但解碼比較快。
對於JPEG:載入快,圖片小,但解碼算法複雜耗時長。
由於iOS系統會讓載入完畢的圖片不會立刻解壓,而是到需要用的時刻才正式解壓。因此會影響性能。
四、使用CATiledLayer實現異步載入和顯示大型圖片
略。
五、分辨率交換
略。
六、使用imageNamed實現緩存
imageName方法是能避免延遲載入,而且該方法在載入後會立刻解壓,但僅僅相應用資源束有效。因此網絡圖片無效。
以前咱們提到使用
[UIImage imageNamed:]
載入圖片有個優勢在於可以立馬解壓圖片而不用等到繪製的時候。但是
[UIImage imageNamed:]
方法有還有一個很顯著的優勢:它在內存中本身主動緩存瞭解壓後的圖片。即便你本身沒有保留對它的不論什麼引用。因此也要注意不能用於載入大圖片,否則會佔用大量的內存資源。
七、本身定義緩存
八、NSCache
NSCache和NSDictionary相似。都是直接經過鍵值進行訪問,但不一樣的是,NSCache所持有的對象在內存不足的時候。會本身主動將其釋放。
九、文件格式與載入性能
略。
|
Layer Performance(圖層性能)
一、隱形繪製
二、文本
CATextLayer與UILable都是直接將文本繪製在圖層的寄宿層內,因此要避免頻繁的修改,若該文本需要頻繁修改,可以先將其放在一個子圖層上,經過contentMode來等比例縮放寄宿層。
三、光柵化
咱們提到了
CALayer
的
shouldRasterize
屬性(光柵化)。它可以解決重疊透明圖層的混合失靈問題。
啓用
shouldRasterize
屬性會將圖層繪製到一個屏幕以外的圖像。而後這個圖像將會被緩存起來並繪製到實際圖層的
contents
和子圖層。假設有很是多的子圖層或者有複雜的效果應用,這樣作就會比重繪所有事務的所有幀划得來得多。 但是光柵化原始圖像需要時間。而且還會消耗額外的內存。
當咱們使用得當時,光柵化可以提供很是大的性能優點(如你在第12章所見),但是必定要避免做用在內容不斷變更的圖層上,不然它緩存方面的優勢就會消失。而且會讓性能變的更糟。
爲了檢測你是否正確地使用了光柵化方式,用Instrument查看一下Color Hits Green和Misses Red項目。是否已光柵化圖像被頻繁地刷新(這樣就說明圖層並不是光柵化的好選擇,或則你無心間觸發了沒必要要的改變致使了重繪行爲)。
總結:會佔用較多內存,要避免反覆繪製。因此一旦應用要儘可能下降又一次繪製,而多利用快照緩存。
四、離屏渲染
屏幕外渲染和咱們啓用光柵化時類似。除了它並無像光柵化圖層那麼消耗大。子圖層並無被影響到,而且結果也沒有被緩存,因此不會有長期的內存佔用。但是,假設太多圖層在屏幕外渲染依舊會影響到性能
對於那些需要動畫而且要在屏幕外渲染的圖層來講。你可以用
CAShapeLayer
,
contentsCenter
或者
shadowPath
來得到相同的表現而且較少地影響到性能。
五、混合和過分繪製
開發人員應該儘可能下降重疊圖層的反覆渲染,因爲對於用戶看來某些遮擋的圖層的內容是可有可無的,但渲染就會消耗資源。因此無論不論什麼場景都建議例如如下操做:
固然光柵化是是詳細場景二選擇使用,否則也會引入別的性能問題,如佔用大量內存。
六、下降圖層數量
初始化圖層,處理圖層,打包經過IPC發給渲染引擎,轉化成OpenGL幾何圖形,這些是一個圖層的大體資源開銷。其實,一次性能夠在屏幕上顯示的最大圖層數量也是有限的。
確切的限制數量取決於iOS設備。圖層類型。圖層內容和屬性等。
但是總得說來可以容納上百或上千個,如下咱們將演示即便圖層自己並無作什麼也會遇到的性能問題。
七、對象回收
處理巨大數量的類似視圖或圖層時另外一個技巧就是回收他們。對象回收在iOS頗爲常見。
UITableView
和
UICollectionView
都實用到,
MKMapView
中的動畫pin碼也實用到,還有其它很是多樣例。
|