1. 程式人生 > >iOS中dismissViewController實現多級模態跳轉

iOS中dismissViewController實現多級模態跳轉

最近專案中用到這樣一個邏輯:三個檢視控制器:A,B,C,A模態跳轉到B,B在模態跳轉到C,等C操作完後要直接跳轉回A,那麼問題就來了,我們平時一般用到的只是從誰跳轉過來,就在跳轉回去,那怎麼實現多級返回呢?其實用到的根本的方法還是下面的方法:

- (void)dismissViewControllerAnimated: (BOOL)flag completion: (void (^ __nullable)(void))completion

flag:表示是否執行動畫
competion:是一個block,用在dismiss操作完成後的回撥

通常情況下presentViewController和dismissViewController是一組方法,我門用presentViewController模態跳轉到下個頁面,在用dismissViewController模態返回上個頁面,如果由A跳轉到B,顯然presentViewController方法應該在A裡面執行,那麼當我們希望關閉模態檢視B時,dismissViewController在哪裡執行呢?

標準答案是:在A檢視控制器裡執行。蘋果的文件裡有這麼一段話:

The presenting view controller is responsible for dismissing the view controller it presented. If you call this method on the presented view controller itself, it automatically forwards the message to the presenting view controller.

或者只要簡單的記住一個原則既可:

誰汙染,誰治理!

這是和pushViewController和popViewController那一組方法不同的地方。不過在B檢視中執行也是可以的,因為系統會自動優化,當B檢視控制器沒有present過其他檢視控制器的時候,dismissViewController方法會自動交給B的presentingViewController執行,也就是A檢視。

但是出於嚴謹性考慮,還是應該在A檢視控制器中執行dismissViewController方法。

想要實現直接從C跳轉回A其實也簡單,一個最簡單的解決辦法就是利用通知或者代理,在A中執行dismissViewController方法。此時B和C檢視控制器會發生什麼變化呢?依然摘錄一段蘋果的文件做一下解釋:

If you present several view controllers in succession, thus building a stack of presented view controllers, calling this method on a view controller lower in
the stack dismisses its immediate child view controller and all view controllers above that child on the stack. When this happens, only the top-most view is dismissed in an animated fashion; any intermediate view controllers are simply removed from the stack. The top-most view is dismissed using its modal transition style, which may differ from the styles used by other view controllers lower in the stack.

也就是說,其實在present多個檢視控制器的時候,系統維護了一個棧,以我們現在這個情況為例,從棧底到棧頂依次是A->B->C。當棧中某個位置的檢視控制器執行dismissViewController方法的時候,棧中所有在它之上的檢視控制器都會被dismiss,不同的是,棧頂的檢視控制器將會以動畫方式被dismiss,而中間的檢視控制器只是簡單的remove掉。

這裡已經成功了一半,因為dismissViewController總是要在A方法中執行的。不過這樣做會遇到程式碼耦合的問題。

所謂的程式碼耦合指的是,我們應該儘量避免代理或者通知的使用,比如這時候A檢視控制器換成了A’ 那麼A’在不修改任何程式碼的時候肯定是無法直接放在整個專案裡面使用的。此時要麼重新實現協議,要麼監聽通知。

其實解決辦法很簡單:

    UIViewController *rootVC = self.presentingViewController;

    while (rootVC.presentingViewController) {
        rootVC = rootVC.presentingViewController;
    }

    [rootVC dismissViewControllerAnimated:YES completion:nil];

在迴圈中連續獲取presentingViewController,於是最終可以得到根檢視控制器,這裡就是A,不過這使得A檢視控制器中不用新增任何程式碼,從而解決了耦合的問題。

這樣寫的另一個好處是,不管是多少個檢視控制器之間的跳轉,都可以很方便的完成。