我常聽見iOS程序員問類似這樣的問題:
iOS開發(fā)界面的最好的方法是:Storyboard,NIBs,還是代碼?
對這個問題的各種答案嘹黔,似乎明示或暗示著,必有一個唯一的選擇莫瞬,這個選擇往往要在開發(fā)之前就要做出儡蔓。
我的看法,這個問題的答案應(yīng)該是以一個或多個類似問題的形式來給出疼邀。
哪款車是“最好的”喂江?
來舉一個題外的例子。假如我想買一輛車檩小,我問你一個“簡單”問題:“我的最佳選擇是什么开呐?”
你能真的給我建議一個車型,甚至是一個品牌來回答我的問題嗎?恐怕不能筐付。除非你建議“法拉利”卵惦。相反,你更可能會通過問一些問題來回答:
- 你有多少錢瓦戚?
- 你需要幾個座兒沮尿?
- 你對油耗有多在意?
- 你覺得越野車怎么樣较解?
很顯然畜疾,除非在特定前提下,否則沒有“好車”或“壞車”之分——只有在需求確定的前提下才有好車印衔、壞車之分啡捶。
回到iOS UI設(shè)計問題
就像剛才選車的問題一樣,“開發(fā)iOS UI的最好的方法是什么”這個問題也缺少特定前提奸焙。令你意外的是瞎暑,這個問題的答案也不需要解決所有問題。
概括地說与帆,有三種開發(fā)UI的方法可供你選擇熬丧,并且每一種都各有長短诡宗,各有粉絲和吐槽者静浴。
- iOS Storyboards: 一個可視化的工具即横,可以放置多個views以及他們之間的過渡
- NIBs(or XIBs): 每個NIB文件對應(yīng)于一個單獨的view元素,可以放置在Interface Builder中阵翎,故也算一個可視化工具逢并。注:“NIB”這個名字來源于文件后綴(以前是.nib,現(xiàn)在是.xib贮喧,盡管現(xiàn)在還是讀“NIB”)
- 純代碼: 沒有GUI工具筒狠,用程序的方式來處理所有的位置設(shè)置、動畫等效果
這些選擇中箱沦,任何一個都不比另外的一個“更好”(盡管你可能聽說過哪個更好)。
例如:Storyboards雇庙,是最遲加入到iOS UI工具包中的谓形。我曾聽說“他們將是未來,將替代NIBs和代碼UI”疆前。但是寒跳,我只把Storyboards看作一個利器,而不是一個完全替代NIBs和代碼的替代者竹椒。Storyboards在某些場合是正確的選擇童太,但也不是所有場合。
進一步說,如果你(在一個工程中)可以使用任意一個书释,最恰當?shù)亟鉀Q某個特定問題翘贮,那為什么要保守一個選擇不放呢?
在我的觀點中爆惧,這個問題可以提升到一個更高層次狸页,其答案在我的一系列“軟件開發(fā)原理”中排名還很靠前,這個答案就是:沒有哪一種萬能的語言扯再,萬能的框架或技術(shù)對軟件開發(fā)中的任意問題都是絕對最好的解決方案芍耘。 iOS UI設(shè)計問題也是如此。
本篇iOS開發(fā)引導(dǎo)中熄阻,我們將逐一介紹以上每一種方法斋竞,以及哪些場合適用,哪些場合不適用秃殉,已經(jīng)哪些場合兼而用之窃页。
iOS Storyboards
新手常犯的一個典型錯誤是:創(chuàng)建一個工程級巨大的Storyboard,當然复濒,我剛開始用時也犯了這個錯誤脖卖。
新手常犯的一個典型錯誤是:創(chuàng)建一個工程級巨大的Storyboard。一個Storyboard上是呈現(xiàn)了一個“故事”巧颈,而不應(yīng)該將不相關(guān)的很多故事混合在一起
顧名思義畦木,一個Storyboard就是一個“有故事要講”的板,不應(yīng)該將不相關(guān)的很多故事混合在一起砸泛。一個Storyboard應(yīng)該包含一系列在邏輯上有關(guān)聯(lián)的view controller十籍,而不是所有。
舉例來說唇礁,處理下列問題是使用Storyboard是合理的:
- 授權(quán)和注冊所需的一系列views
- 由多步組成的訂閱入口
- 類似于引導(dǎo)的事務(wù)流
- 此處不會譯
同時勾栗,應(yīng)避免太大的Storyboard,也要避免整個App就一個Storyboard(除非App相對比較簡單)盏筐。在我們深入探討之前围俘,先來看看為什么。
超大Storyboard的不便之處
超大的Storyboard琢融,不但很難瀏覽界牡、很難維護,還給團隊引入了一層復(fù)雜性:當多個程序員同時在一個Storyboard工作時漾抬,代碼沖突就在所難免宿亡。因為Storyboard本質(zhì)上是文本文件(xml),merge往往不是一件很容易的事纳令。
程序員看代碼時挽荠,是通過代碼的語義來理解克胳。所以手工合并時,可以讀懂沖突的兩邊文件圈匆,并相應(yīng)處理漠另。而Storyboard是由xcode管理的xml,每行代碼的往往并不好弄明白臭脓。
舉一個很簡單的栗子:假如兩個程序員都改變了一個UILabel(使用了自動布局)的位置酗钞,后提交(試圖提交)的取到已提交者的代碼,就會產(chǎn)生下面的沖突(注意已沖突的id屬性):
<layoutGuides>
<viewControllerLayoutGuide type="top" id="ar5-ed-tdC"/>
<viewControllerLayoutGuide type="bottom" id="enX-PS-xZQ"/>
</layoutGuides>
<layoutGuides>
<viewControllerLayoutGuide type="top" id="6cK-2r-Dvq"/>
<viewControllerLayoutGuide type="bottom" id="EbB-ky-ghh"/>
</layoutGuides>
id屬性本身沒有提供任何有用的信息来累,所以你不知道該怎么做砚作。唯一合理的做法是從兩邊選一個、舍棄另一個嘹锁。但這樣會不會有神馬“副作用”呢葫录?誰知道?反正你不知道领猾。
?為了使UI設(shè)計問題簡化米同,建議在一個工程中使用多個Storyboard。
何時使用Storyboard
Storyboard最好被用于彼此有聯(lián)系的多個view controller時摔竿,這樣主要是能簡化view controller直接的切換面粮。一定程度上,他們可以被看做NIBs的可視化和view controller之間切換的組合继低。
除了簡化切換熬苍,另一個顯著的好處是可以省去一些代碼,諸如:pop袁翁,push柴底,present 以及 dismiss view controllers。另外view controller是自動分配粱胜,所以無需alloc init
最后柄驻,雖然說最好是應(yīng)用于“多個”view controllers的場景,但對“單個”table view controller應(yīng)用Storyboard也是蠻好的焙压。原因有3:
- 可以當場設(shè)計tabel cell原型鸿脓,所有的東西都在一起
- 多個cell模板可以在父table view controller中設(shè)計
- 使創(chuàng)建static table views成為可能
有人會說,多個cell模板也可以用NIBs來設(shè)計冗恨。當然答憔,這只是個人喜好問題:有的人喜好所有東西都放在一起,有的人卻無所謂掀抹。
何時不要 用Storyboard
下面這些情況:
- 如果view有很復(fù)雜的Layout,或動態(tài)Layout心俗,最好用代碼實現(xiàn)
- 如果view已經(jīng)用NIB或代碼實現(xiàn)
【譯者:下面這段我不是太理解傲武,就大概翻譯一下】
這些情況蓉驹,要么把view從storyboard提出去,要么把他嵌入到view controller中揪利。前一種做法态兴,破壞了storyboard的“可視化流程”(visual flow),但是沒有任何實現(xiàn)上的負面影響疟位。后一種做法瞻润,保持了可視化流程,但是需要額外的開發(fā)工作甜刻,因為view不是集成到view controller中的:它只是作為一個組件嵌入進來绍撞,所以view controller必需和它進行交互。
一般的優(yōu)點和缺點
現(xiàn)在我們對“在iOS UI設(shè)計中得院,storyboard何時有用”有了一個大概了解傻铣,在開始了解NIBs前,在過一般storyboard的優(yōu)勢和劣勢
優(yōu)點:性能
你可能會覺得祥绞,加載一個storyboard時非洲,它所有的view controllers會馬上被實例化。然而蜕径,這只是一種抽象两踏,實際實現(xiàn)并非如此,相反兜喻,只是初始(initial)view controller(如果有的話)被創(chuàng)建梦染。其它的view controller動態(tài)地實例化,無論是通過segue虹统,還是代碼實現(xiàn)弓坞。
優(yōu)點:原型
用storyboard可以簡化用戶界面和流程的原型和組織。事實上车荔,一個由views和導(dǎo)航組成的完整渡冻、可工作的原型應(yīng)用程序,用storyboard很容易實現(xiàn)——只需要不多的代碼
缺點:復(fù)用性
對于移動和復(fù)制忧便,storyboard的表現(xiàn)就不太好了族吻。一個storyboard必須和它所依賴的view controllers一起移動。換句話說珠增,一個單獨的view controller不能被單獨抽取出來作為一個單獨的實體在其它地方使用超歌,它依賴于storyboard的其它部分才能工作。
缺點:數(shù)據(jù)流
應(yīng)用程序運行時蒂教,經(jīng)常需要將數(shù)據(jù)在view controller之間傳遞巍举。然而,storyboard的可視化流程在這種情況下會被破壞凝垛,因為在Interface Builder中無法傳數(shù)據(jù)懊悯。storyboard負責(zé)處理view controller之間的切換蜓谋,但卻不處理數(shù)據(jù)流。所以炭分,“目標”controller必須用代碼“配置”桃焕,這樣就破壞了“可視化”。
這些情況下捧毛,我們不得不依賴于prepareForSegue:sender观堂,再加上if/else-if:
(void) prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
NSString *identifier = [segue identifier];
if ([identifier isEqualToString@"segue_name_1"]) {
MyViewController *vc = (MyViewController *) [segue destinationViewController];
[vc setData:myData];
} else if ([identifier isEqualToString@"segue_name_2"]) {
...
} else if ...
}
我認為這種方式很容易出錯,而且也過于繁瑣呀忧。
NIBs
NIBs是(較)老的設(shè)計UI的方式师痕。
在這里,“老”并不意味著“壞”荐虐、“過時”七兜、“不推薦”。事實上福扬,應(yīng)該深刻理解:storyboard并不是能完全取代NIBs的方式腕铸,它們僅僅是在某些情況下的合適方式。
使用NIB铛碑,一個單獨的view被設(shè)計出來狠裹,然后根據(jù)需要把它“附加”到view controller上去。
如果我們用“面向?qū)ο蟆钡姆绞絹碓O(shè)計UI汽烦,那么就應(yīng)該把view controller’s view拆分成獨立的模塊涛菠,每一個模塊是一個由單獨的NIB文件設(shè)計的view(或者多個模塊組合到同一個文件)。這樣做明顯的優(yōu)勢是:每一個元素都易開發(fā)撇吞、易測試俗冻、易調(diào)試。
NIBs也有像storyboard那樣的“合并沖突”問題牍颈,但相對來說較不嚴重迄薄,因為NIB所操作的范圍要小。
何時用NIBs設(shè)計UI
部分可用的場合如:
- 模式views【譯者:那些彈出的對話框之類吧】
- 簡單的登錄煮岁、注冊views
- 設(shè)置
- Popup windows
- 可復(fù)用view模板
- 可復(fù)用tabel cell模板
然后……
何時不要用NIBs
下面這些情況應(yīng)避免使用NIBs:
- 如果view中有動態(tài)內(nèi)容讥蔽,且根據(jù)內(nèi)容的不同Layout會有較大差異
- 如果view天然就不容易用Interface Builder設(shè)計
- 如果view controllers有復(fù)雜的transition,但用storyboard方式可以簡化
一般的優(yōu)點和缺點
讓我們來看看NIBs的優(yōu)缺點画机。
優(yōu)點:復(fù)用性
如果多個類共用相同的Layout冶伞,那么NIBs是很方便的。
舉個簡單的栗子步氏。
【譯者:這個栗子是關(guān)于登錄和注冊view復(fù)用同一個NIB响禽,因為他們都由username+password組成。不是太會翻譯,故略過】
優(yōu)點(也是缺點):性能
NIBs是“懶加載”金抡,所以直到加載之前是不占內(nèi)存的瀑焦。這是一個優(yōu)勢腌且,但同時梗肝,因為懶加載過程有開銷【這里不太會譯】,所以也是一個劣勢铺董。
代碼(用程序?qū)崿F(xiàn)UI)
任何可以用storyboard或NIBs實現(xiàn)的UI都可以用純代碼實現(xiàn)(當然巫击,曾經(jīng)一度,沒有那么多工具時就是這樣的)
NIBs和storyboard不能實現(xiàn)的精续,都可以用代碼實現(xiàn)坝锰。
也許更重要的是,NIBs和storyboard不能實現(xiàn)的重付,都可以用代碼實顷级。當然,那是代碼的技術(shù)特性确垫。換一個角度弓颈,NIBs和storyboard本身就是代碼實現(xiàn)的,所以它們的功能天然就是代碼全部功能的一個子集∩鞠疲現(xiàn)在直接來看代碼的優(yōu)點和缺點吧翔冀。
優(yōu)點:本質(zhì)
用代碼實現(xiàn)UI的最大的好處是:如果你知道如何用代碼來實現(xiàn)UI,那么你就知道背后發(fā)生的事情披泪,可是NIBs和storyboard卻不一定纤子。
類比一下:計算器是一個有用的工具。但是款票,學(xué)會手工計算也是一個不錯的事控硼。
不只是iOS,任何可視化RAD(Rapid Application Development)工具(比如:Visual Studio艾少、Delphi)卡乾,Visual HTML RAD開發(fā)環(huán)境算是最差的代表:它們可以產(chǎn)生代碼(當然,往往是很差的)姆钉,聲稱“不需要html只是”说订,一切都可以可視化完成。但是潮瓶,沒有一個web程序員能“不用弄臟手”【譯者:指的是不用寫代碼】就可以寫出網(wǎng)頁來陶冷,因為他們知道手寫的html和CSS可以產(chǎn)生更模塊化、更有效的代碼毯辅。
所以埂伦,掌握用代碼構(gòu)建iOS UI可以讓你有更多控制,也更明白各部分是如何拼接起來的思恐,進而提高你作為一個程序員的“上限”
優(yōu)點:但代碼是唯一的選擇時
有一些場景沾谜,只能用代碼構(gòu)建UI膊毁。動態(tài)Layout——可視元素動來動去,Layout根據(jù)內(nèi)容有較大調(diào)整基跑,是典型的例子婚温。
優(yōu)點:合并沖突
NIBs和storyboard飽受合并沖突之苦,而代碼卻完全不受此苦媳否。所有代碼都是有意義的栅螟,所以解決沖突跟其他(代碼沖突)情況是一樣一樣的。
缺點:原型
直到代碼運行起來之前篱竭,你看不到一個Layout長的什么樣力图。更甚,你不能可視化地擺放views和controls掺逼,所以吃媒,將Layout文檔(specs)轉(zhuǎn)為可見的view需要更多時間,而NIBs和storyboard卻可以馬上給你一個可以預(yù)覽的效果吕喘。
缺點:重構(gòu)
重構(gòu)很久之前寫的代碼或別人的代碼——代碼中元素被放置赘那,或動畫時用的自定義的方法或奇怪的數(shù)字,將會是一個很費勁的活兽泄,調(diào)試也一樣費勁漓概。
優(yōu)點:性能
性能方面,NIBs和storyboard是先加載病梢、解析胃珍,最后再翻譯成代碼。不用說蜓陌,代碼構(gòu)建UI不會有此轉(zhuǎn)換過程觅彰。
優(yōu)點:復(fù)用
任何代碼構(gòu)建的view都可以用“可復(fù)用”的方式來實現(xiàn)。來看幾個例子:
- 兩個或更多views共用一個行為钮热,但又有些微的區(qū)別填抬。可以通過一個基類和兩個派生類來解決此問題隧期,很優(yōu)雅飒责。
- 有時不得不拆分一個工程,目的是基于同一套代碼仆潮,產(chǎn)生兩個(或更多)不同的應(yīng)用程序宏蛉,每一個有自己特殊的定制【譯者:這個例子有點莫名其妙】
如果是用NIBs或storyboard來實現(xiàn)這同樣的過程,將會復(fù)雜的多性置。模板文件【就是那些定義UI的xml文件吧】不支持繼承拾并,所以可能的解決方案也就是下面這些:
- 復(fù)制NIB和storyboard文件。從此以后,他們將開始不同的“生命”嗅义,跟原始文件也沒有了關(guān)系屏歹。
- 用代碼改寫(override)可視效果和行為。這種方式之碗,在簡單情況下或許奏效蝙眶,但復(fù)雜了就可能導(dǎo)致異常的復(fù)雜性。大規(guī)模的override可能導(dǎo)致原先用NIBs或storyboard實現(xiàn)的UI效果完全無用继控,如械馆,某個控件在Interface Builder中表現(xiàn)是一種行為,而在程序運行起來之后卻完全是另外一種行為武通,這時你就會相當頭疼。
何時使用代碼
當有下列情形時珊搀,用代碼是不錯的選擇:
- 動態(tài)Layout
- views有諸如圓角冶忱、陰影等特殊效果
- 其他,如果用NIBs或storyboard會很復(fù)雜或不方便的情況
何時不用代碼
總體來說境析,代碼構(gòu)建UI何時都可以用囚枪。它們“鮮有”成為錯誤選擇的情況。
……
同一個工程劳淆,多種工具
storyboard链沼,NIBs和代碼是構(gòu)建UI的三種不同的工具。很幸運沛鸵,我們都可以選擇括勺。對于堅持只用代碼的那些家伙來說,另外兩個工具根本不會考慮曲掰,因為用代碼可以實現(xiàn)任何技術(shù)上可能的東西疾捍,而另外兩個都有它們的局限性。但對于其他的程序員栏妖,xcode這把“軍刀”提供了三種不同的工具乱豆,而且可以在同一個工程中任意選用。
你會問吊趾,那我怎么選擇宛裕?看你自己喜好吧。這里有一些建議供參考:
- 將相關(guān)的所有頁面歸到一組(這樣你會有多個組)论泛,每一組實現(xiàn)在獨立的storyboard中
- 將不可復(fù)用table cells放在storyboard中——table view controller中
- 將可復(fù)用的table cells放在NIBs中——以鼓勵復(fù)用揩尸,并避免重復(fù)勞動,用代碼來加載這些NIBs
- 用NIBs來設(shè)計自定義的views孵奶、controls等
- 用代碼構(gòu)建“很動態(tài)”的views疲酌,更泛一點說,就是不容易用storyboard或NIB是實現(xiàn)的views。
【譯者:下面還有一個簡單的例子朗恳,就不譯了】
譯完此文湿颅,想起自己在實際開發(fā)中的經(jīng)歷,有幾點想說的:
——
主題色的故事
在起初開發(fā)階段粥诫,UI沒有給出主題色油航,我們就是按自己喜歡瞎來的。到了最后才給怀浆,我們只能逐個view檢查谊囚,把NIBs和storyboard中的顏色值替換。這換一遍人還可以承受执赡,就怕那天UI同學(xué)很追求完美镰踏,覺得“咦,這個顏色還是差那么一點點沙合,我再微調(diào)一下吧”奠伪。哥呀,我就崩潰了首懈。
這個故事觸發(fā)我想绊率,還是得寫在代碼中(以這個主題色為例),如果UI要換究履,我只要改一個變量值(一行代碼哦)就夠了滤否。當然,我也沒有辦法抗拒NIBs在設(shè)計簡單最仑、固定頁面時的便利藐俺。于是,我就又引入一個機制:在每一NIB所對應(yīng)的UIView中提供一個機會盯仪,將主題色等這種需要全局統(tǒng)一的UI元素重新設(shè)一次(也就是作者在文中提到的override)紊搪。也就是說,最終在UI上的顯示效果將由此處代碼的設(shè)置為最終結(jié)果全景,NIB文件中的被覆蓋(那就當做程序員開發(fā)過程中的“第一層防銹漆”吧)
適配屏幕的故事
為了適應(yīng)各種size的手機屏耀石,有些頁面得采用“占頁面百分比”的方式來擺放UI元素。這種情況爸黄,只能用code實現(xiàn)了滞伟。因為最終的Point坐標是在運行時計算出來的,用NIB和storyboard是斷不可能實現(xiàn)的炕贵。當然梆奈,這樣的頁面一般也是比較簡單(就是因為UI元素少,才需要根據(jù)屏幕大小調(diào)整一下位置称开、大小等)