无法 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
,这样你只需将变量传递给顶视图,视图层次结构中的所有内容都会继承它。
我无法更新 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
andExampleView
: ifstate.message
(any value thatstate
publishes) changes, you need to redraw your body" - "And
ExampleState
: after updating your message variable, publish the new value!"
最后 - 为了完成 - 还有 @EnvironmentObject
,这样你只需将变量传递给顶视图,视图层次结构中的所有内容都会继承它。