无法更新/修改 SwiftUI 视图的 @state var

Posted

技术标签:

【中文标题】无法更新/修改 SwiftUI 视图的 @state var【英文标题】:Unable to update/modify SwiftUI View's @state var 【发布时间】:2020-01-17 08:34:08 【问题描述】:

我无法更新 ExampleView 的 message var,即使我可以看到 updateMessage() 正在被调用。这是我的 Playground 代码的简化/复杂的 SwiftUI 示例,它不起作用。在 updateMessage() 中调用时,message 变量不会更新。因此,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())

【问题讨论】:

您只能在其自己的body 块内设置视图的State。您将ExampleView 的状态设置为ContentView.body,这是不允许的。我认为这是你应该使用@Binding的地方。 【参考方案1】:

您应该只更改视图 inside 自己的主体块的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)
    

【讨论】:

您是否仅限于 Bool 或 enum?我想使用 Int,或者在示例中使用 String 不,您可以使用任何适合您的情况。关键是使用父级的binding 变量触发更改。 好的,我使用绑定示例让它工作了。我遇到的一个问题是在视图正文中使用Text(self.message)。那是行不通的。它必须是Text(message)。谢谢!【参考方案2】:

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

ContentView 被调用,它用 ExampleView 初始化 coloredLabel 在 ContentView 中按下按钮 self.coloredLabel.updateMessage() 被调用 消息已打印 变量 self.coloredLabel.message 被修改 ContentView 不会重绘,因为它没有收到有关更改的通知 更具体地说,堆栈中的 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 - 这个类已经发布了可以观察的变量

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

“嘿,ContentViewExampleView:如果 state.messagestate 发布的任何值)发生变化,您需要重新绘制身体” “和ExampleState:更新你的消息变量后,发布新值!”

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

【讨论】:

【参考方案3】:

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

 struct ExampleView: View 

        var message: String = "Hello"

        var body: some View 
            Text(self.message)
        

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

【讨论】:

以上是关于无法更新/修改 SwiftUI 视图的 @state var的主要内容,如果未能解决你的问题,请参考以下文章

在 SwiftUI 中使用 Map 时在视图更新期间获取修改状态

无法使用@EnvironmentObject SwiftUI 更新先前视图上的文本

状态更改时自定义 SwiftUI 视图不会更新

在 SwiftUI 中修改 ForEach 内的视图

如何切换数组中对象的属性并更新 SwiftUI 视图?

SwiftUI 将数据传递给子视图