iOS 用戶界面:故事板、NIB 和自定義代碼

已發表: 2022-03-11

我經常聽到 iOS 開發者問同一個關鍵問題的一些變體:

在 iOS 中開發 UI 的最佳方式是什麼:通過 Storyboard、NIB 或代碼?

對這個問題的回答,無論是明確的還是隱含的,都傾向於假設有一個相互排斥的選擇要做,一個通常在開發之前預先解決的選擇。

我認為答案應該採取一個或多個反問的形式。

什麼是“最好”的汽車?

讓我用一個題外話的例子來解釋。 假設我想買一輛車,我問你一個簡單的問題:“最好的選擇是什麼?”

你真的可以通過推荐一個模型,甚至一個品牌來回答嗎? 不太可能,除非你推薦法拉利。 相反,您可能會回答一些其他問題,例如:

  • 你的預算是多少?
  • 你需要幾個座位?
  • 你關心油耗嗎?
  • 你對跑車有什麼看法?

很明顯,沒有車或車之類的東西,除非它被放置在適當的環境中——只有基於特定需求的好車或壞車。

返回 iOS UI 設計

就像我們的汽車查詢一樣, “開發 iOS UI 的最佳方法是什麼”問題缺乏上下文。 令人驚訝的是,答案不一定是包羅萬象的。

從廣義上講,您可以採用三種類型的用戶界面設計方法,每種方法都有其優點和缺點,它的粉絲和討厭者:

  • iOS Storyboards : 一種用於佈置多個應用程序視圖以及它們之間的轉換的可視化工具。
  • NIB (或 XIB) :每個 NIB 文件對應一個視圖元素,可以在 Interface Builder 中進行佈局,使其也成為可視化工具。 請注意,名稱“NIB”是從文件擴展名派生的(以前是 .nib,現在是 .xib,儘管保留了舊的發音)。
  • 自定義代碼:即沒有 GUI 工具,而是以編程方式處理所有自定義定位、動畫等。

這些選項都沒有比任何其他選項普遍更好(儘管您可能會聽到)。

例如,故事板是 iOS UI 工具包的最新成員。 我被告知它們是未來,它們將取代 NIB 和自定義代碼 UI。 我認為 Storyboards 是一個有用的工具,但與其說是替代品,不如說是對 NIB 和自定義代碼的補充。 在某些情況下,故事板是正確的選擇,但不是所有情況。

本 iOS 開發教程旨在探索 3 種 iOS UI 設計方法之間的區別。

此外,當您可以全部使用它們(在同一個項目中)時,為什麼要靜態地堅持一個選項,選擇最適合手頭特定問題的機制?

在我看來,這是一個可以在更高層次上概括的問題,其答案在我的軟件開發原則列表中排名靠前:沒有通用的語言、框架或技術是每個人的通用最佳選擇軟件開發問題。 iOS UI 設計也是如此。

在本 iOS 開發教程中,我們將探索這些方法中的每一種,並介紹應該使用和應該使用它們的用例,以及可以將它們混合在一起的方式。

iOS 故事板

一個經典的初學者錯誤是創建一個龐大的項目範圍的 iOS 故事板。 當我第一次開始使用 Storyboards 時,我也犯了這個錯誤(可能是因為它是一條誘人的路線)。

一個經典的初學者錯誤是創建一個龐大的項目範圍的故事板。 故事板是一個有故事要講的板。 它不應該被用來將不相關的故事混合成一個大卷。

顧名思義,故事板是一個有故事要講的板。 它不應該被用來將不相關的故事混合成一個大卷。 故事板應該包含邏輯上相互關聯的視圖控制器——這並不意味著每個視圖控制器。

例如,在處理時使用 Storyboard 是有意義的:

  • 一組用於身份驗證和註冊的視圖。
  • 多步驟訂單輸入流程。
  • 類似嚮導(即教程)的流程。
  • 一組主從視圖(例如,配置文件列表、配置文件詳細信息)。

同時,應避免使用大型 Storyboard,包括單個應用範圍的 Storyboard(除非應用相對簡單)。 在我們深入之前,讓我們看看為什麼。

大型 iOS 故事板的愚蠢之處

大型 Storyboard 除了難以瀏覽和維護之外,還給團隊環境增加了一層複雜性:當多個開發人員同時處理同一個 Storyboard 文件時,源代碼控制衝突是不可避免的。 雖然情節提要在內部表示為文本文件(實際上是 XML 文件),但合併通常並非易事。

當開發人員查看源代碼時,他們將其賦予語義意義。 因此,當手動合併時,他們能夠閱讀和理解衝突的雙方並採取相應的行動。 相反,故事板是由 Xcode 管理的 XML 文件,每行代碼的含義並不總是很容易理解。

讓我們舉一個非常簡單的例子:假設兩個不同的開發者改變了UILabel的位置(使用自動佈局),而後者推動了他的改變,產生了這樣的衝突(注意衝突的id屬性):

 <layoutGuides> <viewControllerLayoutGuide type="top"/> <viewControllerLayoutGuide type="bottom"/> </layoutGuides> <layoutGuides> <viewControllerLayoutGuide type="top"/> <viewControllerLayoutGuide type="bottom"/> </layoutGuides>

id本身並沒有提供任何關於它的真正意義的指示,所以你沒有什麼可使用的。 唯一有意義的解決方案是選擇衝突的兩個方面之一併丟棄另一個。 會不會有副作用? 誰知道? 不是你。

為了緩解這些 iOS 界面設計問題,在同一個項目中使用多個故事板是推薦的方法。

何時使用故事板

故事板最好與多個互連的視圖控制器一起使用,因為它們的主要簡化在於視圖控制器之間的轉換。 在某種程度上,它們可以被認為是 NIB 的組合,在視圖控制器之間具有視覺和功能流。

故事板最好與多個互連的視圖控制器一起使用,因為它們的主要簡化在於視圖控制器之間的轉換。

除了簡化導航流程之外,另一個明顯的優勢是它們消除了彈出、推送、呈現和關閉視圖控制器所需的樣板代碼。 此外,視圖控制器是自動分配的,因此無需手動allocinit

最後,雖然 Storyboard 最適用於涉及多個視圖控制器的場景,但在使用單個表視圖控制器時使用 Storyboard 也是合理的,原因有以下三個:

  • 就地設計表格單元原型的能力有助於將各個部分保持在一起。
  • 可以在父表視圖控制器內設計多個單元格模板。
  • 可以創建靜態表視圖(一個期待已久的添加,不幸的是僅在 Storyboards 中可用)。

有人可能會爭辯說,也可以使用 NIB 設計多個單元模板。 事實上,這只是一個偏好問題:一些開發人員更喜歡將所有東西都放在一個地方,而另一些則不在乎。

何時使用 iOS 故事板

幾個案例:

  • 視圖具有復雜或動態的佈局,最好用代碼實現。
  • 該視圖已使用 NIB 或代碼實現。

在這些情況下,我們可以將視圖留在 Storyboard 之外,或者將其嵌入到視圖控制器中。 前者打破了故事板的視覺流程,但沒有任何負面的功能或開發影響。 後者保留了這種視覺流程,但它需要額外的開發工作,因為視圖沒有集成到視圖控制器中:它只是作為組件嵌入,因此視圖控制器必須與視圖交互而不是實現它。

一般優點和缺點

現在我們已經了解了 Storyboard 何時在 iOS UI 設計中有用,在我們繼續本教程中的 NIB 之前,讓我們來看看它們的一般優點和缺點。

優點:性能

直觀地說,您可以假設當 Storyboard 被加載時,它的所有視圖控制器都會立即實例化。 幸運的是,這只是一個抽象,而不是實際實現:相反,只有初始視圖控制器(如果有)被創建。 其他視圖控制器是動態實例化的,無論是在執行 segue 時還是從代碼中手動實例化。

臨:原型

故事板簡化了用戶界面流程的原型設計和模擬。 實際上,一個完整的帶有視圖和導航的原型應用程序可以使用 Storyboard 和幾行代碼輕鬆實現。

缺點:可重用性

在移動或複制方面,iOS Storyboards 的定位很差。 故事板必須連同其所有相關的視圖控制器一起移動。 換句話說,單個視圖控制器不能作為單個獨立實體單獨提取和在其他地方重用; 它依賴於情節提要的其餘部分來發揮作用。

缺點:數據流

當應用程序轉換時,通常需要在視圖控制器之間傳遞數據。 但是,在這種情況下,故事板的視覺流程被破壞了,因為在 Interface Builder 中沒有發生這種情況的痕跡。 Storyboard 負責處理視圖控制器之間的流程,但不負責處理數據流。 因此,必須使用代碼配置目標控制器,從而覆蓋視覺體驗。

Storyboard 負責處理視圖控制器之間的流程,但不負責處理數據流。

在這種情況下,我們必須依賴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 ... }

我發現這種方法容易出錯並且不必要地冗長。

筆尖

NIB 是執行 iOS 界面設計的舊方法。

在這種情況下,“舊”並不意味著“壞”、“過時”或“已棄用”。 事實上,重要的是要了解 iOS Storyboard 並不是 NIB 的通用替代品。 在某些情況下,它們只是簡化了 UI 實現。

使用 NIB,可以設計任意視圖,然後開發人員可以根據需要將其附加到視圖控制器。

如果我們將面向對象的設計應用於我們的 UI,那麼將視圖控制器的視圖分解為單獨的模塊是有意義的,每個模塊都實現為具有自己的 NIB 文件(或將多個模塊分組到同一個文件中)的視圖。 這種方法的明顯優勢是每個組件更容易開發、更容易測試和更容易調試。

NIB 共享我們在 Storyboard 中看到的合併衝突問題,但程度較輕,因為 NIB 文件以較小的規模運行。

何時在 iOS UI 設計中使用 NIB

所有用例的一個子集是:

  • 模態視圖
  • 簡單的登錄和註冊視圖
  • 設置
  • 彈出窗口
  • 可重複使用的視圖模板
  • 可重複使用的表格單元格模板

同時…

何時使用 NIB

您應該避免將 NIB 用於:

  • 具有動態內容的視圖,其中佈局根據內容髮生顯著變化。
  • 本質上不容易在 Interface Builder 中設計的視圖。
  • 具有復雜過渡的視圖控制器可以通過 Storyboard 進行簡化。

一般優點和缺點

更一般地說,讓我們來看看使用 NIB 的優缺點。

優點:可重用性

當多個類共享相同的佈局時,NIB 會派上用場。

作為一個簡單的用例,包含用戶名和密碼文本字段的視圖模板可以使用假設的TTLoginViewTTSignupView視圖來實現,這兩個視圖都可以源自同一個 NIB。 TTLoginView必須隱藏密碼字段,並且兩者都必須指定相應的靜態標籤(例如“輸入您的用戶名”與“輸入您的密碼”),但這些標籤將具有相同的基本功能和類似的佈局。

優缺點:性能

NIB 是延遲加載的,因此它們在必須使用內存之前不會使用內存。 雖然這可能是一個優勢,但延遲加載過程存在延遲,這也使其成為一個缺點。

iOS 自定義代碼(程序化 UI)

任何可以使用 Storyboard 和 NIB 完成的 iOS 界面設計也可以使用原始代碼來實現(當然,曾經有一段時間,開發人員沒有如此豐富的工具集)。

使用 NIB 和 Storyboard 無法完成的事情總是可以用代碼來實現。

也許更重要的是,NIB 和 Storyboard 無法完成的事情總是可以用代碼來實現——當然,前提是它在技術上是可行的。 另一種看待它的方式是,NIB 和 Storyboard 是用代碼實現的,所以它們的功能自然是一個子集。 讓我們直接進入優缺點。

優點:引擎蓋下

以編程方式創建 iOS UI 的最大優勢:如果您知道如何編寫用戶界面,那麼您就知道幕後發生了什麼,而對於 NIB 和 Storyboard 則不一定如此。

進行比較:計算器是一個有用的工具。 但是知道如何手動執行計算並不是一件壞事。

這不僅限於 iOS,還適用於任何可視化 RAD 工具(例如,Visual Studio 和 Delphi,僅舉幾例)。 可視化 HTML RAD 環境代表了一個典型的邊界案例:它們用於生成(通常寫得不好)代碼,聲稱不需要 HTML 知識,並且一切都可以可視化完成。 但是,任何 Web 開發人員都不會在不親自動手的情況下實現網頁:他們知道手動處理原始 HTML 和 CSS 將導致更模塊化、更高效的代碼。

因此,掌握 iOS 用戶界面的編碼可以讓您更好地控制和了解這些部分如何組合在一起,從而提高您作為開發人員的上限。

Pro:當代碼是唯一的選擇時

在某些情況下,自定義 iOS 代碼是 UI 設計的唯一選擇。 動態佈局是典型的例子,其中視圖元素被移動,流程或佈局根據內容進行顯著調整。

優點:合併衝突

雖然 NIB 和 Storyboard 遭受合併衝突的嚴重影響,但代碼沒有同樣的錯誤。 所有代碼都具有語義含義,因此解決衝突並不比平常更困難。

缺點:原型設計

在您實際看到佈局之前,很難弄清楚佈局的外觀。 此外,您無法直觀地定位視圖和控件,因此將佈局規範轉換為有形視圖可能需要更長的時間,而 NIB 和故事板可以讓您立即預覽事物的渲染方式。

缺點:重構

重構很久以前或其他人編寫的代碼也變得更加複雜:當使用自定義方法和幻數定位和動畫元素時,調試會話可能會變得很艱鉅。

優點:性能

在性能方面,Storyboards 和 NIBs 會受到加載和解析的開銷; 最後,它們被間接翻譯成代碼。 不用說,使用代碼製作的 UI 不會發生這種情況。

優點:可重用性

任何以編程方式實現的視圖都可以以可重用的方式設計。 讓我們看幾個用例:

  • 兩個或多個視圖共享一個共同的行為,但它們略有不同。 一個基類和兩個子類優雅地解決了這個問題。
  • 一個項目必須被分叉,目的是創建一個單一的代碼庫,但生成兩個(或更多)不同的應用程序,每個應用程序都有特定的定制。

對於 NIB 和 Storyboard,相同的 UI 設計過程會復雜得多。 模板文件不允許繼承,可能的解決方案僅限於以下幾種:

  • 複製 NIB 和 Storyboard 文件。 之後,他們分別生活,與原始檔案沒有任何關係。
  • 用代碼覆蓋外觀和行為,這可能在簡單的情況下有效,但在其他情況下可能會導致嚴重的複雜性。 代碼的大量覆蓋也會使視覺設計變得無用,並演變成令人頭疼的問題,例如,當某個控件在 Interface Builder 中以一種方式顯示時,但在應用程序運行時看起來完全不同。

何時使用代碼

當您有以下情況時,使用自定義代碼進行 iOS 用戶界面設計通常是一個很好的選擇:

  • 動態佈局。
  • 帶有效果的視圖,例如圓角、陰影等。
  • 任何使用 NIB 和 Storyboard 的情況都很複雜或不可行。

何時使用代碼

通常,始終可以使用代碼製作的 UI。 他們很少是一個壞主意,所以我會放一個這裡。

儘管 NIB 和 Storyboard 帶來了一些優勢,但我覺得沒有合理的缺點可以列出來阻止代碼使用(也許除了懶惰)。

一個項目,多種工具

故事板、NIB 和代碼是構建 iOS 用戶界面的三種不同工具。 我們很幸運擁有它們。 程序化 UI 的狂熱者可能不會考慮其他兩個選項:代碼允許您做技術上可能的所有事情,而替代方案有其局限性。 對於其他開發人員來說,Xcode 軍刀提供了三種工具,可以在同一個項目中有效地同時使用它們。

怎麼樣,你問? 隨你喜歡。 以下是一些可能的方法:

  • 將所有相關屏幕分組到單獨的組中,並使用自己獨特的 Storyboard 來實現每個組。
  • 在表格視圖控制器內使用情節提要設計不可重用的表格單元格。
  • 在 NIB 中設計可重用的表格單元以鼓勵重用並避免重複,但通過自定義代碼加載這些 NIB。
  • 使用 NIB 設計自定義視圖、控件和中間對象。
  • 將代碼用於高度動態的視圖,更一般地用於通過 Storyboard 和 NIB 不易實現的視圖,同時在 Storyboard 中容納視圖轉換。

最後,讓我們看一個將它們聯繫在一起的最後一個示例。

一個簡單的用例

假設我們要開發一個具有幾個不同視圖的基本消息應用程序:

  • 關注的朋友列表(帶有可重複使用的單元格模板,以使 UI 在未來列表中保持一致)。
  • 配置文件詳細信息視圖,由單獨的部分組成(包括配置文件信息、統計信息和工具欄)。
  • 發送給朋友和從朋友接收的消息列表。
  • 一個新的消息表單。
  • 一個標籤雲視圖,顯示用戶消息中使用的不同標籤,每個標籤的大小與其使用次數成正比。

此外,我們希望視圖按如下方式流動:

  • 單擊關注的朋友列表中的項目會顯示相關朋友的個人資料詳細信息。
  • 配置文件詳細信息顯示配置文件名稱、地址、統計信息、最新消息的簡短列表和工具欄。

為了實現這個 iOS 應用,我們所有的三個 UI 工具都會派上用場,我們可以使用:

  • 具有四個視圖控制器(列表、詳細信息、消息列表和新消息表單)的故事板。
  • 用於可重用配置文件列表單元模板的單獨 NIB 文件。
  • 配置文件詳細信息視圖的三個單獨的 NIB 文件,一個用於組成它的每個單獨部分(配置文件詳細信息、統計信息、最後三個消息),以實現更好的可維護性。 這些 NIB 將被實例化為視圖,然後添加到視圖控制器。
  • 標籤雲視圖的自定義代碼。 此視圖是無法在 Interface Builder 中設計的典型示例,既不能通過 StoryBoard 也不能通過 NIB。 相反,它完全通過代碼實現。 為了維護 Storyboard 的視覺流程,我們選擇在 Storyboard 中添加一個空的視圖控制器,將標籤雲視圖實現為獨立視圖,並以編程方式將視圖添加到視圖控制器中。 顯然,視圖也可以在視圖控制器內部實現,而不是作為獨立視圖,但我們將它們分開以便更好地重用。

一個非常基本的模型可能如下所示:

此圖說明了一個使用 Storyboard、NIB 和自定義 iOS 代碼的 iOS 用戶界面設計項目。

至此,我們概述了一個相當複雜的 iOS 應用程序的基本結構,其核心視圖將我們的三種主要 UI 設計方法聯繫在一起。 請記住:沒有二元決定,因為每個工具都有其優點和缺點。

包起來

正如本教程中所研究的,Storyboards 為 iOS UI 設計和視覺流程添加了顯著的簡化。 他們還消除了樣板代碼; 但所有這一切都是有代價的,以靈活性為代價。 與此同時,NIB 通過專注於單一視圖提供更大的靈活性,但沒有視覺流程。 當然,最靈活的解決方案是代碼,它往往相當不友好,而且天生就不是視覺的。

如果這篇文章引起了您的興趣,我強烈建議您觀看 Ray Wenderlich 的精彩辯論,花 55 分鐘討論 NIB、故事板和代碼製作的 UIS。

最後,我想強調一件事:不惜一切代價避免使用不當的 iOS UI 設計工具。 如果一個視圖不能用 Storyboard 設計,或者如果它可以用 NIB 或代碼以更簡單的方式實現,不要使用 Storyboard。 同樣,如果不能使用 NIB 設計視圖,請不要使用 NIB。 這些規則雖然簡單,但對您作為開發人員的教育大有幫助。