翻譯自“Using Unwind Segues”
本文檔是關(guān)于unwind segue的概述。
1 介紹
通過(guò)push,modal,popover和其他類型的segues添加到導(dǎo)航棧(navigation stack)的視圖控制器,可以通過(guò)unwind segue從導(dǎo)航棧返回。使用unwind segue可以從導(dǎo)航層次(navigation hierarchy)中返回上一步或上幾步。普通的segue會(huì)創(chuàng)建一個(gè)目標(biāo)視圖控制器的新實(shí)例,然后轉(zhuǎn)換;而unwind segue轉(zhuǎn)換到導(dǎo)航層次中已經(jīng)存在的視圖控制器。轉(zhuǎn)換開(kāi)始前,為源和目標(biāo)視圖控制器提供回調(diào)函數(shù)。通過(guò)這些回調(diào)函數(shù)在兩個(gè)視圖控制器之間傳遞數(shù)據(jù)。
在Interface Builder中創(chuàng)建一個(gè)unwind segue時(shí),不需要指定目標(biāo)視圖控制器。當(dāng)unwind segue觸發(fā)時(shí),運(yùn)行時(shí)動(dòng)態(tài)判斷跳轉(zhuǎn)到導(dǎo)航層次中哪一個(gè)視圖控制器。這個(gè)過(guò)程可自定義,并為容器視圖控制器提供額外可自定義的方面。
2 創(chuàng)建Unwind Segue
在Interface Builder中添加unwind segue之前,至少需要定義一個(gè)unwind action。Unwind action是一個(gè)實(shí)例方法,UIStoryboardSegue是唯一的參數(shù),返回類型為IBAction。對(duì)方法名沒(méi)有特殊的要求,但是方法名在整個(gè)應(yīng)用程序必須是唯一的和有意義的。跟IBAction和IBOutlet一樣,unwind action不需要在頭文件中聲明。有一個(gè)例外,如果開(kāi)發(fā)一個(gè)框架,希望框架的使用者創(chuàng)建一個(gè)能觸發(fā)框架中action的unwind segue時(shí),需要在頭文件中聲明。
清單 1:定義unwind action的例子
/* Objective-C */
- (IBAction)unwindToMainMenu:(UIStoryboardSegue*)sender
{
UIViewController *sourceViewController = sender.sourceViewController;
// Pull any data from the view controller which initiated the unwind segue.
}
/* Swift */
@IBAction func unwindToMainMenu(sender: UIStoryboardSegue)
{
let sourceViewController = sender.sourceViewController
// Pull any data from the view controller which initiated the unwind segue.
}
在storyboard中創(chuàng)建unwind segue時(shí),在unwind segue要返回的視圖控制器中指定unwind action的名稱。該unwind action在unwind segue觸發(fā)前執(zhí)行。通過(guò)UIStoryboardSegue參數(shù)的sourceViewController,接收來(lái)自初始化unwind segue的視圖控制器的數(shù)據(jù)。
2.1 添加Unwind Segue到Storyboard
使用control+drag從場(chǎng)景(scene)中的一個(gè)對(duì)象(按鈕、列表視圖單元格),到場(chǎng)景的退出圖標(biāo),來(lái)添加一個(gè)unwind segue,如圖1中的高亮顯示。從彈出的菜單中為該segue選擇一個(gè)unwind action。
圖 1:場(chǎng)景的退出圖標(biāo)是右上角的橙色圖標(biāo)

要添加一個(gè)只能代碼觸發(fā)的unwind segue,control+drag從場(chǎng)景的視圖控制器圖標(biāo)到退出圖標(biāo),如圖2。從彈出的菜單中選擇一個(gè)unwind action。為了能從代碼引用segue,需要添加segue的標(biāo)識(shí)符。一個(gè)unwind segue通過(guò)發(fā)送performSegueWithIdentifier:sender:消息觸發(fā)。
圖 2:創(chuàng)建一個(gè)在代碼中使用的unwind segue

添加一個(gè)unwind segue后,會(huì)出現(xiàn)在場(chǎng)景的提綱中,如圖3所示。
圖 3:每個(gè)場(chǎng)景中的unwind segue

3 Unwind過(guò)程
本節(jié)描述發(fā)生在unwind segue初始化后的過(guò)程,包括在場(chǎng)景中使用控件交互,和在視圖控制器中發(fā)送performSegueWithIdentifier:sender:消息。
1. Unwind Segue遍歷導(dǎo)航層次定位到目標(biāo)視圖控制器。
2. 初始化unwind segue的視圖控制器發(fā)送prepareForSegue:sender:消息。默認(rèn)的實(shí)現(xiàn)什么也不做。視圖控制器通過(guò)覆寫這個(gè)方法,傳遞數(shù)據(jù)到目標(biāo)視圖控制器。
3. 目標(biāo)視圖控制器的unwind action被觸發(fā)。
4. Unwind segue在源和目標(biāo)視圖控制器之間執(zhí)行轉(zhuǎn)場(chǎng)動(dòng)畫。
容器視圖控制器有額外的職責(zé)。
3.1 Unwind Segues怎么確定目標(biāo)視圖控制器
一個(gè)unwind segue初始化后,首先需要找到導(dǎo)航層次中最近的,實(shí)現(xiàn)了unwind segue創(chuàng)建時(shí)指定的unwind action的視圖控制器。這個(gè)視圖控制器就是unwind segue的目標(biāo)視圖控制器。如果沒(méi)有找到合適的視圖控制器,unwind segue會(huì)被終止。
從初始化unwind segue的視圖控制器開(kāi)始,查找順序?yàn)椋?br>
1. 響應(yīng)鏈的下一個(gè)是發(fā)送了viewControllerForUnwindSegueAction:fromViewController:withSender:消息的視圖控制器。模態(tài)顯示的視圖控制器,是調(diào)用了presentViewController:animated:completion:的視圖控制器,否則就是parentViewController。
默認(rèn)的實(shí)現(xiàn)是,搜索接收者的childViewControllers數(shù)組中想要處理unwind action的視圖控制器。如果接收者的子視圖控制器都不想處理unwind action,接收者檢測(cè)自己是否想要處理unwind action,如果想就返回self。兩種情況下,使用canPerformUnwindSegueAction:fromViewController:withSender:判斷視圖控制器是否想要處理unwind action。
2. 如果第一步中,沒(méi)有視圖控制器從viewControllerForUnwindSegueAction:fromViewController:withSender:方法返回,從響應(yīng)鏈下一個(gè)view controller重復(fù)搜索。
如果接收者響應(yīng)該action,canPerformUnwindSegueAction:fromViewController:withSender:的默認(rèn)實(shí)現(xiàn)是返回YES。如果需要額外的條件來(lái)確定能否成為unwind segue的目標(biāo)視圖控制器,可以覆寫 canPerformUnwindSegueAction:fromViewController:withSender:。為了消除導(dǎo)航棧中有多個(gè)同一個(gè)視圖控制器實(shí)例的歧義,這有可能是必須的。
重要:如果視圖控制器覆寫 canPerformUnwindSegueAction:fromViewController:withSender:,只當(dāng)接收者想要處理unwind action時(shí)返回YES。不要代表子視圖控制器返回YES。
4 容器視圖控制器的職責(zé)
在unwind過(guò)程中,容器視圖控制器有兩個(gè)職責(zé)。如果使用SDK提供的容器視圖控制器(UINavigationController,UITabBarController,...),這些都自動(dòng)處理了。
4.1 選擇一個(gè)子視圖控制器來(lái)處理Unwind Action
如果自定義的邏輯需要判斷容器的哪一個(gè)子視圖控制器處理unwind action,就應(yīng)該實(shí)現(xiàn)清單2中的方法。例如,UINavigationController的實(shí)現(xiàn)是從導(dǎo)航層次的最底層開(kāi)始搜索。如果容器的所有子視圖控制器都不處理,就觸發(fā)父類的實(shí)現(xiàn)并返回結(jié)果。
清單 2:覆寫這個(gè)方法返回想要處理unwind action的子視圖控制器
/* Objective-C */
- (UIViewController *)viewControllerForUnwindSegueAction:(SEL)action fromViewController:(UIViewController *)fromViewController withSender:(id)sender
/* Swift */
override func viewControllerForUnwindSegueAction(action: Selector, fromViewController: UIViewController, withSender sender: AnyObject?) -> UIViewController?
提示:總是使用canPerformUnwindSegueAction:fromViewController:withSender:確定視圖控制器是否想要處理unwind action。
4.2 兩個(gè)子視圖控制器之間的轉(zhuǎn)場(chǎng)
當(dāng)unwind segue找到目標(biāo)視圖控制器,它嘗試為轉(zhuǎn)場(chǎng)找到unwind executor。Unwind executor是初始化unwind segue的視圖控制器和目標(biāo)視圖控制器的普通原型。它的職責(zé)是創(chuàng)建一個(gè)UIStoryboardSegue,用來(lái)執(zhí)行源和目標(biāo)視圖控制器的可視化轉(zhuǎn)場(chǎng)。
當(dāng)容器視圖控制器從viewControllerForUnwindSegueAction:fromViewController:withSender:返回一個(gè)視圖控制器,容器視圖控制器成為轉(zhuǎn)場(chǎng)的unwind executor。因此,容器視圖控制器需要實(shí)現(xiàn)清單3中的方法,該方法返回UIStoryboardSegue,它執(zhí)行從源視圖控制器到目標(biāo)視圖控制器轉(zhuǎn)場(chǎng)的動(dòng)畫和需要的其他步驟。返回的對(duì)象是一個(gè)從segueWithIdentifier:source:destination:performHandler:創(chuàng)建的UIStoryboardSegue實(shí)例,或者是自定義子類的實(shí)例。
清單3:實(shí)現(xiàn)該方法返回fromViewController和toViewController中間轉(zhuǎn)場(chǎng)的UIStoryboardSegue
/* Objective-C */
- (UIStoryboardSegue *)segueForUnwindingToViewController:(UIViewController *)toViewController fromViewController:(UIViewController *)fromViewController identifier:(NSString *)identifier
/* Swift */
override func segueForUnwindingToViewController(toViewController: UIViewController, fromViewController: UIViewController, identifier: String?) -> UIStoryboardSegue
提示:容器視圖控制器和fromViewController之間可能有額外的視圖控制器。不能假設(shè)fromViewController是容器的直接子視圖控制器。