SwiftUI作為一個聲明式UI框架,如何自定義組件,是很多小伙伴都非常關心的問題。
struct MyView: View {
var body: some View {
Text("Hello, World!")
}
}
上述代碼就是一個標準的自定義組件實現。通過上述代碼,我們可以看到:
- 自定義組件必須被聲明為struct。
- 自定義組件必須滿足View協議。
- 自定義組件必須定義符合協議的計算屬性:body。
我們可以通過上述的規范,來定義需要的元素,組成組件。
當然,我們的組件也可以傳參:
struct MyView: View {
var text: String
var body: some View {
Text(text)
.font(.largeTitle)
.padding()
.background(Color.blue)
.clipShape(Capsule())
}
}
struct ContentView: View {
var body: some View {
MyView(text: "Hello World")
}
}
如果傳入的參數發生了變化,當前組件也會重新渲染。
這里有一點和UIKit不同的是,在UIKit里,我們處理的往往是一個固定的組件,內部結構和元素基本上都是確定的,沒有變化,如果涉及到需要定義某一部分元素的,UITableView, UICollectionView等也都幫我們處理好了。
但是這個也不是沒有代價的,這里的代價就是我們只能使用UIKit提供的布局組件,有很多限制和學習成本。 同時我們想自行定義布局時,也很麻煩。
而SwiftUI不同,我們在SwiftUI中定義布局組件的成本還是相當低的。
比如下面的代碼就是我們定義一個GridView所需要的代碼:
struct GridStack<Content: View>: View {
let rows: Int
let columns: Int
let content: (Int, Int) -> Content
var body: some View {
VStack {
ForEach(0..<rows, id: \.self) { row in
HStack {
ForEach(0..<self.columns, id: \.self) { column in
self.content(row, column)
}
}
}
}
}
}
在這里,我們看到,GridStack只定義了Content如何擺放,如何重復,卻沒有定義Content到底是什么組件。看起來是不是有點像UICollectionView呢?
上述的代碼,可以以下面的形式進行調用。
struct ContentView1: View {
var body: some View {
GridStack(rows: 3, columns: 3) { (row, col) in
Text("R\(row)C\(col)")
}
}
}
但是這樣聲明還是有問題的,就是Content無法一次性傳入多個元素,只能傳入一個參數。
為了解決這個問題,我們需要引入ViewBuilder。
struct GridStack<Content: View>: View {
let rows: Int
let columns: Int
let content: (Int, Int) -> Content
init(rows: Int, columns: Int, @ViewBuilder _ content: @escaping (Int, Int) -> Content) {
self.rows = rows
self.columns = columns
self.content = content
}
var body: some View {
VStack {
ForEach(0..<rows, id: \.self) { row in
HStack {
ForEach(0..<self.columns, id: \.self) { column in
self.content(row, column)
}
}
}
}
}
}
在聲明時,給content參數加上@ViewBuilder
的注解,就可以一次性傳入多個元素。類似下面這樣。
struct ContentView: View {
var body: some View {
GridStack(rows: 3, columns: 3) { (row, col) in
Text("R\(row)C\(col)")
Text("R\(row)C\(col)")
Text("R\(row)C\(col)")
}
}
}
但是這里仍然有問題,就是這個組件內部無法傳遞超過10個元素。這個是ViewBuilder的設計限制的。
參考: