構建 iOS 界面:子類化 Views
作者:Reda Lemeden, 原文鏈接 ,原文日期:2016-04-28
譯者: wiilen ;校對: bestswifter ;定稿: CMB
這篇文章是 構建 iOS 界面 系列的第四篇,本篇重點介紹:在沒有原生系統編程經驗的情況下,如何實現 iOS 的設計 —— 這對 Web 設計師及開發者們來說是極好的。這里也提供前面幾篇文章: 第一部分 - 第二部分 - 第三部分 。
在 上一篇 文章中,我們交替使用 Interface Builder 和 Swift,實現了一個自定義的按鈕 —— 如果你一遍又一遍重復這個過程,除非你開發的是一個手電筒 App,UI 上只有一個按鈕,不然這項工作很快就會讓人心累。即便不談無聊的重復工作,如果只更新一點功能上的細節,也需要對每一個按鈕的實例進行修改,這種做法也是不靠譜的。下面我們將介紹一種更好的方法。
更恰當的方法
我們之前也提過 ,通過繼承已有類的方法和屬性,來創建一個新的類,這個過程被稱為子類化。子類可以有選擇地重寫父類的行為,如果我們自定義 UIButton 的默認外觀,子類化正是我們需要的方法。讓我們看看具體應該怎么做。
如果你之前就下過 Swiftbot 工程項目,可以直接打開。你也可以 從 GitHub 上下載 。
在 project navigator 右鍵點擊父文件夾,選擇 New File… 來添加一個新文件:
選擇 iOS 下的 Source ,然后從模版中選擇 Cocoa Touch Class 。
把類的命名為 RoundedCornerButton ,然后將 Subclass of 那一行設為 UIButton ,其他部分不動。在 Swift 中一般使用駝峰式命名法。為這個類取一個可以描述具體用途的名字,是一種好習慣。
在剛生成的 Swift 文件中,刪除所有的注釋 —— 那些開頭帶有 // 的代碼。最后代碼看起來應該像下面這樣:
import UIKit
class RoundedCornerButton: UIButton { }
上面這段代碼幾乎是在 Swift 中創建一個子類所需要的最少代碼。 第一篇文章 中也介紹過, import UIKit 可以讓我們訪問那些定義在 UIKit 中的 API,這個例子中指的是 UIButton 。
只創建一個子類還不夠,目前 Interface Builder 依然把我們的按鈕當作 UIButton 。在我們增加代碼之前,子類還只相當于父類的一個副本。
類與實例
我們 之前 提到過,在 Swift 中,每個 control 都由 UIKit 中的某個類來表示。不過那時候我們還沒有說明的是,這些類只定義了 view 對象最基礎的外觀和行為。換句話說,我們很少直接使用它們。
這也是實例發揮作用的地方。實例指的是遵循給定類的規范而構建的對象。在這個例子中,我們在 IB 中添加的按鈕是 RoundedCornerButton 的實例。
請注意 UIButton 類是如何在不具體設定值的情況下,聲明每個按鈕都需要有個 buttonType 屬性的。按鈕的實例可以自己決定 buttonType 。
現在,將我們的按鈕改為這個新的子類的實例。
在 storyboard 中,選中這個按鈕,點擊右側工具欄中的第三個(ID)圖標。這會切換到 Identity inspector ,你可以在這里修改這個按鈕實例獨有的屬性,比如它的類和 identifier。
在 Class 選項框中,輸入之前創建的子類的名字。這會將這個按鈕修改為 RoundedCornerButton 的實例,這樣我們之前用代碼創建的自定義行為,就都能應用到這個按鈕上了。
對子類的處理先到這里,由于現在我們不需要從 view controller 中直接訪問這個按鈕實例,讓我們先把 之前創建 的 outlet connection 移除。有幾種方式可以做到這點,最簡單的方法是:點擊右側面板最后一個圖標,切換到 Connections inspector ,點擊 Referencing Outlets 下 roundedCornerButton 旁邊的 x 。
刪除了 outlet 之后,我們需要移除 ViewController.swift 中對這個按鈕的所有引用。刪除該類聲明部分的所有代碼,最后代碼看起來應該是下面這樣:
class ViewController: UIViewController { }
我們接下來沒有什么需要用到 Interface Builder 的地方了。在我們回去繼續處理子類之前,我們會給你一些啟發,幫助你了解如何使用子類化來擴展 UIKit controls。
子類化的常用策略
當我們使用子類化時,最常見的任務 —— 同時也常是最有挑戰性的任務 —— 是弄清楚哪些方法和屬性需要重寫,以及執行你自己添加的代碼的順序。如果什么地方出錯了,一般是因為你對錯誤的方法進行了重寫,或是代碼執行順序出了差錯。
對 UIView 的子類來說,你常常想讓 view 在加載完后立即應用自定義的樣式。一般我們對下面這些方法進行重寫:
- awakeFromNib() ,在 view 從 IB 中加載時被調用。
- drawRect(_:) ,在 view 需要將自己繪制到屏幕上時被調用。
- layoutSubviews() ,在 view 需要確定 subview 的大小與位置時被調用。
當然還有更多其他方法,這篇文章中無法一一介紹。如果感到好奇,你可以通過閱讀 官方的 UIView 文檔 來了解細節。
重寫
為了在 Swift 中重寫一個方法,我們在方法的開頭添加 override 關鍵字,就像下面這樣:
class RoundedCornerButton: UIButton {
override func awakeFromNib() { }
}
我們重寫了 awakeFromNib() 方法,在這個方法中加入我們的對圖層的自定義,看上去這是一個不錯的選擇。如果你在做出這些改動之后運行你的 App,你會發現四個角依然是直角。這不出所料,因為我們移除了那些在 view controller 中設置實例圖層的 cornerRadius 的代碼。
在之前的代碼中,為了設置圓角,我們是這樣做的:
roundedCornerButton.layer.cornerRadius = 4
由于現在我們直接在按鈕的子類中進行修改,我們不需要再引用 roundedCornerButton :
class RoundedCornerButton: UIButton {
override func awakeFromNib() {
layer.cornerRadius = 4
}
}
在這個例子中, layer 等同于 self.layer , self 是一個對該實例的引用。這意味著在 Swift 中你很少需要寫 self ,除非編譯器建議你這么做。
再次運行你的 App,現在我們的按鈕上應該已經應用了圓角效果。
接下來的部分比較有趣:如果你在 IB 中按住 alt 來拖動并復制按鈕,新的按鈕會與原來的按鈕完全相同,你不需要在 view controller 中改動新按鈕的屬性來達到這個效果。
不過這里也有個小問題。目前我們在 IB 中設置了按鈕的背景顏色。這意味著如果以后需要修改所有按鈕的顏色,我們需要在 IB 中手動修改每個按鈕。
這個問題容易解決。我們只需要在子類中直接修改 roundedCornerButton 的背景顏色屬性,這樣所有的按鈕的背景顏色都會被改為同一顏色:
class RoundedCornerButton: UIButton {
override func awakeFromNib() {
layer.cornerRadius = 4
backgroundColor = UIColor(red: 0.75, green: 0.20, blue: 0.19, alpha: 1.0)
}
}
如果你運行 App,你會發現兩個按鈕的顏色都變成了 Tall Poppy 色 —— 一種由 Kromatic 命名的顏色。上面說的方法也可以用于修改字體、字體顏色,甚至可以用于添加新的行為,比如展示某種進行中的狀態。
結語
子類化是一種構建自定義 iOS 界面的強大工具。你也可以不使用它,但如果你需要構建一個健全的、可擴展的、模塊化的系統,它會為你提供許多幫助。
本文由 SwiftGG 翻譯組翻譯,已經獲得作者翻譯授權,最新文章請訪問http://swift.gg。
來自:http://swift.gg/2016/08/16/building-ios-interfaces-subclassing-views/