SwiftUI:自定义容器
尽管您不太可能经常这样做,但我想至少告诉您,完全可以在SwiftUI应用中创建自定义容器。这需要更高级的Swift知识,因为它利用了Swift的一些强大功能,因此如果发现不理解,可以跳过。
为了进行测试,我们将创建一种新型的名为GridStack
的堆栈,它将使我们能够在网格内创建任意数量的视图。我们要说的是,有一个名为GridStack
的新结构体,该结构体符合View
协议并具有一定数量的行和列,并且在网格内部将有很多内容单元格,它们本身必须符合View
协议。
在Swift中,我们可以这样写:
struct GridStack<Content: View>: View {
let rows: Int
let columns: Int
let content: (Int, Int) -> Content
var body: some View {
// more to come
}
}
第一行struct GridStack<Content: View>: View
使用了Swift的一个更高级的功能,称为泛型。在这种情况下,它的意思是“您可以提供所需的任何种类的内容,但是无论内容如何,都必须符合View协议。”在冒号之后,我们再次重复View
,以说GridStack
本身也符合View
协议。
请特别注意let content
这一行——它定义了一个闭包,该闭包必须能够接受两个整数并返回我们可以显示的某种内容。
我们需要通过结合多个垂直和水平堆栈以创建所需数量的单元格来完成body
属性。我们不需要说明每一个单元格中的内容,因为我们可以通过使用适当的行和列调用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)
}
}
}
}
}
提示:遍历范围时,只有在我们确定范围内的值不会随时间变化时,SwiftUI才能直接使用范围。在这里,我们使用的ForEach
带有0 .. <rows
和0 .. <columns
,这两个值都是可以随时间变化的值,例如,我们可能会添加更多行。在这种情况下,我们需要向ForEach
添加第二个参数,id: \ .self
,以告诉SwiftUI如何识别循环中的每个视图。我们将在项目5中对此进行详细说明。
现在我们有了一个自定义容器,我们可以使用它来编写一个视图,如下所示:
struct ContentView: View {
var body: some View {
GridStack(rows: 4, columns: 4) { row, col in
Text("R\(row) C\(col)")
}
}
}
我们的GridStack
能够接受任何类型的单元格内容,只要它符合View
协议。因此,如果需要,我们可以给单元格一个堆栈:
GridStack(rows: 4, columns: 4) { row, col in
HStack {
Image(systemName: "\(row * 4 + col).circle")
Text("R\(row) C\(col)")
}
}
想进一步吗?
为了获得更大的灵活性,我们可以利用SwiftUI的一种称为视图构建器的功能,该功能允许我们发送多个视图并将其形成隐式堆栈。
要使用此功能,我们需要为GridStack
结构体创建自定义初始化程序,以便我们可以将content
闭包标记为使用SwiftUI的视图构建器系统:
init(rows: Int, columns: Int, @ViewBuilder content: @escaping (Int, Int) -> Content) {
self.rows = rows
self.columns = columns
self.content = content
}
多数情况下,这只是将参数直接复制到结构的属性中,但是请注意,这里有@ViewBuilder
属性。您还将看到@escaping
属性,该属性使我们可以存储闭包,以便以后使用。
有了适当的设置,SwiftUI现在将在我们的单元格闭包内部自动创建一个隐式水平堆栈:
GridStack(rows: 4, columns: 4) { row, col in
Image(systemName: "\(row * 4 + col).circle")
Text("R\(row) C\(col)")
}
这两种方式均有效,所以无论您喜欢哪个都可以。
Previous: 自定义修饰符 | Hacking with iOS: SwiftUI Edition | Next: 视图和修饰符项目——挑战 |
---|
赏我一个赞吧~~~