教程: CAReplicatorLayer動畫

本文翻譯自:http://www.ios-animations-by-emails.com/posts/2015-march#tutorial

在這個教程中我會用CAReplicatorLayer實現三種炫酷的動畫效果,代碼用 Xcode 7/Swift2實現。

我們首先將重新實現iOS內置音樂應用的“volumn bars”效果,歌曲"Pyramid Song"的右方。


接下來我們實現一個自定義的activity indicator,最后我們用一種特別的方式實現 raywenderlich.com的logo。

1. 基本的Replicator動畫

CAReplicatorLayer是一個容器層,我們向其中增加內容,然后它對其進行復制。我們放入一個圖形,它將生成多個圖形。最棒的是我們可以自己定義這些復制圖形角度偏差以及和上一個圖形之間的透明度、顏色變化等。這種特性能讓我們創造出炫酷的動畫效果。

此教程第一部分我們就來復制iOS Music app上的這個動畫!


這個動畫的特點是一根紅色的bar上下伸縮,然后旁邊復制了兩個這樣的動畫,只不過動畫有一些偏移而已。
首先創建一個新的xcode poject,選擇Single View template。打開 ViewController.swift
viewDidLoad()中加入:

animation1()

并且在ViewController中加上這個空方法:

func animation1() {

}

現在我們在animation1()中創建我們的replicator layer:

let r = CAReplicatorLayer()
r.bounds = CGRect(x:0.0, y:0.0, width:60.0, height: 60.0)
r.position = view.center
r.backgroundColor = UIColor.lightGrayColor().CGColor
view.layer.addSublayer(r)

我們創建了一個CAReplicatorLayer的實例,設置了bounds和position,為了確定layer的位置,我們暫且設置layer的背景色為light gray。

啟動程序,我們會看到我們的layer只是靜靜地待在那:


現在,我們來創建第一個bar,加入到animation1()中:

let bar = CALayer()
bar.bounds = CGRect(x:0.0, y:0.0, width:8.0, height:40.0)
bar.position = CGPoint(x:10.0, y:75.0)
bar.cornerRadius = 2.0
bar.backgroundColor = UIColor.redColor().CGColor

r.addSubLayer(bar)

這段代碼創建了一個紅色的圓角矩形,設置它的位置在replicator layer的左邊緣。啟動程序,我們會看到:



這個紅色的bar出現在replicator layer的外面,是因為我們會讓bar進行上下位移。動畫開始的位置正好是在replicator layer的下面 -- 這就是為什么紅色bar會看起來有那么一點偏移。

接下來我們加入紅色bar的動畫:

let move = CABasicAnimation(keyPath: "position.y")
move.toValue = bar.position.y - 35.0
move.duration = 0.5
move.autoreverses = true
move.repeatCount = Float.infinity

bar.addAnimation(move, forKey: nil)

這樣會讓bar無限的重復上下位移...雖然很丑陋...但是是個不錯的開始!
下面來見識下replicator的魔力!加入下面這行代碼:

r.instanceCount = 3

這行代碼告訴replicator我們想要三個我們屏幕上的bar的拷貝,包括了最初我們自己創建的那一個。如果這時候我們啟動程序我們并不會看到任何改變,因為所有的這三個拷貝都在同一個位置同一時間做著同步的動畫,我們需要增加一些offset:

r.instanceTransform = CATransform3DMakeTranslation(20.0, 0.0, 0.0)

這樣我們就告訴了replicator layer,我們應用這個transform到所有的拷貝當中。我們設置instanceTransform來對每一份拷貝進行20的位置偏移,當我們運行程序會看到:


我們最初的紅色bar終于有了兩份克隆并且上下位移著!最后一步是讓動畫開始時間也有一定的偏移:

r.instanceDelay = 0.33

instanceDelay這個屬性是延遲replicator layer對每個copy的渲染,因此看起來就會有我們想要的有層次的動畫。

接下來我們還需要兩處改變:

  • 把replicator layer外面的紅色bar給隱藏掉,加入r.masksToBounds = true;
  • 刪除replicator layer的backgroundColor

最后我們能看到我們想要的結果:


2. Activity indicator

我們現在來實現更為復雜的復制動畫!在viewDidLoad()中替換animation1()為:

animation2()

接下來一樣,提供一個空的方法實現:

func animation2() {

}

這章我們會創建一個activity indicator,并且動畫比iOS系統自帶的activity更為精細。
我們從animation2()中加入replicator layer來開始:

let r = CAReplicatorLayer()
r.bounds = CGRect(x:0.0, y:0.0, width:200.0, height:200.0)
r.cornerRadius = 10.0
r.backgroundColor = UIColor(white:0.0, alpha:0.75).CGColor
r.position = view.center

view.layer.addSublayer(r)

和之前的一樣,我們創建了一個帶有背景色的空replicator layer,但這次我們保留這個背景色。
接下來加入一個簡單的layer來畫一個白色的矩形:

let dot = CALayer()
dot.bounds = CGRect(x:0.0, y :0.0, width:14.0, height:14.0)
dot.position = CGPoint(X:100.0, y:40.0)
dot.backgroundColor = UIColor(white:0.8, alpha:1.0).CGColor
dot.borderColor = UIColor(white:1.0, alpha:1.0).CGColor
dot.borderWidth = 1.0
dot.cornerRadius = 2.0

r.addSublayer(dot)

我們創建了一個14*14的圓角矩形,最后我們把它加入到replicator中,啟動程序我們可以看到:



接下來配置replicator來復制15個點,這15個點以2π/15的角度差來布局:

let nrDots: Int = 15
r.instanceCount = nrDots
let angle = CGFloat(2*M_PI) / CGFloat(nrDots)
r.instanceTransform = CATransform3DMakeRotation(angle, 0.0, 0.0, 1.0)

再次運行程序,能夠看到:



我們可以嘗試性修改nrDots的值為10、25或者其他值:



現在我們來創建一個1.5秒的scale動畫:
let durition:CFTimeInterval = 1.5
let shrink = CABasicAnimation(keyPath: "transform.scale")
shrink.fromValue = 1.0
shrink.toValue = 0.1
shrink.duration = duration
shrink.repeatCount = Float.infinity

dot.addAnimation(shrink, forKey: nil)

這段代碼讓每個同步謎一樣地縮小消失又出現。。。
下面大家應該都知道了,該加一些delay了:

r.instanceDelay = duration/Double(nrDots)

這樣我們的點就開始像在旋轉起來了,但是第一遍的動畫非常奇怪,所有點都是先是大的,然后到第二遍才開始正確的動畫。
解決這個問題我們只需要在animation2()的最后加上這段代碼:

dot.transform = CATransfor3DMakeScale(0.01, 0.01, 0.01)

看起來很棒:



其實動畫比想象的要簡單是不是,我們可以在這個基礎的activity indicator代碼上添加自己的一些嘗試,便可以創建出更酷的效果。

3. Follow the leader

最后的動畫叫做 Follow the leader(不會翻譯...),在這個動畫中我們通過path路徑來實現這個效果。
還是在viewDidLoad()當中,替換方法animation2()為:

animation3()

同上,先創建個空的方法實現:

func animation3() {

}

這個動畫中,我們需要更多的輔助函數,用PaintCode這個app我們可以快速生成貝賽爾曲線的代碼,這個曲線路徑便是我們需要的動畫path,加入這個方法到ViewController中:

func rw() -> CGPath {
    let bezierPath = UIBezierPath()     
    bezierPath.moveToPoint(CGPointMake(31.5, 71.5)) 
    bezierPath.addLineToPoint(CGPointMake(31.5, 23.5)) 
    bezierPath.addCurveToPoint(CGPointMake(58.5, 38.5), controlPoint1: CGPointMake(31.5, 23.5), controlPoint2: CGPointMake(62.46, 18.69)) 
    bezierPath.addCurveToPoint(CGPointMake(53.5, 45.5), controlPoint1: CGPointMake(57.5, 43.5), controlPoint2: CGPointMake(53.5, 45.5)) 
    bezierPath.addLineToPoint(CGPointMake(43.5, 48.5)) 
    bezierPath.addLineToPoint(CGPointMake(53.5, 66.5)) 
    bezierPath.addLineToPoint(CGPointMake(62.5, 51.5)) 
    bezierPath.addLineToPoint(CGPointMake(70.5, 66.5)) 
    bezierPath.addLineToPoint(CGPointMake(86.5, 23.5)) 
    bezierPath.addLineToPoint(CGPointMake(86.5, 78.5)) 
    bezierPath.addLineToPoint(CGPointMake(31.5, 78.5)) 
    bezierPath.addLineToPoint(CGPointMake(31.5, 71.5)) 
    bezierPath.closePath()

    var t = CGAffineTransformMakeScale(3.0, 3.0)
    return CGPathCreateCopyByTransformingPath(bezierPath.CGPath, &t)!
}

這個方法創建了一個CGPath,之后我們便會用它來創建關鍵幀動畫。
animation()中加入:

let r = CAReplicatorLayer()
r.bounds = view.bounds
r.backgroundColor = UIColor(white:0.0, alpha:0.75).CGColor
r.position = view.center

view.layer.addSublayer(r)

下面來創建我們的第一個layer:

let dot = CALayer()
dot.bounds = CGRect(x:0.0, y:0.0, width:10.0, height:10.0)
dot.backgroundColor = UIColor(white:0.8, alpha:1.0).CGColor
dot.borderColor = UIColor(white:1.0, alpha:1.0).CGColor
dot.borderWidth = 1.0
dot.cornerRadius = 5.0
dot.shouldRasterize = true
dot.rasterizationScale = UIScreen.mainScreen().scale

r.addSublayer(dot)

我們創建了一個小的銀色圓,這時候我們如果運行程序就會看到這個圓位于屏幕的左上方。
接著就是動畫:

let move = CAKeyframeAnimation(keyPath:"position")
move.path = rw()
move.repeatCount = Float.infinity
move.duration = 4.0
dot.addAnimation(move, forKey: nil)

這個動畫會圍繞著rw()生成的path做位移,持續4秒并且永遠重復著。
老樣子,我們加入copy以及delay:

r.instanceCount = 20
r.instanceDelay = 0.1


設置instanceColormultiplies the original content color by the color you provide(水平有限,翻譯不出,反正這屬性挺神奇,可以試試),并且和instanceGreenOffset一起能做出更炫酷的效果。

r.instanceColor = UIColor(red:0.0, green:1.0, blue:0.0, alpha:1.0).CGColor
r.instanceGreenOffset = -0.03

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

推薦閱讀更多精彩內容