現在在蘋果應用商店上有超過140萬的App,想讓你的app事件非常具有挑戰的事情。你有這樣一個機會,在你的應用的數據完全加載出來之前,你可以通過一個很小的窗口來捕獲用戶的關注。
沒有比這個更好的地方讓用戶大為贊嘆,當你的應用程序載入畫面的時候,你可以添加一個愉快的動畫,作為先導,以加載你的應用。
在本教程中,你將學習如何做出這樣的動畫。你將學習如何建立piece-by-piece,采用前衛的技術來創建一個流體和迷人的動畫。
下載本教程開始的工程,把它保存到一個方便的地方,并在Xcode中打開。
打開HolderView.swift。在這個UIView的子類中,添加下面的子層在動畫的上面展示:
OvalLayer.swift:這是第一層,從0慢慢變大,然后很短的時間抖動
TriangleLayer.swift:當OvalLayer層正在抖動的時候出現,當這層旋轉的時候,OvalLayer收縮到沒有,只讓TriangleLayer可視
RectangleLayer.swift:這層作為TriangleLayer可視的容器
ArcLayer.swift:這層填充RectangleLayer通過一個動畫,類似于玻璃中充滿水的樣子
打開OvalLayer.swift,最初的項目已經包含了初始化這些層和所有在你的動畫中將使用的貝塞爾路徑的代碼。你會看到expand(),wobble()和contract()這些方法里面都是空的,通過這個教程你將填充這些方法。所有其他的*Layer的文件都是類似的功能結構。
Note:如果你想學習關于貝塞爾路徑的知識,可以點擊這個教程Modern Core Graphics with Swift
最后,打開ViewController.swift,看到addHolderView(),這個方法中添加了HolderView的實例對象,作為控制器視圖的中心的子視圖。這個視圖將包含所有的動畫。該視圖控制器只需要將它放在屏幕上,然后該視圖將執行實際的動畫的代碼。
animateLabel()方法是有HolderView類提供的代理回調,當你完成動畫的次序時,你將會填充它。
addButton()僅僅是在視圖上增加了一個按鈕,以便您可以點擊并重新啟動動畫。
構建并運行該項目,你應該看到一個空白的屏幕。這是件完美的事情在空白的畫布上開始制作你新的動畫!
在教程的最后,你的app將會是下面這樣的:
因此,事不宜遲,讓我們開始吧!
動畫開始有個紅色的橢圓形,然后擴展到視圖的中心,然后微微搖晃。
打開HolderView.swift,然后在HolderView上方附近定義下面的常量:
let?ovalLayer?=?OvalLayer()
添加下面的方法:
func?addOval()?{
layer.addSublayer(ovalLayer)
ovalLayer.expand()
}
你第一次添加的OvalLayer的實例作為視圖層的子視圖。
在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)
}
這個方法創建了一個CABasicAnimation實例,來改變橢圓的路徑從ovalPathSmall到ovalPathLarge。初始的項目中為你提供了這些貝塞爾路徑。設置removedOnCompletion為false,fillMode的值為KCAFillModeForwards,在動畫中一旦動畫完成,讓橢圓開辟一個新的路徑。
最后,打開ViewController.swift,添加下面那一行到addHolderView(),就在view.addSubview(holderView)的下面:
holderView.addOval()
addOval在橢圓被添加到控制器的視圖中后開始動畫。
構建并運行你的app,你的動畫將會顯示下面這樣:
在橢圓被添加到視圖中并擴展后,下一步就是在步驟中加一些反彈,讓其擺動。
打開HolderView.swift,然后添加下面的方法:
func?wobbleOval()?{
ovalLayer.wobble()
}
這個方法是讓其在OvalLayer中擺動。
打開OvalLayer.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)
}
這里有很多的代碼,但它分解的很好,來看下這里干了些什么:
1、從遠處到垂直壓扁
2、從垂直壓扁改為水平和垂直都壓扁
3、切換回垂直壓扁
4、完成動畫,結束回到最開始的位置
5、將所有的動畫整合到CAAnimationGroup中,然后將這組動畫添加到OvalLayout中。
每個后續動畫的beginTime是先前動畫的beginTime和其持續時間的總和。重復這個動畫組兩次,就會看到搬動稍微拉長。
即時你現在添加了所有搖晃動畫的代碼,你也看不到你想看到的動畫。
回到HolderView.swift中,添加下面的代碼到addOval()中:
NSTimer.scheduledTimerWithTimeInterval(0.3,?target:?self,?selector:?"wobbleOval",
userInfo:?nil,?repeats:?false)
這里創建了一個定時器,在OvalLayer完成擴展動畫后,調用wobbleOval()
構建并運行你的app,看下現在的動畫:
這是非常微妙的,但這是一個非常重要的因素去創建一個真正令人愉快的動畫。你不需要讓其充滿整個屏幕!
即將看到神奇的事情!你即將把橢圓變成三角形。在用戶的眼中,這個轉變應該看上去是完全無縫的。那么你需要使用兩個顏色相同的單獨的形狀來實現它。
打開HolderView.swift,添加下面的代碼到HolderView中,就在你之前添加的ovalLayer常量的下面:
let?triangleLayer?=?TriangleLayer()
這里定義了一個TriangleLayer實例,跟你之前定義的ovalLayer一樣。
現在,實現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)
}
這段代碼做了下面這些事情:
1、該行添加了前面初始化的TriangleLayer實例,作為HolderView層里的一個子層
2、之前知道了擺動動畫運行兩次的總共時間是1.8,中間的地方將會是一個非常好的地方去開始變形處理。因此,添加一個計時器,在延遲0.9后,執行drawAnimatedTriangle()方法。
接下來,添加下面的功能:
func?drawAnimatedTriangle()?{
triangleLayer.animate()
}
這個方法是實現剛剛寫的定時器的功能,它讓三角形開始動。
現在,打開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)
}
這段代碼讓三角形的角伴隨著OvalLayer層的晃動一個一個出來;在最初的項目中貝塞爾路徑已經定義了每個角。最左邊的角第一個出來,接著是右邊的,最后是最上面的那個角。通過創建三個基于CABasicAnimation的實例,添加到CAAnimationGroup中。
構建并運行你的app,將會看到目前的動畫狀態;當橢圓晃動的時候,三角形的每個角開始出來知道三個角全部顯示出來,就像這樣:
要實現其變形,你需要讓HolderView360度旋轉當OvalLayer收縮的時候,最終僅留下TriangleLayer
打開HolderView.swift,添加下面的代碼在drawAnimatedTriangle()的最后:
NSTimer.scheduledTimerWithTimeInterval(0.9,?target:?self,?selector:?"spinAndTransform",
userInfo:?nil,?repeats:?false)
在三角形動畫完成后設置一個定時器,0.9秒的這個時間也是一次次通過嘗試而得到的。
現在,添加下面的方法:
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()
}
剛剛創建的定時器一旦橢圓晃動停止并且三角形的每個角都出現時調用這個函數。下面看下這個函數的詳細功能:
1、更新該層的錨點略低于視圖的中心。這讓旋轉閑的更加自然。這是因為,橢圓和三角形事實上以垂直方向從視圖的中心偏離。因此,如果視圖繞著中心旋轉,那么橢圓和三角似乎是垂直移動的。
2、CABasicAnimation讓圖層旋轉360度或者2π弧度。旋轉是繞著z軸,垂直于屏幕的表面進入和移出。
3、OvalLayer調用contract()方法執行動畫,縮減橢圓的大小直至看不見。
現在,打開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)
}
這是讓OvalLayer返回最開始的路徑上ovalPathSmall,通過執行一個CABasicAnimation動畫。這是跟之前調用的動畫是相反的。
構建并運行你的app;一旦動畫結束后,就只剩下三角形在屏幕的中間。
在接下來的部分中,你將會繪制一個長方形的容器來創建一個外殼。要做到這一點,你需要使用RectangleLayer的畫筆。你將要做兩次,通過使用紅色和藍色作為筆的顏色。
打開HolderView.swift,然后定義兩個RectangularLayer常量,如下,在triangleLayer的下方:
let?redRectangleLayer?=?RectangleLayer()
let?blueRectangleLayer?=?RectangleLayer()
接下來添加下面的代碼在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)
這里你創建了兩個定時器分別調用drawRedAnimatedRectangle()和drawBlueAnimatedRectangle()。
首先畫紅色的矩形框,之后完成向右的一個旋轉動畫。在紅色的矩形框繪制接近完成的時候開始繪制藍色的矩形框。
添加下面的兩個方法:
func?drawRedAnimatedRectangle()?{
layer.addSublayer(redRectangleLayer)
redRectangleLayer.animateStrokeWithColor(Colors.red)
}
func?drawBlueAnimatedRectangle()?{
layer.addSublayer(blueRectangleLayer)
blueRectangleLayer.animateStrokeWithColor(Colors.blue)
}
一旦你添加RectangleLayer作為HolderView的一個子層時,就開始調用animateStrokeWithColor(color:),并開始執行繪制邊框的動畫。
現在打開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)
}
這里通過添加一個CABasicAnimation去繪制一個筆。CAShapeLayer中的strokeEnd指示著畫筆去描繪這個矩形框。
構建并運行你的app,將會看到下面的動畫:
現在有了容器,下一步就是將它填補起來。這個效果類似于水填充滿玻璃,這是一個偉大的詩視覺效果!
打開HolderView.swift,就在RectangleLayer屬性的后面,添加一個常量:
let?arcLayer?=?ArcLayer()
添加下面的代碼在drawBlueAnimatedRectangle()的最后面:
NSTimer.scheduledTimerWithTimeInterval(0.40,?target:?self,?selector:?"drawArc",
userInfo:?nil,?repeats:?false)
這里創建了一個計時器,一旦藍色的RectangleLayer繪制完成后便去調用drawArc()。
添加下面的方法:
func?drawArc()?{
layer.addSublayer(arcLayer)
arcLayer.animate()
}
在執行動畫之前,這里增加了一個在HolderView層上已經創建了的ArcLayer的實例。
打開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)
}
這個動畫非常類似于之前的擺動的動畫;創建了一個CAAnimationGroup,包含5個基于路徑的CABasicAnimation實例。每個路徑隨著高度的增加都有一個稍微不同的弧度。最后,將CAAnimationGroup應用到圖層中,并指示著它不被移除,在動畫完成之前將保持這個狀態。
構建并運行你的app,將會看到神奇的一幕!
剩下的事情就是將藍色的HolderView擴大直至填充滿整個屏幕,并在視圖添加一個UILabel作為logo。
打開HolderView.swift,添加下面的代碼到drawArc()的最后面:
NSTimer.scheduledTimerWithTimeInterval(0.90,?target:?self,?selector:?"expandView",
userInfo:?nil,?repeats:?false)
這里創建了一個計時器,當ArcLayer填充滿容器的時候調用expandView()。
現在,添加下面的方法:
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()
})
}
該方法做了寫什么事情呢:
1、holder view的背景被設置為藍色,和你之前填充的矩形框的顏色一樣;
2、這個frame擴大到之前設定的RectangleLayer畫筆的寬度;
3、所有的子層都被移除。現在沒有橢圓,沒有三角形,沒有矩形圖層;
4、添加一個動畫,讓HolderView擴大至填充滿整個屏幕。一旦該動畫執行了,你需要調用addLabel()方法;
在下面添加該方法:
func?addLabel()?{
delegate?.animateLabel()
}
動畫顯示這個Label僅僅使用了視圖的委托動能。
現在打開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()
})
}
依次注釋每個部分:
1、將HolderView從視圖上移除,并設置視圖的背景顏色為藍色;
2、創建UILabel,內容為“S”,作為logo,添加到視圖中;
3、執行一個彈簧動畫顯示那個Label。一旦動畫執行完,調用addButton()方法,在視圖中添加一個按鈕,當點擊時,重復顯示該動畫過程。
構建并運行你的app,給自己鼓掌吧,然后花點時間來享受自己實現了什么!
你可以到這里下載完整的項目
由于第一次翻譯外文,可能很多缺陷和不足,求各路大神指正!無比感謝!原文地址
此譯文已發表在我的CSDN博客上zmp1123
另外,獲取更多的iOS開發的相關資料、資訊、課程,可關注iOS開發者開發者公眾平臺!