如何在 SwiftUI 中选择性地传入绑定?

McG*_*ire 8 swift swiftui

我有一个视图,它在内部管理一个跟踪当前索引的 @State 变量。所以像:

struct ReusableView: View {

    @State var index: Int = 0

    var body: some View {
        Text("The index is \(self.index)"
        // A button that changes the index
    }

}
Run Code Online (Sandbox Code Playgroud)

此视图将在整个应用程序中重复使用。有时父视图需要访问索引,所以我这样重构它:

struct ParentView: View {

    @State var index: Int = 0

    var body: some View {
      ReusableView($index)
    }

}

struct ReusableView: View {

    @Binding var index: Int

    var body: some View {
        Text("The index is \(self.index)"
        // A button that changes the index
    }

}

Run Code Online (Sandbox Code Playgroud)

问题

我不想强制父视图始终保持索引的状态。换句话说,我想选择性地允许父视图负责状态变量,但默认为可重用视图来维护状态,以防父视图不关心索引。

试图

我尝试以某种方式初始化可重用视图上的绑定,以防父视图不提供绑定:

struct ReusableView: View {

    @Binding var index: Int

    init(_ index: Binding<Int>? = nil) {
        if index != nil {
            self._index = index
        } else {
            // TODO: Parent didn't provide a binding, init myself.
            // ?
        }
    }

    var body: some View {
        Text("The index is \(self.index)"
        // A button that changes the index
    }

}
Run Code Online (Sandbox Code Playgroud)

谢谢!

LuL*_*aGa 9

您想要实现的主要问题是,当索引由父级处理时,您的视图需要一个@Binding,但是当它处理索引本身时,它需要@State。有两种可能的解决方案。

如果视图在没有 index 属性时可以忽略它:

struct ReusableView: View {

    @Binding var index: Int?

    init(_ index: Binding<Int?>) {
        self._index = index
    }

    init() {
       self._index = .constant(nil)
    }

    var body: some View {
        VStack {
            index.map { Text("The index is \($0)") }
        }
    }   
}
Run Code Online (Sandbox Code Playgroud)

优点是它非常简单——只有两个初始值设定项,但是当它由 ResusableView 本身处理(它是一个常量)时,您不能更改索引的值。

如果视图在没有 index 属性时不能忽略它:

struct ReusableView: View {

    private var content: AnyView

    init(_ index: Binding<Int>? = nil) {
        if let index = index {
            self.content = AnyView(DependentView(index: index))
        } else {
            self.content = AnyView(IndependentView())
        }
    }

    var body: some View {
        content
    }

    private struct DependentView: View {

        @Binding var index: Int

        var body: some View {
            Text("The index is \(index)")
        }
    }

    private struct IndependentView: View {

        @State private var index: Int = 0

        var body: some View {
            Text("The index is \(index)")
        }
    }

}
Run Code Online (Sandbox Code Playgroud)

明显的优势是您可以将视图绑定到值或将其作为自己的状态进行管理。如您所见,ReusableView 只是两个不同视图的包装器,一个管理自己的 @State,另一个绑定到其父视图的 @State。