蘋果原來的開發(fā)文檔已更新,此翻譯已過時(shí)。。。
來源自蘋果開發(fā)文檔ViewController Programming Guide for iOS
View Controller Basics
運(yùn)行在基于iOS設(shè)備的應(yīng)用程序只有有限的屏幕空間來顯示內(nèi)容,因此他們?nèi)绾谓o用戶呈現(xiàn)信息必須有創(chuàng)造性。那些有很多信息需要顯示的應(yīng)用程序在開始時(shí)因此只能顯示一部分,然后當(dāng)用戶跟應(yīng)用程序交互時(shí)顯示和隱藏額外的內(nèi)容。視圖控制器對(duì)象為管理內(nèi)容和協(xié)調(diào)內(nèi)容的顯示和隱藏提供了基礎(chǔ)設(shè)置。通過擁有不同的視圖控制器類控制用戶界面的單獨(dú)部分,你把用戶界面的實(shí)現(xiàn)分成幾個(gè)更小更容易管理的單元。
在你能在應(yīng)用程序中使用視圖控制器之前,你徐啊要對(duì)愛iOS應(yīng)用程序中用來顯示內(nèi)容的主要類有一個(gè)基本的理解,包括窗口和視圖。任何視圖控制器實(shí)現(xiàn)的關(guān)鍵部分是管理顯示內(nèi)容的視圖。但是,管理視圖不是視圖控制器執(zhí)行的唯一工作。當(dāng)過渡發(fā)生時(shí),大多是視圖控制器還跟其他視圖控制器交流和協(xié)調(diào)。因?yàn)橐晥D控制器管理著很多鏈接,包括內(nèi)部的視圖和相關(guān)對(duì)象的鏈接以及外部的其它控制器之間的鏈接,理解對(duì)象之間的各種鏈接有時(shí)候很困難。作為替代,使用界面生成器來創(chuàng)建故事板。故事板讓應(yīng)用程序中的關(guān)系的可視化更簡(jiǎn)單并大大簡(jiǎn)化初始化對(duì)象在運(yùn)行時(shí)所需的努力
一、屏幕,窗口和視圖創(chuàng)建可視化界面
圖1-1顯示了一個(gè)簡(jiǎn)單的界面。左側(cè),你可以看見組成該界面的各種對(duì)象并理解它們是如何互相連接的。
圖1-1

這里有三個(gè)主要對(duì)象在工作:
- 一個(gè)UIScreen對(duì)象表示連接到設(shè)備的一個(gè)物理屏幕。
- 一個(gè)UIWindow對(duì)象為屏幕提供繪圖支持
- 一組UIView對(duì)象來執(zhí)行繪圖。這些對(duì)象被連接到窗口,當(dāng)窗口要求它們回執(zhí)內(nèi)容時(shí)繪制。
圖1-2顯示了這些類(以及相關(guān)重要類)在UIKit中如何被定義。
圖1-2

盡管你不需要理解關(guān)于視圖的所有知識(shí),進(jìn)而理解視圖控制器,但是它在考慮視圖的最顯著功能上很有幫助:
- 一個(gè)視圖表示一個(gè)用戶界面元素。每個(gè)視圖覆蓋了一個(gè)特定的區(qū)域。在那個(gè)區(qū)域中,它顯示內(nèi)容或響應(yīng)用戶事件。
- 在一個(gè)視圖層次機(jī)構(gòu)里可以被嵌套。子視圖相對(duì)于它們的子視圖定位和繪制。因此,當(dāng)父視圖發(fā)生移動(dòng),其子視圖也跟著移動(dòng)。該層次結(jié)構(gòu)通過把它們放在一個(gè)通用父類中讓集合一組相關(guān)視圖變得容易。
- 視圖可以動(dòng)畫它們的屬性值。當(dāng)一個(gè)屬性值的改變發(fā)生動(dòng)畫時(shí),該屬性值在定義的時(shí)間區(qū)內(nèi)逐漸改變直到它達(dá)到目標(biāo)新值。交叉在多個(gè)視圖的多個(gè)屬性的改變可以在一個(gè)動(dòng)畫里協(xié)調(diào)動(dòng)畫。
- 動(dòng)畫對(duì)于iOS應(yīng)用程序開發(fā)是至關(guān)重要的。因?yàn)榇蠖鄶?shù)應(yīng)用程序在一個(gè)時(shí)間只顯示內(nèi)容的一部分,動(dòng)畫允許用戶看到一個(gè)過渡何時(shí)發(fā)生以及新內(nèi)容從哪來。一個(gè)瞬間過渡可能讓用戶困惑。
- 視圖很少理解它們?cè)趹?yīng)用程序中扮演的角色。比如,圖1-1顯示了一個(gè)按鈕(標(biāo)題hello),它是視圖的一種特殊形式,被稱為控件。控件知道如何響應(yīng)用戶在該區(qū)域的交互,但是它們不知道它們?cè)摽刂剖裁础O喾矗?dāng)用戶控件發(fā)生交互時(shí),它給應(yīng)用程序中的其它對(duì)象發(fā)送消息。該靈活性允許一個(gè)單類(UIButton)提供多個(gè)按鈕的實(shí)現(xiàn),每個(gè)都能出發(fā)一個(gè)不同的操作。
一個(gè)復(fù)雜的應(yīng)用程序需要很多視圖,這些視圖都被組合進(jìn)視圖層次結(jié)構(gòu)里。它需要?jiǎng)赢嬤@些視圖的子集到屏幕或使其離開屏幕來提供一個(gè)更大界面的錯(cuò)覺。最后,保持視圖類可重用,視圖類需要對(duì)它們?cè)趹?yīng)用程序中執(zhí)行的特殊角色一無所知。所以應(yīng)用程序邏輯(大腦)還需要被放在其他地方。視圖控制器就是把應(yīng)用程序的視圖聯(lián)系到一起的大腦。
二、視圖控制器管理視圖
每個(gè)視圖控制器都組織管理一個(gè)視圖;該視圖常常是一個(gè)視圖層次結(jié)構(gòu)中的根視圖。視圖控制器就是MVC模式里的控制器對(duì)象,但是視圖控制器還有特殊任務(wù)iOS期望它去執(zhí)行。這些任務(wù)由UIViewController類定義,所有視圖控制器都從它這繼承。所有視圖控制器執(zhí)行視圖和資源管理任務(wù);其他職責(zé)取決于如何使用視圖控制器。
圖1-3顯示了從圖1-1延伸而來的界面,但是這里添加了一個(gè)視圖控制器。你絕不是直接把視圖分派到窗口上。你應(yīng)該給窗口分派一個(gè)視圖控制器,然后視圖控制器自動(dòng)添加其視圖給窗口。
圖1-3 一個(gè)附加到窗口的視圖控制器自動(dòng)添加其視圖到窗口,使它們成為窗口的子視圖。
圖1-3

視圖控制器只在視圖被需要時(shí)才小心的加載該視圖。他還能在特定條件下釋放該視圖。因?yàn)檫@些原因,視圖控制器在管理應(yīng)用程序中的資源方面起著關(guān)鍵作用。
視圖控制器是協(xié)調(diào)其所有被管理視圖的行為的原始地方。比如,當(dāng)一個(gè)按鈕被按下,它給視圖控制器發(fā)送了一個(gè)消息。盡管視圖本身可能不知道它執(zhí)行的任務(wù),但是視圖控制器應(yīng)該知道按鈕下意味著什么,以及它該如何響應(yīng)。控制器可能更新數(shù)據(jù)對(duì)象,動(dòng)畫或改變存儲(chǔ)在其視圖中的屬性值,或甚至把另一個(gè)視圖控制器的內(nèi)容顯示到屏幕上。
通常應(yīng)用程序?qū)嵗拿總€(gè)視圖控制器只顯示應(yīng)用程序數(shù)據(jù)的一個(gè)子集。它知道如何顯示那個(gè)特殊的數(shù)據(jù)集,而不需要了解其它類型的數(shù)據(jù)。因此,一個(gè)應(yīng)用程序數(shù)據(jù)模型,用戶接口設(shè)計(jì),以及你創(chuàng)建的視圖控制器都互相影響。
圖1-4顯示了一個(gè)管理食譜的應(yīng)用程序。該應(yīng)用程序顯示了三個(gè)相關(guān)到時(shí)獨(dú)立的視圖。第一個(gè)視圖列出了應(yīng)用程序管理的食譜。點(diǎn)擊一個(gè)食譜顯示第二個(gè)視圖,它描述了選中的視圖。點(diǎn)擊詳細(xì)視圖中的食譜圖片,打開第三個(gè)視圖,一張放大的圖片。每個(gè)視圖都有一個(gè)獨(dú)立的視圖控制器對(duì)象管理--呈現(xiàn)適當(dāng)?shù)囊晥D,用數(shù)據(jù)填充子視圖,以及在視圖層次結(jié)構(gòu)里響應(yīng)用戶交互。
圖1-4

該例子說明了視圖控制器的一些常見要素:
- 每個(gè)視圖都只被一個(gè)視圖控制器管理。當(dāng)一個(gè)視圖被分配給視圖控制器的view屬性,視圖控制器就擁有了該視圖。如果視圖是一個(gè)子視圖,它可能被同一個(gè)或不同的視圖控制器。當(dāng)你學(xué)習(xí)容器視圖控制器(container view controllers)時(shí),你將學(xué)習(xí)到更多關(guān)于如何使用多個(gè)視圖控制器來組織一個(gè)單一的視圖層次結(jié)構(gòu)。
- 每個(gè)視圖控制器跟應(yīng)用程序數(shù)據(jù)的一個(gè)子集發(fā)生交互。比如,Photo Controller 只需要知道要顯示的照片。
- 因?yàn)槊總€(gè)視圖控制器只提供用戶體驗(yàn)的一個(gè)子集,視圖控制器必須相互交流來讓該體驗(yàn)完美銜接。它們還可能跟其他控制器相交流,比如數(shù)據(jù)控制器或文檔對(duì)象。
三、視圖控制器的分類
圖1-5顯示了UIKit框架中可用的視圖控制器類,以及其它對(duì)視圖控制器很重要的類。比如,UITabBarController對(duì)象管理一個(gè)UITabBar對(duì)象,UITabBar對(duì)象實(shí)際上顯示了跟標(biāo)簽界面相關(guān)的標(biāo)簽。其它框架定義的額外的視圖控制器類沒有顯示在該圖中。
圖1-5 UIKit中的視圖控制器類

視圖控制器,不管是那些由iOS提供的還是你自己定義的視圖控制器,都可以被分為兩個(gè)基本類別(內(nèi)容視圖控制器和容器視圖控制器),它們反應(yīng)了視圖控制器在應(yīng)用程序中扮演的角色。
1.內(nèi)容視圖控制器顯示內(nèi)容
內(nèi)容視圖控制器使用一個(gè)視圖或一個(gè)由一組視圖組成的視圖層次結(jié)構(gòu)在屏幕上呈現(xiàn)內(nèi)容。之前描述的控制器已經(jīng)有內(nèi)容視圖控制器。一個(gè)內(nèi)容視圖控制器通常知道應(yīng)用程序數(shù)據(jù)的子集,該數(shù)據(jù)跟控制器在應(yīng)用程序中所扮演的角色有關(guān)。
以下列出了應(yīng)用程序使用內(nèi)容視圖控制器的常用地方:
- 給用戶顯示數(shù)據(jù)
- 從用戶那收集數(shù)據(jù)
- 執(zhí)行一個(gè)指定任務(wù)
- 在一組可行命令或選項(xiàng)之間導(dǎo)航,比如一個(gè)游戲的啟動(dòng)屏幕
內(nèi)容視圖控制器是應(yīng)用程序的主要協(xié)調(diào)對(duì)象,因?yàn)樗鼈冎罃?shù)據(jù)和應(yīng)用程序提供給用戶的任務(wù)的具體詳情。
你創(chuàng)建的每個(gè)內(nèi)容視圖控制器對(duì)象負(fù)責(zé)管理一個(gè)但已是圖層次結(jié)構(gòu)中的所有視圖。一個(gè)視圖控制器和其視圖層次結(jié)構(gòu)中所有視圖之間的一對(duì)一對(duì)應(yīng)關(guān)系是主要的設(shè)計(jì)考慮。你不應(yīng)該使用多個(gè)內(nèi)容視圖控制器來管理同一個(gè)視圖層次。同樣的,你也不應(yīng)該使用一個(gè)單一內(nèi)容視圖控制器對(duì)象來管理多個(gè)屏幕上的內(nèi)容價(jià)值。
關(guān)于定義你的內(nèi)容視圖控制器以及實(shí)現(xiàn)所需的行為的信息,請(qǐng)看Creating Custom Content View Controllers
1)關(guān)于表格視圖控制器
很多應(yīng)用程序顯示表格數(shù)據(jù)。因此,iOS提供了一個(gè)專門用來管理表格數(shù)據(jù)的內(nèi)建的UIViewController的子類。UITableViewController管理一個(gè)表哥視圖并支持很多標(biāo)準(zhǔn)表格相關(guān)的行為,比如選擇(selection)管理,行編輯,以及表格配置。這些額外的支持減少了你創(chuàng)建和初始化一個(gè)基于表格界面必須編寫的代碼總量。你還可以子類化UITableViewController來添加其它自定義行為。
圖1-6顯示了一個(gè)使用表格視圖控制器的例子。因?yàn)樗荱IViewController的一個(gè)子類,表格視圖控制器仍然有一個(gè)指向接口根視圖的指針(通過其view屬性)
圖1-6

更多關(guān)于表視圖的信息,請(qǐng)查看Table View Programming Guide for iOS
2.容器視圖控制器安排其它視圖控制器的內(nèi)容
容器視圖控制器包含了其他視圖控制器擁有的內(nèi)容。這些其它視圖控制器都明確地被作為其子視圖控制器分配給容器視圖控制器。一個(gè)容器視圖控制器可以是其它控制器的父控制器也可以是另一個(gè)控制器的子控制器。最終,這些控制器的組合建立一個(gè)視圖層次結(jié)構(gòu)。
每種類型的容器視圖控制器建立了一個(gè)讓其子控制器能在里面操作的用戶界面。該用戶界面的視覺呈現(xiàn),以及它施加給自控制器的設(shè)計(jì)可以在不同類型的容器之間廣闊地變化。比如,以下有一些方法讓不同的容器視圖控制器可以區(qū)分它們自己:
- 一個(gè)容器提供它自己的API來管理其子控制器。
- 一個(gè)容器決定子控制器之間是否有關(guān)系,以及什么關(guān)系。
- 一個(gè)容器跟其它視圖控制器一樣一個(gè)視圖層次。一個(gè)容器還可以添加子控制器的任何視圖到它的層次。容器決定何時(shí)添加這樣的一個(gè)視圖,以及它應(yīng)該如何被調(diào)整尺寸以適應(yīng)容器的視圖層次結(jié)構(gòu),但是除此之外子視圖控制器保留了對(duì)視圖和其子視圖的職責(zé)。
- 一個(gè)容器可能施加特定設(shè)計(jì)構(gòu)思到其子控制器。比如,一個(gè)容器可能一些特定的視圖控制器類限制其子控制器,或它可能期望那些控制器來提供額外起的內(nèi)容來配置容器的視圖。
每個(gè)內(nèi)建的容器類都圍繞著一個(gè)重要的用戶界面原則來組織。你可以使用這些容器管理的用戶界面來組織復(fù)雜的應(yīng)用程序。
1)關(guān)于導(dǎo)航控制器
導(dǎo)航控制器呈現(xiàn)按層次組織的數(shù)據(jù),它是UINavigationController類的一個(gè)實(shí)例。該類的方法對(duì)管理一個(gè)基于棧的內(nèi)容視圖控制器集合提供支持。該棧表示用戶通過層次數(shù)據(jù)獲取的路徑,棧的末端反應(yīng)起始點(diǎn),棧的頂端反應(yīng)洪湖在數(shù)據(jù)中的當(dāng)前位置。
圖1-7顯示了通訊錄應(yīng)用程序的屏幕,它使用一個(gè)導(dǎo)航控制器來呈現(xiàn)聯(lián)絡(luò)信息給用戶。每個(gè)頁(yè)面頂部的導(dǎo)航欄是導(dǎo)航控制器所有。每個(gè)屏幕顯示給用戶的其余部分是由一個(gè)內(nèi)容視圖控制器管理,內(nèi)容視圖控制器顯示數(shù)據(jù)層次中特定層的信息。當(dāng)用戶在界面中跟控件發(fā)生交互,那些控件告訴導(dǎo)航控制器顯示序列中的下一個(gè)視圖控制器或丟棄當(dāng)前的視圖控制器。
圖1-7

盡管一個(gè)導(dǎo)航控制器的主要任務(wù)是管理它的子視圖控制器,但是它還管理一些視圖。特別是,它管理一個(gè)導(dǎo)航欄(顯示用戶在數(shù)據(jù)層次中的當(dāng)前位置信息),一個(gè)按鈕(導(dǎo)航到前一個(gè)屏幕),以及當(dāng)前視圖控制器需要的任何自定義控件。你不能直接修改視圖控制器擁有的視圖。作為替代,你配置控件,這些控件由導(dǎo)航控制器顯示,它們通過設(shè)置在每個(gè)子視圖控制器上的屬性來顯示。
關(guān)于如何配置和使用導(dǎo)航控件對(duì)象的信息,請(qǐng)看Navigation Controllers。
2)關(guān)于標(biāo)簽欄控制器
標(biāo)簽欄控制器是一個(gè)容器視圖控制器,你可以用它來把應(yīng)用程序分成兩個(gè)或更多不同的操作模式。標(biāo)簽欄控制器是UITabBarController類的一個(gè)實(shí)例。標(biāo)簽欄有多個(gè)標(biāo)簽,每個(gè)表示一個(gè)子視圖控制器。選擇一個(gè)標(biāo)簽導(dǎo)致標(biāo)簽欄控制器在屏幕上顯示相關(guān)視圖控制器的視圖。
圖1-8 顯示了Clock應(yīng)用程序的集中模式,以及相關(guān)視圖控制器之間的關(guān)系。每個(gè)模式都有一個(gè)內(nèi)容視圖控制器來管理主要內(nèi)容區(qū)域。在Clock應(yīng)用程序中,Clock和Alarm視圖控制器都顯示一個(gè)導(dǎo)航風(fēng)格的界面來沿著屏幕的頂部容納一些額外的控件。其它模式使用內(nèi)容視圖控制器來呈現(xiàn)一個(gè)單一屏幕。
圖1-8

當(dāng)你的應(yīng)用程序呈現(xiàn)不同種類數(shù)據(jù)或者以不同方式呈現(xiàn)相同數(shù)據(jù)的時(shí)候,你可以使用標(biāo)簽欄控制器。
關(guān)于如何卑職和使用一個(gè)標(biāo)簽欄,請(qǐng)看Tab Bar Controllers
3)關(guān)于拆分視圖控制器
拆分視圖控制器是把屏幕分成多個(gè)部分,每個(gè)部分都可以被單獨(dú)更新。一個(gè)拆分視圖控制器的外形可能很大程度上依賴于它的方向。拆分視圖控制器是UISplitViewController類的一個(gè)實(shí)例。一個(gè)拆分視圖界面的內(nèi)容來源于兩個(gè)子視圖控制器。
圖1-9顯示了MultipleDetailViews例子應(yīng)用程序中的一個(gè)拆分視圖界面。在豎直模式,只有詳細(xì)視圖被顯示。列表視圖用一個(gè)彈出菜單(popover)打開。然而,當(dāng)屏幕處于水平模式時(shí),拆分視圖控制器一邊一個(gè)顯示兩個(gè)子視圖控制器的內(nèi)容。
圖1-9

拆分視圖控制器僅在iPad上支持,旨在幫助您利用該設(shè)備的打屏幕。它們是iPad應(yīng)用程序中實(shí)現(xiàn)主-細(xì)節(jié)界面的首選方法。
關(guān)于如何配置和使用一個(gè)拆分視圖控制器的信息,請(qǐng)看Popovers
4)關(guān)于彈出視圖控制器
再次看圖1-9,當(dāng)拆分視圖控制器以豎直模式顯示時(shí),主視圖在一個(gè)特殊的控件中顯示,被成為popover。在一個(gè)iPad應(yīng)用程序中,你可以使用popover控制器(UIPopoverController)來實(shí)現(xiàn)彈出控件。
彈出控制器實(shí)際上不是一個(gè)容器,它不繼承自UIViewController。但,實(shí)踐中,彈出視圖控制器跟容器很相似,所以你在使用它們時(shí)可以應(yīng)用相同的編程原則。
關(guān)于更多如何設(shè)置和使用彈出控制器的信息,請(qǐng)查看Popovers
5)關(guān)于頁(yè)面視圖控制器
頁(yè)面視圖控制器是一個(gè)容器視圖控制器,它用來實(shí)現(xiàn)一個(gè)頁(yè)面布局。那個(gè)布局允許用戶翻轉(zhuǎn)內(nèi)容的不同頁(yè)面,就好像翻書一樣。頁(yè)面視圖控制器是UIPageViewController類的一個(gè)實(shí)例。每個(gè)內(nèi)容頁(yè)由一個(gè)內(nèi)容視圖控制器提供。頁(yè)面視圖控制器管理頁(yè)面之間的過渡。當(dāng)需要新頁(yè)面時(shí),頁(yè)面上視圖控制器調(diào)用一個(gè)相關(guān)的數(shù)據(jù)源來為下一個(gè)頁(yè)面取回一個(gè)視圖控制器。
關(guān)于如何配置和使用一個(gè)頁(yè)面視圖控制器的信息,請(qǐng)看Page View Controllers
四、一個(gè)視圖控制器的內(nèi)容可以以不同的方式顯示
要想把一個(gè)視圖控制器的內(nèi)容顯示給用戶,它必須關(guān)聯(lián)一個(gè)窗口。你可以在應(yīng)用程序中使用以下方法來實(shí)現(xiàn):
- 把視圖控制器作為窗口的根視圖控制器
- 把視圖控制器作為一個(gè)容器的子控制器
- 在一個(gè)彈出控件中顯示視圖控制器
- 從另一個(gè)視圖控制器中呈現(xiàn)它
圖1-10顯示了聯(lián)系人應(yīng)用程序的一個(gè)例子。當(dāng)用戶點(diǎn)擊加號(hào)來添加一個(gè)新聯(lián)系方式時(shí),聯(lián)系人視圖控制器呈現(xiàn)添加聯(lián)系人視圖控制器。添加聯(lián)系人屏幕保持可見知道用戶取消操作或?yàn)槁?lián)系方式提供了足夠的信息讓其保存到聯(lián)系人數(shù)據(jù)庫(kù)中。在那時(shí)信息被發(fā)送給聯(lián)系人視圖控制器,然后丟棄呈現(xiàn)的控制器。
圖1-10

被呈現(xiàn)的視圖控制器沒有指定類型---可以是內(nèi)容視圖控制器也可以是帶有一個(gè)內(nèi)容視圖控制器的容器視圖控制器。實(shí)踐中,內(nèi)容視圖控制器是專門設(shè)計(jì)為能讓另一個(gè)控制器呈現(xiàn),因此把它想象成一個(gè)內(nèi)容視圖控制器的變量會(huì)很有幫助。盡管容器視圖控制器定義了被管理控制器之間的特定關(guān)系,但是使用presentation允許你定義被呈現(xiàn)的視圖控制器和呈現(xiàn)它的視圖控制器之間的關(guān)系。
大多數(shù)時(shí)間,你被呈現(xiàn)視圖控制器來收集用戶或?yàn)橐恍┨囟康牟蹲接脩舻淖⒁饬ΑR坏┠莻€(gè)目的完成,被呈現(xiàn)的視圖控制器和呈現(xiàn)它的視圖控制器之間的關(guān)系。
一個(gè)被呈現(xiàn)的視圖控制器本身還可以呈現(xiàn)另一個(gè)視圖控制器,這點(diǎn)值得注意。當(dāng)你需要線性的執(zhí)行多個(gè)模型動(dòng)作時(shí),這個(gè)把視圖控制器鏈接到一起的功能會(huì)很有用。比如,如果用戶點(diǎn)擊圖1-10中的New Contact屏幕上的添加照片按鈕,想要選擇一張已經(jīng)存在的圖片,New Contact視圖控制器呈現(xiàn)一個(gè)圖片選擇器界面。用戶必須丟棄圖片選擇器屏幕,然后單獨(dú)地丟棄New Contact屏幕返回到聯(lián)系人列表。
當(dāng)呈現(xiàn)一個(gè)視圖控制器時(shí),一個(gè)視圖控制器決定花費(fèi)多少屏幕來呈現(xiàn)視圖控制器。屏幕部分默認(rèn)被稱為呈現(xiàn)上下文,呈現(xiàn)上下文被定義用來覆蓋窗口。
關(guān)于如何在應(yīng)用程序中呈現(xiàn)視圖控制器的更多信息,請(qǐng)看Presenting View Controllers from Other View Controllers
五、視圖控制器一起工作創(chuàng)建一個(gè)應(yīng)用程序的界面
視圖控制器管理它們的視圖以及其它相關(guān)對(duì)象,但是它們還是跟其它視圖控制器一起提供一個(gè)無縫的用戶界面。您的應(yīng)用程序的視圖控制器之間的工作和交流的分配是與它們一起工作的一個(gè)重要組成部分。因?yàn)檫@些關(guān)系對(duì)于建立復(fù)雜應(yīng)用程序是如此重要,下一節(jié)回顧已經(jīng)討論過的關(guān)系,并介紹了它們更多細(xì)節(jié)。
1.父子關(guān)系表示包含
一個(gè)視圖控制器層次結(jié)構(gòu)以一個(gè)單一父控制器開始,即窗口的根視圖控制器。如果那個(gè)視圖控制器是一個(gè)容器,它可能有提供內(nèi)容的子控制器。那些子控制器相應(yīng)地也可能是它們自己的子控制器的容器。圖1-11顯示了一個(gè)視圖控制器層次結(jié)構(gòu)。根視圖控制器是一個(gè)帶有4個(gè)標(biāo)簽的標(biāo)簽視圖控制器。第一個(gè)標(biāo)簽使用一個(gè)帶有自己的子控制器的導(dǎo)航控制器,其它三個(gè)標(biāo)簽由內(nèi)容視圖控制器管理,它們沒有子控制器。
圖1-11

每個(gè)視圖控制器填充的區(qū)域由其父控制器決定。跟視圖控制器的區(qū)域是由窗口決定的。在圖1-11中,標(biāo)簽視圖控制器從窗口獲取它的尺寸。它給標(biāo)簽欄留出空間,并把剩余空間給子視圖。如果導(dǎo)航控制器是剛剛顯示的控件,它給導(dǎo)航欄留出控件,并把剩余空間留給內(nèi)容控制器。在每個(gè)步驟,子視圖控制器的視圖由父視圖重新調(diào)整尺寸并放入父視圖的視圖層次結(jié)構(gòu)中。
該視圖和視圖控制器的組合還為應(yīng)用程序處理事件建立了響應(yīng)鏈。
2.同胞關(guān)系代表一個(gè)容器內(nèi)的同級(jí)
容器的類型定義了其子視圖控制器共享的關(guān)系(如果存在)。比如,比較標(biāo)簽視圖控制器和導(dǎo)航控制器。
- 在一個(gè)標(biāo)簽視圖控制器中,標(biāo)簽標(biāo)示內(nèi)容的獨(dú)立屏幕;標(biāo)簽欄控制器沒有給它的子視圖控制器定義一個(gè)關(guān)系,盡管你的應(yīng)用程序可以這么做。
- 在一個(gè)導(dǎo)航欄控制器中,顯示同胞的相關(guān)視圖被安排在一個(gè)棧中。同胞常常跟相連的同胞共享一個(gè)連接。
旅途1-12顯示了跟導(dǎo)航控制器有關(guān)的一個(gè)通用視圖控制器配置。第一個(gè)子控制器Master顯示了不顯示所有的細(xì)節(jié)可用的內(nèi)容。當(dāng)一個(gè)數(shù)據(jù)被選擇時(shí),它推送一個(gè)新的同胞到導(dǎo)航控制器,這樣用戶就能看到額外的細(xì)節(jié)。想似的,如果油壺需要查看更多細(xì)節(jié),該同胞可以推送另一個(gè)視圖控制器來顯示可用的最詳細(xì)內(nèi)容。當(dāng)同胞們有像該例子中的一個(gè)良好定義,它們常常直接或通過容器控制器互相協(xié)作。參考圖1-15
圖1-12

3.呈現(xiàn)表示另一個(gè)界面的瞬時(shí)顯示
當(dāng)一個(gè)視圖控制器想要另一個(gè)視圖控制器執(zhí)行一個(gè)任務(wù)時(shí),它呈現(xiàn)另一個(gè)視圖控制器。呈現(xiàn)它的視圖控制器管理該行為。它配置被呈現(xiàn)的視圖控制器,從它那接收信息,并最終丟棄它。然而,當(dāng)它被呈現(xiàn)時(shí),被呈現(xiàn)的視圖控制器的視圖暫時(shí)被添加到窗口的視圖層次結(jié)構(gòu)中。
圖1-13,一個(gè)連接到標(biāo)簽視圖的內(nèi)容視圖呈現(xiàn)了一個(gè)視圖控制器來執(zhí)行一個(gè)任務(wù)。Content是呈現(xiàn)視圖控制器,Modal視圖控制器是被呈現(xiàn)的視圖控制器。
圖1-13

當(dāng)一個(gè)視圖控制器被呈現(xiàn)時(shí),它覆蓋的一部分屏幕由另一個(gè)視圖控制器提供的呈現(xiàn)上下文定義。提供呈現(xiàn)上下文的視圖控制器不需要跟呈現(xiàn)它的視圖控制器是同一個(gè)。圖1-14顯示了跟圖1-13同樣的視圖控制器層次。你可以看到內(nèi)容視圖呈現(xiàn)了視圖控制器,但是它不提供呈現(xiàn)上下文。相反,視圖控制器由標(biāo)簽控制器所呈現(xiàn)。因此,即使呈現(xiàn)的視圖控制器只覆蓋了標(biāo)簽視圖控制器提供給它的部分屏幕,但是被呈現(xiàn)的視圖控制器仍然使用了標(biāo)簽視圖控制器所擁有的整個(gè)區(qū)域。
圖1-14

4.控制流表示內(nèi)容控制器之間的整個(gè)協(xié)作
擁有多個(gè)視圖控制器的應(yīng)用程序,視圖控制器的創(chuàng)建和銷毀通常貫穿于應(yīng)用程序的整個(gè)生命周期。在生命周期中,視圖控制器互相交流以呈現(xiàn)一個(gè)無縫的用戶體驗(yàn)。這些關(guān)系代表應(yīng)用程序的控制流。
這樣的控制流最常見的發(fā)生時(shí)間是當(dāng)一個(gè)新的視圖控制器被實(shí)例化時(shí)。通常,一個(gè)視圖控制器被實(shí)例化是因?yàn)榱硪粋€(gè)視圖控制器中的動(dòng)作。第一個(gè)視圖控制器,被成為源視圖控制器引出第二個(gè)視圖控制器,即目標(biāo)視圖控制器。如果目標(biāo)視圖控制器需要給用戶呈現(xiàn)數(shù)據(jù),源視圖控制器常常提供那些數(shù)據(jù)。相似地,如果源視圖控制器需要從目標(biāo)視圖控制器獲取信息,它負(fù)責(zé)建立兩個(gè)視圖控制器之間的連接。
圖1-15顯示了這些關(guān)系的最通用例子
圖中:
- 一個(gè)導(dǎo)航控制器的一個(gè)子控制器推送另一個(gè)子控制器到導(dǎo)航棧。
- 一個(gè)視圖控制器呈現(xiàn)另一個(gè)視圖控制器。
- 一個(gè)視圖控制器在一個(gè)彈出菜單里顯示另一個(gè)視圖控制器。

每個(gè)控制器都有前一個(gè)控制器所配置。當(dāng)多個(gè)控制器一起工作,它們建立一個(gè)貫穿整個(gè)應(yīng)用程序的交流鏈。
鏈中每個(gè)連接的控制流都由目標(biāo)視圖控制器定義。源視圖控制器使用目標(biāo)視圖控制器提供的各種約定。
- 目標(biāo)視圖控制器提供用來配置其數(shù)據(jù)和呈現(xiàn)的各種屬性。
- 如果目標(biāo)視圖控制器需要跟鏈中前一個(gè)視圖控制器交流,它使用委托。當(dāng)源視圖控制器配置目標(biāo)視圖控制器的其他屬性,它還應(yīng)該提供一個(gè)實(shí)現(xiàn)委托協(xié)議的對(duì)象。
使用該控制流約定的好處是它讓每對(duì)源和目標(biāo)視圖控制器之間有一個(gè)清楚的責(zé)任分工。當(dāng)源視圖控制器要求目標(biāo)視圖控制器執(zhí)行一個(gè)任務(wù)時(shí),數(shù)據(jù)沿著路徑往下流動(dòng);源視圖控制器驅(qū)動(dòng)該進(jìn)程。比如,它可能提供目標(biāo)控制器應(yīng)該顯示的特定數(shù)據(jù)。在一個(gè)方向,當(dāng)一個(gè)視圖控制器需要把交流信息傳回催生它的源控制器時(shí),數(shù)據(jù)沿著路徑向上流動(dòng)。比如它可能在任務(wù)完成時(shí)發(fā)生交流。
此外,通過持續(xù)實(shí)現(xiàn)這種控制流模型,你還可以確保目標(biāo)視圖控制器絕不會(huì)知道關(guān)于源視圖控制器的太多配置信息。即使它確實(shí)了解關(guān)于鏈中的前一個(gè)視圖控制器。它也只知道實(shí)現(xiàn)委托協(xié)議的類,而不是類中的類。為了防止視圖控制器之間了解太多,讓控制器們獨(dú)立變得更加重要。對(duì)于閱讀你的代碼的其他人,一個(gè)持續(xù)實(shí)現(xiàn)的控制流模型讓它能很容易就看到任何一對(duì)控制器之間的交流路徑
六、故事板幫助你設(shè)計(jì)用戶界面
當(dāng)你用故事板實(shí)現(xiàn)你的應(yīng)用程序時(shí),你可以使用界面生成器來組織你的應(yīng)用程序視圖控制器以及任何相關(guān)視圖。圖1-16顯示了界面生成器上的一個(gè)界面。Interface Builder中的可視化布局一目了然,讓您可以了解您的應(yīng)用程序中的流。你可以看到哪些視圖控制器是由應(yīng)用程序?qū)嵗模约八鼈儽粚?shí)例化的順序。更重要的是,你可以配置視圖的復(fù)雜集合以及故事板中的其它對(duì)象。由次產(chǎn)生的故事板作為一個(gè)文件被存儲(chǔ)在項(xiàng)目中。當(dāng)你建立項(xiàng)目時(shí),項(xiàng)目中的故事板被處理并拷貝到應(yīng)用程序束(bundle)中,在那里它們?cè)谶\(yùn)行時(shí)有應(yīng)用程序加載。
圖1-16
](https://developer.apple.com/library/ios/featuredarticles/ViewControllerPGforiPhoneOS/Art/after_modal_segue_2x.png)
通常,iOS能根據(jù)需要在故事板中自動(dòng)實(shí)例化視圖控制器。相似的,跟每個(gè)控制器相關(guān)的視圖層次在它需要被顯示時(shí)自動(dòng)被加載。視圖控制器和視圖都以界面生成器中的相同配置被實(shí)例化。因?yàn)榇蠖鄶?shù)該行為都是自動(dòng)化為你完成的,所以它極大地簡(jiǎn)化了在應(yīng)用程序中使用視圖控制器的所需的工作。
關(guān)于創(chuàng)建故事板的完整細(xì)節(jié)在Xcode User Guide中有描述。目前,你需要知道一些在應(yīng)用程序中實(shí)現(xiàn)故事板所需的必要術(shù)語(yǔ)。
一個(gè)場(chǎng)景表示一個(gè)屏幕上的內(nèi)容區(qū)域,它由一個(gè)視圖控制器管理。你可以把場(chǎng)景想象成一個(gè)視圖控制器以及其相關(guān)的視圖層次結(jié)構(gòu)。
你在同一個(gè)故事板中創(chuàng)建場(chǎng)景之間的關(guān)系。關(guān)系在一個(gè)故事板中用從一個(gè)場(chǎng)景到另一個(gè)場(chǎng)景的一個(gè)連接箭頭可視化表達(dá)。當(dāng)你在兩個(gè)對(duì)象之間做一個(gè)連接時(shí),界面生成器通常自動(dòng)推斷一個(gè)新關(guān)系的細(xì)節(jié)。存在兩種重要類型的關(guān)系:
-
包含表示兩個(gè)場(chǎng)景之間的一個(gè)父子關(guān)系。當(dāng)父控制器被實(shí)例化時(shí),包含在其它視圖控制器里的視圖控制器也被實(shí)例化。比如,從一個(gè)導(dǎo)航控制器到另一個(gè)場(chǎng)景的第一個(gè)鏈接定義了把第一個(gè)視圖控制器推送到導(dǎo)航棧。該控制器在導(dǎo)航控制器被實(shí)例化時(shí)自動(dòng)被實(shí)例化。
在一個(gè)故事板轉(zhuǎn)哦給你使用包含關(guān)系的一個(gè)好處是Interface Builder可以調(diào)整子視圖控制器的外形來反映其祖先的存在。當(dāng)內(nèi)容視圖控制器出現(xiàn)在你的最終應(yīng)用程序中時(shí),這樣做允許Interface Builder顯示內(nèi)容視圖控制器。
-
Segue表示從一個(gè)場(chǎng)景到另一個(gè)場(chǎng)景的一個(gè)可視化過渡。在運(yùn)動(dòng)時(shí),segues可以由不同的動(dòng)作觸發(fā)。當(dāng)一個(gè)segue被觸發(fā)時(shí),它導(dǎo)致一個(gè)新視圖控制器被實(shí)例化并過渡到屏幕上。
盡管一個(gè)segue 總是從一個(gè)視圖控制器到另一個(gè),有時(shí)該過程中可以涉及第三個(gè)對(duì)象。該對(duì)象實(shí)際觸發(fā)segue。比如,如果你做一個(gè)連接從一個(gè)按鈕(該按鈕在源視圖控制器的視圖層次結(jié)構(gòu)中)到目標(biāo)視圖控制器,當(dāng)用戶點(diǎn)擊該按鈕,segue被觸發(fā)。當(dāng)直接從源視圖控制器到目標(biāo)視圖控制器做一個(gè)segue,它通常表示你打算通過程序觸發(fā)一個(gè)segue。
不同類型的segues 提供了兩個(gè)不同視圖控制器之間所需的常用的過渡:- Push segue 把目標(biāo)視圖控制器推送到一個(gè)導(dǎo)航控制器棧中。
- Modal segue 表示目標(biāo)視圖控制器。
- Popover segue 在一個(gè)popover里顯示目標(biāo)視圖控制器。
- Custom segue 允許你設(shè)計(jì)自己的過渡來顯示目標(biāo)視圖控制器。
Segues共享一個(gè)通用編程模型。在該模型中,目標(biāo)控制器由iOS自動(dòng)實(shí)例化,然后調(diào)用源視圖控制器來配置它。該行為符合控制流模型,該模型在Control Flow Represents Overall Coordination Between Content Controllers.中描述。
你還可以在一個(gè)視圖控制器和存醋在同一個(gè)場(chǎng)景中的對(duì)象之間創(chuàng)建鏈接。這些Outlets和Actions讓你能仔細(xì)的定義視圖控制器和其相關(guān)對(duì)象之間的關(guān)系。關(guān)系本身在故事板中不是默認(rèn)可見的,但是它們能在Interface Builder中的Connections Inspector中查看。