Как связать массив и список, если массив является членом ObservableObject? - PullRequest
0 голосов
/ 23 сентября 2019

Я хочу создать MyViewModel, который получает данные из сети, а затем обновляет массив результатов.MyView должен подписаться на $model.results и показать List, заполненный результатами.

К сожалению, я получаю сообщение об ошибке "Тип выражения неоднозначен без дополнительного контекста".

Какправильно ли использовать ForEach для этого случая?

import SwiftUI
import Combine

class MyViewModel: ObservableObject {
    @Published var results: [String] = []

    init() {
        DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
            self.results = ["Hello", "World", "!!!"]
        }
    }
}

struct MyView: View {
    @ObservedObject var model: MyViewModel

    var body: some View {
        VStack {
            List {
                ForEach($model.results) { text in
                    Text(text)
                 // ^--- Type of expression is ambiguous without more context
                }
            }
        }
    }
}

struct MyView_Previews: PreviewProvider {
    static var previews: some View {
        MyView(model: MyViewModel())
    }
}

PS Если я заменю модель на @State var results: [String], все работает нормально, но мне нужно иметь отдельный class MyViewModel: ObservableObject для моих целей

1 Ответ

3 голосов
/ 24 сентября 2019

Исправление

Измените свой блок ForEach на

ForEach(model.results, id: \.self) { text in
    Text(text)
}

Пояснение

Сообщения об ошибках SwiftUI не отображаются.не делаю тебе никаких одолжений.Реальное сообщение об ошибке (которое вы увидите, если вы измените Text(text) на Text(text as String) и удалите $ до model.results), это «Общий параметр ID не может быть выведен».

Другими словами, чтобы использовать ForEach, элементы, которые вы перебираете, должны быть однозначно идентифицированы одним из двух способов.

  1. Если элемент является структурой или классом, вы можете сделать егосоответствовать протоколу идентификации, добавив свойство var id: Hashable.В этом случае вам не нужен параметр id.
  2. Другой вариант - указать ForEach, что следует использовать в качестве уникального идентификатора, используя параметр id. Обновление: Вы должны гарантировать, что в вашей коллекции нет повторяющихся элементов.Если два элемента имеют одинаковый идентификатор, любое изменение, внесенное в одно представление (например, смещение), произойдет с обоими представлениями.

В этом случае мы выбрали вариант 2 и сказали ForEach использоватьСам строковый элемент в качестве идентификатора (\.self).Мы можем сделать это, так как String соответствует протоколу Hashable.

Как насчет $?

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

  • Переключатель должен обновить значение Bool в ответ на переключатель
  • AСлайдер должен обновить значение Double в ответ на слайд
  • TextField должен обновить значение String в ответ на ввод

Способ, которым мы определяем, что должно быть это двасвязь между состоянием приложения и представлением осуществляется с помощью Binding<SomeType>.Таким образом, Toggle требует, чтобы вы передали ему Binding<Bool>, для Slider - Binding<Double>, а для TextField требуется Binding<String>.

. Именно здесь @State оболочка свойства (или @Publishedвнутри @ObservedObject). Эта оболочка свойства «оборачивает» значение, которое оно содержит в Binding (наряду с некоторыми другими вещами, чтобы SwiftUI знал, что он обновляет представления при изменении значения).Если нам нужно получить значение, мы можем просто сослаться на myVariable, но если нам нужно связывание, мы можем использовать сокращение $myVariable.

Итак, в этом случае ваш исходный код содержал ForEach($model.results).Другими словами, вы говорили компилятору: «Итерация по этому Binding<[String]>», но Binding - это не коллекция, которую вы можете повторять.Удаление $ говорит: «Итерация по этому [String]», и Array - это коллекция, которую вы можете перебирать.

...