Невозможно обновить / изменить @state var в SwiftUI View - PullRequest
1 голос
/ 17 января 2020

Я не могу обновить message var в ExampleView, хотя вижу, что вызывается updateMessage(). Вот мой упрощенный / свернутый пример SwiftUI кода Playground, который не работает. message var не обновляется при вызове в updateMessage(). В результате пользовательский интерфейс 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 ]

2 голосов
/ 17 января 2020

Вы должны изменить только 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 или et c):

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)
    }
}
0 голосов
/ 26 февраля 2020

На самом деле, переменная действительно обновляется, но ваше представление контента не получает информацию об этом. Вот что происходит:

  • вызывается ContentView, он инициализирует ColoredLabel с ExampleView
  • при нажатии кнопки в ContentView
  • self.coloredLabel.updateMessage () get вызывается
  • сообщение печатается
  • переменная self.coloredLabel.message изменена
  • ContentView не перерисовывается, так как он не уведомлен об изменении
  • , более конкретно, ColoredLabel внутри вашего стека не обновляется

сейчас, у вас есть различные варианты: @State, @Binding и @PublishedObject, @ObservedObject. Вам нужен один из этих издателей, поэтому ваш взгляд на самом деле замечает, что он должен что-то делать.

Либо вы рисуете новый ExampleView каждый раз, когда нажимаете кнопку, в этом случае вы можете использовать @State переменная в ContentView:

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, @ Опубликовано

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 - этот класс имеет опубликованные переменные, которые можно наблюдать

чтобы возобновить (вот как я понимаю):

  • "Эй, ContentView и ExampleView: если state.message (любое значение, которое публикуется state) изменится, вы нужно перерисовать ваше тело "
  • " и ExampleState: после обновления вашей переменной сообщения опубликуйте sh новое значение! "

наконец - для завершения - есть @EnvironmentObject, таким образом, вам нужно будет только передать переменную в верхние виды, и все вниз по иерархии представлений буду наследовать.

0 голосов
/ 17 января 2020

Одно из решений, которое должно работать, но, как говорят парни, вы можете работать с @Binding

 struct ExampleView: View {

        var message: String = "Hello"

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

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