SwiftUIHacking with iOS: SwiftUI Edition

SwiftUI:自定义容器

2020-03-27  本文已影响0人  韦弦Zhy

\color{red}{\Large \mathbf{Hacking \quad with \quad iOS: SwiftUI \quad Edition - proj.3}}

{\Huge \mathbf{Views \ and \ Modifiers}}

尽管您不太可能经常这样做,但我想至少告诉您,完全可以在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 .. <rows0 .. <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)")
}

这两种方式均有效,所以无论您喜欢哪个都可以。

译自Custom containers

Previous: 自定义修饰符 Hacking with iOS: SwiftUI Edition Next: 视图和修饰符项目——挑战

赏我一个赞吧~~~

上一篇 下一篇

猜你喜欢

热点阅读