利用IBDesignable和IBInspectable建立客製化元件

你有嘗試過在舊版的Xcode建立過一個客製化的元件嗎?這並不容易,困難的地方在於你不能在 Interface Builder 看到你的設計。當每次你的設計有變更的時後,也只能法反覆的在模擬器(simulator)裡面執行到並測試。這挺麻煩的,也可能會花費好幾個小時的時間在設計一個單獨的元件(component)上面。


自Xcode 6 開始,iOS開發人員可以利用 IBDesignable 和 IBInspectable 功能建構客製化 control 並即時在 Interface Builder 預覽變更。很明顯的,這是一個提升生產力的巨大優勢。

在這個單元裡面,我們將為大家介紹 IBDesignable 和 IBInspectable,並且向大家展示如何來利用這個新功能。要瞭解並學習一個新的功能,除了建立一個Demo程式來學習之外,我認為應該沒有其他更好的方式了。所以,在本章節我們將一起建立一個名為Rainbow客製化介面來學習本章的內容。

Rainbow

IBDesignable 和 IBInspectable

隨著 IBDesignable 和 IBInspectable的推出,開發人員被允許可以建立interface (或view) 並即時地顯示在Interface Builder內。 在一般情況下,要應用這個新功能,在Swift裡面你只需要建立新的類別並繼承自UIView或UIControl的類別,然後在建立的新類別名稱前面加上一個@IBDesignable的前綴字串即可。 如果你使用的是Objective-C,你需要使用IB_DESIGNABLE來代替。 Swift的範例程式如下

@IBDesignable 
class Rainbow: UIView {
}

在舊版本的Xcode,你可以編輯一個使用者預先定義的runtime屬性來更改Interface Builder內原先物件的屬性。(如:layer.cornerRadius) 。但問題是,你必須事先知道這個屬性的確切名稱。隨著IBInspectable出現又往前邁進了一步。你可以透過IBInspectable預先定義一個可視化的物件(visual class)的屬性。而這個屬性將會直接顯示在Interface Builder內,所以你可以用更直覺的方式來變更這個屬性的參數:

提示: 如果你想改變的RGB顏色的值,只要打開你的調色板,並切換拉動滑動拉霸來改變RGB值。

Custom IBDesignable Control

同樣的,如果你正在使用Swift開發一個app,你要做的只是輸入也要變更的屬性並在前面加入@IBInspectable的前綴字串,示範程式如下:

@IBInspectable var firstColor: UIColor = UIColor.blackColor() {
     // Update your UI when value changes
}

是不是感到有點困惑了呢? 不用擔心。當我們完成這個demo的專案,你將會明白我的意思。

建立你的Xcode專案

就讓我們開始建立這個專案吧,首先我們在Xcode開啟一個新的專案並且選擇Single View Application當作範本,然後輸入RainbowDemo當作專案的名稱。對了,我們將使用Swift為這個專案的語言,所以當專案建立時別忘了選擇它。

完成後,透過專案導覽列(Project Navigator)選擇專案裡面的Main.storyboard,然後從元件Library裡面內拖曳一個新的view元件裡面到View Controller內。 並且調整新的view元件的顏色為#38334C (或任何你喜歡的顏色) 並且改變view物件的尺寸為600×343。接著將這個view元件置中存放在main view。 最後別忘了變更main view的顏色與view物件一樣。

ibinspectable-create-view

在Xcode 6,你必須為view元件要配置Auto layout constraint來支援所有iOS裝置內螢幕尺寸不同的問題。 Auto Layout在最新版本的Xcode非常強大。要建立簡單的constraints,你只要在Auto Layout 選單立面點選 Issues 的提示符號,並且選擇 Add Missing Constraints 的選項,然後Xcode將會為view自動建立必須的layout constraints。

Demo2

建立客製化的View類別

現在我們已經在storyboard建立了view,現在是時候來建立我們的客製化view class了。 我們將使用Swift類別當做這個類別的範本。然後命名為Rainbow

swift-class-template

接著在類別下面插入如下的代碼:

import UIKit

class Rainbow: UIView {
    required init(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
    }
    
    override init(frame: CGRect) {
        super.init(frame: frame)
    }
}

如前之前提到的,這個可視類別是UIView的子類。 為了使我們的客製化類別能夠被即時的預覽,我們需要override 這兩個 initializers如下所示。 下一步我們將透過選擇 assistant editor切換至view內:

assistant-editor

接著,在assistant editor內選擇main storyboard,之後你便可以即時的看到你在建構的元件。請記住,你必須在Identity inspector下面改變view的class name為Rainbow:

IBDesignable Demo App

實體化IBDesignable Controls

第一步 開啟控制器的的即時預覽,我們需要設定客製化的view為Designable透過添加@IBDesignable前綴字串在我們的類別名稱之前:

@IBDesignable 
class Rainbow: UIView {
    ...
}

是的,就是如你看到的這麼簡單。 但是,這個簡單的關鍵字將讓你的往後再開發上變得更加容易。下一步,我們將會為我們即將添加的圓型添加一些顏色屬性。 我們在Rainbow 類別下面插入下面的代碼:

@IBInspectable var firstColor: UIColor = UIColor(red: (37.0/255.0), green: (252.0/255), blue: (244.0/255.0), alpha: 1.0)
@IBInspectable var secondColor: UIColor = UIColor(red: (171.0/255.0), green: (250.0/255), blue: (81.0/255.0), alpha: 1.0)
@IBInspectable var thirdColor: UIColor = UIColor(red: (238.0/255.0), green: (32.0/255), blue: (53.0/255.0), alpha: 1.0)

在上面的代碼裡,我們先預訂每個屬性都有預設的顏色,然後在使用者變更數值之後他將會告訴它告訴view需要進行圖形的重新繪製。最重要的事,我們替每個屬性加上@IBInspectable的前綴關鍵子。 所以如果你前往Attributes inspectable去進行檢視,你應該可以看到這些我們客製化的數屬性可以在Interface Builder下直接檢視:

color-pickers

很酷吧? 透過指定屬性為IBInspectable,你可以直覺地使用顏色選擇器直接進行編輯。

我們接著來建置Rainbow class的主要功能,為了能在螢幕上產生一個圓型。 我們在類別裡面插入如下的代碼:

func addOval(lineWidth: CGFloat, path: CGPathRef, strokeStart: CGFloat, strokeEnd: CGFloat, strokeColor: UIColor, fillColor: UIColor, shadowRadius: CGFloat, shadowOpacity: Float, shadowOffsset: CGSize) {
    
    let arc = CAShapeLayer()
    arc.lineWidth = lineWidth
    arc.path = path
    arc.strokeStart = strokeStart
    arc.strokeEnd = strokeEnd
    arc.strokeColor = strokeColor.CGColor
    arc.fillColor = fillColor.CGColor
    arc.shadowColor = UIColor.blackColor().CGColor
    arc.shadowRadius = shadowRadius
    arc.shadowOpacity = shadowOpacity
    arc.shadowOffset = shadowOffsset
    layer.addSublayer(arc)
}

為了增加程式碼的乾淨性跟易讀性,我們建置了一個共用方法來根據使用者所提供的參數來繪製半圓型或者一整個圓型。我們利用CAShapeLayer類別可以用很省力的方式來完成繪製一個圓型或圓弧型的工作。你可以使用strokeStart和strokeEnd屬性來控筆觸的起點和終點。 通過改變stokeEnd的值(介於0.0和1.0之間),可以得出一個完整的圓型或部分的圓弧型。其他的屬性則用來設定筆觸的顏色,陰影的顏色,等等。 你可以閱讀官方文件檔案 來獲得關於CAShapeLayer所有屬性以及更多的相關的細節。

接著,插入下面的方法在Rainbow類別內:

override func drawRect(rect: CGRect) {
    // Add ARCs
    self.addCirle(80, capRadius: 20, color: self.firstColor)
    self.addCirle(150, capRadius: 20, color: self.secondColor)
    self.addCirle(215, capRadius: 20, color: self.thirdColor)
}

func addCirle(arcRadius: CGFloat, capRadius: CGFloat, color: UIColor) {
    let X = CGRectGetMidX(self.bounds)
    let Y = CGRectGetMidY(self.bounds)
    
    // Bottom Oval
    let pathBottom = UIBezierPath(ovalInRect: CGRectMake((X - (arcRadius/2)), (Y - (arcRadius/2)), arcRadius, arcRadius)).CGPath
    self.addOval(20.0, path: pathBottom, strokeStart: 0, strokeEnd: 0.5, strokeColor: color, fillColor: UIColor.clearColor(), shadowRadius: 0, shadowOpacity: 0, shadowOffsset: CGSizeZero)
    
    // Middle Cap
    let pathMiddle = UIBezierPath(ovalInRect: CGRectMake((X - (capRadius/2)) - (arcRadius/2), (Y - (capRadius/2)), capRadius, capRadius)).CGPath
    self.addOval(0.0, path: pathMiddle, strokeStart: 0, strokeEnd: 1.0, strokeColor: color, fillColor: color, shadowRadius: 5.0, shadowOpacity: 0.5, shadowOffsset: CGSizeZero)

    // Top Oval
    let pathTop = UIBezierPath(ovalInRect: CGRectMake((X - (arcRadius/2)), (Y - (arcRadius/2)), arcRadius, arcRadius)).CGPath
    self.addOval(20.0, path: pathTop, strokeStart: 0.5, strokeEnd: 1.0, strokeColor: color, fillColor: UIColor.clearColor(), shadowRadius: 0, shadowOpacity: 0, shadowOffsset: CGSizeZero)
    
}

預設實體化時drawRect方法是不會執行任何結果。 為了在view裡面能繪製圓圈,我們需要override現有drawRect方法來實現我們自己的繪製代碼。在addCircle 有三個參數: arcRadiuscapRadiuscolorarcRadius 是圓型的內圍的半徑circle,而capRadius圓型外圍的半徑(也就是圓型線條寬度)。

addCircle方法裡面,我們利用了UIBezierPath來繪製弧形,他的運作原理如下:

  1. 首先,他繪製了半圈底部
  2. 在接下來它繪製一個完整的小圓弧形的邊緣
  3. 最後,繪製圓的另一半

drawRect 方法裡面,我們呼叫了剛剛建立的addCircle方法三次,來繪製不同顏色的圓型。下面的圖形說明了圓型的繪製過程:

ibdesignable-circles

提示: 如果你需要了解UIBezierPath的更多資訊,你可以前往蘋果官方文件獲得更多的資訊。

有了IBInspectable屬性,你可以自由的在Interface Builder改變每個圓圈的顏色而不需要撰寫程式碼:

Screen Shot 2015-03-15 at 2.31.40 PM

很明顯的,你可以進一步的擴展 arcRadius 成為IBInspectable 屬性。 我將指派他當作你的練習題。

ibdesignable-exercise

總結

在經歷了這個教學課程之後,我希望你能明白你能了解IBDesignable和IBInspectable在Xcode 6裡面的使用方式。 隨著Interface Builder可以透過本文的方式來近行及時的預覽,這將提供一個更有效率的方法來建立客製化的元件(componenents)。

供你參考,從這裡 下載完整的Xcode project。 一如往常的,請你給我一些回應並且分享關於這篇教學文章的各種想法。

譯者簡介:黃凱揚-系統分析與資深程式設計師,畢業南台科技大學電子系,曾在人力資源相關領域進行系統開發,13年的系統開發整合經驗,近年來協助客戶行 App 軟體以及 WebBase系統開發與系統分析與整合。

原文Creating Your Own Custom Controls Using IBDesignable in Xcode 6


Ziad 為高級 iOS 開發員、流動裝置策略指導及創業顧問。在App Store成立初期,Ziad已經開始編寫iOS程式及遊戲,為客戶編寫過超過80個程式。時至今日,他擁有個人的流動程式開發工作室 TAMIN LAB。歡迎到 Linkedin 與 Ziad 聯絡。

blog comments powered by Disqus
Shares
Share This