
花了將近一周的時間去學習ios動畫,因為對于一個ios開發(fā)者來說,動畫內(nèi)容絕對是一門必修課。聽了不少課,也看了不少文章,終于對動畫有了初步的了解和自己的一些小總結(jié)。但是傻傻笨笨的我,給自己挖了一個坑,為了填這個坑花了快兩天時間,真夠笨的!不過最終還是完美解決,小小成就感就來了!
關(guān)于動畫,網(wǎng)上流傳著許許多多的文章,基本上都適合初學者入門。那些文章大概思路都是這樣的:
1.介紹什么是動畫
2.動畫可以分為UIView動畫和CA動畫。(其他動畫暫時忽略)
3.UIView動畫分為常規(guī)模式和閉包模式?,F(xiàn)在主要用閉包模式。
4.UIView閉包模式有基本“杜蕾斯”動畫,杜蕾斯彈性動畫,轉(zhuǎn)場動畫,關(guān)鍵幀動畫。
5.CA動畫有基本動畫,轉(zhuǎn)場動畫,關(guān)鍵幀動畫,組動畫,彈性動畫。
6.UIView動畫和CA動畫的關(guān)系,即UIView動畫是CA動畫的封裝。各有優(yōu)勢各有特色。
無可否認的是這些對自己在初步認識動畫階段,起到了很大的幫助作用,起碼讓自己對動畫有個大概的了解。但是僅僅這些,還是會讓初學者掉進坑里,比如我。所以學習動畫以后的總結(jié)經(jīng)驗,就不可或缺了。這才不會讓自己第二次掉進同一個坑。待會我要記錄下自己怎么掉坑,填坑的。
我是先學習CA動畫的,明白了CA動畫能夠細微調(diào)整的意義,也見識了CA動畫是怎么讓開發(fā)者去掌控每一個環(huán)節(jié)的。這個過程中我也筆記了基礎(chǔ)的知識:(有一部分摘抄,一部分是自己總結(jié))
//CAAnimation:
//所有動畫對象的父類,負責控制動畫的持續(xù)時間和速度,是個抽象類,不能直接使用,應(yīng)該使用它具體的子類
//duration:動畫的持續(xù)時間
//repeatCount:動畫的重復次數(shù)
//repeatDuration:動畫的重復時間
//removedOnCompletion:默認為YES,代表動畫執(zhí)行完畢后就從圖層上移除,圖形會恢復到動畫執(zhí)行前的狀態(tài)。如果想讓圖層保持顯示動畫執(zhí)行后的狀態(tài),那就設(shè)置為NO,不過還要設(shè)置fillMode為kCAFillModeForwards
//fillMode:決定當前對象在非active時間段的行為.比如動畫開始之前,動畫結(jié)束之后
//beginTime:可以用來設(shè)置動畫延遲執(zhí)行時間,若想延遲2s,就設(shè)置為CACurrentMediaTime()+2,CACurrentMediaTime()為圖層的當前時間
//timingFunction:速度控制函數(shù),控制動畫運行的節(jié)奏
//delegate:動畫代理
//keyPath: 通過指定CALayer的一個屬性名稱達到相應(yīng)的動畫效果,比如說,指定"position"為keyPath,就修改CALayer的position屬性值,以達到平移的動畫效果
//A. CGAffineTransform
//從CG就可以看出它是屬于Core Graphics的東西,實際上UIView的transform屬性就是CGAffineTransform類型,用它可以做二維平面上的縮放、旋轉(zhuǎn)、平移。
//B.CATransform3D(layer)
//它可以做到讓圖層在三維空間內(nèi)平移、旋轉(zhuǎn)等。
- CABasicAnimation動畫比較簡單,不做多介紹,只留筆記重點。如果有誤,歡迎指正。
//A.CABasicAnimation
//CABasicAnimation(keyPath: "transform")可以實現(xiàn)2D和3D動畫。取決于keyPath。
//如果keyPath: "transform" 則是3D動畫。rotation屬性才能體現(xiàn)3D效果
//如果keyPath: "transform.rotation" 則是2D動畫。
//2D動畫變換前的原始狀態(tài)。view.transform = CGAffineTransformIdentity
//3D動畫變換前的原始狀態(tài)view.layer.transform = CATransform3DIdentity
-
CAKeyFrameAnimation動畫功能強大,有必要對其屬性闡述一下。
//B. CAKeyFrameAnimation
//CApropertyAnimation的子類,跟CABasicAnimation的區(qū)別是: CABasicAnimation只能從一個數(shù)值(fromValue)變到另一個數(shù)值(toValue),而CAKeyframeAnimation會使用一個NSArray保存這些數(shù)值
//屬性解析:
//values:就是上述的NSArray對象。里面的元素稱為”關(guān)鍵幀”(keyframe)。動畫對象會在指定的時間(duration)內(nèi),依次顯示values數(shù)組中的每一個關(guān)鍵幀
//path:可以設(shè)置一個CGPathRef\CGMutablePathRef,讓層跟著路徑移動。path只對CALayer的anchorPoint和position起作用。如果你設(shè)置了path,那么values將被忽略
//keyTimes:可以為對應(yīng)的關(guān)鍵幀指定對應(yīng)的時間點,其取值范圍為0到1.0,keyTimes中的每一個時間值都對應(yīng)values中的每一幀.當keyTimes沒有設(shè)置的時候,各個關(guān)鍵幀的時間是平分的
//CABasicAnimation可看做是最多只有2個關(guān)鍵幀的CAKeyframeAnimation
//這里有必要提供一下快速構(gòu)建values的方法
let arr = [(20,30),(100,100),(100,300),(50,300)].map{ (x:Int,y:Int) -> NSValue in
NSValue(CGPoint: CGPoint(x: x, y: y))} keyAnimate.values = arr CAAnimationGroup也比較簡單,就是對多個動畫的組合。
//C. CAAnimationGroup
//CAAnimation的子類,可以保存一組動畫對象,將CAAnimationGroup對象加入層后,組中所有動畫對象可以同時并發(fā)運行.支持多個動畫組合。
//屬性解析:
//animations:用來保存一組動畫對象的NSArray
//默認情況下,一組動畫對象是同時運行的,也可以通過設(shè)置動畫對象的beginTime屬性來更改動畫的開始時間CATransition是一個比較有意思的動畫,轉(zhuǎn)場效果挺多。
//D. CATransition
//CAAnimation的子類,用于做轉(zhuǎn)場動畫,能夠為層提供移出屏幕和移入屏幕的動畫效果。iOS比Mac OS X的轉(zhuǎn)場動畫效果少一點
//UINavigationController就是通過CATransition實現(xiàn)了將控制器的視圖推入屏幕的動畫效果
//屬性解析:
//type:動畫過渡類型
/*
fade
push
moveIn
reveal
cube
oglFlip
suckEffect
rippleEffect
pageCurl
pageUnCurl
cameraIrisHollowOpen
cameraIrisHollowClose
*/
//subtype:動畫過渡方向
//startProgress:動畫起點(在整體動畫的百分比)
//endProgress:動畫終點(在整體動畫的百分比)-
CASpringAnimation是彈性動畫,能夠表現(xiàn)出非常性感細膩的效果
//E.CASpringAnimation
屬性:默認值
damping:10.0
mass :1.0
stiffness:100.0
initialVelocity:0.0let sprintAni = CASpringAnimation(keyPath: "position.y") sprintAni.damping = 10 sprintAni.mass = 5 sprintAni.stiffness = 50 sprintAni.initialVelocity = 3 sprintAni.duration = 2 sprintAni.toValue = 300 sprintAni.fillMode = kCAFillModeForwards sprintAni.removedOnCompletion = false yourView.layer.addAnimation(sprintAni, forKey: "anykey")
以上是CA動畫的類型,我學習完它再去學UIView動畫,所以知道UIView動畫其實就是CA動畫的封裝,優(yōu)點是快捷方便,UIView的彈性動畫完美體現(xiàn)了這點。缺點是不能細微調(diào)整。這里不作UIView的詳細介紹。
雖然UIView動畫是對CA核心動畫的封裝,但還是有必要對他們加以總結(jié),這可是目前在網(wǎng)上找不到的寶貴經(jīng)驗哦?。ń?jīng)驗可能有誤,歡迎指正)
//UIView的動畫跟CAAnimation動畫的異同:
//1.都能控制動畫開始執(zhí)行時刻。uiview的delay。CA中的beginTime。
//2.都能在動畫結(jié)束后實現(xiàn)控制。uiview有閉包。CA中有代理函數(shù)didFinish。
//3.uiView有彈性動畫和關(guān)鍵幀動畫,CA中也有,而且更為豐富。
//4.uiView組合動畫用cgaffinetransformconcat。CA中用CAanimationGroup,并支持多組合。
// uiview 的多組合則可以通過創(chuàng)建多個uiview.animation來實現(xiàn)。多是指兩個以上。
// 這里的組合是指為同一個對象的不同屬性進行組合。
// 如果是不同對象要實現(xiàn)同一個動畫,則直接在uiview的內(nèi)容中添加。或者直接在CA中賦予多個對象的layer。
//5.uiView實現(xiàn)2D或者3D動畫,取決于里面設(shè)置的動畫屬性。若設(shè)置的是layer層,則可以實現(xiàn)3D動畫。(rotation屬性可以體現(xiàn))
// CA動畫則取決于key。如果是transform,則可以3D.如果是transform.rotation,則可以是2D。
// CA中transform屬性有rotation,scale,translation。
// CA中key還可以是bounds,position,opacity。這里的position等價于uiview的center。
//6.uiview動畫完畢之后屬性已經(jīng)更改。CA動畫則不會改變實際位置,即使表面改變了。
下面我則要記錄下我在學習UIView動畫的時候是怎么給自己挖坑的,并怎么最終把坑填上獲得小小成就感的。其實當完美解決問題的那一刻,發(fā)現(xiàn)代碼是如此的簡單,可就為了那一段代碼,讓我費勁了力氣,花盡了腦汁才得以解決。只怪自己經(jīng)驗不足咯!都說怪我咯希望能幫助到有同樣困惑的人兒
關(guān)鍵字:中斷,終止,中止,取消,停止UIView動畫
問題發(fā)現(xiàn):
- UIView動畫在duration內(nèi),也就是正在執(zhí)行的過程中,我再次觸發(fā)了同樣的動畫,此時動畫就會不正常顯示。
問題起源:
- 發(fā)現(xiàn)這個問題的時候,其實很多人就想到可能會轉(zhuǎn)用CA動畫去實現(xiàn),因為CA動畫在執(zhí)行過程中,再次觸發(fā)的話,它會重新來過,并不會出現(xiàn)錯亂。我也是想到了這個辦法,但是我就想知道在UIView動畫中怎么解決這個問題的!所以問題就這樣起源了~
問題解決:
- 1.首先肯定是想到再次觸發(fā)前先把上一次動畫取消掉,想想應(yīng)該是很快就把問題給解決了吧,因為從邏輯上并沒有什么錯誤。于是我觸發(fā)的前面加了一句self.textView?.layer.removeAllAnimations()
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
self.textView?.layer.removeAllAnimations()
self.animation()
}
可是問題真的解決了嗎?當然不是。添加后的現(xiàn)象是我再次觸發(fā)時,動畫立馬停止了??雌饋砗竺鎠elf.animation()并沒有執(zhí)行一樣。
2.于是我開始請教百度叫獸,失望的是幾乎把百度翻了個遍,也沒找到答案。納悶了,難道只有我才遇到這個問題嗎?只有我經(jīng)驗淺腦子笨才掉這個坑嗎?唯一在網(wǎng)上找到一個相關(guān)的文章《如何中止UIView動畫?》
http://samwei12.gitcafe.io/2015/09/09/%E5%A6%82%E4%BD%95%E5%8F%96%E6%B6%88UIView%E5%8A%A8%E7%94%BB/ 簡書上也有。又是OC版本的,OC就OC吧,抱著一線希望把OC代碼轉(zhuǎn)換成Swift后,一執(zhí)行丫的還是不管用!夢想再次破滅~3.這時想到了swift交流群,在群上一問三不知,這該如何是好。大神都不出來幫我~
4.還是自己找原因吧。在UIView動畫執(zhí)行完的閉包里面添加一些打印信息吧。于是我添加了print("finish")
{ (finish:Bool) -> Void in
if finish {print("finish")}
}
此時我在第二次又觸發(fā)動畫的時候發(fā)現(xiàn),只打印了一次finish!這finish是第一次動畫執(zhí)行的還是第二次動畫執(zhí)行的?從現(xiàn)象上就很好解釋了,肯定是第二次動畫打印的finish。而且還有一個現(xiàn)象就是,第二次觸發(fā)動畫的時候,立馬就打印finish,這也就是為什么看不到第二次動畫的執(zhí)行!原來本意是要停掉上一次正在執(zhí)行的動畫,再接著執(zhí)行第二次動畫?,F(xiàn)在問題是第二次也被停掉了?。?!到底問題出現(xiàn)在哪里???靈光一閃,突然想到了延時!就是停掉第一次動畫的時候,延時一下,再執(zhí)行第二次動畫看行不行?
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
self.textView?.layer.removeAllAnimations()
self.performSelector("animation", withObject: nil, afterDelay: 0.3)
}5.duang~的一下,成功了!只要在執(zhí)行動畫的前面添加一個細小的延時,就可以完美解決了這個可惡的問題!后面我再測了一下,把afterDelay改成0 ,也同樣成功了,這這又如何解釋,就留給大家吧
問題回首:
- 解決的代碼非常簡單,可對我來說真的容易么?在沒有百度君簡書君的幫助下,孤軍奮戰(zhàn),血戰(zhàn)到底,我容易么?
有了UIView動畫的填坑經(jīng)驗,我自個再到CA中去解決類似問題就迎刃而解了!什么?剛剛不是說CA中不存在這種問題嗎??這里有必要說明一點,就是當CA動畫是非無止境動畫(就是會停止的動畫),在還沒停止之前再次觸發(fā),是不會發(fā)生這些錯亂問題的。
然而要是CA動畫是個無止境的動畫,也就是如果動畫委托協(xié)議的animationDidStop中再次調(diào)用動畫函數(shù)的話,這時再來個觸發(fā)相同動畫,動畫就是亂得一塌糊涂了!接下來就記錄一下怎么輕松解決這個問題的。
@IBAction func next(sender: AnyObject) {
self.transition()
}
func transition(){
let transition = CATransition()
transition.delegate = self
//動畫過渡類型
transition.type = "pageCurl"
//動畫過渡類型方向
transition.subtype = kCATransitionFromLeft
transition.duration = 1
transition.setValue("second", forKey: "whichAnimation")
self.iv.layer.addAnimation(transition, forKey: nil)
}
override func animationDidStop(anim: CAAnimation, finished flag: Bool) {
switch anim.valueForKey("whichAnimation") as! String{
case "one" :
print("hello")
case "second" :
print("finish")
self.next("repeat")
default :print("grandre")
}
}
這樣的代碼確實能夠運行,能夠循環(huán)調(diào)用動畫,實現(xiàn)無止境。但是問題是當再次點擊按鍵觸發(fā)動畫的話,這代碼的bug就一漏無遺了。
- 解決初探1:再次觸發(fā)之前,去掉所有動畫。
@IBAction func next(sender: AnyObject) {
self.iv.layer.removeAllAnimations()
self.transition()
}
結(jié)果:失敗。原因:self.iv.layer.removeAllAnimations()執(zhí)行后會調(diào)用委托協(xié)議,導致死循環(huán)。
-
解決初探2:添加“是否自動完成動畫”標志。如果是自動完成一輪動畫,則執(zhí)行委托協(xié)議代碼,如果不是自動完成,則不執(zhí)行。從而避免了死循環(huán)。
@IBAction func next(sender: AnyObject) {
ifAutoFinishAnimate = false
self.iv.layer.removeAllAnimations()//這里沒打印是因為標志置false了
self.transition()
}
func transition(){
let transition = CATransition()
transition.delegate = self
//動畫過渡類型
transition.type = "pageCurl"//動畫過渡類型方向 transition.subtype = kCATransitionFromLeft transition.duration = 1 // 一定要在加載動畫之前設(shè)置setValue transition.setValue("second", forKey: "whichAnimation") self.iv.layer.addAnimation(transition, forKey: nil) ifAutoFinishAnimate = true //動畫完成之后恢復標志,才能執(zhí)行委托協(xié)議代碼 } override func animationDidStop(anim: CAAnimation, finished flag: Bool) { if ifAutoFinishAnimate == true{ switch anim.valueForKey("whichAnimation") as! String{ case "one" :print("hello") case "second" :print("finish") self.next("2") default :print("baba") } } }結(jié)果:失??!現(xiàn)象是“根本停不下來!”此時原因應(yīng)該就是UIView動畫的原因一樣了!
- 解決初探3:添加延時。
@IBAction func next(sender: AnyObject) {
ifAutoFinishAnimate = false
self.iv.layer.removeAllAnimations()//這里沒打印是因為標志置false了
performSelector("transition", withObject: nil, afterDelay: 0.3)
}
結(jié)果:Done!完美解決!這經(jīng)驗真管用!afterDelay改成0,這次就不行咯!至于為什么,同樣留給大家思考吧。所以記錄所遇到的問題并加以總結(jié)是對自己非常有幫助的。