【實(shí)例教程】你會(huì)用swift創(chuàng)建復(fù)雜的加載動(dòng)畫嗎
時(shí)至今日,iOS 應(yīng)用商店已經(jīng)擁有超過(guò)了140萬(wàn) 應(yīng)用,讓你自己的應(yīng)用脫穎而出確實(shí)是個(gè)不小的挑戰(zhàn)。不過(guò),在你的應(yīng)用掉入默默無(wú)聞的大黑洞之前,你擁有一個(gè)小小的機(jī)遇窗,它能幫你吸引用戶的注意。
想讓你的用戶喝彩尖叫,沒(méi)有比應(yīng)用加載界面更好的地方 ,在這個(gè)地方,你可以添加一個(gè)討人喜歡的動(dòng)畫來(lái)作為你登陸或者認(rèn)證流程的先導(dǎo)。
在這個(gè)教程中,你將要學(xué)會(huì)如何利用先進(jìn)的技術(shù)來(lái)創(chuàng)建一個(gè)流暢并且迷人的動(dòng)畫。
開始吧??!
從這里下載啟動(dòng)項(xiàng)目,保存在一個(gè)合適的路徑并用Xcode打開。
打開 HolderView.swift 。 在這個(gè)UIView 的子類中,你可以添加些子層(在Layers的下級(jí)目錄中可以找到),使之像上面的動(dòng)畫一樣生動(dòng)
OvalLayer.swift: 這是第一層,它從零尺寸擴(kuò)展,然后會(huì)有一小段時(shí)間的搖擺
TriangleLayer.swift: 接下來(lái)的這個(gè)層TriangleLayer會(huì)在OvalLayer 搖擺的時(shí)候出現(xiàn),當(dāng)此視圖轉(zhuǎn)動(dòng)時(shí),OvalLayer 會(huì)縮小到零尺寸,并在TriangleLayer 中消失。
RectangleLayer.swift: 這個(gè)層是TriangleLayer 用于分類的可視化容器
ArcLayer.swift: 這個(gè)層動(dòng)畫特效填充在RectangleLayer 中,這和杯子里填充了水(效果)非常相似
打開OvalLayer.swift, 啟動(dòng)項(xiàng)目已經(jīng)包含了用于初始化這個(gè)層的代碼和所有你會(huì)在動(dòng)畫里用到的Bezier path(對(duì)象)。你會(huì)看到expand(),wobble()和contract()方法都是空的。你可以通過(guò)參考這個(gè)指導(dǎo)書來(lái)填充這些方法。所有其他的 *Layer (以layer結(jié)尾)的文件都用了相似的方式構(gòu)建。
注意:如果你想要學(xué)習(xí)更多的Bezier paths,那就檢出我們系列指導(dǎo)書 Modern Core Graphics with Swift
最后,打開ViewController.swift 查看addHolderView()方法,這個(gè)方法添加了一個(gè)HolderView 作為一個(gè)子視圖,放到viewcontroller 視圖的中間。這個(gè)視圖將會(huì)放置所有的動(dòng)畫。viewcontroller僅僅需要把它放到屏幕上,這個(gè)視圖將會(huì)照管好現(xiàn)行的動(dòng)畫代碼。
animateLabel() 是由類 HolderView 提供的代理回調(diào)函數(shù),此類中你會(huì)用你完成的動(dòng)畫序列來(lái)填充。addButton()方法只是添加一個(gè)按鈕到視圖中,用于觸摸和重啟動(dòng)畫。
編譯并運(yùn)行你的應(yīng)用;你會(huì)看到一個(gè)空白屏幕。一個(gè)空白的畫布--這就是用于開始創(chuàng)建你的新動(dòng)畫的完美的載體。在指導(dǎo)書的最后,你的應(yīng)用會(huì)看起來(lái)是這樣的:
現(xiàn)在不需再費(fèi)周折,我們開始吧
添加一個(gè)橢圓
這個(gè)動(dòng)畫從一個(gè)橢圓開始,橢圓是從屏幕中間擴(kuò)展到視圖,然后在周圍有點(diǎn)搖擺。
打開 HolderView.swift ,在 HolderView 類的頂端附近聲明如下的常量
- let ovalLayer = OvalLayer()
現(xiàn)在在此類的底部添加如下方法
- func addOval() {
- layer.addSublayer(ovalLayer)
- ovalLayer.expand()
- }
這段代碼首先添加了你上面創(chuàng)建的 OverLayer 的實(shí)例作為一個(gè)子層到視圖層,然后調(diào)用 expand() 方法,這是被切掉的需要你來(lái)填充的函數(shù)之一
來(lái)到 OvalLayer.swift 文件,添加如下代碼到 expand() 中:
- func expand() {
- var expandAnimation: CABasicAnimation = CABasicAnimation(keyPath: "path")
- expandAnimation.fromValue = ovalPathSmall.CGPath
- expandAnimation.toValue = ovalPathLarge.CGPath
- expandAnimation.duration = animationDuration
- expandAnimation.fillMode = kCAFillModeForwards
- expandAnimation.removedOnCompletion = false
- addAnimation(expandAnimation, forKey: nil)
- }
這個(gè)函數(shù)創(chuàng)建了一個(gè) CABasicAnimation 的實(shí)例,這個(gè)實(shí)例用于改變橢圓從 ovalPathLarge.到 ovalPathSmall 的路徑。啟動(dòng)項(xiàng)目為你提供了兩者的Bezier paths。
設(shè)置動(dòng)畫的 removedOnCompletion 的值為 false,fillMode 的值為 KCAFillModeForwards ,使得當(dāng)動(dòng)畫結(jié)束的時(shí)候,橢圓保留它新的路徑。
最后,打開 ViewController.swift ,在 view.addSubview(holderView) 下的 addHolderView() 方法中添加如下的線條
- holderView.addOval()
將 holdview 添加到 ViewController 的視圖中后,調(diào)用 addOval 方法來(lái)啟動(dòng)動(dòng)畫
構(gòu)建并運(yùn)行你的應(yīng)用,你的動(dòng)畫現(xiàn)在就會(huì)看起來(lái)像下面(圖例)
搖動(dòng)橢圓
使用視圖中擴(kuò)張的橢圓,下一步就是在橢圓的步調(diào)中設(shè)置一些反彈,使之搖擺起來(lái)
打開 HolderView.swift,在此類的底部,添加下面的函數(shù)
- func wobbleOval() {
- ovalLayer.wobble()
- }
在 OvalLayer 中調(diào)用被切掉的方法 wobble().
現(xiàn)在打開 OverLayer.swift,在 wobble() 中添加如下代碼
- func wobble() {
- // 1
- var wobbleAnimation1: CABasicAnimation = CABasicAnimation(keyPath: "path")
- wobbleAnimation1.fromValue = ovalPathLarge.CGPath
- wobbleAnimation1.toValue = ovalPathSquishVertical.CGPath
- wobbleAnimation1.beginTime = 0.0
- wobbleAnimation1.duration = animationDuration
- // 2
- var wobbleAnimation2: CABasicAnimation = CABasicAnimation(keyPath: "path")
- wobbleAnimation2.fromValue = ovalPathSquishVertical.CGPath
- wobbleAnimation2.toValue = ovalPathSquishHorizontal.CGPath
- wobbleAnimation2.beginTime = wobbleAnimation1.beginTime + wobbleAnimation1.duration
- wobbleAnimation2.duration = animationDuration
- // 3
- var wobbleAnimation3: CABasicAnimation = CABasicAnimation(keyPath: "path")
- wobbleAnimation3.fromValue = ovalPathSquishHorizontal.CGPath
- wobbleAnimation3.toValue = ovalPathSquishVertical.CGPath
- wobbleAnimation3.beginTime = wobbleAnimation2.beginTime + wobbleAnimation2.duration
- wobbleAnimation3.duration = animationDuration
- // 4
- var wobbleAnimation4: CABasicAnimation = CABasicAnimation(keyPath: "path")
- wobbleAnimation4.fromValue = ovalPathSquishVertical.CGPath
- wobbleAnimation4.toValue = ovalPathLarge.CGPath
- wobbleAnimation4.beginTime = wobbleAnimation3.beginTime + wobbleAnimation3.duration
- wobbleAnimation4.duration = animationDuration
- // 5
- var wobbleAnimationGroup: CAAnimationGroup = CAAnimationGroup()
- wobbleAnimationGroup.animations = [wobbleAnimation1, wobbleAnimation2, wobbleAnimation3,
- wobbleAnimation4]
- wobbleAnimationGroup.duration = wobbleAnimation4.beginTime + wobbleAnimation4.duration
- wobbleAnimationGroup.repeatCount = 2
- addAnimation(wobbleAnimationGroup, forKey: nil)
- }
代碼真夠多的。但斷句還是很講究的。 接下來(lái)要做的是:
- 從大路徑下降到被垂直壓扁的動(dòng)畫
- 從垂直壓扁變成水平和垂直都?jí)罕?/li>
- 和垂直擠壓(動(dòng)畫)切換
- 回到大路徑結(jié)束動(dòng)畫
- 把你所有的動(dòng)畫合并到CAAnimationGroup組,并把這個(gè)動(dòng)畫組添加到你的 OvalLayout 中。
每一個(gè)隨后的動(dòng)畫的 beginTime 都是其前一個(gè)動(dòng)畫和動(dòng)畫持續(xù)時(shí)間的 beginTime 總和。你重復(fù)動(dòng)畫組兩次就會(huì)給你一種擺動(dòng)出稍微拉長(zhǎng)的感覺(jué)
#p#
盡管你現(xiàn)在擁有產(chǎn)生搖擺動(dòng)畫的所有代碼,你還是不能調(diào)用你的新動(dòng)畫
我們回到 HolderView.swift,在 addOval() 結(jié)尾處添加如下代碼
- NSTimer.scheduledTimerWithTimeInterval(0.3, target: self, selector: "wobbleOval",
- userInfo: nil, repeats: false)
在這里,你創(chuàng)建了一個(gè)timer定時(shí)器,它會(huì)在OvalLayer已經(jīng)結(jié)束擴(kuò)張后調(diào)用 wobbleOval()
編譯并運(yùn)行你的應(yīng)用,檢查下你的新動(dòng)畫。
這有點(diǎn)微妙,但那對(duì)一個(gè)真正的明快的動(dòng)畫是一個(gè)重要的因素。你不再需要那些滿屏幕都是亂飛的東西了。
開始變身
是時(shí)候來(lái)電有趣的東西了。你將要把一個(gè)橢圓變身成為一個(gè)三角形。在用戶眼里,這個(gè)轉(zhuǎn)變應(yīng)該看上去無(wú)縫連接的。要做到這些,你會(huì)用到兩個(gè)相同顏色的分離的形狀。
打開HolderView.swift,在HolderView類的頂端稍微靠近你早些時(shí)候添加的 OvalLayer 屬性的下面添加如下代碼
- let triangleLayer = TriangleLayer()
這里聲明了一個(gè) TriangleLayer 類的常量,正如你在 OvalLayer 中做的一樣
現(xiàn)在,讓wobbleOval()方法看上去像這樣:
- func wobbleOval() {
- // 1
- layer.addSublayer(triangleLayer) // Add this line
- ovalLayer.wobble()
- // 2
- // Add the code below
- NSTimer.scheduledTimerWithTimeInterval(0.9, target: self,
- selector: "drawAnimatedTriangle", userInfo: nil,
- repeats: false)
- }
上面的代碼做了如下這些事情:
-
這行(代碼)添加了一個(gè) TiangleLayer 實(shí)例,這個(gè)實(shí)例在稍早的時(shí)候作為HolderView層的子層已經(jīng)被初始化過(guò)了。
-
正如你所知道的,因?yàn)檫@個(gè)搖擺動(dòng)畫在1.8s的總間隔時(shí)間內(nèi)運(yùn)行兩次,所以在中間點(diǎn)啟動(dòng)變形過(guò)程會(huì)是一個(gè)非常好的地方。因此,你要添加一個(gè)定時(shí)器timer,它在延遲0.9s之后執(zhí)行drawAnimatedTriangle()
注意:找到動(dòng)畫的正確的間隔或延遲需要反復(fù)實(shí)驗(yàn),這也是一個(gè)好的動(dòng)畫和一個(gè)極好的動(dòng)畫區(qū)別。我鼓勵(lì)你去修補(bǔ)你的動(dòng)畫,讓它們看上去完美。這可能要花點(diǎn)時(shí)間,但確是值得的。
接下來(lái),在此類的底部添加如下的函數(shù)。
- func drawAnimatedTriangle() {
- triangleLayer.animate()
- }
這個(gè)方法會(huì)被你剛剛加入到 wobbleOval() 中的timer定時(shí)器調(diào)用。
現(xiàn)在打開 TriangleLayer.swift,添加如下代碼到 animate()
- func animate() {
- var triangleAnimationLeft: CABasicAnimation = CABasicAnimation(keyPath: "path")
- triangleAnimationLeft.fromValue = trianglePathSmall.CGPath
- triangleAnimationLeft.toValue = trianglePathLeftExtension.CGPath
- triangleAnimationLeft.beginTime = 0.0
- triangleAnimationLeft.duration = 0.3
- var triangleAnimationRight: CABasicAnimation = CABasicAnimation(keyPath: "path")
- triangleAnimationRight.fromValue = trianglePathLeftExtension.CGPath
- triangleAnimationRight.toValue = trianglePathRightExtension.CGPath
- triangleAnimationRight.beginTime = triangleAnimationLeft.beginTime + triangleAnimationLeft.duration
- triangleAnimationRight.duration = 0.25
- var triangleAnimationTop: CABasicAnimation = CABasicAnimation(keyPath: "path")
- triangleAnimationTop.fromValue = trianglePathRightExtension.CGPath
- triangleAnimationTop.toValue = trianglePathTopExtension.CGPath
- triangleAnimationTop.beginTime = triangleAnimationRight.beginTime + triangleAnimationRight.duration
- triangleAnimationTop.duration = 0.20
- var triangleAnimationGroup: CAAnimationGroup = CAAnimationGroup()
- triangleAnimationGroup.animations = [triangleAnimationLeft, triangleAnimationRight,
- triangleAnimationTop]
- triangleAnimationGroup.duration = triangleAnimationTop.beginTime + triangleAnimationTop.duration
- triangleAnimationGroup.fillMode = kCAFillModeForwards
- triangleAnimationGroup.removedOnCompletion = false
- addAnimation(triangleAnimationGroup, forKey: nil)
- }
這段代碼使三角層TriangleLayer的角一個(gè)挨一個(gè)的被彈拉成為橢圓 OvalLayer 層的擺動(dòng)。Bezier path已經(jīng)作為啟動(dòng)工程的一部分被定義好。左邊的角首先執(zhí)行,接下來(lái)是右邊的角,最后是上面的。你完成這個(gè)(動(dòng)畫)需要借助創(chuàng)建三個(gè)基于路徑的CABasicAnimation類的實(shí)例, CABasicAnimation 類已經(jīng)被你添加到 CAAnimationGroup 組中,而組則被放到了 TriangleLayer 中。
構(gòu)建并運(yùn)行你的應(yīng)用,看看當(dāng)前動(dòng)畫的狀態(tài).
完成變形
為了完成變形過(guò)程,你需要在縮小OvalLayer橢圓層的同時(shí),對(duì) HolderView 旋轉(zhuǎn)360度,讓 TriangleLayer 三角層單獨(dú)隔離出來(lái)。
打開 HolderView.swift,在 drawAnimatedTriangle(): 尾部添加如下代碼
- NSTimer.scheduledTimerWithTimeInterval(0.9, target: self, selector: "spinAndTransform",
- userInfo: nil, repeats: false)
這里設(shè)置了一個(gè)定時(shí)器timer,用于在三角形動(dòng)畫結(jié)束后觸發(fā)。0.9s的時(shí)間還次用反復(fù)實(shí)驗(yàn)來(lái)確定
現(xiàn)在在這個(gè)類的底部添加如下的函數(shù)。
- func spinAndTransform() {
- // 1
- layer.anchorPoint = CGPointMake(0.5, 0.6)
- // 2
- var rotationAnimation: CABasicAnimation = CABasicAnimation(keyPath: "transform.rotation.z")
- rotationAnimation.toValue = CGFloat(M_PI * 2.0)
- rotationAnimation.duration = 0.45
- rotationAnimation.removedOnCompletion = true
- layer.addAnimation(rotationAnimation, forKey: nil)
- // 3
- ovalLayer.contract()
- }
你之前創(chuàng)建的定時(shí)器添加了這段代碼,定時(shí)器會(huì)在橢圓停止擺動(dòng)并且三角行的角出現(xiàn)的時(shí)候調(diào)用這個(gè)函數(shù)。在這里我們看下這個(gè)函數(shù)更詳細(xì)的(介紹)
-
更新層的錨點(diǎn)到略微靠近視圖中間的下方。這提供了一個(gè)看上去更加自然的旋轉(zhuǎn)。這是由于橢圓和三角形事實(shí)上比視圖中心在垂直方向上略微偏移。因此,如果視圖圍繞中心旋轉(zhuǎn),橢圓和三角形可能會(huì)垂直方向移動(dòng)
-
應(yīng)用一個(gè)CABasicAnimation類來(lái)對(duì)層做360度旋轉(zhuǎn),或者2*pi的弧度。旋轉(zhuǎn)是圍繞著Z軸,Z軸就是穿過(guò)屏幕,垂直于屏幕平面的軸
-
在OvalLayer中調(diào)用contract()來(lái)展示動(dòng)畫,這個(gè)動(dòng)畫會(huì)削減橢圓的尺寸直到消失
現(xiàn)在打開 OvalLayer.swift,添加如下代碼到 contract() 方法
- func contract() {
- var contractAnimation: CABasicAnimation = CABasicAnimation(keyPath: "path")
- contractAnimation.fromValue = ovalPathLarge.CGPath
- contractAnimation.toValue = ovalPathSmall.CGPath
- contractAnimation.duration = animationDuration
- contractAnimation.fillMode = kCAFillModeForwards
- contractAnimation.removedOnCompletion = false
- addAnimation(contractAnimation, forKey: nil)
- }
這段代碼應(yīng)用 CABasicAnimation 類,將 OvalLayer 設(shè)置它的初始路徑 ovalPathSmall。
構(gòu)建并運(yùn)行你的應(yīng)用程序,當(dāng)動(dòng)畫完成的時(shí)候,只有三角形應(yīng)該被留在屏幕上。
繪制容器
在下面這部分,你將要繪畫一個(gè)矩形容器,用于創(chuàng)建一個(gè)閉合圈。你將會(huì)用到 RectangleLayer 的描邊屬性。你需要這樣做兩次,將紅色和藍(lán)色都作為描邊色。
打開 HolderView.swift, 像下面這樣聲明兩個(gè) RectangularLayer 常量,(位置)就在你稍早時(shí)候 triangleLayer 屬性的下面
- let redRectangleLayer = RectangleLayer()let blueRectangleLayer = RectangleLayer()
接下來(lái)添加如下代碼到 spinAndTransform(): 的尾部。
- NSTimer.scheduledTimerWithTimeInterval(0.45, target: self,
- selector: "drawRedAnimatedRectangle",
- userInfo: nil, repeats: false)
- NSTimer.scheduledTimerWithTimeInterval(0.65, target: self,
- selector: "drawBlueAnimatedRectangle",
- userInfo: nil, repeats: false)
這里創(chuàng)建兩個(gè)定時(shí)器timer分別調(diào)用 drawRedAnimatedRectangle() 和 drawBlueAnimatedRectangle() 。旋轉(zhuǎn)動(dòng)畫結(jié)束后,首先需要畫出矩形,當(dāng)紅色矩形描邊繪畫接近完成的時(shí)候,藍(lán)色矩形描邊開始。
添加下面兩個(gè)方法頭此類的底部
- func drawRedAnimatedRectangle() {
- layer.addSublayer(redRectangleLayer)
- redRectangleLayer.animateStrokeWithColor(Colors.red)
- }
- func drawBlueAnimatedRectangle() {
- layer.addSublayer(blueRectangleLayer)
- blueRectangleLayer.animateStrokeWithColor(Colors.blue)
- }
一旦你添加矩形層 RectangleLayer 作為 HolderView 的子層,你就要調(diào)用 animateStrokeWithColor(color:) 并通過(guò)適當(dāng)?shù)念伾珌?lái)繪畫出邊線。
現(xiàn)在打開 RectangleLayer.swift, 像下面這樣填充 animateStrokeWithColor(color:)
- func animateStrokeWithColor(color: UIColor) {
- strokeColor = color.CGColor
- var strokeAnimation: CABasicAnimation = CABasicAnimation(keyPath: "strokeEnd")
- strokeAnimation.fromValue = 0.0
- strokeAnimation.toValue = 1.0
- strokeAnimation.duration = 0.4
- addAnimation(strokeAnimation, forKey: nil)
- }
這段代碼通過(guò)添加一個(gè) CABasicAnimation對(duì)象,在 RectangleLayer 矩形層周圍繪畫了一個(gè)描邊。CAShapeLayer 的 strokeEnd 的 key(也就是keyPath)指示了在路徑周圍多遠(yuǎn)的距離停止描邊。通過(guò)將這個(gè)屬性值從0調(diào)到1,你會(huì)產(chǎn)生一種路徑被從開始到結(jié)束都被繪畫的錯(cuò)覺(jué)。 而從1到0,將會(huì)產(chǎn)生整個(gè)路徑被抹去的錯(cuò)覺(jué)。
編譯并運(yùn)行你的應(yīng)用,查看兩個(gè)描邊是如何看起來(lái)像他們構(gòu)建的容器的。
#p#
填充容器
動(dòng)畫的下一步就是填充容器。你要尋找到的效果就像是水填充到玻璃杯中。這是個(gè)非常棒的視覺(jué)特效,使之為一個(gè)大的飛濺特效
打開 HolderView.swift,在 RectangleLayer 屬性稍靠下添加如下的常量
- let arcLayer = ArcLayer()
現(xiàn)在在drawBlueAnimatedRectangle():尾部添加如下的代碼
- NSTimer.scheduledTimerWithTimeInterval(0.40, target: self, selector: "drawArc",
- userInfo: nil, repeats: false)
這(段代碼)創(chuàng)建了一個(gè)定時(shí)器,用于當(dāng)藍(lán)色 RectangleLayer 完成繪畫后調(diào)用 drawArc()
在類的結(jié)尾添加如下的函數(shù)
- func drawArc() {
- layer.addSublayer(arcLayer)
- arcLayer.animate()
- }
這段代碼是在你動(dòng)畫填充之前,添加了上面已經(jīng)創(chuàng)建ArcLayer 的實(shí)例對(duì)象到HolderView 層。
打開ArcLayer.swift 然后添加如下代碼到animate():
- func animate() {
- var arcAnimationPre: CABasicAnimation = CABasicAnimation(keyPath: "path")
- arcAnimationPre.fromValue = arcPathPre.CGPath
- arcAnimationPre.toValue = arcPathStarting.CGPath
- arcAnimationPre.beginTime = 0.0
- arcAnimationPre.duration = animationDuration
- var arcAnimationLow: CABasicAnimation = CABasicAnimation(keyPath: "path")
- arcAnimationLow.fromValue = arcPathStarting.CGPath
- arcAnimationLow.toValue = arcPathLow.CGPath
- arcAnimationLow.beginTime = arcAnimationPre.beginTime + arcAnimationPre.duration
- arcAnimationLow.duration = animationDuration
- var arcAnimationMid: CABasicAnimation = CABasicAnimation(keyPath: "path")
- arcAnimationMid.fromValue = arcPathLow.CGPath
- arcAnimationMid.toValue = arcPathMid.CGPath
- arcAnimationMid.beginTime = arcAnimationLow.beginTime + arcAnimationLow.duration
- arcAnimationMid.duration = animationDuration
- var arcAnimationHigh: CABasicAnimation = CABasicAnimation(keyPath: "path")
- arcAnimationHigh.fromValue = arcPathMid.CGPath
- arcAnimationHigh.toValue = arcPathHigh.CGPath
- arcAnimationHigh.beginTime = arcAnimationMid.beginTime + arcAnimationMid.duration
- arcAnimationHigh.duration = animationDuration
- var arcAnimationComplete: CABasicAnimation = CABasicAnimation(keyPath: "path")
- arcAnimationComplete.fromValue = arcPathHigh.CGPath
- arcAnimationComplete.toValue = arcPathComplete.CGPath
- arcAnimationComplete.beginTime = arcAnimationHigh.beginTime + arcAnimationHigh.duration
- arcAnimationComplete.duration = animationDuration
- var arcAnimationGroup: CAAnimationGroup = CAAnimationGroup()
- arcAnimationGroup.animations = [arcAnimationPre, arcAnimationLow, arcAnimationMid,
- arcAnimationHigh, arcAnimationComplete]
- arcAnimationGroup.duration = arcAnimationComplete.beginTime + arcAnimationComplete.duration
- arcAnimationGroup.fillMode = kCAFillModeForwards
- arcAnimationGroup.removedOnCompletion = false
- addAnimation(arcAnimationGroup, forKey: nil)
- }
這個(gè)動(dòng)畫和之前的搖擺動(dòng)畫很相似。你創(chuàng)建了一個(gè) CAAnimationGroup 動(dòng)畫組,動(dòng)畫組中包含五個(gè)基于路徑的 CABasicAnimation 實(shí)例對(duì)象。
每個(gè)路徑因高度遞增而有了稍微不同的弧,這些路徑也是啟動(dòng)項(xiàng)目的一部分。最后,將 CAAnimationGroup 動(dòng)畫組應(yīng)用到層中,并使得動(dòng)畫組在完成的時(shí)候不會(huì)被移除,因而當(dāng)動(dòng)畫完成的時(shí)候,它依然保留了自己的狀態(tài)。
構(gòu)建并運(yùn)行你的應(yīng)用,看看這個(gè)神奇的展開吧。
完成動(dòng)畫
剩下要做的就是擴(kuò)展藍(lán)色的HolderView視圖來(lái)填充整個(gè)屏幕,并且添加一個(gè)UILabel作為一個(gè)logo添加到視圖中
打開 HolderView.swift,在drawArc() 的結(jié)尾添加如下代碼
- NSTimer.scheduledTimerWithTimeInterval(0.90, target: self, selector: "expandView",
- userInfo: nil, repeats: false)
這(段代碼)創(chuàng)建了一個(gè)定時(shí)器,用于在 ArcLayer 填充到容器后調(diào)用 expandView()
現(xiàn)在,添加下面的函數(shù)到同一個(gè)類的底部:
- func expandView() {
- // 1
- backgroundColor = Colors.blue
- // 2
- frame = CGRectMake(frame.origin.x - blueRectangleLayer.lineWidth,
- frame.origin.y - blueRectangleLayer.lineWidth,
- frame.size.width + blueRectangleLayer.lineWidth * 2,
- frame.size.height + blueRectangleLayer.lineWidth * 2)
- // 3
- layer.sublayers = nil
- // 4
- UIView.animateWithDuration(0.3, delay: 0.0, options: UIViewAnimationOptions.CurveEaseInOut,
- animations: {
- self.frame = self.parentFrame
- }, completion: { finished in
- self.addLabel()
- })
- }
代碼分析
-
HolderView視圖的背景設(shè)置為藍(lán)色,和你填充到矩形的顏色匹配
-
幀擴(kuò)展到你稍早時(shí)候添加的RectangleLayer矩形層的描邊寬度,
-
所有的子層都移除。現(xiàn)在沒(méi)有了橢圓,沒(méi)有了三角形,沒(méi)有了矩形圖層
-
添加動(dòng)畫,并擴(kuò)張HolderView填充屏幕,當(dāng)動(dòng)畫結(jié)束的時(shí)候,調(diào)用addLabel().
在類的底部,添加如下函數(shù)
- func addLabel() {
- delegate?.animateLabel()
- }
這里只是簡(jiǎn)單的調(diào)用視圖的代理函數(shù),展示label標(biāo)簽。
現(xiàn)在打開ViewController.swift,添加如下代碼到animateLabel():
- func animateLabel() {
- // 1
- holderView.removeFromSuperview()
- view.backgroundColor = Colors.blue
- // 2
- var label: UILabel = UILabel(frame: view.frame)
- label.textColor = Colors.white
- label.font = UIFont(name: "HelveticaNeue-Thin", size: 170.0)
- label.textAlignment = NSTextAlignment.Center
- label.text = "S"
- label.transform = CGAffineTransformScale(label.transform, 0.25, 0.25)
- view.addSubview(label)
- // 3
- UIView.animateWithDuration(0.4, delay: 0.0, usingSpringWithDamping: 0.7, initialSpringVelocity: 0.1, options: UIViewAnimationOptions.CurveEaseInOut,
- animations: ({
- label.transform = CGAffineTransformScale(label.transform, 4.0, 4.0)
- }), completion: { finished in
- self.addButton()
- })
- }
依次帶入各個(gè)注釋段
-
從視圖中移除HolderView ,并設(shè)置視圖的背景顏色為藍(lán)色。
-
創(chuàng)建一個(gè)文本為"S"的UIlabel標(biāo)簽對(duì)象,用于展示logo,并添加到視圖。
-
標(biāo)簽對(duì)象使用一個(gè)彈性動(dòng)畫來(lái)使之伸縮。一旦動(dòng)畫結(jié)束,調(diào)用 addButton() 來(lái)添加一個(gè)按鈕到視圖中,當(dāng)按鈕按下的時(shí)候,重復(fù)動(dòng)畫。
構(gòu)建并運(yùn)行應(yīng)用程序,給自己點(diǎn)個(gè)贊,花個(gè)時(shí)間來(lái)欣賞自己構(gòu)建的動(dòng)畫吧。
下一步
你可以從這里 下載 最終完整的項(xiàng)目。
這個(gè)指導(dǎo)書包含了相當(dāng)多的不一樣的動(dòng)畫技術(shù),當(dāng)這些動(dòng)畫都堆疊在一起的時(shí)候,能夠創(chuàng)造一個(gè)相當(dāng)復(fù)雜的加載動(dòng)畫,這確實(shí)能夠讓你的應(yīng)用在第一次被(用戶)運(yùn)行的時(shí)候就眼前一亮。
從這里,放松自由的玩玩不一樣的(動(dòng)畫 的)定時(shí)和形狀,看看你能組裝成哪些很酷的動(dòng)畫
如果你想讓你新發(fā)現(xiàn)的動(dòng)畫技術(shù)提升一個(gè)檔次,那我建議你看下我們的(這本)書iOS Animations by Tutorials.
我希望通過(guò)這本指導(dǎo)書你能得到極大的樂(lè)趣,并且,如果你有任何問(wèn)題或者建議,請(qǐng)加入我們下面的論壇討論吧