Как мы можем прочитать переменную @State из другой структуры в SwiftUI?

#swiftui

#swiftui

Вопрос:

Я хочу знать, есть ли простой или правильный способ считывания значения переменной состояния из другого представления, я знаю использование .onChange или Binding или ObservableObject(class) и …, но мне хотелось бы знать, есть ли другой лучший способ?

Например, в этом коде у меня есть представление с именем TextView, которое имеет значение состояния, и я вызываю это представление внутри моего contentView, теперь я помещаю текст в свой contentView, который я хочу прочитать значение состояния TextView. Существует ли пространственный метод для этой работы?

 struct ContentView: View {
    @State var readStringOfTextView: String = ""

    var body: some View {
        TextView()

        Text(readStringOfTextView)
            .foregroundColor(Color.blue)
    }
}

struct TextView: View {
    @State var stringOfText: String = "Hello, world!"

    var body: some View {
        Text(stringOfText)
            .padding()
            .foregroundColor(Color.red)
    }
}
  

Ответ №1:

Весь смысл состояния в том, что оно является внутренним для представления. Если вы пытаетесь прочитать ее в другом месте, что-то пошло не так в вашем дизайне. Инструмент, который вам нужен в этом случае @Binding . contentView должен передать привязку к TextView. Любые изменения в TextView будут видны contentView (в вашем примере это не имеет смысла, потому stringOfText что не может измениться, но я предполагаю, что остальная часть вашего кода каким-то образом изменяет его). В вашем примере это будет выглядеть примерно так:

 struct ContentView: View {
    @State var readStringOfTextView: String = ""
    var body: some View {
        TextView(stringOfText: $readStringOfTextView)
        Text(readStringOfTextView)
            .foregroundColor(Color.blue)
    }
}

struct TextView: View {
    @Binding var stringOfText : String

    var body: some View {
        Text(stringOfText)
            .padding()
            .foregroundColor(Color.red)
            .onAppear {
                stringOfText = "Hello, world!"
            }
    }
}
  

Можно напрямую передавать данные вверх по иерархии представлений, используя настройки, но это намного сложнее и не подходит для решения проблемы, которую вы описали. Несмотря на это, вот как это будет выглядеть:

  • Создайте PreferenceKey для передачи данных
  • Установите ключ предпочтения в дочерних представлениях, используя .preference
  • Прочитайте PreferenceKey в родительском представлении, используя .onPreferenceChange или .overlayPreferenceValue или .backgroundPreferenceValue .
 struct TextPreference: PreferenceKey {
    static var defaultValue = "default"
    static func reduce(value: inout String, nextValue: () -> String) {
        value = nextValue()
    }
}

struct ContentView: View {
    @State var readStringOfTextView: String = ""

    var body: some View {
        VStack {
            TextView()
                .onPreferenceChange(TextPreference.self) { value in
                    readStringOfTextView = value
                }
            Text(readStringOfTextView)
                .foregroundColor(Color.blue)
        }
    }
}

struct TextView: View {
    @State var stringOfText : String = "Hello, world!"

    var body: some View {
        Text(stringOfText)
            .padding()
            .foregroundColor(Color.red)
            .preference(key: TextPreference.self, value: stringOfText)
    }
}
  

Комментарии:

1. Как я уже сказал, я уже знаю использование состояния и привязки, … TextView что-то не замечает! вы помещаете туда привязку! посмотрите мой код, пожалуйста. Я хочу прочитать состояние TextView, может быть, более близким и логичным способом будет ObservableObject, который будет разделен между TextView и contentView, который кто-то может прочитать или записать, я ищу лучший способ

2. Да, ObservableObject был бы правильным инструментом, если у вас есть состояние, в котором вы хотите, чтобы все наблюдали и делились несколькими представлениями. Вы не должны читать состояние других представлений. Состояние явно внутреннее. Настройки также работают для этого, но, как правило, не являются подходящим инструментом для того, что вы описываете. Если она «похожа на модель», то вам нужна наблюдаемая.

3. Итак, вот почему я задал этот вопрос, я не был уверен, может ли быть или не может быть лучшего способа, чем метод ObservableObject! когда я собираюсь использовать ObservableObject, я делаю перевернутую разработку, чтобы решить проблему, и в этом случае я использую не State, а StateObject, так правильно ли говорить, что State недоступен извне, но StateObject есть !?! и могу ли я сказать, что лучший и правильный ответ — StateObject ?

4. StateObject предоставляет право собственности на что-то, что в противном случае было бы ObservedObject . Это удобно, потому что это означает, что в представлении верхнего уровня вам не нужно передавать объект. Но вы все равно не должны пытаться опрашивать объекты состояния дочерних представлений. «Государственные» вещи всегда являются частными. Представления никогда не являются владельцами состояния для представлений более высокого уровня (или других частей системы). Лучший обзор того, как это работает, взят из WWDC20: developer.apple.com/videos/play/wwdc2020/10040