无法 update/modify SwiftUI 视图的 @state var

Unable to update/modify SwiftUI View's @state var

我无法更新 ExampleView 的 message var,即使我可以看到正在调用 updateMessage()。这是我的 simplified/convoluted SwiftUI 无法运行的 Playground 代码示例。 message var 在 updateMessage() 中调用时不会更新。结果,UI Text() 也没有更新。

为什么@State var message 没有更新?正确的更新方式是什么?

import SwiftUI
import PlaygroundSupport

struct ContentView: View {
    let coloredLabel = ExampleView()

    var body: some View {
        VStack {
            coloredLabel
                .foregroundColor(Color.red)
                .padding()
            Button(action: {
                self.coloredLabel.updateMessage()
            }) {
                Text("Press me")
            }

        }
    }
}

struct ExampleView: View {
    @State private var message: String = "Hello"

    var body: some View {
        Text(self.message)
    }

    func updateMessage() {
        print("updateMessage ran") // this prints
        self.message = "Updated"
    }
}

PlaygroundPage.current.setLiveView(ContentView())

您应该只更改 State 视图 其自身的正文块内。如果您需要从父视图更改它,您可能希望将值从父视图传递给它并改为 Binding

struct ContentView: View {
    @State private var message = "Hello"

    var body: some View {
        VStack {
            ExampleView(message: $message)
                .foregroundColor(Color.red)
                .padding()
            Button("Press me") {
                self.message = "Updated"
            }
        }
    }
}

struct ExampleView: View {
    @Binding var message: String

    var body: some View {
        Text(message)
    }
}

如果您需要在 ExampleView 中封装消息,您可以使用 Bool(或 enum 等)代替:

struct ContentView: View {
    @State private var updated = false

    var body: some View {
        VStack {
            ExampleView(isUpdated: $updated)
                .foregroundColor(Color.red)
                .padding()
            Button("Press me") {
                self.updated = true
            }
        }
    }
}

struct ExampleView: View {
    @Binding var isUpdated: Bool
    private var message: String { isUpdated ? "Updated" : "Hello" }

    var body: some View {
        Text(message)
    }
}

应该可行的解决方案之一,但正如大家所说,您可以使用 @Binding

 struct ExampleView: View {

        var message: String = "Hello"

        var body: some View {
            Text(self.message)
        }

        mutating func updateMessage() {
            print("updateMessage ran")
            message = "Updated"
        }
    }

实际上,变量确实得到了更新,但您的内容视图并没有得到通知。这是发生了什么:

  • ContentView 被调用,它用 ExampleView
  • 初始化 coloredLabel
  • 您按下 ContentView 中的按钮
  • self.coloredLabel.updateMessage() 被调用
  • 消息已打印
  • 变量self.coloredLabel.message被修改
  • ContentView 不会重绘,因为它没有收到有关更改的通知
  • 更具体地说,您的 Stack 中的 coloredLabel 没有更新

现在,您有不同的选择: @State@Binding@PublishedObject, @ObservedObject。您需要这些发布者之一,因此您的视图实际上注意到它需要做一些事情。

或者你每次按下按钮时绘制一个新的ExampleView,在这种情况下你可以在ContentView中使用一个@State变量:

struct ContentView: View {
    @State private var string = "Hello"

    var body: some View {
        VStack {
            ExampleView(message: string)
                .foregroundColor(Color.red)
                .padding()
            Button(action: {
                self.string = "Updated"
            }) {
                Text("Press me")
            }

        }
    }
}

struct ExampleView: View {
    var message: String

    var body: some View {
        Text(self.message)
    }
}

这可能不是您想要的。

接下来可以使用@Binding 已经有人推荐了

最后,您可以使用 ObservableObject @ObservedObject, @Published

class ExampleState: ObservableObject {
    @Published var message: String = "Hello"
    func update() {
        message = "Updated"
    }
}

struct ContentView: View {
    @ObservedObject var state = ExampleState()

    var body: some View {
        VStack {
            ExampleView(state: state)
                .foregroundColor(Color.red)
                .padding()
            Button(action: {
                self.state.update()
            }) {
                Text("Press me")
            }

        }
    }
}

struct ExampleView: View {
    @ObservedObject var state: ExampleState

    var body: some View {
        Text(state.message)
    }
}

这是什么意思: class ExampleState: ObservableObject - 这个class已经发布了可以观察到的变量

继续(我是这么理解的):

  • "Hey, ContentView and ExampleView: if state.message (any value that state publishes) changes, you need to redraw your body"
  • "And ExampleState: after updating your message variable, publish the new value!"

最后 - 为了完成 - 还有 @EnvironmentObject,这样你只需将变量传递给顶视图,视图层次结构中的所有内容都会继承它。