輕松學(xué)習(xí)之二——iOS利用Runtime自定義控制器POP手勢動畫

前言

蘋果在IOS7以后給導(dǎo)航控制器增加了一個Pop的手勢抛猫,只要手指在屏幕邊緣滑動孩灯,當(dāng)前的控制器的視圖就會跟隨你的手指移動峰档,當(dāng)用戶松手后,系統(tǒng)會判斷手指拖動出來的大小來決定是否要執(zhí)行控制器的Pop操作掀亩。

nav_pop_origin.gif

這個操作的想法非常好槽棍,但是系統(tǒng)給我們規(guī)定的范圍必須是屏幕左側(cè)邊緣才可以觸發(fā)抬驴,這樣實際使用過程中對于有些產(chǎn)品會產(chǎn)生不便布持,于是有些app就采取整個屏幕都響應(yīng)這個手勢并且pop動畫還是用系統(tǒng)原生的,這樣操作起來確實方便好多按傅。

nav_pop_custom.gif

開始大家一定會有疑問唯绍,給控制器的View加個手勢然后拖動控制器的View時改變它的frame不就可以了嗎灌侣?沒錯,加手勢這個想法是正確的牛柒。但是痊乾,由我們自己來改變控制器視圖的位置是比較麻煩的哪审,細(xì)心的朋友一定發(fā)現(xiàn)了,我們自定義pop手勢上面的導(dǎo)航欄也是在隨著你的手勢拖拽而變動的滴须,所以這樣做還需要負(fù)責(zé)導(dǎo)航欄的動畫扔水,而且有一個重點問題,如果單獨拖動view主届,這個view下面會是黑黑的一片待德,因為控制器的push和pop層級是由系統(tǒng)管理的将宪。

nav_pop_failed.gif

所以走這條路雖然可以,但實現(xiàn)起來會比較艱辛簸喂。那么,如何實現(xiàn)這個效果呢扼倘?今天就給大家提供兩套實現(xiàn)方案再菊。


[1]

方案一:自定義UIViewControllerInteractiveTransitioning對象,實現(xiàn)導(dǎo)航控制器代理方法秉剑。

這個是蘋果官方推薦的做法稠诲,在WWDC 2013 218 - Custom Transitions Using View Controllers中有說明臀叙。

這套方案雖然實現(xiàn)比較麻煩,但是動畫相對靈活渊涝,你可以實現(xiàn)這樣的效果,

nav_pop_cube.gif

也可以有這種效果。

nav_pop_flip.gif

其實這個拖動過程屬于導(dǎo)航控制器的動畫岁疼,所以我們需要重寫UINavigationController的兩個代理方法蚯姆,navigationController:animationControllerForOperation:fromViewController:toViewController:(名字很長下面就稱為方法1)和
navigationController:interactionControllerForAnimationController:(方法2)龄恋。
解釋一下他們的作用,方法1是蘋果提供給我們用來重寫控制器之間轉(zhuǎn)場動畫的(pop或者push)它碎。方法2你可以這樣理解显押,蘋果讓我們返回一個交互的對象乘碑,用來實時管理控制器之間轉(zhuǎn)場動畫的完成度,通過它我們可以讓控制器的轉(zhuǎn)場動畫與用戶交互(注意一點套腹,如果方法1返回是nil电禀,方法2是不會調(diào)用的笤休,也就是說,只有我們自定義的動畫才可以與控制器交互)政基。

下面我們來看一下實現(xiàn)過程腋么。為了便于大家理解亥揖,我會盡量在Demo中的注釋寫的最清晰明了。
同時摧扇,我們先用最簡單的代碼實現(xiàn)扛稽,在這篇文章的最后我會對本例中的Demo提供一個相對合理的寫法。

首先在方法1中用含,我們返回一個遵守了UIViewControllerAnimatedTransitioning協(xié)議的對象啄骇,它就是自定義的動畫對象瘟斜,我們給它起名PopAnimation螺句,在這個類中實現(xiàn)兩個方法來自定義轉(zhuǎn)場動畫。

屏幕快照 2015-03-28 下午6.49.05.png

再來看方法2芽唇,我們需要返回一個遵守了UIViewControllerInteractiveTransitioning協(xié)議的對象(提示一下披摄,這兩個協(xié)議容易混淆勇凭,要注意區(qū)分虾标,一個是負(fù)責(zé)動畫灌砖,一個是負(fù)責(zé)交互過程)基显,蘋果已經(jīng)有一個類專門處理這個功能,它叫UIPercentDrivenInteractiveTransition库继,當(dāng)然你也可以自定義一個這樣的類宪萄。我們可以這樣理解它的作用:前面在方法1中返回的動畫,會在執(zhí)行的過程中被系統(tǒng)分解以用于用戶交互静汤,這個交互過程的動畫完成度就由它來調(diào)控居凶。下面我們來看一下如何使用它侠碧。(為了讓控制器視圖拖動舆床,我們給控制器的視圖加了一個拖動手勢,在拖動方法里我們對這個對象進行操作)

屏幕快照 2015-03-29 下午12.33.59.png

最后在視圖控制器里重寫導(dǎo)航欄的兩個方法谷暮。


屏幕快照 2015-03-29 下午12.37.51.png

有兩點不要忘記:

  1. 設(shè)置導(dǎo)航控制器的代理為當(dāng)前控制器。
  2. 給控制器加手勢颊埃。

OK蝶俱,這樣我們就完成了這個過程。

nav_pop_own.gif

[2]

方案二:Runtime+KVC

要了解這樣的做法罗标,需要有Runtime的一些知識闯割,會涉及到私有變量竿拆、私有方法的獲取宙拉,但是這樣做比較簡單也比較有趣谢澈,如果你感興趣就繼續(xù)看下去吧澳化。關(guān)于Runtime的知識,今后我會分享到博客里井濒,朋友們敬請期待列林。

為了方便大家閱讀下面的代碼希痴,我們需要先了解系統(tǒng)的這個手勢。

前面我們了解到虏缸,這個手勢屬于UINavigationController刽辙,我們就跳到它的頭文件里看看能不能找到線索甲献。這個思路是正確的晃洒,確實有一個手勢叫做interactivePopGestureRecognizer。屬性為readonly氧骤,就是說我們不能給他換成自定義的手勢筹陵,但是可以設(shè)置enable=NO际歼。ok鹅心,既然找到了它纺荧,就打印一下看看它到底是一個什么手勢。

屏幕快照 2015-03-26 下午5.17.35.png

通過log议泵,我們看到他屬于UIScreenEdgePanGestureRecognizer這個類(之前我是沒有用到過)先口,它繼承自UIPanGestureRecognizer瞳收,出現(xiàn)在IOS7以后螟深,是專門處理在屏幕邊緣觸發(fā)的手勢類型,并且只有一個屬性叫edges凡蜻,用來設(shè)置它的觸發(fā)邊緣(上划栓、下舰讹、左月匣、右、全部)素标⊥吩猓看到這里一些朋友會想癣诱,直接改它的edges為全部可不可以撕予?經(jīng)過試驗了解到,改這個屬性是沒用的实抡,它只能用來觸發(fā)邊緣,設(shè)為全部的意思是四個方向的邊緣會觸發(fā)踩寇,而且用來做控制器POP手勢的只有左邊緣六水。

我們繼續(xù)看它的log缩擂。控制臺除了打印了它的類懈费,還打印了它的觸發(fā)target:_UINavigationInteractiveTransition(這是一個私有類憎乙,看來是專門用來做導(dǎo)航控制器交互動畫的)叉趣,和action:handleNavigationTransition(這是它的一個私有方法)疗杉,我們要做的就是新建一個UIPanGestureRecognizer烟具,讓它的觸發(fā)和系統(tǒng)的這個手勢相同,這就需要利用runtime獲取系統(tǒng)手勢的target和action嗡午。

那么如何獲取這個target呢荔睹?一開始我用kvc想直接獲取這個手勢的target言蛇,程序崩潰了腊尚,原來它根本沒有這樣一個屬性。所以我能想到的是丢胚,先利用runtime遍歷它的所有成員變量携龟,看看系統(tǒng)是怎么存儲這個屬性的峡蟋,


屏幕快照 2015-03-29 下午3.25.02.png

通過log我們可以看到蕊蝗,UIGestureRecognizer有一個叫_targets的屬性蓬戚,它的類型為NSMutableArray宾抓。


屏幕快照 2015-03-29 下午3.25.09.png

它是用數(shù)組來存儲每一個target-action幢泼,所以可以動態(tài)的增加手勢觸發(fā)對象讲衫。那么又是什么存儲每一個target-action呢涉兽?為了了解這個我們拿到這個屬性的名字"_targets"通過kvc獲取它花椭,接著打印出來。
屏幕快照 2015-03-29 下午3.33.54.png

屏幕快照 2015-03-29 下午3.34.01.png

可以看到,由于系統(tǒng)重寫了它的description方法雕蔽,所以我們沒辦法通過打印獲取這個對象是什么類型批狐。既然不能打印,那么我們就用斷點調(diào)試承冰,來看它的真實類型困乒,

屏幕快照 2015-03-29 下午3.37.32.png

我們看到娜搂,原來每一個target-action是用UIGestureRecognizerTarget這樣一個類來存儲的吱抚,它也是一個私有類秘豹。
蘋果把許多的類做私有化也是有原因所在既绕,其實在平時我們拿到這個類也是沒有用的岸更,他們的目的之一是避免對開發(fā)者公開無用的類,影響了封裝性谭企。所以在類的設(shè)計上债查,還是要向蘋果學(xué)習(xí)盹廷。

下面直接看代碼。

我們在控制器的ViewDidLoad加上這段代碼久橙,并且它只需要執(zhí)行一次俄占。


屏幕快照 2015-03-29 下午4.07.48.png

優(yōu)化

這個demo我會提供給大家,下面簡單說下程序的優(yōu)化思路淆衷。

  • 優(yōu)化點一:對于方案一缸榄,其實不應(yīng)該把導(dǎo)航控制器的代理方法以及手勢處理的方法交給視圖控制器,因為這段代碼不是屬于某一個視圖控制器祝拯,而是全局的導(dǎo)航控制器甚带,所以我們應(yīng)該參考蘋果的設(shè)計思想:新建一個專門管理交互過程的對象,這個類我們叫做NavigationInteractiveTransition。

  • 優(yōu)化點二:再來看之前的ViewDidLoad中只執(zhí)行一次的代碼晴氨,其實寫在這里也不夠妥當(dāng),同樣的碉输,這段代碼也不屬于某一個Controller瑞筐,優(yōu)化方案是新建一個導(dǎo)航控制器,在這個導(dǎo)航控制器的viewDidLoad中寫上這些代碼腊瑟,這樣也并不需要dispatch once。

  • 優(yōu)化點三:由于我們自定義的手勢是加在一個私有view上块蚌,這個view是一個全局的闰非,所以當(dāng)這個控制器為根控制器時,我們的手勢還是在起作用峭范,這就相當(dāng)于對根控制器做了pop操作财松,這會出現(xiàn)一個錯誤nested pop animation can result in corrupted navigation bar。導(dǎo)致這個錯誤的原因還有一個纱控,如果我們pop的動畫正在執(zhí)行辆毡,再去觸發(fā)一次手勢,會導(dǎo)致導(dǎo)航控制器和導(dǎo)航條的動畫混亂甜害。為了避免問題出現(xiàn)我們需要成為手勢的代理舶掖,判斷當(dāng)前控制器是否為根控制器并且pop或者push動畫是否在執(zhí)行(這個變量是私有的,需要用kvc來獲榷辍)眨攘。


    屏幕快照 2015-03-30 下午5.06.24.png

經(jīng)過最后的優(yōu)化,視圖控制器可以什么都不寫嚣州,想使用這個效果鲫售,只要使用我們自定義的導(dǎo)航控制器就可以了,這樣的好處是手勢動畫與控制器完全解耦该肴,并且不用給每一個控制器都addGesture情竹。


給大家推薦一個倉庫https://github.com/nst/iOS-Runtime-Headers,這個倉庫可以調(diào)取蘋果的所有私有方法頭文件匀哄,相當(dāng)強大秦效。

最后放上這個demo的地址:https://github.com/zys456465111/CustomPopAnimation(使用時,切換工程的scheme就能切換不同方案涎嚼。對于方案二棉安,只需要導(dǎo)航控制器的類就可以了。)

感謝大家铸抑,輕松學(xué)習(xí)系列還會繼續(xù)下去贡耽,我會盡量寫出更多通俗易懂的文章,讓開發(fā)變得輕松起來,我的微博:http://weibo.com/JazysYu 蒲赂。


  1. 方案一阱冶。 ?

  2. 方案二。 ?

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末滥嘴,一起剝皮案震驚了整個濱河市木蹬,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌若皱,老刑警劉巖镊叁,帶你破解...
    沈念sama閱讀 206,126評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異走触,居然都是意外死亡晦譬,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,254評論 2 382
  • 文/潘曉璐 我一進店門互广,熙熙樓的掌柜王于貴愁眉苦臉地迎上來敛腌,“玉大人,你說我怎么就攤上這事惫皱∠穹” “怎么了?”我有些...
    開封第一講書人閱讀 152,445評論 0 341
  • 文/不壞的土叔 我叫張陵旅敷,是天一觀的道長生棍。 經(jīng)常有香客問我,道長媳谁,這世上最難降的妖魔是什么足绅? 我笑而不...
    開封第一講書人閱讀 55,185評論 1 278
  • 正文 為了忘掉前任,我火速辦了婚禮韩脑,結(jié)果婚禮上氢妈,老公的妹妹穿的比我還像新娘。我一直安慰自己段多,他們只是感情好首量,可當(dāng)我...
    茶點故事閱讀 64,178評論 5 371
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著进苍,像睡著了一般加缘。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上觉啊,一...
    開封第一講書人閱讀 48,970評論 1 284
  • 那天拣宏,我揣著相機與錄音,去河邊找鬼杠人。 笑死勋乾,一個胖子當(dāng)著我的面吹牛宋下,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播辑莫,決...
    沈念sama閱讀 38,276評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼学歧,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了各吨?” 一聲冷哼從身側(cè)響起枝笨,我...
    開封第一講書人閱讀 36,927評論 0 259
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎揭蜒,沒想到半個月后横浑,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,400評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡屉更,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,883評論 2 323
  • 正文 我和宋清朗相戀三年徙融,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片偶垮。...
    茶點故事閱讀 37,997評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖帝洪,靈堂內(nèi)的尸體忽然破棺而出似舵,到底是詐尸還是另有隱情,我是刑警寧澤葱峡,帶...
    沈念sama閱讀 33,646評論 4 322
  • 正文 年R本政府宣布砚哗,位于F島的核電站,受9級特大地震影響砰奕,放射性物質(zhì)發(fā)生泄漏蛛芥。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,213評論 3 307
  • 文/蒙蒙 一军援、第九天 我趴在偏房一處隱蔽的房頂上張望仅淑。 院中可真熱鬧,春花似錦胸哥、人聲如沸涯竟。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,204評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽庐船。三九已至,卻和暖如春嘲更,著一層夾襖步出監(jiān)牢的瞬間筐钟,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,423評論 1 260
  • 我被黑心中介騙來泰國打工赋朦, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留篓冲,地道東北人李破。 一個月前我還...
    沈念sama閱讀 45,423評論 2 352
  • 正文 我出身青樓,卻偏偏與公主長得像纹因,于是被迫代替她去往敵國和親喷屋。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,722評論 2 345

推薦閱讀更多精彩內(nèi)容

  • 轉(zhuǎn)載自:http://www.cocoachina.com/ios/20150401/11459.html 前言 ...
    梅西121閱讀 548評論 1 2
  • 發(fā)現(xiàn) 關(guān)注 消息 iOS 第三方庫瞭恰、插件屯曹、知名博客總結(jié) 作者大灰狼的小綿羊哥哥關(guān)注 2017.06.26 09:4...
    肇東周閱讀 12,022評論 4 62
  • 看電影其實是一種生活的體驗,是自己無法在現(xiàn)實中實現(xiàn)的體驗惊畏,或者是曾經(jīng)有過的回憶恶耽,只是大多數(shù)人都希望去體驗繁華,而不...
    八音先生閱讀 223評論 0 0
  • “這一板條拍下去颜启,我覺得他再也爬不起來了”但是他沒有偷俭,在靜靜的血腥味里,他又站了起來缰盏。 一 小四初三開學(xué)后不久涌萤,隔...
    笑眼朋朋閱讀 967評論 2 3
  • 三奇自從在一次交通事故中廢了一只腿,就知道自己的后半生只能坐在輪椅上度過了口猜。這次交通事故不僅在生活上給他帶來了翻天...
    小閑云閱讀 790評論 6 9