本文翻譯自:http://www.ios-animations-by-emails.com/posts/2015-march#tutorial。
在這個教程中我會用CAReplicatorLayer
實(shí)現(xiàn)三種炫酷的動畫效果柠衍,代碼用 Xcode 7/Swift2實(shí)現(xiàn)倒信。
我們首先將重新實(shí)現(xiàn)iOS內(nèi)置音樂應(yīng)用的“volumn bars”效果铅匹,歌曲"Pyramid Song"的右方凿宾。
接下來我們實(shí)現(xiàn)一個自定義的activity indicator,最后我們用一種特別的方式實(shí)現(xiàn) raywenderlich.com的logo滩届。
1. 基本的Replicator動畫
CAReplicatorLayer是一個容器層腐芍,我們向其中增加內(nèi)容差导,然后它對其進(jìn)行復(fù)制。我們放入一個圖形猪勇,它將生成多個圖形设褐。最棒的是我們可以自己定義這些復(fù)制圖形角度偏差以及和上一個圖形之間的透明度、顏色變化等泣刹。這種特性能讓我們創(chuàng)造出炫酷的動畫效果助析。
此教程第一部分我們就來復(fù)制iOS Music app上的這個動畫!
這個動畫的特點(diǎn)是一根紅色的bar上下伸縮椅您,然后旁邊復(fù)制了兩個這樣的動畫外冀,只不過動畫有一些偏移而已。
首先創(chuàng)建一個新的xcode poject掀泳,選擇Single View template雪隧。打開 ViewController.swift。
在 viewDidLoad()中加入:
animation1()
并且在ViewController
中加上這個空方法:
func animation1() {
}
現(xiàn)在我們在animation1()
中創(chuàng)建我們的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)
我們創(chuàng)建了一個CAReplicatorLayer
的實(shí)例员舵,設(shè)置了bounds和position脑沿,為了確定layer的位置,我們暫且設(shè)置layer的背景色為light gray固灵。
啟動程序捅伤,我們會看到我們的layer只是靜靜地待在那:
現(xiàn)在,我們來創(chuàng)建第一個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)
這段代碼創(chuàng)建了一個紅色的圓角矩形,設(shè)置它的位置在replicator layer的左邊緣祠汇。啟動程序仍秤,我們會看到:
這個紅色的bar出現(xiàn)在replicator layer的外面,是因?yàn)槲覀儠宐ar進(jìn)行上下位移可很。動畫開始的位置正好是在replicator layer的下面 -- 這就是為什么紅色bar會看起來有那么一點(diǎn)偏移诗力。
接下來我們加入紅色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無限的重復(fù)上下位移...雖然很丑陋...但是是個不錯的開始!
下面來見識下replicator的魔力!加入下面這行代碼:
r.instanceCount = 3
這行代碼告訴replicator我們想要三個我們屏幕上的bar的拷貝苇本,包括了最初我們自己創(chuàng)建的那一個袜茧。如果這時候我們啟動程序我們并不會看到任何改變,因?yàn)樗械倪@三個拷貝都在同一個位置同一時間做著同步的動畫瓣窄,我們需要增加一些offset:
r.instanceTransform = CATransform3DMakeTranslation(20.0, 0.0, 0.0)
這樣我們就告訴了replicator layer笛厦,我們應(yīng)用這個transform到所有的拷貝當(dāng)中。我們設(shè)置instanceTransform
來對每一份拷貝進(jìn)行20的位置偏移俺夕,當(dāng)我們運(yùn)行程序會看到:
我們最初的紅色bar終于有了兩份克隆并且上下位移著裳凸!最后一步是讓動畫開始時間也有一定的偏移:
r.instanceDelay = 0.33
instanceDelay
這個屬性是延遲replicator layer對每個copy的渲染,因此看起來就會有我們想要的有層次的動畫劝贸。
接下來我們還需要兩處改變:
- 把replicator layer外面的紅色bar給隱藏掉姨谷,加入
r.masksToBounds = true
; - 刪除replicator layer的
backgroundColor
最后我們能看到我們想要的結(jié)果:
2. Activity indicator
我們現(xiàn)在來實(shí)現(xiàn)更為復(fù)雜的復(fù)制動畫!在viewDidLoad()
中替換animation1()
為:
animation2()
接下來一樣映九,提供一個空的方法實(shí)現(xiàn):
func animation2() {
}
這章我們會創(chuàng)建一個activity indicator梦湘,并且動畫比iOS系統(tǒng)自帶的activity更為精細(xì)。
我們從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)
和之前的一樣件甥,我們創(chuàng)建了一個帶有背景色的空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)
我們創(chuàng)建了一個14*14的圓角矩形嚼蚀,最后我們把它加入到replicator中禁灼,啟動程序我們可以看到:
接下來配置replicator來復(fù)制15個點(diǎn),這15個點(diǎn)以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)
再次運(yùn)行程序轿曙,能夠看到:
我們可以嘗試性修改nrDots的值為10弄捕、25或者其他值:
現(xiàn)在我們來創(chuàng)建一個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)
這段代碼讓每個同步謎一樣地縮小消失又出現(xiàn)。导帝。守谓。
下面大家應(yīng)該都知道了,該加一些delay了:
r.instanceDelay = duration/Double(nrDots)
這樣我們的點(diǎn)就開始像在旋轉(zhuǎn)起來了您单,但是第一遍的動畫非常奇怪斋荞,所有點(diǎn)都是先是大的,然后到第二遍才開始正確的動畫虐秦。
解決這個問題我們只需要在animation2()
的最后加上這段代碼:
dot.transform = CATransfor3DMakeScale(0.01, 0.01, 0.01)
看起來很棒:
其實(shí)動畫比想象的要簡單是不是平酿,我們可以在這個基礎(chǔ)的activity indicator代碼上添加自己的一些嘗試,便可以創(chuàng)建出更酷的效果悦陋。
3. Follow the leader
最后的動畫叫做 Follow the leader(不會翻譯...)蜈彼,在這個動畫中我們通過path路徑來實(shí)現(xiàn)這個效果。
還是在viewDidLoad()
當(dāng)中俺驶,替換方法animation2()
為:
animation3()
同上幸逆,先創(chuàng)建個空的方法實(shí)現(xiàn):
func animation3() {
}
這個動畫中,我們需要更多的輔助函數(shù),用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)!
}
這個方法創(chuàng)建了一個CGPath,之后我們便會用它來創(chuàng)建關(guān)鍵幀動畫拍顷。
在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)
下面來創(chuàng)建我們的第一個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)
我們創(chuàng)建了一個小的銀色圓抚太,這時候我們?nèi)绻\(yùn)行程序就會看到這個圓位于屏幕的左上方。
接著就是動畫:
let move = CAKeyframeAnimation(keyPath:"position")
move.path = rw()
move.repeatCount = Float.infinity
move.duration = 4.0
dot.addAnimation(move, forKey: nil)
這個動畫會圍繞著rw()
生成的path做位移菇怀,持續(xù)4秒并且永遠(yuǎn)重復(fù)著凭舶。
老樣子,我們加入copy以及delay:
r.instanceCount = 20
r.instanceDelay = 0.1
設(shè)置
instanceColor
multiplies 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