使用 IBDesignable 與 IBInspectable 製作更美觀的 UI 元件


一些開發者不喜歡使用介面建構器 (Interface Builder) 來建構 App UI。一切都可以使用程式來撰寫,即使是 UI 也不例外。我個人比較喜歡混合 Storyboard 與程式來佈局 App。

不過如果要教導初學者使如何建構 App,介面建構器比較能夠無痛上手。使用介面建構器來設計 App UI 是非常直覺的,即使沒有任何 iOS 程式經驗的人也可以入門。最棒的是你不需要撰寫一行程式既可以客製化一個 UI 元件(例如,按鈕)。舉例來說,你可以在屬性檢閱器 (Attributes Inspector) 變更背景顏色或者字型大小。你可以很容易地將預設的按鈕透過屬性的客製化,將其外觀變得更加美觀。

Building-button-in-IB
在介面建構器中設置一個按鈕

本文摘自《iOS 12 App程式設計進階攻略》一書。如果你想更深入學習Swift程式設計,請到AppCoda網站購買完整電子版。

這樣說好了,介面建構器還是有其限制,不是所有 UI 元件的屬性都可以做設定。譬如,你可以使用介面建構器來建造一個像這樣的按鈕嗎?

a-better-button
更炫的按鈕

要自訂一個像這樣的按鈕,你還是需要寫一些程式,或者開發你自己的類別。這不會是一個很大的問題。不過,倘若你能夠在介面建構器中設計這樣的按鈕,且即時檢視結果是不是更好呢?

IBInspectableIBDesignable 是兩個可以讓這樣的概念成真的關鍵字。另外,在本篇文章中,我將介紹這兩個屬性。並告訴你如何利用它們來自訂 UI 元件。

IBInspectable 與 IBDesignable 的介紹

簡單的說,IBInspectable 可以讓你在介面建構器中加入而外的功能,透過標示UIView 的一個類別屬性,如 IBInspectable 之後,此屬性接著便會在屬性檢閱器打開 (expose) 一個選項。另外,倘若你標示一個 UIview 類別像是 IBDesignable,介面建構器會即時渲染 (render) 整個自訂視圖。這表示當你編輯這些選項時,你可以見到這個自訂視圖的變更結果。

我們以下面這個例子,來進一步了解 IBInspectableIBDesignable

rounded-corner-button
圓角按鈕

你可能對如何製作一個圓角按鈕非常熟悉。倘若你不知道怎麼做,你可以修改 layer(層)的屬性來達成。每一個視圖物件的背後都是一個 CALayer。要將按鈕改成圓角,你可以在程式中像這樣設定 layer 的 cornerRadius 屬性:

編者話:想了解 cornerRadius 屬性,可以參考這篇文章

設定為正值的話,便可以讓 layer 在它的背景畫出圓角。另外一個達到相同結果的方式是在識別檢閱器 (Identity inspector) 中設定「使用者定義運行屬性 (User Defined Runtime Attributes)」。

User-Defined-Runtime-Attributes
設定按鈕的運行屬性

「使用者定義運行屬性」是介面建構器中非常強大的功能,可以讓你設置視圖的屬性。不過,它不是非常直覺。你必須記得視圖的每一個屬性,或者需要透過文件來查詢所需的屬性。

為了能夠讓視圖的客製化效果更佳, IBInspectable 在 Xcode 6 被導入。但這不並表示你不需要寫程式,寫程式還是必要的。不過你可在屬性檢閱器中取得更多功能的屬性設定。要建立一個圓角的按鈕,你可以像這樣宣告一個類別,並使用 @IBInspectable 來標註 cornerRadius 屬性:

現在,當你在 Storyboard 中建立一個 RoundedCornerButton 物件,介面建構器會在屬性檢閱器增加另外一個稱作圓角半徑 (Corner Radius) 的選項,以及可以設定的值。

set-corner-radius
圓角半徑設定選項現在已經出現在屬性檢閱器中

如果你仔細看一下這個選項的名稱,Xcode會自動將屬性名稱從 cornerRadius 轉換成 Corner Radius 。雖然這只是一個不怎麼起眼的功能,不過卻可以讓每一個選項都更容易辨識。

cornerRadius 屬性有一個 CGFloat 的型態。介面建構器顯示了 Corner Radius 選項並以數值來做調整。不是所有的屬性都可以夾在屬性檢閱器中,根據 Apple 的文件,IBInspector 會增加以下的型態:

  • Int
  • CGFloat
  • Double
  • String
  • Bool
  • CGPoint
  • CGSize
  • CGRect
  • UIColor
  • UIImage

倘若你宣告一個 IBInspectable 屬性,不過卻漏掉了支援的型態,介面建構器將不會在屬性檢閱器中為你產生這個選項。

雖然這個 @IBInspectable 關鍵字可以讓開發者公開任何的視圖屬性至介面建構器,你無法馬上看到調整後的結果,每次修改完圓角半徑值之後,你需要執行這個專案,才能夠見到按鈕在畫面的呈現情況。

IBDesignable 讓視圖客製化更進化,你現在可以將一個 UIView 類別以 @IBDesignable 關鍵字來做標註,如此介面建構器便會讓這個自訂的視圖能夠即時的更新。

使用如上面相同的例子(但是使用@IBDesignable 關鍵字),介面建構器可以依照屬性的變更來即時做顯示。

IBDesignable-1
介面建構器將按鈕在畫面上作即時的更新

建立一個有設計感的按鈕

現在你應該對 IBInspectableIBDesignable 有了基本的概念,我們來看如何在我們每日的工作中利用到它。底下的圖顯示一個標準的系統按鈕以及我們準備要建立,具有設計感的按鈕。

button-demo-1
標準按鈕(左圖), 有質感的按鈕(右圖)

從 iOS 7 開始,系統的按鈕比較像是一個可以按的標籤,我們計畫透過屬性檢閱器來建立一個有設計感的按鈕,並且可以馬上在介面建構器中檢視到這些變化。我們可以從以下的客製化項目來製作出有設計感的按鈕:

  • 圓角 (Corner radius)
  • 邊框寬度 (Border width)
  • 邊框顏色 (Border color)
  • 標題距離左側、右側、上方與底邊邊距 (padding)
  • 圖片距離左側、右側,上面與底部邊距
  • 圖片左側/右側對齊 (alignment)
  • 漸層顏色 (Gradient color)

我們來開始吧。首先,使用 Single View Application 模板來建立一個新專案,並將其命名為 FancyButton

IBDesignable-2
建立一個新專案並命名為 FancyButton

專案建立之後,下載這個圖片包,並加上所有的圖示至素材目錄中。

好的,我們已經設定好專案,是時候來建立一個有設計感的按鈕。我們將為這個按鈕建立一個自訂類別。所以在專案導覽器中的 FancyButton 按下右鍵,並選取 New File… 。接著選擇 Cocoa Touch Class 模板。然後將這個新類別命名為 FancyButton ,並設定它的子類別為 UIButton

圓角半徑、邊框寬度與邊框顏色

我們從圓角半徑、邊框寬度與邊框顏色來開始。更新FancyButton 類別如下:

我們藉由@IBDesignable 這個關鍵字告訴介面建構器 FancyButton 要能夠即時更新,然後我們使用 IBInspectable 關鍵字來宣告三個屬性 (cornerRadius、 borderWidth 與 borderColor)。

現在開啟 Main.storyboard 來切換至介面建構器。我們將會加入一個按鈕來測試 FancyButton 類別。從元件庫拖曳一個按鈕元件至視圖控制器,並變更其標題為 SIGN IN (或者任意的標題名稱)。在識別檢閱器,將自訂類別 (Custom Class),從預設變更為 FancyButton

IBDesignable-3
設定自訂類別為 FancyButton

見證奇蹟的時刻到了!至屬性檢閱器,你會見到一個名稱為 Fancy Button 的區塊,其中有三個選項(包括 Corner Radius、Border Width、Border Color)。

IBDesignable-4
在屬性檢閱器中出現了 Fancy 按鈕的屬性

你現在可以很容易建立一個如下圖所示的按鈕。倘若你建立一個相同的按鈕,將它調整為 343 x 50 點,設定它的圓角半徑為 4 ,邊框寬度為 1 ,以及邊框顏色設定為 red 。你可以測試其他的組合來觀察外觀的變化。

IBDesignable-5
加上邊框的按鈕

標題與圖片邊距

現在試著將控制元件的水平對齊,從置中改為靠左。我們來看會變成如何。

IBDesignable-6
將水平對齊從置中改為靠左對齊

你可以從上圖見到,標題標籤與左側邊緣沒有間距。要如何將標題標籤加入邊距呢?UIButton 類別中有一個稱作 titleEdgeInsets 的屬性,可以用來調整標題標籤。你可以在四個不同的插入點(上面、左側、底邊、右側)中指定不同的值,設定為正值時會將標題靠近按鈕中間。現在修改 FancyButton 類別,並加入以下的 IBInspectable 屬性 :

我們加入四個新的屬性來讓開發者使用 FancyButton ,在介面建構器中設定標題標籤的邊距,你現在可以切換到 Main.storyboard 來做測試。

IBDesignable-7
設定標題標籤的邊距

按鈕與圖片

UIButton 可以讓你將標題標籤以圖片來代替,你可以將標題設為 blank ,並變更圖片選項為 facebook (也就是稍早前所導入的圖片),透過圓角半徑以及邊框選項的變更,你可以很容易地建立像這樣的按鈕:

IBDesignable-8
按鈕與圖片

透過按鈕元件的設置,你可以設定不同的值來變更按鈕的設計,譬如說,你想要建立一個加上邊框與圖片的圓形按鈕,你可以設定圓角的半徑為按鈕寬度的一半,並且設定圓角寬度為正值(譬如說 5)。下圖就是按鈕的範例介紹。

IBDesignable-9
按鈕加上圖片

圖片邊距

在某些情況中,你想要將標題跟圖片加進一個按鈕中,譬如說,你想要建立一個 Sign in with Facebook 按鈕與 Facebook 圖示,你可以設定按鈕的標題為 SIGN IN WITH FACEBOOK,而圖片為 Facebook,這張圖片會自動置於標題的左側。

順便說明一下,因為 Facebook 圖示為藍色,倘若你想要變更其顏色,你需要將按鈕的型態從 Custom 改為 System。這張圖片接著會被視為模板圖片,你可以透過 Tint 選項來變更其顏色。

預設上,Facebook 圖片與按鈕的左側沒有空間,還有,圖片與標題標籤間也沒有間距。你可以設定標題的左側邊距為 20 來加入間距,但是你要如何幫 Facebook 圖片加入邊距呢?

IBDesignable-10
加上標題與圖片的按鈕

titleEdgeInsets 類似,UIButton 有另外一個稱作 imageEdgeInsets 的屬性,可以讓你在圖片周圍加入邊距。現在打開 FancyButton.swift ,並在類別中插入以下的 IBInspectable 屬性:

完成變更之後,回到介面建構器,現在你只要透過設定 Image Left Padding 的值,就可以在圖片與按鈕的視圖的邊緣間加入邊距。

IBDesignable-11
加入圖片的間距

圖片與標題右側對齊

圖片預設是對齊按鈕標題的左側。那麼如果你想要將圖片對齊標題的右側,該怎麼修改呢?

其實有好幾種方式可以辦到,而我會採用 imageEdgeInsets.left 這個方式來完成。

IBDesignable-12

參考一下上圖,要將按鈕的圖片視圖移到按鈕的右側邊緣,你可以設定 imageEdgeInsets.left 的值如下:

不過以上的算式並沒有將圖片視圖的右側邊距計算進去。

IBDesignable-13

倘若我們想要將按鈕的圖片如上圖一樣對齊,我們必須將公式變更如下:

現在我們來進到實作部分,在 FancyButton 類別插入以下的程式:

我們加入一個稱作 enableImageRightAligned 的屬性,來指示是否讓圖片靠右對齊。稍後,當你進到屬性檢閱器,你會見到一個可以讓你切換的 ON/OFF 開關。

因為我們依照按鈕的寬度(也就是 self.bounds.width)來計算左側邊距(也就是 imageEdgeInsets.left ),我們需要覆寫 layoutSubviews() 方法並更新其屬性。

變更完程式之後,切換回 Storyboard 並使用 FancyButton 來建立另一個按鈕,現在你可以設定 Enable Image Right AlignedON 以及將 Image Right Padding 設為 20 ,如下所示。

IBDesignable-14

顏色漸層

按鈕如果沒有漸層的話,稱不上有質感,對吧?所以最後一項功能,我們將實作的是為FancyButton 類別建立一個 IBInspectable 選項 。

那麼你該如何無痛且快速地建立一個漸層效果呢?

在 iOS SDK中,有一個稱作 CAGradientLayer 的類別,可以在其背景顏色上畫出漸層顏色。這是一個 CALayer 的子類別,只要像以下加入這幾行程式就可以讓開發者產生漸層顏色:

CAGradientLayer 物件也各種設定不同漸層效果的屬性。不過,基本上你需要提供兩種顏色給 API 建立漸層顏色。在程式中,我們設定第一個顏色為 blue,而第二個顏色為 red。倘若你將這段程式放入 layoutSubviews() 方法中,你會見到以下的結果:

button-demo-2
漸層按鈕的範例

如你所見,預設漸層的方向是由上往下,倘若你想要變更為水平(譬如說由左至右)方向的漸層,你可以像這樣修改 startPointendPoint 屬性:

我已經介紹了漸層顏色的基礎,我們來修改 FancyButton 類別來支援漸層顏色,在 FancyButton 類別中加入三個新屬性,並更新 layoutSubview() 方法如下:

這個 enableGradientBackground 屬性指示是否要將漸層效果應用在按鈕上,另外兩個屬性,可以讓你定義漸層顏色。

如果按鈕有採用漸層,我們建立 CAGradientLayer 物件,並以特定顏色來產生漸層效果。

現在你已經準備在介面建構器中測試,你可以在屬性檢閱器中啟用漸層功能,設定兩種顏色選項。不過,還有一件事要注意的是,介面建構器無法即時呈現漸層效果。

IBDesignable-15
漸層按鈕範例

想要見到漸層效果,你必須要在模擬器中執行App,下圖所呈現的即為漸層效果。

IBDesignable
漸層按鈕範例

總結

這個按鈕是不是很有質感且更酷呢?你現在有一個 FancyButton 類別,可以讓任何的 Xcode 專案來使用。假如你在一個團隊中,你也可以將這個類別分享給團隊的開發成員,他們也可以馬上在 Storyboard 中建立一個有質感的按鈕,且可以即時的檢視其變更的結果。

IBInspectableIBDesignable 可以應用在大部分的視圖物件中,這裡提供一個作業,試著建立另一個客製化的物件,並讓開發者在介面建構器中設定它的屬性。

為了方便參考,您可以在這裡下載範例專案。

本文摘自《iOS 12 App 程式設計進階攻略》一書。如果你想更深入學習 Swift 程式設計,請到 AppCoda 網站購買完整電子版。


軟件工程師,AppCoda 創辦人。著有《iOS 10 App 程式設計實力超進化實戰攻略》、《iOS 9 App 程式設計實力超進化實戰攻略》、《養成iOS 8 App程式設計實力的25堂課》,以及《iOS 8 App程式設計進階實力的30項關鍵技巧》。曾任職於HSBC, FedEx等公司,專責軟體開發、系統設計。2012年創立AppCoda技術部落格,定期發表iOS程式教學文章。現時專注發展AppCoda,致力於iOS程式教學,產品設計及開發。

blog comments powered by Disqus
訂閲電子報

訂閲電子報

AppCoda致力於發佈優質iOS程式教學,你不必每天上站,輸入你的電子郵件地址訂閱網站的最新教學文章。每當有新文章發佈,我們會使用電子郵件通知你。

已收你的指示。請你檢查你的電郵,我們已寄出一封認證信,點擊信中鏈結才算完成訂閱。

Shares
Share This