SwiftUI也是可以自定义容器的,我们将创建一个名为 GridStack 的新容器,它可以让我们在网格内创建任意数量的视图。
//<Content:View>意味着内容必须是符合View协议的控件,而GridView本身也必须符合View协议
struct GridStack <Content: View>: View {
let rows: Int, columns: Int
//定义了一个闭包,返回需要展示的内容
let content: (Int, Int) -> Content
var body: some View {
VStack {
ForEach(0..<rows, id: \.self) { row in
HStack {
ForEach(0..<columns, id: \.self) {column in
self.content(row, column)
}
}
}
}
}
}
当循环遍历范围时,只有当我们确定范围内的值不会随时间变化时,SwiftUI 才能直接使用范围。这里我们使用ForEach 0..<rows和0..<columns,这两个值都可以随时间变化——例如,我们可能会添加更多行或列。在这种情况下,我们需要添加第二个参数ForEach,id: .self以告诉SwiftUI它如何能够识别循环中的每个视图。
此时,我们的自定义容器就可以使用了:
struct ContentView: View {
var body: some View {
GridStack(rows: 4, columns: 4) { row, col in
HStack {
Image(systemName: "\(row * 4 + col).circle")
Text("R\(row) C\(col)")
}
}
}
}
为了更灵活的使用自定义容器,我们可以使用@ViewBuilder-视图构建器,它允许我们发送多个视图并让它为我们形成一个隐式堆栈。
我们为GridStack创建一个自定义初始化器,以便我们可以将content闭包标记为使用 SwiftUI 的视图构建器系统:
init(rows: Int, columns: Int, @ViewBuilder content: @escaping (Int, Int) -> Content) {
self.rows = rows
self.columns = columns
self.content = content
}
有了它,SwiftUI 现在将自动在我们的单元格闭包中创建一个 隐式水平堆栈:
GridStack(rows: 4, columns: 4) { row, col in
Image(systemName: "\(row * 4 + col).circle")
Text("R\(row) C\(col)")
}
网友评论