[譯] Core Animation 編程指南 - 高級動畫技巧

本文首發地址html

有不少方法去配置基於屬性或者關鍵幀的動畫,來爲你作更多的事情。Apps 須要同時或順序執行多個動畫可使用高級行爲去同步多個動畫的時間或將它們串在一塊兒。你可使用動畫對象的其餘類型去建立視覺形變和其它有意思的動畫效果。git

過渡動畫支持更改圖層可見性

如上述標題所示,過渡動畫對象會爲圖層建立視覺過渡動畫效果。過渡對象最多見的用法就是動畫的顯示一個圖層,隱藏另外一個圖層。不像基於屬性的動畫,動畫圖層的某個屬性,過渡動畫操做圖層的緩存圖像去建立視覺效果,這個僅經過改變屬性是很難或者不可能實現的。過渡的標準類型容許你執行顯示、推進、移動或交叉漸變更畫。在 OS X上,你也可使用 Core Image 過濾器去建立過渡動畫,可實現如擦拭、頁面捲曲、波紋或自定義效果。github

執行過渡動畫,你須要建立一個 CATransition 對象,並將它添加到涉及過渡動畫的圖層上。你使用過渡對象去指定須要執行的過渡類型,即動畫的起點和終點。你也不須要整個過渡動畫。在動畫期間,過渡對象容許你指定開始和結束的過程值。這些值容許你在動畫的中點開始或者結束動畫。編程

例 5-1 展現了實現兩個視圖之間建立一個推進過渡動畫的代碼。在該例中,myView1myView2 在同一父視圖的同一個位置,但僅 myView1 可見。推進過渡動畫會致使 myView1 向左邊滑動消失,同時 myView2 從右向左滑動顯示。更新兩個視圖的 hidden 屬性確保在動畫結束時兩個視圖的可見性是正確的。緩存

例 5-1 在 iOS 上給兩個視圖添加過渡動畫bash

CATransition* transition = [CATransition animation];
transition.startProgress = 0;
transition.endProgress = 1.0;
transition.type = kCATransitionPush;
transition.subtype = kCATransitionFromRight;
transition.duration = 1.0;
 
// 給兩個圖層添加過渡動畫
[myView1.layer addAnimation:transition forKey:@"transition"];
[myView2.layer addAnimation:transition forKey:@"transition"];
 
// 最後,改變圖層的可見性
myView1.hidden = YES;
myView2.hidden = NO;

// Swift 
let transition = CATransition()
transition.startProgress = 0.0
transition.endProgress = 1.0
transition.type = .push
transition.subtype = .fromRight
transition.duration = 3.0

myView1.layer.add(transition, forKey: "transition")
myView2.layer.add(transition, forKey: "transition")

myView1.isHidden = true
myView2.isHidden = false
複製代碼

下方爲譯者補全的 例 5-1 測試代碼:app

class ViewController: UIViewController {
    var myView1: UIView!
    var myView2: UIView!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        setupSubviews()
    }
    
    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        let transition = CATransition()
        transition.startProgress = 0.0
        transition.endProgress = 1.0
        transition.type = .push
        transition.subtype = .fromRight
        transition.duration = 3.0

        myView1.layer.add(transition, forKey: "transition")
        myView2.layer.add(transition, forKey: "transition")

        myView1.isHidden = true
        myView2.isHidden = false
    }
}

extension ViewController {
    func setupSubviews() {
        myView2 = UIView(frame: CGRect(x: 100, y: 100, width: 50, height: 50))
        myView2.backgroundColor = UIColor.blue
        view.addSubview(myView2)
        
        myView1 = UIView(frame: CGRect(x: 100, y: 100, width: 50, height: 50))
        myView1.backgroundColor = UIColor.red
        view.addSubview(myView1)
    }
}
複製代碼

當一個過渡包含兩個圖層時,你能夠對兩個圖層使用同一個過渡對象。使用同一個過渡對象能夠簡化你的代碼。可是,若是每一個圖層的過渡參數是不一樣的,你就必須對它們使用不一樣的過渡對象了。ide

例 5-2 展現瞭如何使用 Core Image 過濾器在 OS X 上實現過渡效果。在用你須要的參數配置完過濾器後,將它賦值給過渡對象的 filter 屬性。在這以後,應用動畫的過程就和其它類型動畫對象一致了。函數

例 5-2 使用 Core Image 過濾器在 OS X 上實現過渡動畫測試

// Create the Core Image filter, setting several key parameters.
CIFilter* aFilter = [CIFilter filterWithName:@"CIBarsSwipeTransition"];
[aFilter setValue:[NSNumber numberWithFloat:3.14] forKey:@"inputAngle"];
[aFilter setValue:[NSNumber numberWithFloat:30.0] forKey:@"inputWidth"];
[aFilter setValue:[NSNumber numberWithFloat:10.0] forKey:@"inputBarOffset"];
 
// Create the transition object
CATransition* transition = [CATransition animation];
transition.startProgress = 0;
transition.endProgress = 1.0;
transition.filter = aFilter;
transition.duration = 1.0;
 
[self.imageView2 setHidden:NO];
[self.imageView.layer addAnimation:transition forKey:@"transition"];
[self.imageView2.layer addAnimation:transition forKey:@"transition"];
[self.imageView setHidden:YES];
複製代碼

注意:當在動畫中使用 Core Image 過濾器時,最棘手的部分就是配置過濾器。例如,使用條形滑動過渡,若是輸入角度太高或者太低均可能會致使過渡效果沒有發生同樣。若是你沒有看見你期待的動畫效果,能夠調整過濾器的參數來看一下結果是否會發生變化。

自定義動畫的時間

時間是動畫重要的一部分,使用 Core Animation ,你能夠經過 CAMediaTiming 協議的方法和屬性給你的動畫指定的精確時間信息。兩個 Core Animation 類遵照該協議。CAAnimation 類遵照它,因此你能夠給你的動畫對象指定時間信息。CALayer 遵照它,因此你能夠給你的隱式動畫配置一些事件相關的特性,即便隱式過渡對象包含的動畫一般提供優先的默認時間信息。

當討論時間和動畫時,理解圖層對象是如何與時間協做是很是重要的。每一個圖層有它們本身的本地時間,來管理動畫時間。一般,兩個不一樣圖層的本地時間足夠接近,你能夠給兩個圖層指定相同的時間值,用戶一般不會察覺。可是,圖層的本地時間能夠被它的父圖層修改,或者它本身的時間參數。例如,改變圖層的 speed 屬性會致使圖層(包括它的子圖層)上的動畫持續時間按比例變化。

爲協助你確保給定圖層的時間值是合適的,CALayer 類定義了 convertTime:fromLayer:convertTime:toLayer: 方法。你可使用這些方法將固定時間轉爲圖層的本地時間,或將時間值從一個圖層轉到另外一個圖層。這些方法考慮了可能影響層的本地時間的媒體定時屬性,並返回一個能夠與另外一層一塊兒使用的值。在例 5-3 中,展現瞭如何使用常規方法獲取圖層的本地時間。CACurrentMediaTime() 是一個返回計算機當前時鐘時間的便利函數,該方法獲取時鐘時間並將它轉爲圖層的本地時間。

例 5-3 得到圖層的當前本地時間

CFTimeInterval localLayerTime = [myLayer convertTime:CACurrentMediaTime() fromLayer:nil];
複製代碼

一旦你得到了圖層的本地時間值,你可使用該值去更新動畫對象或圖層的時間相關屬性。經過這些時間屬性,你能夠實現一些有意思的動畫行爲,包含:

  • 使用 beginTime 屬性設置動畫的開始時間。一般,動畫在下一個更新循環期間開始。你可使用 beginTime 參數將動畫的開始時間延遲幾秒鐘。將兩個動畫串在一塊兒的方式是:將一個動畫的開始時間設置爲另外一個動畫的結束時間。若是你延遲了動畫的開始,你可能還想把 fillMode 屬性值設置爲 kCAFillModeBackwards 。該填充模式會形成圖層顯示動畫的開始值,即便圖層樹中的圖層對象包含不一樣的值。不用該填充模式,你將會看到在動畫執行以前調到結束值。其餘的模式也是有效的。
  • autoreverses 屬性會使動畫在指按期間內執行,而後返回到動畫初始值狀態。你能夠將該屬性和 repeatCount 屬性聯合使用,在開始值和結束值之間來回動畫顯示。將自動複製動畫的重複計數設置爲整數(如1.0),會致使動畫在其起始值中止。添加額外的半步(例如重複計數爲1.5)會致使動畫在其結束值中止。
  • 組動畫使用 timeOffset 屬性能夠在比其它動畫更晚的時間開始一些動畫。

暫停、恢復動畫

爲了暫停動畫,你能夠利用圖層遵照 CAMediaTiming 協議的事實,將圖層動畫的速率設置爲 0.0。將速率改成非零值即恢復動畫。例 5-4 展現瞭如何暫停恢復圖層動畫的代碼實現。

例 5-4 暫停重啓圖層的動畫

- (void)pauseLayer:(CALayer*)layer {
   CFTimeInterval pausedTime = [layer convertTime:CACurrentMediaTime() fromLayer:nil];
   layer.speed = 0.0;
   layer.timeOffset = pausedTime;
}
 
- (void)resumeLayer:(CALayer*)layer {
   CFTimeInterval pausedTime = [layer timeOffset];
   layer.speed = 1.0;
   layer.timeOffset = 0.0;
   layer.beginTime = 0.0;
   CFTimeInterval timeSincePause = [layer convertTime:CACurrentMediaTime() fromLayer:nil] - pausedTime;
   layer.beginTime = timeSincePause;
}
複製代碼

顯式事務容許你改變更畫參數

你作的每一個圖層的改變必須是事務的一部分。CATransaction 類管理動畫的建立和組織,讓它們在合適的時間執行。在多數狀況下,你不須要建立本身的事務。不管什麼時候你給圖層添加隱式或顯示動畫,Core Animation 都會自動建立隱式事務。然而,你也能夠建立顯式事務來更精確的管理動畫。

你使用 CATransaction 類的方法去建立管理事務。調用 begin 類方法去開始新的事務;調用 commit 類方法去終止事務。在這兩個方法之間是你想要的事務改變的部分。例如,改變圖層的兩個屬性,你可使用例 5-5 的代碼。

例 5-5 建立顯式事務

[CATransaction begin];
theLayer.zPosition=200.0;
theLayer.opacity=0.0;
[CATransaction commit];
複製代碼

使用事務的一個主要緣由是,在顯式事務範圍內,你能夠修改持續時間,時間函數,和其它的參數。你也能夠賦值一個 completion block 去獲取事務,當組動畫完成的時候,使你的 app 能夠被通知。使用 setValue:forKey: 方法和事務字典包含的鍵來改變響應的動畫參數。例如,將默認的持續時間改成 10 秒,你將改變 kCATransactionAnimationDuration 鍵的值,如例 5-6 所示。

例 5-6 改變更畫默認的持續時間

[CATransaction begin];
[CATransaction setValue:[NSNumber numberWithFloat:10.0f]
                 forKey:kCATransactionAnimationDuration];
// Perform the animations
[CATransaction commit];
複製代碼

在你想給集合中不一樣動畫設置不一樣值得時候,你能夠嵌入事務。將事務嵌入到另外一個事務,只需再調一次 begin 類方法便可。每一次 begin 方法的調用都要有相匹配的 commit 方法的調用。只有在提交最外層事務的更改後,Core Animation 纔會開始相關的動畫。

例 5-7 展現了將一個事務嵌入到另外一個事務的例子。在該例中,內部事務更改與外部事務相同的動畫參數,但使用不一樣的值。

例 5-7 嵌套顯式事務

[CATransaction begin]; // Outer transaction
 
// Change the animation duration to two seconds
[CATransaction setValue:[NSNumber numberWithFloat:2.0f]
                forKey:kCATransactionAnimationDuration];
// Move the layer to a new position
theLayer.position = CGPointMake(0.0,0.0);
 
[CATransaction begin]; // Inner transaction
// Change the animation duration to five seconds
[CATransaction setValue:[NSNumber numberWithFloat:5.0f]
                 forKey:kCATransactionAnimationDuration];
 
// Change the zPosition and opacity
theLayer.zPosition=200.0;
theLayer.opacity=0.0;
 
[CATransaction commit]; // Inner transaction
 
[CATransaction commit]; // Outer transaction
複製代碼

給動畫添加透視效果

Apps 能夠在三維空間中操做圖層,可是爲了簡單起見,Core Animation 使用平行投影顯示圖層,這基本上將場景展平爲二維平面。這種默認行爲會致使具備不一樣 zPosition 值的大小相同的圖層顯示爲相同的大小,即便它們在z軸上相距很遠。你一般會看到三維場景的點已經消失了。可是,你能夠經過修改圖層的轉換矩陣以包含透視信息來更改該行爲。

修改場景透視圖時,須要修改包含正在查看的圖層的最高層的 sublayerTransform 矩陣。經過將相同的透視信息應用於全部子層,修改最高級簡化了你必須寫的代碼。它還確保透視正確應用於在不一樣平面中彼此重疊的同級子圖層。

例 5-8 展現了爲父圖層建立簡單透視變換的方法。在這種狀況下,eyePosition 變量指定沿 z 軸查看圖層的相對距離。一般,你爲 eyePosition 指定一個正值,以保持圖層以預期的方式定向。值越大,場景越平坦,而值越小,圖層間的視覺差別越大。

例 5-8 給父圖層添加透視形變

CATransform3D perspective = CATransform3DIdentity;
perspective.m34 = -1.0/eyePosition;
 
// Apply the transform to a parent layer.
myParentLayer.sublayerTransform = perspective;
複製代碼

經過父圖層的配置,你能夠改變任何子圖層的 zPosition 屬性,觀察它們的大小如何基於它們與眼的相對距離而變化的。

最後

相關文章
相關標籤/搜索