視圖控制器管理著構(gòu)成應(yīng)用程序用戶界面中的一部分視圖梁剔,其負(fù)責(zé)加載和處理這些視圖大诸,管理與這些視圖的交互苟蹈,并協(xié)調(diào)視圖對(duì)其展示的數(shù)據(jù)內(nèi)容的變更作出響應(yīng)蜡豹。視圖控制器還能與其他視圖控制器對(duì)象協(xié)調(diào)工作,幫助管理應(yīng)用程序的整體界面半哟。
視圖控制器的主要職責(zé)包括以下內(nèi)容:
- 更新視圖的內(nèi)容來響應(yīng)底層數(shù)據(jù)的變化酬滤。
- 響應(yīng)用戶與視圖的交互。
- 調(diào)整視圖尺寸并管理整個(gè)界面的布局寓涨。
視圖控制器的作用
視圖控制器是應(yīng)用程序內(nèi)部結(jié)構(gòu)的基礎(chǔ)盯串。每個(gè)應(yīng)用程序至少含有一個(gè)視圖控制器,大多數(shù)應(yīng)用程序都含有多個(gè)視圖視圖控制器戒良。每個(gè)視圖控制器管理著應(yīng)用程序的用戶界面体捏,以及該界面和底層數(shù)據(jù)之間的交互。視圖控制器也便于在不同用戶界面之間的轉(zhuǎn)換。
UIViewController
類定義了一些方法和屬性來管理視圖几缭、處理事件河泳、從一個(gè)視圖控制器切換到另一個(gè)視圖控制器和協(xié)調(diào)應(yīng)用程序的其他部分,可以通過繼承UIViewController
(或其子類之一)來添加實(shí)現(xiàn)應(yīng)用程序行為所需的自定義代碼奏司。
有兩種類型的視圖控制器:
- 內(nèi)容視圖控制器乔询,其管理著應(yīng)用程序內(nèi)容的一部分。
- 容器視圖控制器韵洋,其收集來自于其他視圖控制器的信息并以便于導(dǎo)航的方式呈現(xiàn)或者以不同方式呈現(xiàn)這些視圖控制器的內(nèi)容竿刁。
視圖管理
視圖控制器最重要的作用是管理視圖的層次結(jié)構(gòu)。每個(gè)視圖控制器都含有一個(gè)包含所有視圖控制器內(nèi)容的根視圖搪缨,可以添加需要顯示內(nèi)容的視圖到該根視圖中食拜。下圖顯示了視圖控制器和視圖之間的內(nèi)置關(guān)系,視圖控制器總是具有對(duì)其根視圖的強(qiáng)引用副编,并且每個(gè)視圖都具有對(duì)其子視圖的強(qiáng)引用负甸。
內(nèi)容視圖控制器自己管理其包含的所有視圖,容器視圖控制器管理其所包含的所有視圖以及來自其一個(gè)或多個(gè)子視圖控制器的根視圖痹届。容器視圖控制器并不管理其子視圖控制器的內(nèi)容呻待,只管理其子視圖控制器的根視圖。下圖說明了UISplitViewController
與其子視圖控制器之間的關(guān)系队腐。UISplitViewController
管理其子視圖的整體尺寸和位置蚕捉,但子視圖控制器管理這些視圖的實(shí)際內(nèi)容。
數(shù)據(jù)封送
視圖控制器充當(dāng)其管理的視圖與應(yīng)用程序數(shù)據(jù)之間的媒介柴淘。子類化UIViewController
的時(shí)候迫淹,可以添加任何需要在子類中管理的數(shù)據(jù)變量。添加自定義變量會(huì)創(chuàng)建一個(gè)如下圖所示的關(guān)系为严,其中視圖控制器具有對(duì)數(shù)據(jù)的引用以及用于呈現(xiàn)該數(shù)據(jù)的視圖敛熬。
應(yīng)該始終在視圖控制器和數(shù)據(jù)對(duì)象中保持清晰的職責(zé)分離。大多數(shù)確保數(shù)據(jù)結(jié)構(gòu)完整性的邏輯應(yīng)屬于數(shù)據(jù)對(duì)象本身第股。視圖控制器可以驗(yàn)證來自視圖的輸入应民,然后以數(shù)據(jù)對(duì)象需要的格式打包輸入,但是應(yīng)該最小化視圖控制器在管理實(shí)際數(shù)據(jù)中的角色夕吻。
UIDocument
對(duì)象是一種獨(dú)立于視圖控制器管理數(shù)據(jù)的方式诲锹。文檔對(duì)象是一個(gè)知道如何讀寫數(shù)據(jù)到持久存儲(chǔ)的控制器對(duì)象。子類化UIDocument
時(shí)梭冠,可以添加任何需要的邏輯和方法來提取數(shù)據(jù)辕狰,并將其傳遞給視圖控制器或者應(yīng)用程序的某部分改备。視圖控制器可以存儲(chǔ)其接收到的任何數(shù)據(jù)的副本控漠,以便更新視圖,但文檔仍然擁有真實(shí)的數(shù)據(jù)。
用戶交互
視圖控制器是響應(yīng)者對(duì)象盐捷,能夠處理響應(yīng)者鏈中傳遞的事件偶翅,但視圖控制器很少直接處理觸摸事件。相反碉渡,視圖通常會(huì)處理自己的觸摸事件聚谁,并將結(jié)果報(bào)告給其關(guān)聯(lián)的委托或目標(biāo)對(duì)象(通常是視圖控制器)的方法。因此滞诺,視圖控制器中的大多數(shù)事件都是使用委托方法或操作方法處理的形导。
資源管理
視圖控制器對(duì)其視圖和它創(chuàng)建的任何對(duì)象承擔(dān)全部責(zé)任。UIViewController
類自動(dòng)處理視圖管理的大多數(shù)方面习霹,例如朵耕,UIKit自動(dòng)釋放不再需要的任何視圖相關(guān)的資源。子類化UIViewController
時(shí)淋叶,需要自己負(fù)責(zé)管理創(chuàng)建的任何對(duì)象阎曹。
當(dāng)可用空閑內(nèi)存不足時(shí),UIKit會(huì)要求應(yīng)用程序釋放不再需要的資源煞檩。其中一種方式是通過調(diào)用視圖控制器的didReceiveMemoryWarning
方法來刪除對(duì)不再需要的對(duì)象的引用或者稍后重新創(chuàng)建处嫌。例如,在該方法中刪除緩存的數(shù)據(jù)斟湃。發(fā)生內(nèi)存不足的情況時(shí)熏迹,釋放盡可能多的內(nèi)存非常重要。消耗太多內(nèi)存的應(yīng)用程序可能會(huì)被系統(tǒng)徹底終止以恢復(fù)內(nèi)存桐早。
適應(yīng)性
視圖控制器負(fù)責(zé)呈現(xiàn)其視圖癣缅,并使該呈現(xiàn)適應(yīng)底層環(huán)境。每個(gè)iOS應(yīng)用程序都應(yīng)該能夠在iPad上運(yùn)行哄酝,并且可以在幾種不同尺寸的iPhone上運(yùn)行友存。不是為每個(gè)設(shè)備提供不同的視圖控制器和視圖層,而是使用單個(gè)視圖控制器來更簡單地調(diào)整其視圖以適應(yīng)不斷變化的空間需求陶衅。
在iOS中屡立,視圖控制器需要處理粗粒度的變化和細(xì)粒度的變化。當(dāng)視圖控制器的特性改變時(shí)搀军,會(huì)發(fā)生粗粒度的變化膨俐。特征是描述整體環(huán)境的屬性,例如顯示比例罩句。其中兩個(gè)最重要的特性是視圖控制器的水平和垂直尺寸類別焚刺,是它們表示視圖控制器在給定維度中有多少空間∶爬茫可以使用size class changes來改變布局視圖的方式乳愉,如下圖所示兄淫。如果horizontal size class是規(guī)則的,視圖控制器利用額外的水平空間來排列其內(nèi)容蔓姚。如果horizontal size class是緊湊的捕虽,視圖控制器垂直排列其內(nèi)容。
根據(jù)給定的size class坡脐,可以隨時(shí)進(jìn)行更細(xì)粒度的尺寸更改泄私。當(dāng)用戶將iPhone從縱向旋轉(zhuǎn)到橫向時(shí),size class可能不會(huì)改變备闲,但屏幕尺寸通常會(huì)改變晌端。在使用自動(dòng)布局時(shí),UIKt會(huì)自動(dòng)調(diào)整視圖的尺寸和位置以匹配新尺寸恬砂。視圖控制器可以根據(jù)需要進(jìn)行其他調(diào)整斩松。
視圖控制器層次結(jié)構(gòu)
應(yīng)用程序的視圖控制器之間的關(guān)系定義了每個(gè)視圖控制器所需的行為,維護(hù)正確的視圖控制器關(guān)系可以確保自動(dòng)行為在需要時(shí)傳遞給正確的視圖控制器觉既。如果違反了UIKit規(guī)定的規(guī)則和呈現(xiàn)關(guān)系惧盹,則應(yīng)用程序的表現(xiàn)可能和預(yù)期不一致。
根視圖控制器
根視圖控制器是視圖控制器層次結(jié)構(gòu)的錨點(diǎn)瞪讼。每個(gè)窗口只有一個(gè)根視圖控制器钧椰,此根視圖控制器的內(nèi)容填充該窗口。根視圖控制器定義了用戶看到的初始內(nèi)容符欠。下圖顯示了根視圖控制器和窗口之間的關(guān)系嫡霞,因?yàn)榇翱诒旧頉]有可見的內(nèi)容,所以視圖控制器的視圖提供了所有的內(nèi)容希柿。
根視圖控制器可以從UIWindow
對(duì)象的rootViewController
屬性訪問诊沪。當(dāng)使用storyboard來配置視圖控制器時(shí),UIKit會(huì)在啟動(dòng)時(shí)自動(dòng)設(shè)置該屬性的值曾撤。對(duì)于以編程方式創(chuàng)建的窗口端姚,必須自己設(shè)置根視圖控制器。
容器視圖控制器
容器視圖控制器允許使用更易于管理和可重用的界面來組裝復(fù)雜的界面挤悉。容器視圖控制器將一個(gè)或多個(gè)子視圖控制器的內(nèi)容與可選的自定義視圖混合在一起渐裸,來創(chuàng)建其最終界面。例如装悲,UINavigationController
對(duì)象顯示來自其子視圖控制器的內(nèi)容以及由其管理的導(dǎo)航欄和可選工具欄昏鹃。UIKit包含多個(gè)容器視圖控制器,包括UINavigationController
诀诊、UISplitViewController
和UIPageViewController
洞渤。
容器視圖控制器的視圖總是會(huì)填充給定的空間,其通常被指定為窗口的根視圖控制器属瓣。容器視圖控制器也可以以模態(tài)的方式呈現(xiàn)载迄,或者作為其他容器的子項(xiàng)安裝奈懒。下圖顯示了在容器并排放置兩個(gè)子視圖。
由于容器視圖控制器管理其子項(xiàng)宪巨,UIKit定義了如何在自定義容器中設(shè)置這些子項(xiàng)的規(guī)則。
呈現(xiàn)一個(gè)視圖控制器
呈現(xiàn)一個(gè)視圖控制器時(shí)溜畅,通常會(huì)隱藏當(dāng)前視圖控制器的內(nèi)容來將當(dāng)前視圖控制器的內(nèi)容替換為新視圖控制器的內(nèi)容捏卓。呈現(xiàn)最常用于模態(tài)地顯示新內(nèi)容。在呈現(xiàn)一個(gè)視圖控制器時(shí)慈格,UIKit會(huì)在呈現(xiàn)視圖控制器和其呈現(xiàn)的視圖控制器之間創(chuàng)建如下圖所示的關(guān)系怠晴。
當(dāng)呈現(xiàn)涉及到容器視圖控制器時(shí),UIKit可能會(huì)修改呈現(xiàn)鏈來簡化必須編寫的代碼浴捆。不同的呈現(xiàn)風(fēng)格對(duì)應(yīng)的視圖在屏幕上的顯示方式有不同的規(guī)則蒜田,例如全屏呈現(xiàn)總是覆蓋整個(gè)屏幕。在呈現(xiàn)一個(gè)視圖控制器時(shí)选泻,UIKit會(huì)查找為呈現(xiàn)提供合適上下文的視圖控制器冲粤。在許多情況下,UIKit會(huì)選擇最近的容器視圖控制器页眯,但也可能選擇窗口的根視圖控制器梯捕。在某些情況下,也可以直接告訴UIKit哪個(gè)視圖控制器定義了呈現(xiàn)上下文窝撵,并且應(yīng)該處理呈現(xiàn)傀顾。
下圖顯示了容器視圖控制器為呈現(xiàn)提供上下文的原因。在執(zhí)行全屏呈現(xiàn)時(shí)碌奉,新視圖控制器需要覆蓋整個(gè)屏幕短曾。容器視圖控制器決定是否處理呈現(xiàn),而不需要其子視圖控制器知道容器視圖的邊界赐劣。
設(shè)計(jì)技巧
視圖控制器是在iOS上運(yùn)行的應(yīng)用程序的基本工具嫉拐,并且UIKit的視圖控制器基礎(chǔ)結(jié)構(gòu)可以很容易地創(chuàng)建復(fù)雜的界面,而無需編寫大量的代碼魁兼。在實(shí)現(xiàn)我們自己的視圖控制器時(shí)椭岩,請(qǐng)使用一下提示和指導(dǎo)原則,以確保我們不會(huì)干涉可能會(huì)干擾系統(tǒng)預(yù)期的自然行為璃赡。
盡可能使用系統(tǒng)提供的視圖控制器
許多iOS框架定義了視圖控制器判哥,使用這些系統(tǒng)提供的視圖控制器可一節(jié)省時(shí)間并確保為用戶提供一致的體驗(yàn)。大多數(shù)系統(tǒng)控制器都是為特定任務(wù)而設(shè)計(jì)的某些控制器提供對(duì)用戶數(shù)據(jù)(如聯(lián)系人)的訪問碉考,也可能提供訪問硬件或提供專門調(diào)整的界面來管理媒體塌计。例如,UIKit中的UIImagePickerController
類顯示用于獲取圖片和視頻以及訪問用戶相機(jī)膠卷的標(biāo)準(zhǔn)界面侯谁。
在創(chuàng)建自己的自定義視圖控制器前锌仅,請(qǐng)查看現(xiàn)有的框架是否存在能夠執(zhí)行需要的任務(wù)的視圖控制器:
- UIKit框架提供視圖控制器用于顯示彈窗警告章钾,拍照和錄像,以及管理iCloud上的文件热芹。UIKit還定義許多可用于組織內(nèi)容的標(biāo)準(zhǔn)容器視圖控制器贱傀。
- GameKit框架提供了視圖控制器可用于匹配玩家以及管理排行榜、成就和其他游戲功能伊脓。
- Address Book UI框架提供了用于顯示和選擇聯(lián)系人信息的視圖控制器府寒。
- MediaPlayer框架提供了用于播放和管理視頻以及從用戶庫中選擇媒體資源的視圖控制器。
- EventKit UI框架提供了用于顯示和編輯用戶日歷數(shù)據(jù)的視圖控制器报腔。
- GLKit框架提供了用于管理OpenGL渲染界面的視圖控制器株搔。
- Multipeer Connectivity框架提供了用于檢測其他用戶并邀請(qǐng)他們進(jìn)行連接的視圖控制器。
- Message UI框架提供了用于撰寫電子郵件和SMS消息的視圖控制器纯蛾。
- PassKit框架提供了用于顯示通行證并將其添加到Passbook的視圖控制器纤房。
- Social框架為Twitter、Facebook和其他社交媒體網(wǎng)站提供了編輯消息的視圖控制器翻诉。
- AVFoundation框架提供了用于顯示媒體資源的視圖控制器炮姨。
重要:切勿修改系統(tǒng)提供的視圖控制器的視圖層次結(jié)構(gòu)。每個(gè)視圖控制器都擁有其視圖層次結(jié)構(gòu)碰煌,并負(fù)責(zé)維護(hù)該層次結(jié)構(gòu)的完整性剑令。進(jìn)行更改可能會(huì)在代碼中引入錯(cuò)誤,或阻止其所屬視圖控制器的正常運(yùn)行拄查。使用系統(tǒng)視圖控制器時(shí)吁津,總是依靠視圖控制器公開的可用方法和屬性進(jìn)行所有修改的。
有關(guān)使用特定視圖控制器的信息堕扶,可查看相應(yīng)框架的參考文檔碍脏。
保證每個(gè)視圖控制器獨(dú)立運(yùn)行
視圖控制器應(yīng)始終是自給自足的對(duì)象,沒有視圖控制器應(yīng)該含有有關(guān)于另一個(gè)視圖控制器的內(nèi)部工作或視圖層次結(jié)構(gòu)的信息稍算。在兩個(gè)視圖控制器需要來回通信或傳遞數(shù)據(jù)的情況下典尾,它們應(yīng)該始終使用明確定義的公共接口來實(shí)現(xiàn)。
代理設(shè)計(jì)模式經(jīng)常用于管理視圖控制器之間的通信糊探。通過委托钾埂,一個(gè)對(duì)象定義了一個(gè)相關(guān)的委托對(duì)象進(jìn)行通信的協(xié)議,該委托對(duì)象是任何符合該協(xié)議的對(duì)象科平。委托對(duì)象的確切類型是不重要的褥紫,重要的是它實(shí)現(xiàn)了協(xié)議的方法。
僅將根視圖用作其他視圖的容器
僅將視圖控制器的根視圖用作其余內(nèi)容的容器瞪慧。使用根視圖作為容器可以為所有視圖提供一個(gè)共同的父視圖髓考,這使得許多布局操作變得更簡單。許多自動(dòng)布局約束需要共同的父視圖來正確布置視圖弃酌。
知道數(shù)據(jù)該存在于哪里
在模型 - 視圖 - 控制器設(shè)計(jì)模式中氨菇,視圖控制器的作用是促進(jìn)模型對(duì)象和視圖對(duì)象之間的數(shù)據(jù)移動(dòng)儡炼。視圖控制器可能會(huì)將一些數(shù)據(jù)存儲(chǔ)在臨時(shí)變量中并執(zhí)行一些驗(yàn)證,但其主要職責(zé)是確保其視圖包含準(zhǔn)確的信息查蓉。而模型數(shù)據(jù)對(duì)象負(fù)責(zé)管理實(shí)際數(shù)據(jù)并確保數(shù)據(jù)的完整性乌询。
UIDocument
和UIViewController
類之間的關(guān)系就是一個(gè)數(shù)據(jù)和接口分離的例子。具體而言豌研,兩者之間不存在默認(rèn)關(guān)系妹田。UIDocument
對(duì)象負(fù)責(zé)協(xié)調(diào)數(shù)據(jù)的加載和保存,而UIViewController
對(duì)象協(xié)調(diào)屏幕上的視圖顯示聂沙。如果需要在兩個(gè)對(duì)象之間創(chuàng)建關(guān)系,請(qǐng)記住視圖控制器應(yīng)該只緩存文檔中的信息以提高效率初嘹,實(shí)際的數(shù)據(jù)仍然屬于文檔對(duì)象及汉。
適應(yīng)變化
應(yīng)用程序可以在各種iOS設(shè)備上運(yùn)行,并且視圖控制器被設(shè)計(jì)為適應(yīng)這些設(shè)備上不同尺寸的屏幕屯烦。并不是使用單獨(dú)的視圖控制器來管理不同屏幕上的內(nèi)容坷随,而是使用內(nèi)置的適配性支持響應(yīng)視圖控制器中的尺寸和size class的更改。UIKit發(fā)送的通知使我們有機(jī)會(huì)對(duì)用戶界面進(jìn)行大規(guī)模和小規(guī)模的更改驻龟,而無需更改視圖控制器代碼的其余部分温眉。