web-dev-qa-db-ja.com

SwiftUIビューの@state変数を更新または変更できません

updateMessage()が呼び出されているのを確認できても、ExampleViewのmessage変数を更新できません。これが、機能していないPlaygroundコードの単純化/複雑化したSwiftUIの例です。 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())
3
xta

実際には、変数は更新されますが、コンテンツビューにはそれが通知されません。これは何が起こるかです:

  • ContentViewが呼び出され、初期化されます colorLabel と ExampleView
  • contentViewでボタンを押す
  • self.coloredLabel.updateMessage()が呼び出されます
  • メッセージが印刷されます
  • 変数self.coloredLabel.messageが変更されます
  • 変更について通知されないため、ContentViewは再描画されません。
  • すなわち、 colorLabel スタック内が更新されない

現在、さまざまなオプションがあります:@State@Bindingおよび@PublishedObject, @ObservedObject。これらのパブリッシャーの1つが必要なので、ビューは実際に何かを行う必要があることに気づきます。

ボタンを押すたびに新しいExampleViewを描画するか、この場合は@StateContentViewの変数:

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も同様に、変数をトップビューに渡すだけで、ビュー階層の下のすべてがそれを継承します。

1
Alex

うまくいくはずのソリューションの1つですが、みんなが言うように、@Binding

 struct ExampleView: View {

        var message: String = "Hello"

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

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