WWDC 2014 筆記 - iOS界面開發的大一統

http://www.cocoachina.com/industry/20140729/9269.html


(via:OneV's Den)
 
本文是我的 WWDC 2014 筆記 中的一篇,涉及的 Session 有
 
iOS 8 和 OS X 10.10 中一個被強調了多次的主題就是大一統,Apple 希望通過 Hand-off 和各種體驗的無縫切換和集成將用戶黏在由 Apple 設備構成的生態圈中。而對開發者而言,今年除了 Swift 的一個大主題也是平臺的統一。在 What's New in Cocoa Touch 的 Seesion 一開始,UIKit 的工程師 Luke 就指出了 iOS 8 SDK 的最重要的關鍵字就是自適應 (adaptivity)。這是一個很激動人心的詞,首先自適應是一種設計哲學,儘量使事情保持簡單,我們便可從中擢取優雅;另一方面,可能這也是 Apple 不得不做的轉變。隨着傳說中的更大屏和超大屏的 iPhone 6 的到來,開發者在爲 iOS 進行開發的時候似乎也開始面臨着和安卓一樣的設備尺寸的碎片化的問題。而 iOS 8 所着重希望解決的,就是這一問題。
 
Size Classes
首先最值得一說的是,iOS 8 應用在界面設計時,迎來了一個可以說是革命性的變化 - Size Classes。
 
基本概念
在 iPad 和 iPhone 5 出現之前,iOS 設備就只有一種尺寸。我們在做屏幕適配時需要考慮的僅僅有設備方向而已。而很多應用並不支持轉向,這樣的話就完全沒有屏幕適配的工作了。隨着 iPad 和 iPhone 5,以及接下來的 iPhone 6 的推出,屏幕尺寸也變成了需要考慮的對象。在 iOS 7 之前,爲一個應用,特別是 universal 的應用製作 UI 時,我們總會首先想我們的目標設備的長寬各是多少,方向變換以後佈局又應該怎麼改變,然後進行佈局。iOS 6 引入了 Auto Layout 來幫助開發者使用約束進行佈局,這使得在某些情況下我們不再需要考慮尺寸,而可以專注於使用約束來規定位置。
 
既然我們有了 Auto Layout,那麼其實通過約束來指定視圖的位置和尺寸是沒有什麼問題的了,從這個方面來說,屏幕的具體的尺寸和方向已經不那麼重要了。但是實戰中這還不夠,Auto Layout 正如其名,只是一個根據約束來進行佈局的方案,而在對應不同設備的具體情況下的體驗上還有欠缺。一個最明顯的問題是它不能根據設備類型來確定不同的交互體驗。很多時候你還是需要判斷設備到底是 iPhone 還是 iPad,以及現在的設備方向究竟是豎直還是水平來做出判斷。這樣的話我們還是難以徹底擺脫對於設備的判斷和依賴,而之後如果有新的尺寸和設備出現的話,這種依賴關係顯然顯得十分脆弱的(想想要是有 iWatch 的話..)。
 
所以在 iOS 8 裏,Apple 從最初的設計哲學上將原來的方式推翻了,並引入了一整套新的理念,來適應設備不斷的發展。這就是 Size Classes。
 
不再根據設備屏幕的具體尺寸來進行區分,而是通過它們的感官表現,將其分爲普通 (Regular) 和緊密 (Compact) 兩個種類 (class)。開發者便可以無視具體的尺寸,而是對這這兩類和它們的組合進行適配。這樣不論在設計時還是代碼上,我們都可以不再受限於具體的尺寸,而是變成遵循尺寸的視覺感官來進行適配。

 
簡單來說,現在的 iPad 不論橫屏還是豎屏,兩個方向均是 Regular 的;而對於 iPhone,豎屏時豎直方向爲 Regular,水平方向是 Compact,而在橫屏時兩個方向都是 Compact。要注意的是,這裏和談到的設備和方向,都僅僅只是爲了給大家一個直觀的印象。相信隨着設備的變化,這個分類也會發生變動和更新。Size Classes 的設計哲學就是尺寸無關,在實際中我們也應該儘量把具體的尺寸拋開腦後,而去儘快習慣和適應新的體系。
 
UITraitCollection 和 UITraitEnvironment
爲了表徵 Size Classes,Apple 在 iOS 8 中引入了一個新的類--UITraitCollection。這個類封裝了像水平和豎直方向的 Size Class 等信息。iOS 8 的 UIKit 中大多數 UI 的基礎類 (包括 UIScreen,UIWindow,UIViewController 和 UIView) 都實現了 UITraitEnvironment 這個接口,通過其中的 traitCollection 這個屬性,我們可以拿到對應的 UITraitCollection 對象,從而得知當前的 Size Class,並進一步確定界面的佈局。
 
和 UIKit 中的響應者鏈正好相反,traitCollection 將會在 view hierarchy 中自上而下地進行傳遞。對於沒有指定 traitCollection 的 UI 部件,將使用其父節點的 traitCollection。這在佈局包含 childViewController 的界面的時候會相當有用。在 UITraitEnvironment 這個接口中另一個非常有用的是 -traitCollectionDidChange:。在 traitCollection 發生變化時,這個方法將被調用。在實際操作時,我們往往會在 ViewController 中重寫 -traitCollectionDidChange: 或者 -willTransitionToTraitCollection:withTransitionCoordinator: 方法 (對於 ViewController 來說的話,後者也許是更好的選擇,因爲提供了轉場上下文方便進行動畫;但是對於普通的 View 來說就只有前面一個方法了),然後在其中對當前的 traitCollection 進行判斷,並進行重新佈局以及動畫。代碼看起來大概會是這個樣子:
  1. - (void)willTransitionToTraitCollection:(UITraitCollection *)newCollection  
  2.               withTransitionCoordinator:(id <UIViewControllerTransitionCoordinator>)coordinator 
  3.     [super willTransitionToTraitCollection:newCollection  
  4.                  withTransitionCoordinator:coordinator]; 
  5.     [coordinator animateAlongsideTransition:^(id <UIViewControllerTransitionCoordinatorContext> context) { 
  6.         if (newCollection.verticalSizeClass == UIUserInterfaceSizeClassCompact) { 
  7.             //To Do: modify something for compact vertical size 
  8.         } else { 
  9.             //To Do: modify something for other vertical size 
  10.         } 
  11.         [self.view setNeedsLayout]; 
  12.     } completion:nil]; 
在兩個 To Do 中,我們應該刪除或者添加或者更改不同條件下的 Auto Layout 約束 (當然,你也可以幹其他任何你想做的事情),然後調用 -setNeedsLayout 來在上下文中觸發轉移動畫。如果你堅持用代碼來處理的話,可能需要面臨對於不同 Size Classes 來做移除舊的約束和添加新的約束這樣的事情,可以說是很麻煩 (至少我覺得是麻煩的要死)。但是如果我們使用 IB 的話,這些事情和代碼都可以省掉,我們可以非常方便地在 IB 中指定各種 Size Classes 的約束 (稍後會介紹如何使用 IB 來對應 Size Classes)。另外使用 IB 不僅可以節約成百上千行的佈局代碼,更可以從新的 Xcode 和 IB 中得到很多設計時就可以實時監視,查看並且調試的特性。可以說手寫 UI 和使用 IB 設計的時間消耗和成本差距被進一步拉大,並且出現了很多手寫 UI 無法實現,但是 IB 可以不假思索地完成的任務。從這個意義上來說,新的 IB 和 Size Classes 系統可以說無情地給手寫代碼判了個死緩。
 
另外,新的 API 和體系的引入也同時給很多我們熟悉的 UIViewController 的有關旋轉的老朋友判了死刑,比如下面這些 API 都棄用了:
  1. @property(nonatomic, readonly) UIInterfaceOrientation interfaceOrientation 
  2.   
  3. - willRotateToInterfaceOrientation:duration: 
  4. - willAnimateRotationToInterfaceOrientation:duration: 
  5. - didRotateFromInterfaceOrientation: 
  6. - shouldAutomaticallyForwardRotationMethods 
現在全部統一到了 viewWillTransitionToSize:withTransitionCoordinator:,旋轉的概念不再被提倡使用。其實仔細想想,所謂旋轉,不過就是一種 Size 的改變而已,我們都被 Apple 騙了好多年,不是麼?
 
Farewell, I will NOT miss you at all.
 
在 Interface Builder 中使用 Size Classes
第一次接觸 Xcode 6 和打開 IB 的時候你可能會驚呼,爲什麼我的畫布變成正方形了。我在第一天 Keynote 結束後在 Moscone Center 的食堂裏第一次打開的時候,還滿以爲自己找到了 iWatch 方形顯示屏的確鑿證據。到後來才知道,這是新的 Size Classes 對應的編輯方式。
 
既然我們不需要關心實際的具體尺寸,那麼我們也就完全沒有必要在 IB 中使用像 3.5/4 寸的 iPhone 或是 10 寸的 iPad 來分開對界面進行編輯。使用一個通用的具有 "代表" 性質的尺寸在新體系中確實更不容易使人迷惑。
 
在現在的 IB 界面的正下方,你可以看到一個 wAny hAny 的按鈕 (因爲今年 NDA 的一個明確限制是不能發相關軟件截圖,雖然其實可能沒什麼太大問題,但是還是尊重 license 比較好),這代表現在的 IB 是對應任意高度和任意寬度的。點擊後便可以選擇需要爲哪種 Size Class 進行編輯。默認情況在 Any Any 下的修改會對任意設備和任意方向生效,而如果先進行選擇後再進行編輯,就表示編輯只對選中的設定生效。這樣我們就很容易在同一個 storyboard 文件裏對不同的設備進行適配:按照設備需要添加或者編輯某些約束,或者是在特定尺寸下隱藏某些 view (使用 Attribute Inspector 裏的 Installed 選框的加號添加)。這使得使用 IB 製作通用程序變簡單了,我們不再需要爲 iPhone 和 iPad 準備兩套 storyboard 了。
 
可以發揮的想象空間實在太大,一套界面佈局通喫所有設備的畫面太美好,我都不敢想。
 
Size Classes 和 Image Asset 及 UIAppearence
Image Asset 裏也加入了對 Size Classes 的支持,也就是說,我們可以對不同的 Size Class 指定不同的圖片了。在 Image Asset 的編輯面板中選擇某張圖片,Inspector 裏現在多了一個 Width 和 Height 的組合,添加我們需要對應的 Size Class, 然後把合適的圖拖上去,這樣在運行時 SDK 就將從中挑選對應的 Size 的圖進行替換了。不僅如此,在 IB 中我們也可以選擇對應的 size 來直接在編輯時查看變化(新的 Xcode 和 IB 添加了非常多編輯時的可視化特性,關於這方面我有計劃單獨寫一篇可視化開發的文章進行說明)。
 
這個特性一個最有用的地方在於對於不同屏幕尺寸可能我們需要的圖像尺寸也有所不同。比如我們希望在 iPhone 豎屏或者 iPad 時的按鈕高一些,而 iPhone 橫屏時由於屏幕高度實在有限,我們希望得到一個扁一些的按鈕。對於純色按鈕我們可以通過簡單的約束和拉伸來實現,但是對於有圖案的按鈕,我們之前可能就需要在 VC 裏寫一些髒代碼來處理了。現在,只需要指定好特定的 Image Asset,然後配置合適的 (比如不含有尺寸限制) 約束,我們就可以一行代碼不寫,就完成這樣複雜的各個機型和方向的適配了。
 
實際做起來實在是太簡單了..但拿個 demo 說明一下吧,比如下面這個實現了豎直方向 Compact 的時候將笑臉換成哭臉 -- 當然了,一行代碼都不需要。

另外,在 iOS 7 中 UIImage 添加了一個 renderingMode 屬性。我們可以使用 imageWithRenderingMode: 並傳入一個合適的 UIImageRenderingMode 來指定這個 image 要不要以 Template 的方式進行渲染。在新的 Xcode 中,我們可以直接在 Image Asset 裏的 Render As 選項來指定是不是需要作爲 template 使用。而相應的,在 UIApperance 中,Apple 也爲我們對於 Size Classes 添加了相應的方法。使用 +appearanceForTraitCollection: 方法,我們就可以針對不同 trait 下的應用的 apperance 進行很簡單的設定。比如在上面的例子中,我們想讓笑臉是綠色,而哭臉是紅色的話,不要太簡單。首先在 Image Asset 裏的渲染選項設置爲 Template Image,然後直接在 AppDelegate 里加上這樣兩行:
  1. UIView.appearanceForTraitCollection(UITraitCollection(verticalSizeClass:.Compact)).tintColor = UIColor.redColor()   
  2.         UIView.appearanceForTraitCollection(UITraitCollection(verticalSizeClass:.Regular)).tintColor = UIColor.greenColor() 
 
 

完成,只不過拖拖鼠標,兩行簡單的代碼,隨後還能隨喜換色,果然是大快所有人心的大好事。
 
UIViewController 的表現方式
 
UISplitViewController
在用 Regular 和 Compact 統一了 IB 界面設計之後,Apple 的工程師可能發現了一個讓人兩難的歷史問題,這就是 UISplitViewController。一直做 iPhone 而沒太涉及 iPad 的童鞋可能對着這個類不是很熟悉,因爲它們是 iPad Only 的。iPad 推出時爲了適應突然變大的屏幕,並且遠離 "放大版 iTouch" 的詬病,Apple 爲 iPad 專門設計了這個主從關係的 ViewControlle容器。事實也證明了這個設計在 iPad 上確實是被廣泛使用,是非常成功的。
 
現在的問題是,如果我們只有一套 UI 畫布的話,我們要怎麼在這個單一的畫布上處理和表現這個 iPad Only 的類呢?
 
答案是,讓它在 iPhone 上也能用就行了。沒錯,現在你可以直接在 iPhone 上使用 SplitViewController 了。在 Regular 的寬度時,它保持原來的特性,在 DetailViewController 中顯示內容,這是毫無疑問的。而在 Compact 中,我們第一想法就是以 push 的表現形式展示。在以前,我們可能需要寫不少代碼來處理這些事情,比如在 AppDelegate 中就在一開始判斷設備是不是 iPad,然後爲應用設定兩套完全不同的導航:一套基於 UINavigationController,另一套基於 UISplitViewController。而現在我們只需要一套 UISplitViewController,並將它的 MasterViewController 設定爲一個 navgationController 就可以輕鬆搞定所有情況了。
 
也許你會想,即使這樣,我是不是還是需要判斷設備是不是 iPad,或者現在的話是判斷 Size Class 是不是 Compact,來決定是要做的到底是 navVC 的 push 還是改變 splitVC 的 viewControllers。其實不用,我們現在可以無痛地不加判斷,直接用統一的方式來完成兩種表現方式。這其中的奧妙在於我們不需要使用 (事實上 iOS 8 後 Apple 也不再提倡使用) UINavigationController 的 pushViewController:animated: 方法了 (又一個老朋友要和我們說再見了)。其實雖然很常用,但是這個方法是一直受到社區的議論的:因爲正是這個方法的存在使得 ViewController 的耦合特性上了一個檔次。在某個 ViewController 中這個方法的存在時,就意味着我們需要確保當前的 ViewController 必須處於一個導航棧的上下文中,這是完全和上下文耦合的一種方式 (雖然我們也可以很蛋疼地用判斷 navController 是不是 nil 來繞開,但是畢竟真的很醜,不是麼)。
 
我們現在有了新的展示 viewController 的方法,-showViewController:sender: 以及 -showDetailViewController:sender:。調用這兩個方法時,將順着包括調用 vc 自身的響應鏈而上,尋找最近的實現了這個方法的 ViewController 來執行相應代碼。在 iOS SDK 的默認實現中,在 UISplitViewController 這樣的容器類中,已經有這兩個方法的實現方式,而 UINavigationController 也實現了 -showViewController:sender: 的版本。對於在 navController 棧中的 vc,會調用 push 方式進行展示,而對 splitVC,showViewController:sender: 將在 MasterViewController 中進行 push。而 showDetailViewController:sender: 將根據水平方向的 Size 的情況進行選擇:對於 Regular 的情況,將在 DetailViewController 中顯示新的 vc,而對於 Compact 的情況,將由所在上下文情況發回給下級的 navController 或者是直接以 modal 的方式展現。關於這部分的具體內容,可以仔細看看這個示例項目和相關的文檔 (beta版)。
 
這麼設計的好處是顯而易見的,首先是解除了原來的耦合,使得我們的 ViewController 可以不被侷限於導航控制器上下文中;另外,這幾個方法都是公開的,也就是說我們的 ViewController 可以實現這兩個方法,截斷響應鏈的響應,並實現我們自己的呈現方式。這在自定義 Container Controller 的時候會非常有用。
 
UIPresentationController
iOS 7 中加入了一套實現非常漂亮的自定義轉場動畫的方法 (如果你還不知道或者不記得了,可以看看我去年的這篇筆記)。Apple 在解耦和重用上的努力確實令人驚歎。而今年,順着自適應和平臺開發統一的東風,在呈現 ViewController 的方式上 Apple 也做出了從 iOS SDK 誕生以來最大的改變。iOS 8 中新加入了一個非常重要的類 UIPresentationController,這個 NSObject 的子類將用來管理所有的 ViewController 的呈現。在實現方式上,這個類和去年的自定義轉場的幾個類一樣,是完全解耦合的。而 Apple 也在自己的各種 viewController 呈現上完全統一地使用了這個類。
 
再見 UIPopoverController
和 SplitViewController 類似,UIPopoverController 原來也只是 iPad 使用的,現在 iPhone 上也將適用。準確地說,現在我們不再使用 UIPopoverController 這個類 (雖然現在文檔還沒有將其標爲 deprecated,但是估計也是遲早的事兒了),而是改用一個新的類 UIPopoverPresentationController。這是 UIPresentationController 的子類,專門用來負責呈現以 popover 的形式呈現內容,是 iOS 8 中用來替代原有的 UIPopoverController 的類。
 
比起原來的類,新的方式有什麼優點呢?最大的優勢是自適應,這和 UISplitViewController 在 iOS 8 下的表現是類似的。在 Compact 的寬度條件下,UIPopoverPresentationController 的呈現將會直接變成 modal 出來。這樣我們基本就不再需要去判斷 iPhone 還是 iPad (其實相關的判定方法也已經被標記成棄用了),就可以對應不同的設備了。以前我們可能要寫類似這樣的代碼:
  1. if UIDevice.currentDevice().userInterfaceIdiom == .Pad {   
  2.     let popOverController = UIPopoverController(contentViewController: nextVC) 
  3.     popOverController.presentPopoverFromRect(aRect, inView: self.view, permittedArrowDirections: .Any, animated: true
  4. else { 
  5.     presentViewController(nextVC, animated: true, completion: nil) 
而現在需要做的是:
 
  1. nextVC.modalPresentationStyle = .Popover   
  2. let popover = nextVC.popoverPresentationController   
  3. popover.sourceRect = aRect   
  4. popover.permittedArrowDirections = .Any 
  5.  
  6. presentViewController(nextVC, animated: true, completion: nil)   
沒有可惡的條件判斷,一切配置井井有條,可讀性也非常好。
 
除了自適應之外,新方式的另一個優點是非常容易自定義。我們可以通過繼承 UIPopoverPresentationController 來實現我們自己想要的呈現方式。其實更準確地說,我們應該繼承的是 UIPresentationController,主要通過實現 -presentationTransitionWillBegin 和 -presentationTransitionDidEnd: 來自定義我們的展示。像以前我們想要實現只佔半個屏幕,後面原來的 view 還可見的 modal,或者是將從下到上的動畫改爲百葉窗或者漸隱漸現,那都是可費勁兒的事情。而在 UIPresentationController 的幫助下,一切變得十分自然和簡單。在自己的 UIPresentationController 子類中:
  1. override func presentationTransitionWillBegin() {   
  2.     let transitionCoordinator = self.presentingViewController.transitionCoordinator() 
  3.     transitionCoordinator.animateAlongsideTransition({context in 
  4.         //Do animation here 
  5.     }, completion: nil) 
  6.   
  7. override func presentationTransitionDidEnd(completed: Bool)  {   
  8.     //Do clean here 
 
具體的用法和 iOS 7 裏的自定義轉場很類似,設定需要進行呈現操作的 ViewController 的 transition delegate,在 UIViewControllerTransitioningDelegate 的 -presentationControllerForPresentedViewController:sourceViewController: 方法中使用 -initWithPresentedViewController:presentingViewController: 生成對應的 UIPresentationController 子類對象返回給 SDK,然後就可以喝茶看戲了。
 
再見 UIAlertView, 再見 UIActionSheet
自適應和 UIPresentationController 給我們帶來的另一個大變化是 UIAlertView 和 UIActionSheet 這兩個類的消亡 (好吧其實算不上消亡,棄用而已)。現在,Alert 和 ActionSheet 的呈現也通過 UIPresentationController 來實現。原來在沒有 Size Class 和需要處理旋轉的黑暗年代 (抱歉在這裏用了這個詞,但是我真的一點也不懷念那段處理設備旋轉的時光) 裏,把這兩個 view 顯示出來其實幕後是一堆噁心的事情:創建新的 window,處理新 window 的大小和方向,然後將 alert 或者 action sheet 按合適的大小和方向加到窗口上,然後還要考慮處理轉向,最後顯示出來。雖然 Apple 幫我們做了這些事情,但是輪到我們使用時,往往它們也只能滿足最基本的需求。在適配 iPhone 和 iPad 時,UIAlertView 還好,但是對於 UIActionSheet 我們往往又得進行不同處理,來選擇是不是需要 popover。
 
另外一個要命的地方是因爲這兩個類是 iOS 2.0 開始就存在的爺爺級的類了,而最近一直也沒什麼大的更新,設計模式上還使用的是傳統的 delegate 那一套東西。實際上對於這種很輕很明確的使用邏輯,block handler 纔是最好的選擇,君不見滿 GitHub 的 block alert view 的代碼,但是沒轍,4.0 纔出現的 block 一直由於種種原因,在這兩個類中一直沒有得到官方的認可和使用。
 
而作爲替代品的 UIAlertController 正是爲了解決這些問題而出現的,值得注意的是,這是一個 UIViewController 的子類。可能你會問 UIAlertController 對應替代 UIAlertView,這很好,但是 UIActionSheet 怎麼辦呢?哈..答案是也用 UIAlertController,在 UIAlertController 中有一個 preferredStyle 的屬性,暫時給我們提供了兩種選擇 ActionSheet 和 Alert。在實際使用時,這個類的 API 還是很簡單的,使用工廠方法創建對象,進行配置後直接 present 出來:
  1. let alert = UIAlertController(title: "Test", message: "Msg", preferredStyle: .Alert) 
  2.   
  3. let okAction = UIAlertAction(title: "OK", style: .Default) {   
  4.     [weak alert] action in 
  5.     print("OK Pressed"
  6.     alert!.dismissViewControllerAnimated(true, completion: nil) 
  7. alert.addAction(okAction)   
  8. presentViewController(alert, animated: true, completion: nil)   
 
使用上除了小心循環引用以外,並沒有太多好說的。在 Alert 上加文本輸入也變得非常簡單了,使用 -addTextFieldWithConfigurationHandler: 每次向其上添加一個文本輸入,然後在 handler 裏拿數據就好了。
 
要記住的是,在幕後,做呈現的還是 UIPresentationController。
 
UISearchDisplayController -> UISearchController
最後想簡單提一下在做搜索欄的時候的同樣類似的改變。在 iOS 8 之前做搜索欄簡直是一件讓人崩潰的事情,而現在我們不再需要討厭的 UISearchDisplayController 了,也沒有莫名其妙的在視圖樹中強制插入 view 了 (如果你做過搜索欄,應該知道我在說什麼)。這一切在 iOS 8 中也和前面說到的 alert 和 actionSheet 一樣,被一個 UIViewController 的子類 UISearchController 替代了。背後的呈現機制自然也是 UIPresentationController,可見新的這個類在 iOS 8 中的重要性。
 
總結
對於廣大 iOS 開發者賴以生存的 UIKit 來說,這次最大的變化就是 Size Classes 的引入和新的 Presentation 系統了。在 Keynot 上 Craig 就告訴我們,iOS 8 SDK 將是 iOS 開發誕生以來最大的一次變革,此言不虛。雖然 iOS 8 SDK 的廣泛使用估計還有要有個兩年時間,但是不同設備的開發的 API 的統一這一步已然邁出,這也正是 Apple 之後的發展方向。正如兩年前的 Auto Layout 正在今天大放光彩一樣,之後 Size Classes 和新的 ViewController 也必將成爲日常開發的主力工具。
 
程序員嘛,果然需要每年不斷學習,才能跟上時代。

     ViewController是iOS應用程序中重要的部分,是應用程序數據和視圖之間的重要橋樑,ViewController管理應用中的衆多視圖。iOS的SDK中提供很多原生ViewController,以支持標準的用戶界面,例如表視圖控制器(UITableViewController)、導航控制器(UINavigationController)、標籤欄控制器(UITabbarController)和iPad專有的UISplitViewController等。

 

        按結構可以對iOS的所有ViewController分成兩類:
        1、主要用於展示內容的ViewController,這種ViewController主要用於爲用戶展示內容,並與用戶交互,如UITableViewController,UIViewController。
        2、用於控制和顯示其他ViewController的ViewController。這種ViewController一般都是一個 ViewController的容器。如UINavigationController,UITabbarController。它們都有一個屬 性:viewControllers。其中UINavigationController表示一種Stack式結構,push一個 ViewController或pop一次,因此後一個ViewController一般會依賴前一個ViewController。而 UITabbarController表示一個Array結構,各個ViewController是並列的。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章