你好,歡迎來到IOS教程網

 Ios教程網 >> IOS訊息 >> 關於IOS >> UIViewController的誤用

UIViewController的誤用

編輯:關於IOS

什麼是UIViewController的誤用

UIViewController是iOS開發中最常見也最重要的部件之一,可以說絕大多數的app都用到了UIViewController來管 理頁面的view。它是MVC的核心結構和橋梁構成,可以說UIViewController是絕大多數開發者所花時間最多的部分了。

但是正是這樣一個重要的類卻經常被誤用,從而導致app的不穩定,莫名奇妙的bug甚至無法通過appstore的審查。最常見和最可怕的誤用在於 在一個UIViewController裡加入本來不應該由它管理的其他UIViewController,也即違反了Apple在開發者文檔中關於 UIViewController的描述:

Each custom view controller object you create is responsible for managing all of the views in a single view hierarchy. In iPhone applications, the views in a view hierarchy traditionally cover the entire screen, but in iPad applications they may cover only a portion of the screen. The one-to-one correspondence between a view controller and the views in its view hierarchy is the key design consideration. You should not use multiple custom view controllers to manage different portions of the same view hierarchy. Similarly, you should not use a single custom view controller object to manage multiple screens worth of content.

一個ViewController應該且只應該管理一個view hierarchy,而通常來說一個完整的view hierarchy指的是整整占滿的一個屏幕。而很多app滿屏中會有各個區域分管不同的功能,一些開發者喜歡直接新建一個ViewController 和一套相應的View來完成所要的功能(比如我自己=_=)。雖然十分方便,但是卻面臨很多風險..

一般來說,只要你的代碼中含有類似這樣的語句,那你一定是誤用UIViewController了

<code class="hljs">viewController.view.bounds = CGRectMake(50, 50, 100, 200);  
[viewController.view addSubview:someOtherViewController.view];
</code>

這樣的代碼可能導致莫名的bug,也會令接手的開發者無所適從。

小題大做吧,這樣用會有什麼問題呢

一個很麻煩的問題是,這將會導致你的app在不同的iOS版本上有不同的表現。在iOS5之前,能夠對viewController進行管理的類有 UINavigationController,UITabBarController和iPad專有的UISplitViewController。而 在iOS5中加入了可自定義的ViewControllers的容器。由於新的SDK的處理機制,iOS4前通過addSubview加到當前 controller的view上的view的呈現,將不會觸發被加入view hierarchy的view的controller的viewWillAppear:方法。而且,新加入的viewController也不會接收到諸 如didReceiveMemoryWarning等委托方法,而且也不能響應所有的旋轉事件!而iOS5中由於所謂的custom container VC的出現,上述方法又能夠運行良好,這導致了同樣代碼在不同終端產生不同的行為,為之後的維護和進一步開發埋下了隱患。另外,用這樣的方法所添加的 viewController顯然違背了Apple的本意,它的parentViewController,interfaceOrientation顯 然都是錯誤的,有時候甚至會出現觸摸事件無法響應等嚴重問題。

好吧,那我們要怎麼辦

如果你已經在一個app裡這樣誤用了大量的viewController,那可能的辦法也許是盡力去自行處理各種非正常的狀況,比如在 addSubview之後手動調用加入的vc的viewWillAppear:,以及在收到didReceiveMemoryWarning後順次調用子 VC的didReceiveMemoryWarning(顯然都是很蛋疼的做法啊)。但是需要注意的是iOS5中這些方法的調用似乎是沒有問題的(至少我 測試是這樣),因此需要對不同版本系統進行分別處理。可以用UIDevice的方法確定運行環境的系統版本:

<code class="hljs cpp"><span class="hljs-comment">// System Versioning Preprocessor Macros</span>
<span class="hljs-preprocessor">#<span class="hljs-keyword">define</span> SYSTEM_VERSION_EQUAL_TO(v) ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] == NSOrderedSame)</span>
<span class="hljs-preprocessor">#<span class="hljs-keyword">define</span> SYSTEM_VERSION_GREATER_THAN(v) ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] == NSOrderedDescending)</span>
<span class="hljs-preprocessor">#<span class="hljs-keyword">define</span> SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(v) ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] != NSOrderedAscending)</span>
<span class="hljs-preprocessor">#<span class="hljs-keyword">define</span> SYSTEM_VERSION_LESS_THAN(v) ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] == NSOrderedAscending)</span>
<span class="hljs-preprocessor">#<span class="hljs-keyword">define</span> SYSTEM_VERSION_LESS_THAN_OR_EQUAL_TO(v) ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] != NSOrderedDescending)</span>
</code>

在合適的時機判定判定系統版本,手動調用對應方法:

<code class="hljs cpp"><span class="hljs-keyword">if</span> (SYSTEM_VERSION_LESS_THAN(@“<span class="hljs-number">5.0</span>”))  
{ 
    <span class="hljs-comment">//viewWillAppear或didReceiveMemoryWarning或其他</span>
}
</code>

顯然,這樣的代碼既非優雅亦難維護,而且隨著iOS版本的更新,誰也不知道這段代碼之後會不會有什麼問題,無形中增加了開發成本。

真正的解決之道

當然是嚴格遵守Apple提供的設計規范,每個VC管理一個view hierarchy。在設計的時候,永遠記住你的view和view controller都需要重用,而不恰當的使用view controller會導致重用性大打折扣。而通用的view有時也需要一個類似controller的東西來管理它的subview的行為,或者做出某 些相應,這個時候我們不妨想一想一些Apple寫的經典的view是如何實現的,比如UITableView和UIPickerView,依靠 protocol的各種方法進行配置。 作為自定義的view的controller應當是直接繼承自NSObject的類,而不應該是UIViewController。一個 UIViewController可以包含若干個這樣的controller來控制一個view中的不同部分的功能實現,而對於對應的自定義view是代 碼寫的還是nib出來的就無所謂了。當然,如果是新接觸iOS開發的話,我個人不建議使用Interface Builder,除非你確實清楚IB到底在背後為你做了什麼。在當你完全清楚之後,IB確實能極大提升開發效率(特別是在Xcode4以後),但是如果你 的對IB和view加載連接的概念如同毛線團的話,IB的使用只會讓你以及讓你的同事茫然失措。 在iOS5中提供了所謂的container of View Controllers,有興趣的童鞋可以參看WWDC 2011的Session 102 – Implementing UIViewController Containment(需要一個野生開發者賬號)

一些資料

作為iOS開發者,Apple的關於UIViewController的文檔以及開發者的一些討論是必讀的,簡單整理如下:

  • View Controller Programming Guide for iOS
  • 關於誤用UIViewController而造成的私有API調用的討論
  • stack overflow上關於誤用view controller的討論
  1. 上一頁:
  2. 下一頁:
蘋果刷機越獄教程| IOS教程問題解答| IOS技巧綜合| IOS7技巧| IOS8教程
Copyright © Ios教程網 All Rights Reserved