小男孩‘自慰网亚洲一区二区,亚洲一级在线播放毛片,亚洲中文字幕av每天更新,黄aⅴ永久免费无码,91成人午夜在线精品,色网站免费在线观看,亚洲欧洲wwwww在线观看

分享

輕松學(xué)習(xí)之二

 聽雨軒0209 2015-11-19

前言

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

nav_pop_origin.gif
nav_pop_origin.gif

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

nav_pop_custom.gif
nav_pop_custom.gif

開始大家一定會(huì)有疑問,給控制器的View加個(gè)手勢(shì)然后拖動(dòng)控制器的View時(shí)改變它的frame不就可以了嗎?沒錯(cuò),加手勢(shì)這個(gè)想法是正確的。但是,由我們自己來改變控制器視圖的位置是比較麻煩的,細(xì)心的朋友一定發(fā)現(xiàn)了,我們自定義pop手勢(shì)上面的導(dǎo)航欄也是在隨著你的手勢(shì)拖拽而變動(dòng)的,所以這樣做還需要負(fù)責(zé)導(dǎo)航欄的動(dòng)畫,而且有一個(gè)重點(diǎn)問題,如果單獨(dú)拖動(dòng)view,這個(gè)view下面會(huì)是黑黑的一片,因?yàn)榭刂破鞯膒ush和pop層級(jí)是由系統(tǒng)管理的。

nav_pop_failed.gif
nav_pop_failed.gif

所以走這條路雖然可以,但實(shí)現(xiàn)起來會(huì)比較艱辛。那么,如何實(shí)現(xiàn)這個(gè)效果呢?今天就給大家提供兩套實(shí)現(xiàn)方案。


[1]

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

這個(gè)是蘋果官方推薦的做法,在WWDC 2013 218 - Custom Transitions Using View Controllers中有說明。

這套方案雖然實(shí)現(xiàn)比較麻煩,但是動(dòng)畫相對(duì)靈活,你可以實(shí)現(xiàn)這樣的效果,

nav_pop_cube.gif
nav_pop_cube.gif

也可以有這種效果。

nav_pop_flip.gif
nav_pop_flip.gif

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

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

首先在方法1中,我們返回一個(gè)遵守了UIViewControllerAnimatedTransitioning協(xié)議的對(duì)象,它就是自定義的動(dòng)畫對(duì)象,我們給它起名PopAnimation,在這個(gè)類中實(shí)現(xiàn)兩個(gè)方法來自定義轉(zhuǎn)場(chǎng)動(dòng)畫。

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

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

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

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

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

有兩點(diǎn)不要忘記:

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

OK,這樣我們就完成了這個(gè)過程。

nav_pop_own.gif
nav_pop_own.gif

[2]

方案二:Runtime+KVC

要了解這樣的做法,需要有Runtime的一些知識(shí),會(huì)涉及到私有變量、私有方法的獲取,但是這樣做比較簡單也比較有趣,如果你感興趣就繼續(xù)看下去吧。關(guān)于Runtime的知識(shí),今后我會(huì)分享到博客里,朋友們敬請(qǐng)期待。

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

前面我們了解到,這個(gè)手勢(shì)屬于UINavigationController,我們就跳到它的頭文件里看看能不能找到線索。這個(gè)思路是正確的,確實(shí)有一個(gè)手勢(shì)叫做interactivePopGestureRecognizer。屬性為readonly,就是說我們不能給他換成自定義的手勢(shì),但是可以設(shè)置enable=NO。ok,既然找到了它,就打印一下看看它到底是一個(gè)什么手勢(shì)。

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

通過log,我們看到他屬于UIScreenEdgePanGestureRecognizer這個(gè)類(之前我是沒有用到過),它繼承自UIPanGestureRecognizer,出現(xiàn)在IOS7以后,是專門處理在屏幕邊緣觸發(fā)的手勢(shì)類型,并且只有一個(gè)屬性叫edges,用來設(shè)置它的觸發(fā)邊緣(上、下、左、右、全部)??吹竭@里一些朋友會(huì)想,直接改它的edges為全部可不可以?經(jīng)過試驗(yàn)了解到,改這個(gè)屬性是沒用的,它只能用來觸發(fā)邊緣,設(shè)為全部的意思是四個(gè)方向的邊緣會(huì)觸發(fā),而且用來做控制器POP手勢(shì)的只有左邊緣。

我們繼續(xù)看它的log。控制臺(tái)除了打印了它的類,還打印了它的觸發(fā)target:_UINavigationInteractiveTransition(這是一個(gè)私有類,看來是專門用來做導(dǎo)航控制器交互動(dòng)畫的),和action:handleNavigationTransition(這是它的一個(gè)私有方法),我們要做的就是新建一個(gè)UIPanGestureRecognizer,讓它的觸發(fā)和系統(tǒng)的這個(gè)手勢(shì)相同,這就需要利用runtime獲取系統(tǒng)手勢(shì)的target和action。

那么如何獲取這個(gè)target呢?一開始我用kvc想直接獲取這個(gè)手勢(shì)的target,程序崩潰了,原來它根本沒有這樣一個(gè)屬性。所以我能想到的是,先利用runtime遍歷它的所有成員變量,看看系統(tǒng)是怎么存儲(chǔ)這個(gè)屬性的,

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

通過log我們可以看到,UIGestureRecognizer有一個(gè)叫_targets的屬性,它的類型為NSMutableArray。

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

它是用數(shù)組來存儲(chǔ)每一個(gè)target-action,所以可以動(dòng)態(tài)的增加手勢(shì)觸發(fā)對(duì)象。那么又是什么存儲(chǔ)每一個(gè)target-action呢?為了了解這個(gè)我們拿到這個(gè)屬性的名字"_targets"通過kvc獲取它,接著打印出來。

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

可以看到,由于系統(tǒng)重寫了它的description方法,所以我們沒辦法通過打印獲取這個(gè)對(duì)象是什么類型。既然不能打印,那么我們就用斷點(diǎn)調(diào)試,來看它的真實(shí)類型,

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

我們看到,原來每一個(gè)target-action是用UIGestureRecognizerTarget這樣一個(gè)類來存儲(chǔ)的,它也是一個(gè)私有類。
蘋果把許多的類做私有化也是有原因所在,其實(shí)在平時(shí)我們拿到這個(gè)類也是沒有用的,他們的目的之一是避免對(duì)開發(fā)者公開無用的類,影響了封裝性。所以在類的設(shè)計(jì)上,還是要向蘋果學(xué)習(xí)。

下面直接看代碼。

我們?cè)诳刂破鞯腣iewDidLoad加上這段代碼,并且它只需要執(zhí)行一次。

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

優(yōu)化

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

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

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

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

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

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


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

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

感謝大家,輕松學(xué)習(xí)系列還會(huì)繼續(xù)下去,我會(huì)盡量寫出更多通俗易懂的文章,讓開發(fā)變得輕松起來?。


[1]: 方案一。 ?

[2]: 方案二。 ?

    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶 評(píng)論公約

    類似文章 更多