[譯]純代碼建立 UIViewios
翻譯自:swift
https://medium.com/written-code/creating-uiviews-programmatically-in-swift-55f5d14502ae/設計模式
iOS App 由許多視圖組成。視圖的顯示依賴四個值:x,y,width,height。bash
三種方式構建視圖:Storyboards
,Nib files
和編碼實現
。app
UIKit 包含許多標準組件,從簡單的按鈕,到複雜的表格。他們用處普遍,如 UILabel 對象繪製文本字符串,UIImageView 對象繪製圖像。iview
視圖能夠被嵌入到其餘視圖,從而在視圖之間產生父子視圖關係,一個視圖的父視圖被稱爲superview
,子視圖被稱爲subview
。ide
如何組織你的視圖關係着你的應用程序的視覺效果和事件行爲。舉一個例子,有兩個視圖,他們的父子關係決定了如何捕獲事件及響應事件的順序。相似的,當手機方向發生變化,視圖的父子關係也決定了他們作如何修改。佈局
Model-View-Controller 是最經常使用的設計模式。然而在 iOS App 開發過程當中,一般要面臨一個問題:視圖控制器經常變得過於龐大,修改和重構都很痛苦。因此 MVC 也被戲稱爲Massive View Controller
。ui
遵循此模式,咱們應該儘可能確保項目中的每一個類都是Controller、Model或者View。這能有效避免代碼失控。咱們也能夠建立其餘的分組和類,但 App 的核心部分應該是這三種組成。編碼
建立項目的時候,Xcode 會自動爲咱們增長一個 storyboard。爲了展現自定義視圖,咱們幹掉他先。
而後,建立兩個文件:ProfileView 繼承自 UIView,放到 View 分類中。ProfileViewController,繼承自 UIViewController,放在 Controller 分類中。
Auto Layout 決定了屏幕上視圖的 frame。每一個視圖都包含約束條件,經過這些條件來計算出視圖的 width,height,x,y。直接編寫 Auto Layout 代碼並不容易,這裏咱們使用 PureLayout,它提供了功能強大,使用友好的接口來幫助咱們編寫 Auto Layout
首先添加 PureLayout 到你的項目中。我使用 CocoaPods 進行包管理,它依賴 Podfile 文件:
platform :ios, '8.0'
use_frameworks!
pod 'PureLayout', '~> 2.0.5'
複製代碼
執行代碼以安裝依賴:
pod install
複製代碼
這條命令將建立一個以.xcworkspace
爲擴展名的新的工程文件。如今,使用 Xcode 打開它。
ProfileView.swift
文件當前是一個自定義 UIView 類的模板:
import UIKit
class ProfileView: UIView {
}
複製代碼
咱們須要初始化它。初始化在 Swift 中是值得重視的事,你將在這裏瞭解到更多關於它的事。如今,咱們只須要知道有一個主要的初始化器負責初始化當前類全部的屬性。這是一個典型的實現:
import UIKit
import PureLayout
class ProfileView: UIView {
var shouldSetupConstraints = true
override init(frame: CGRect) {
super.init(frame: frame)
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
override func updateConstraints() {
if(shouldSetupConstraints) {
// AutoLayout constraints
shouldSetupConstraints = false
}
super.updateConstraints()
}
}
複製代碼
給自定義的視圖添加約束以前,咱們要覆蓋 updateConstraints
方法。這個方法在運行期間可能會被調用屢次。爲了不屢次添加約束給視圖,咱們須要立一個 flag(shouldSetupContraints)來標示是否已經添加過約束。這個方法的最後,咱們也必須調用父類中的同名方法。(若是你在約束髮生改變以前就調用,可能會 crash)。
咱們來仿寫一個 Twitter iOS app 的我的信息視圖。在下面的圖片中,能夠看到頂部視圖包括一個 Banner 圖,用戶頭像,以及用戶信息。而後下方是全部推展現在列表中以及tabbar。全部的 UIView 元素都拿紫色標註了起來。接下來咱們聚焦在 header view,橙色區域。
咱們先看主要的三部分。banner 和 用戶頭像使用 UIImageViews 進行展現,button 區域使用 UISegmentedControl。
首先,咱們定義三個元素在咱們的 ProfileView 類。bannerView,profileView 和 segmentedControl。
//ProfileView.swift
import UIKit
import PureLayout
class ProfileView: UIView {
var shouldSetupConstraints = true
var bannerView: UIImageView!
var profileView: UIImageView!
var segmentedControl: UISegmentedControl!
override init(frame: CGRect){
super.init(frame: frame)
}
...
複製代碼
在 init 方法中,咱們初始化這些視圖元素的屬性。背景顏色、邊框顏色和其餘基本的視覺屬性。初始化他們的 frame 爲 zero,AutoLayout 會自動調整大小和位置。
將這些視圖元素添加爲 ProfileView 的子視圖使用 addSubview 方法。這個方法將被操做的視圖放在其餘子元素的最上面。代碼以下:
//ProfileView.swift
import UIKit
import PureLayout
class ProfileView: UIView {
var shouldSetupConstraints = true
var bannerView: UIImageView!
var profileView: UIImageView!
var segmentedControl: UISegmentedControl!
let screenSize = UIScreen.main.bounds
override init(frame: CGRect){
super.init(frame: frame)
bannerView = UIImageView(frame: CGRect.zero)
bannerView.backgroundColor = UIColor.gray
bannerView.autoSetDimension(.height, toSize: screenSize.width / 3)
self.addSubview(bannerView)
profileView = UIImageView(frame: CGRect.zero)
profileView.backgroundColor = UIColor.gray
profileView.layer.borderColor = UIColor.white.cgColor
profileView.layer.borderWidth = 1.0
profileView.layer.cornerRadius = 5.0
profileView.autoSetDimension(.width, toSize: 124.0)
profileView.autoSetDimension(.height, toSize: 124.0)
self.addSubview(profileView)
segmentedControl = UISegmentedControl(items: ["Tweets", "Media", "Likes"])
self.addSubview(segmentedControl)
}
...
複製代碼
佈局約束用來描述視圖與其餘視圖的關係和屬性。經過 NSLayoutConstraint 類來使用。
約束有如下幾種:
PureLayout 定義了用來建立約束的視圖屬性,見圖:
屏幕上有三個巨星元素,咱們要把他們的位置大小調整如 Twitter 我的頁面。
//ProfileView.swift
...
override func updateConstraints() {
if(shouldSetupConstraints) {
let edgesInset: CGFloat = 10.0
let centerOffset: CGFloat = 62.0
bannerView.autoPinEdgesToSuperviewEdges(with: UIEdgeInsets.zero, excludingEdge: .bottom)
profileView.autoPinEdge(toSuperviewEdge: .left, withInset: edgesInset)
// 👇🏽 profileView.autoAlignAxis(.horizontal, toSameAxisOf: bannerView, withOffset: centerOffset)
profileView.autoPinEdge(.bottom, to: .bottom, of: bannerView, withOffset: centerOffset)
segmentedControl.autoPinEdge(toSuperviewEdge: .bottom, withInset: edgesInset)
segmentedControl.autoPinEdge(toSuperviewEdge: .left, withInset: edgesInset)
segmentedControl.autoPinEdge(toSuperviewEdge: .right, withInset: edgesInset)
shouldSetupConstraints = false
}
super.updateConstraints()
...
}
複製代碼
下面進行完成 ProfileView 的最後一步。咱們須要在咱們的 Controller(ProfileViewController)中調用。Xcode 已經建立了一個 Controller 模板,幷包含 viewDidLoad: 方法。這個方法會在 Controller 顯示前進行回調。接下來咱們須要實例化咱們的 ProfileView 並展現它。
//ProfileViewController.swift
import UIKit
class ViewController: UIViewController {
var profile: ProfileView!
override func viewDidLoad() {
super.viewDidLoad()
profile = ProfileView(frame: CGRect.zero)
self.view.addSubview(profile)
// AutoLayout
profile.autoPinEdgesToSuperviewEdges(with: UIEdgeInsets.zero)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
}
複製代碼
(最後:本文中的代碼用於展現 Auto Layout。你須要補充其餘代碼才能使其成爲一個完整的項目,加油!💃🏽👋)