花了將近一周的時(shí)間去學(xué)習(xí)ios動(dòng)畫蛔趴,因?yàn)閷τ谝粋€(gè)ios開發(fā)者來說挑辆,動(dòng)畫內(nèi)容絕對是一門必修課。聽了不少課孝情,也看了不少文章鱼蝉,終于對動(dòng)畫有了初步的了解和自己的一些小總結(jié)。但是傻傻笨笨的我箫荡,給自己挖了一個(gè)坑魁亦,為了填這個(gè)坑花了快兩天時(shí)間,真夠笨的羔挡!不過最終還是完美解決洁奈,小小成就感就來了!
關(guān)于動(dòng)畫绞灼,網(wǎng)上流傳著許許多多的文章利术,基本上都適合初學(xué)者入門。那些文章大概思路都是這樣的:
1.介紹什么是動(dòng)畫
2.動(dòng)畫可以分為UIView動(dòng)畫和CA動(dòng)畫低矮。(其他動(dòng)畫暫時(shí)忽略)
3.UIView動(dòng)畫分為常規(guī)模式和閉包模式∮∪現(xiàn)在主要用閉包模式。
4.UIView閉包模式有基本“杜蕾斯”動(dòng)畫,杜蕾斯彈性動(dòng)畫喉钢,轉(zhuǎn)場動(dòng)畫,關(guān)鍵幀動(dòng)畫良姆。
5.CA動(dòng)畫有基本動(dòng)畫肠虽,轉(zhuǎn)場動(dòng)畫,關(guān)鍵幀動(dòng)畫玛追,組動(dòng)畫税课,彈性動(dòng)畫。
6.UIView動(dòng)畫和CA動(dòng)畫的關(guān)系痊剖,即UIView動(dòng)畫是CA動(dòng)畫的封裝韩玩。各有優(yōu)勢各有特色。
無可否認(rèn)的是這些對自己在初步認(rèn)識動(dòng)畫階段陆馁,起到了很大的幫助作用找颓,起碼讓自己對動(dòng)畫有個(gè)大概的了解。但是僅僅這些叮贩,還是會(huì)讓初學(xué)者掉進(jìn)坑里击狮,比如我。所以學(xué)習(xí)動(dòng)畫以后的總結(jié)經(jīng)驗(yàn)益老,就不可或缺了彪蓬。這才不會(huì)讓自己第二次掉進(jìn)同一個(gè)坑。待會(huì)我要記錄下自己怎么掉坑捺萌,填坑的档冬。
我是先學(xué)習(xí)CA動(dòng)畫的,明白了CA動(dòng)畫能夠細(xì)微調(diào)整的意義桃纯,也見識了CA動(dòng)畫是怎么讓開發(fā)者去掌控每一個(gè)環(huán)節(jié)的酷誓。這個(gè)過程中我也筆記了基礎(chǔ)的知識:(有一部分摘抄,一部分是自己總結(jié))
//CAAnimation:
//所有動(dòng)畫對象的父類慈参,負(fù)責(zé)控制動(dòng)畫的持續(xù)時(shí)間和速度呛牲,是個(gè)抽象類,不能直接使用驮配,應(yīng)該使用它具體的子類
//duration:動(dòng)畫的持續(xù)時(shí)間
//repeatCount:動(dòng)畫的重復(fù)次數(shù)
//repeatDuration:動(dòng)畫的重復(fù)時(shí)間
//removedOnCompletion:默認(rèn)為YES娘扩,代表動(dòng)畫執(zhí)行完畢后就從圖層上移除,圖形會(huì)恢復(fù)到動(dòng)畫執(zhí)行前的狀態(tài)壮锻。如果想讓圖層保持顯示動(dòng)畫執(zhí)行后的狀態(tài)琐旁,那就設(shè)置為NO,不過還要設(shè)置fillMode為kCAFillModeForwards
//fillMode:決定當(dāng)前對象在非active時(shí)間段的行為.比如動(dòng)畫開始之前,動(dòng)畫結(jié)束之后
//beginTime:可以用來設(shè)置動(dòng)畫延遲執(zhí)行時(shí)間猜绣,若想延遲2s灰殴,就設(shè)置為CACurrentMediaTime()+2,CACurrentMediaTime()為圖層的當(dāng)前時(shí)間
//timingFunction:速度控制函數(shù)掰邢,控制動(dòng)畫運(yùn)行的節(jié)奏
//delegate:動(dòng)畫代理
//keyPath: 通過指定CALayer的一個(gè)屬性名稱達(dá)到相應(yīng)的動(dòng)畫效果牺陶,比如說伟阔,指定"position"為keyPath,就修改CALayer的position屬性值掰伸,以達(dá)到平移的動(dòng)畫效果
//A. CGAffineTransform
//從CG就可以看出它是屬于Core Graphics的東西皱炉,實(shí)際上UIView的transform屬性就是CGAffineTransform類型,用它可以做二維平面上的縮放狮鸭、旋轉(zhuǎn)合搅、平移。
//B.CATransform3D(layer)
//它可以做到讓圖層在三維空間內(nèi)平移歧蕉、旋轉(zhuǎn)等灾部。
- CABasicAnimation動(dòng)畫比較簡單,不做多介紹惯退,只留筆記重點(diǎn)赌髓。如果有誤,歡迎指正蒸痹。
//A.CABasicAnimation
//CABasicAnimation(keyPath: "transform")可以實(shí)現(xiàn)2D和3D動(dòng)畫春弥。取決于keyPath。
//如果keyPath: "transform" 則是3D動(dòng)畫叠荠。rotation屬性才能體現(xiàn)3D效果
//如果keyPath: "transform.rotation" 則是2D動(dòng)畫匿沛。
//2D動(dòng)畫變換前的原始狀態(tài)影斑。view.transform = CGAffineTransformIdentity
//3D動(dòng)畫變換前的原始狀態(tài)view.layer.transform = CATransform3DIdentity
-
CAKeyFrameAnimation動(dòng)畫功能強(qiáng)大动壤,有必要對其屬性闡述一下。
//B. CAKeyFrameAnimation
//CApropertyAnimation的子類习瑰,跟CABasicAnimation的區(qū)別是: CABasicAnimation只能從一個(gè)數(shù)值(fromValue)變到另一個(gè)數(shù)值(toValue)者娱,而CAKeyframeAnimation會(huì)使用一個(gè)NSArray保存這些數(shù)值
//屬性解析:
//values:就是上述的NSArray對象抡笼。里面的元素稱為”關(guān)鍵幀”(keyframe)。動(dòng)畫對象會(huì)在指定的時(shí)間(duration)內(nèi)黄鳍,依次顯示values數(shù)組中的每一個(gè)關(guān)鍵幀
//path:可以設(shè)置一個(gè)CGPathRef\CGMutablePathRef,讓層跟著路徑移動(dòng)推姻。path只對CALayer的anchorPoint和position起作用。如果你設(shè)置了path框沟,那么values將被忽略
//keyTimes:可以為對應(yīng)的關(guān)鍵幀指定對應(yīng)的時(shí)間點(diǎn),其取值范圍為0到1.0,keyTimes中的每一個(gè)時(shí)間值都對應(yīng)values中的每一幀.當(dāng)keyTimes沒有設(shè)置的時(shí)候,各個(gè)關(guān)鍵幀的時(shí)間是平分的
//CABasicAnimation可看做是最多只有2個(gè)關(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也比較簡單藏古,就是對多個(gè)動(dòng)畫的組合。
//C. CAAnimationGroup
//CAAnimation的子類忍燥,可以保存一組動(dòng)畫對象拧晕,將CAAnimationGroup對象加入層后,組中所有動(dòng)畫對象可以同時(shí)并發(fā)運(yùn)行.支持多個(gè)動(dòng)畫組合梅垄。
//屬性解析:
//animations:用來保存一組動(dòng)畫對象的NSArray
//默認(rèn)情況下厂捞,一組動(dòng)畫對象是同時(shí)運(yùn)行的,也可以通過設(shè)置動(dòng)畫對象的beginTime屬性來更改動(dòng)畫的開始時(shí)間CATransition是一個(gè)比較有意思的動(dòng)畫,轉(zhuǎn)場效果挺多靡馁。
//D. CATransition
//CAAnimation的子類欲鹏,用于做轉(zhuǎn)場動(dòng)畫,能夠?yàn)閷犹峁┮瞥銎聊缓鸵迫肫聊坏膭?dòng)畫效果臭墨。iOS比Mac OS X的轉(zhuǎn)場動(dòng)畫效果少一點(diǎn)
//UINavigationController就是通過CATransition實(shí)現(xiàn)了將控制器的視圖推入屏幕的動(dòng)畫效果
//屬性解析:
//type:動(dòng)畫過渡類型
/*
fade
push
moveIn
reveal
cube
oglFlip
suckEffect
rippleEffect
pageCurl
pageUnCurl
cameraIrisHollowOpen
cameraIrisHollowClose
*/
//subtype:動(dòng)畫過渡方向
//startProgress:動(dòng)畫起點(diǎn)(在整體動(dòng)畫的百分比)
//endProgress:動(dòng)畫終點(diǎn)(在整體動(dòng)畫的百分比)-
CASpringAnimation是彈性動(dòng)畫貌虾,能夠表現(xiàn)出非常性感細(xì)膩的效果
//E.CASpringAnimation
屬性:默認(rèn)值
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動(dòng)畫的類型,我學(xué)習(xí)完它再去學(xué)UIView動(dòng)畫裙犹,所以知道UIView動(dòng)畫其實(shí)就是CA動(dòng)畫的封裝,優(yōu)點(diǎn)是快捷方便衔憨,UIView的彈性動(dòng)畫完美體現(xiàn)了這點(diǎn)叶圃。缺點(diǎn)是不能細(xì)微調(diào)整。這里不作UIView的詳細(xì)介紹践图。
雖然UIView動(dòng)畫是對CA核心動(dòng)畫的封裝掺冠,但還是有必要對他們加以總結(jié),這可是目前在網(wǎng)上找不到的寶貴經(jīng)驗(yàn)哦B氲场(經(jīng)驗(yàn)可能有誤德崭,歡迎指正)
//UIView的動(dòng)畫跟CAAnimation動(dòng)畫的異同:
//1.都能控制動(dòng)畫開始執(zhí)行時(shí)刻。uiview的delay揖盘。CA中的beginTime眉厨。
//2.都能在動(dòng)畫結(jié)束后實(shí)現(xiàn)控制。uiview有閉包兽狭。CA中有代理函數(shù)didFinish憾股。
//3.uiView有彈性動(dòng)畫和關(guān)鍵幀動(dòng)畫,CA中也有,而且更為豐富箕慧。
//4.uiView組合動(dòng)畫用cgaffinetransformconcat服球。CA中用CAanimationGroup,并支持多組合颠焦。
// uiview 的多組合則可以通過創(chuàng)建多個(gè)uiview.animation來實(shí)現(xiàn)斩熊。多是指兩個(gè)以上。
// 這里的組合是指為同一個(gè)對象的不同屬性進(jìn)行組合伐庭。
// 如果是不同對象要實(shí)現(xiàn)同一個(gè)動(dòng)畫粉渠,則直接在uiview的內(nèi)容中添加∷朴牵或者直接在CA中賦予多個(gè)對象的layer渣叛。
//5.uiView實(shí)現(xiàn)2D或者3D動(dòng)畫,取決于里面設(shè)置的動(dòng)畫屬性盯捌。若設(shè)置的是layer層淳衙,則可以實(shí)現(xiàn)3D動(dòng)畫。(rotation屬性可以體現(xiàn))
// CA動(dòng)畫則取決于key。如果是transform箫攀,則可以3D.如果是transform.rotation肠牲,則可以是2D。
// CA中transform屬性有rotation靴跛,scale缀雳,translation。
// CA中key還可以是bounds梢睛,position肥印,opacity。這里的position等價(jià)于uiview的center绝葡。
//6.uiview動(dòng)畫完畢之后屬性已經(jīng)更改深碱。CA動(dòng)畫則不會(huì)改變實(shí)際位置,即使表面改變了藏畅。
下面我則要記錄下我在學(xué)習(xí)UIView動(dòng)畫的時(shí)候是怎么給自己挖坑的敷硅,并怎么最終把坑填上獲得小小成就感的。其實(shí)當(dāng)完美解決問題的那一刻愉阎,發(fā)現(xiàn)代碼是如此的簡單绞蹦,可就為了那一段代碼,讓我費(fèi)勁了力氣榜旦,花盡了腦汁才得以解決幽七。只怪自己經(jīng)驗(yàn)不足咯!都說怪我咯希望能幫助到有同樣困惑的人兒
關(guān)鍵字:中斷溅呢,終止锉走,中止,取消藕届,停止UIView動(dòng)畫
問題發(fā)現(xiàn):
- UIView動(dòng)畫在duration內(nèi)挪蹭,也就是正在執(zhí)行的過程中,我再次觸發(fā)了同樣的動(dòng)畫休偶,此時(shí)動(dòng)畫就會(huì)不正常顯示梁厉。
問題起源:
- 發(fā)現(xiàn)這個(gè)問題的時(shí)候,其實(shí)很多人就想到可能會(huì)轉(zhuǎn)用CA動(dòng)畫去實(shí)現(xiàn)踏兜,因?yàn)镃A動(dòng)畫在執(zhí)行過程中词顾,再次觸發(fā)的話,它會(huì)重新來過碱妆,并不會(huì)出現(xiàn)錯(cuò)亂肉盹。我也是想到了這個(gè)辦法,但是我就想知道在UIView動(dòng)畫中怎么解決這個(gè)問題的疹尾!所以問題就這樣起源了~
問題解決:
- 1.首先肯定是想到再次觸發(fā)前先把上一次動(dòng)畫取消掉上忍,想想應(yīng)該是很快就把問題給解決了吧骤肛,因?yàn)閺倪壿嬌喜]有什么錯(cuò)誤。于是我觸發(fā)的前面加了一句self.textView?.layer.removeAllAnimations()
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
self.textView?.layer.removeAllAnimations()
self.animation()
}
可是問題真的解決了嗎窍蓝?當(dāng)然不是腋颠。添加后的現(xiàn)象是我再次觸發(fā)時(shí),動(dòng)畫立馬停止了吓笙∈缑担看起來后面self.animation()并沒有執(zhí)行一樣。
2.于是我開始請教百度叫獸面睛,失望的是幾乎把百度翻了個(gè)遍絮蒿,也沒找到答案。納悶了叁鉴,難道只有我才遇到這個(gè)問題嗎歌径?只有我經(jīng)驗(yàn)淺腦子笨才掉這個(gè)坑嗎?唯一在網(wǎng)上找到一個(gè)相關(guān)的文章《如何中止UIView動(dòng)畫?》
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.這時(shí)想到了swift交流群腔长,在群上一問三不知袭祟,這該如何是好。大神都不出來幫我~
4.還是自己找原因吧捞附。在UIView動(dòng)畫執(zhí)行完的閉包里面添加一些打印信息吧巾乳。于是我添加了print("finish")
{ (finish:Bool) -> Void in
if finish {print("finish")}
}
此時(shí)我在第二次又觸發(fā)動(dòng)畫的時(shí)候發(fā)現(xiàn),只打印了一次finish鸟召!這finish是第一次動(dòng)畫執(zhí)行的還是第二次動(dòng)畫執(zhí)行的胆绊?從現(xiàn)象上就很好解釋了,肯定是第二次動(dòng)畫打印的finish欧募。而且還有一個(gè)現(xiàn)象就是压状,第二次觸發(fā)動(dòng)畫的時(shí)候,立馬就打印finish跟继,這也就是為什么看不到第二次動(dòng)畫的執(zhí)行种冬!原來本意是要停掉上一次正在執(zhí)行的動(dòng)畫,再接著執(zhí)行第二次動(dòng)畫√蛱牵現(xiàn)在問題是第二次也被停掉了S榱健!金吗!到底問題出現(xiàn)在哪里十兢?趣竣??靈光一閃纪挎,突然想到了延時(shí)期贫!就是停掉第一次動(dòng)畫的時(shí)候,延時(shí)一下异袄,再執(zhí)行第二次動(dòng)畫看行不行通砍?
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
self.textView?.layer.removeAllAnimations()
self.performSelector("animation", withObject: nil, afterDelay: 0.3)
}5.duang~的一下,成功了烤蜕!只要在執(zhí)行動(dòng)畫的前面添加一個(gè)細(xì)小的延時(shí)封孙,就可以完美解決了這個(gè)可惡的問題!后面我再測了一下讽营,把a(bǔ)fterDelay改成0 虎忌,也同樣成功了,這這又如何解釋橱鹏,就留給大家吧
問題回首:
- 解決的代碼非常簡單膜蠢,可對我來說真的容易么?在沒有百度君簡書君的幫助下莉兰,孤軍奮戰(zhàn)挑围,血戰(zhàn)到底,我容易么糖荒?
有了UIView動(dòng)畫的填坑經(jīng)驗(yàn)杉辙,我自個(gè)再到CA中去解決類似問題就迎刃而解了!什么捶朵?剛剛不是說CA中不存在這種問題嗎蜘矢??這里有必要說明一點(diǎn)综看,就是當(dāng)CA動(dòng)畫是非無止境動(dòng)畫(就是會(huì)停止的動(dòng)畫)品腹,在還沒停止之前再次觸發(fā),是不會(huì)發(fā)生這些錯(cuò)亂問題的红碑。
然而要是CA動(dòng)畫是個(gè)無止境的動(dòng)畫珍昨,也就是如果動(dòng)畫委托協(xié)議的animationDidStop中再次調(diào)用動(dòng)畫函數(shù)的話,這時(shí)再來個(gè)觸發(fā)相同動(dòng)畫句喷,動(dòng)畫就是亂得一塌糊涂了镣典!接下來就記錄一下怎么輕松解決這個(gè)問題的。
@IBAction func next(sender: AnyObject) {
self.transition()
}
func transition(){
let transition = CATransition()
transition.delegate = self
//動(dòng)畫過渡類型
transition.type = "pageCurl"
//動(dòng)畫過渡類型方向
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")
}
}
這樣的代碼確實(shí)能夠運(yùn)行唾琼,能夠循環(huán)調(diào)用動(dòng)畫兄春,實(shí)現(xiàn)無止境。但是問題是當(dāng)再次點(diǎn)擊按鍵觸發(fā)動(dòng)畫的話锡溯,這代碼的bug就一漏無遺了赶舆。
- 解決初探1:再次觸發(fā)之前哑姚,去掉所有動(dòng)畫。
@IBAction func next(sender: AnyObject) {
self.iv.layer.removeAllAnimations()
self.transition()
}
結(jié)果:失敗芜茵。原因:self.iv.layer.removeAllAnimations()執(zhí)行后會(huì)調(diào)用委托協(xié)議叙量,導(dǎo)致死循環(huán)。
-
解決初探2:添加“是否自動(dòng)完成動(dòng)畫”標(biāo)志九串。如果是自動(dòng)完成一輪動(dòng)畫绞佩,則執(zhí)行委托協(xié)議代碼,如果不是自動(dòng)完成猪钮,則不執(zhí)行品山。從而避免了死循環(huán)。
@IBAction func next(sender: AnyObject) {
ifAutoFinishAnimate = false
self.iv.layer.removeAllAnimations()//這里沒打印是因?yàn)闃?biāo)志置false了
self.transition()
}
func transition(){
let transition = CATransition()
transition.delegate = self
//動(dòng)畫過渡類型
transition.type = "pageCurl"//動(dòng)畫過渡類型方向 transition.subtype = kCATransitionFromLeft transition.duration = 1 // 一定要在加載動(dòng)畫之前設(shè)置setValue transition.setValue("second", forKey: "whichAnimation") self.iv.layer.addAnimation(transition, forKey: nil) ifAutoFinishAnimate = true //動(dòng)畫完成之后恢復(fù)標(biāo)志烤低,才能執(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)象是“根本停不下來扑馁!”此時(shí)原因應(yīng)該就是UIView動(dòng)畫的原因一樣了涯呻!
- 解決初探3:添加延時(shí)。
@IBAction func next(sender: AnyObject) {
ifAutoFinishAnimate = false
self.iv.layer.removeAllAnimations()//這里沒打印是因?yàn)闃?biāo)志置false了
performSelector("transition", withObject: nil, afterDelay: 0.3)
}
結(jié)果:Done腻要!完美解決复罐!這經(jīng)驗(yàn)真管用!afterDelay改成0闯第,這次就不行咯!至于為什么缀拭,同樣留給大家思考吧咳短。所以記錄所遇到的問題并加以總結(jié)是對自己非常有幫助的。