SwiftUI作為一個(gè)聲明式UI框架宾娜,如何自定義組件侈百,是很多小伙伴都非常關(guān)心的問(wèn)題缀旁。
struct MyView: View {
var body: some View {
Text("Hello, World!")
}
}
上述代碼就是一個(gè)標(biāo)準(zhǔn)的自定義組件實(shí)現(xiàn)更扁。通過(guò)上述代碼盖腕,我們可以看到:
- 自定義組件必須被聲明為struct。
- 自定義組件必須滿足View協(xié)議浓镜。
- 自定義組件必須定義符合協(xié)議的計(jì)算屬性:body赊堪。
我們可以通過(guò)上述的規(guī)范,來(lái)定義需要的元素竖哩,組成組件。
當(dāng)然脊僚,我們的組件也可以傳參:
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")
}
}
如果傳入的參數(shù)發(fā)生了變化相叁,當(dāng)前組件也會(huì)重新渲染。
這里有一點(diǎn)和UIKit不同的是辽幌,在UIKit里增淹,我們處理的往往是一個(gè)固定的組件,內(nèi)部結(jié)構(gòu)和元素基本上都是確定的乌企,沒(méi)有變化虑润,如果涉及到需要定義某一部分元素的,UITableView加酵, UICollectionView等也都幫我們處理好了拳喻。
但是這個(gè)也不是沒(méi)有代價(jià)的,這里的代價(jià)就是我們只能使用UIKit提供的布局組件猪腕,有很多限制和學(xué)習(xí)成本冗澈。 同時(shí)我們想自行定義布局時(shí),也很麻煩陋葡。
而SwiftUI不同亚亲,我們?cè)赟wiftUI中定義布局組件的成本還是相當(dāng)?shù)偷摹?br> 比如下面的代碼就是我們定義一個(gè)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如何擺放捌归,如何重復(fù),卻沒(méi)有定義Content到底是什么組件岭粤∠鳎看起來(lái)是不是有點(diǎn)像UICollectionView呢?
上述的代碼绍在,可以以下面的形式進(jìn)行調(diào)用门扇。
struct ContentView1: View {
var body: some View {
GridStack(rows: 3, columns: 3) { (row, col) in
Text("R\(row)C\(col)")
}
}
}
但是這樣聲明還是有問(wèn)題的雹有,就是Content無(wú)法一次性傳入多個(gè)元素,只能傳入一個(gè)參數(shù)臼寄。
為了解決這個(gè)問(wèn)題霸奕,我們需要引入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)
}
}
}
}
}
}
在聲明時(shí)吉拳,給content參數(shù)加上@ViewBuilder
的注解质帅,就可以一次性傳入多個(gè)元素。類似下面這樣留攒。
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)")
}
}
}
但是這里仍然有問(wèn)題煤惩,就是這個(gè)組件內(nèi)部無(wú)法傳遞超過(guò)10個(gè)元素。這個(gè)是ViewBuilder的設(shè)計(jì)限制的炼邀。
參考: