更改 @State 变量不会更新 SwiftUI 中的视图

iSp*_*n17 11 ios swift swiftui

我有以下观点(去掉不相关的部分):

struct Chart : View {
    var xValues: [String]
    var yValues: [Double]
    @State private var showXValues: Bool = false

    var body = some View {
        ...
        if showXValues {
            ...
        } else {
            ...
        }
        ...
    }
}
Run Code Online (Sandbox Code Playgroud)

然后我想添加一种从外部修改这个值的方法,所以我添加了一个函数:

func showXValues(show: Bool) -> Chart {
    self.showXValues = show
    return self
}
Run Code Online (Sandbox Code Playgroud)

所以我像这样从外部构建图表视图:

Chart(xValues: ["a", "b", "c"], yValues: [1, 2, 3])
    .showXValues(true)
Run Code Online (Sandbox Code Playgroud)

但它的工作方式就好像该值仍然为假。我究竟做错了什么?我认为更新 @State 变量应该更新视图。总的来说,我对 Swift 很陌生,对 SwiftUI 更是如此,我是否缺少某种应该在这里使用的特殊技术?

小智 6

正如在评论中提到的那样,@Binding是要走的路。

这是一个最小的示例,显示了您的代码的概念:

struct Chart : View {
    var xValues: [String]
    var yValues: [Double]
    @Binding var showXValues: Bool

    var body: some View {
        if self.showXValues {
            return Text("Showing X Values")
        } else {
            return Text("Hiding X Values")
        }
    }
}

struct ContentView: View {
    @State var showXValues: Bool = false

    var body: some View {
        VStack {
            Chart(xValues: ["a", "b", "c"], yValues: [1, 2, 3], showXValues: self.$showXValues)
            Button(action: {
                self.showXValues.toggle()
            }, label: {
                if self.showXValues {
                    Text("Hide X Values")
                }else {
                    Text("Show X Values")
                }
            })
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 我觉得这种方法并不令人满意。只有“Chart”应该关注“showXValues”,而不是“ContentView”。我不想将视图状态的定义推入其父级。 (4认同)
  • 但我有 20 个可选参数,如“showXValues”,我不想一直修改它们,我想要类似构建器模式的东西 - 正如我的示例 func 所示。我不想编写 20 个参数的初始值设定项。例如,用户可以告诉我是否要显示 X 值,但我默认隐藏它们。 (3认同)

iSp*_*n17 3

无需创建func-s。我所要做的就是不将属性标记为私有,而是给它们一个初始值,这样它们将在构造函数中成为可选的。因此用户可以指定它们,也可以不关心。像这样:

var showXLabels: Bool = false

这样构造函数就是Chart(xLabels:yLabels)or Chart(xLabels:yLabels:showXLabels)

问题与@State无关。

几年后编辑

实际上,对于二元期权,有一种更简洁的方法可以解决这个问题,例如显示/隐藏内容。对于多于 2 路的配置,仍然需要使用不同的参数。

关键是OptionSets 正是针对这种用例,并且可以在 Foundation 等 Apple 框架中找到它们。

所以,我们只需添加一个Chart.Options结构:

extension Chart {
    struct Options: OptionSet {
        let rawValue: Int

        static var showXValueLabels = Self(rawValue: 1 << 0)
        static var showYValueLabels = Self(rawValue: 1 << 1)
        [... add more options if you want]
    }
}
Run Code Online (Sandbox Code Playgroud)

然后,视图本身仍然更加紧凑,因为它只有一个用于二进制设置的变量:

struct Chart: View {
    var xValues: [String]
    var yValues: [Int]
    var options: Chart.Options = []

    var body: some View {
        VStack {
            Text("A chart")

            if options.contains(.showXValueLabels) {
                [... whatever xValue label specific view]
            }

            if options.contains(.showYValueLabels) {
                [... whatever yValue label specific view]
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

你可以构建Chart这样的:

Chart(xValues: ["a", "b", "c"],
      yValues: [1, 2, 3],
      options: [.showXValueLabels, .showYValueLabels])
Run Code Online (Sandbox Code Playgroud)

或者

Chart(xValues: ["a", "b", "c"],
      yValues: [1, 2, 3],
      options: [.showXValueLabels])
Run Code Online (Sandbox Code Playgroud)

或者只使用默认选项:

Chart(xValues: ["a", "b", "c"],
      yValues: [1, 2, 3])
Run Code Online (Sandbox Code Playgroud)

  • 这个答案与你原来的问题无关。StackOverflow 还可以帮助其他可能偶然发现您的问题的人,您不应该接受无法回答您问题的答案。 (8认同)