У меня есть модель представления:
@MainActor public class TestViewModel: ObservableObject {
@Published var data: [String: [String]]
init() {
self.data = ["a": ["a1", "a2"]]
}
}
Существует вложенное представление, которое использует эти данные:
struct ContainerView: View {
@Binding var data: [String: [String]]
var body: some View {
List() {
ForEach(data.keys.sorted(), id: \.self){ key in
Text(key)
}
}
}
}
struct TestView: View {
@StateObject private var viewModel = TestViewModel()
var body: some View {
ContainerView(
data: $viewModel.data
)
}
}
Это работает так, как ожидалось.
Но предположим, что у меня есть много разных типов опубликованных данных. Было бы неплохо, если бы я мог просто передать саму модель представления, что я действительно могу сделать:
struct ContainerView: View {
@ObservedObject var viewModel: TestViewModel
var body: some View {
List() {
ForEach($viewModel.data.keys.sorted(), id: \.self){ key in
Text(key)
}
}
}
}
struct TestView: View {
@StateObject private var viewModel = TestViewModel()
var body: some View {
ContainerView(
viewModel: viewModel
)
}
}
Это отлично работает для большинства случаев использования ViewModel
внутри ContainerView
(которые я опустил для краткости), но не для этого. В ForEach
я получаю следующие ошибки:
Невозможно назначить свойство: «keys» — это свойство, доступное только для получения.
Невозможно вызвать значение нефункционального типа «Binding<() -> [Dictionary<String, [String]>.Keys.Element]>» (также известное как «Binding<() -> Array>»).
Поиск элемента динамического ключевого пути не может ссылаться на метод экземпляра sorted().
Что здесь происходит?
Но не является ли это также привязкой в первом случае? Где-нибудь в ForEach
есть ли попытка записи в свойство (не .sorted()
просто читает)?
Думаю, нет. При привязке вы используете этот API: ForEach(Binding<MutableCollection & RandomAccessCollection>, id: KeyPath<C.Element, _>) { Binding<C.Element> -> _}
. С первым вы получаете доступ к этому: ForEach(RandomAccessCollection, content: (Identifiable) -> View)
Почему вы хотите использовать привязку? просто используйте «ForEach(viewModel.data.keys.sorted()». Если вы хотите изменить ky или содержимое данных, это работа ViewModel, и, поскольку вы получаете ее как ObservedObject, проблем нет.
@PtitXav Если вы опубликуете ответ, я смогу его принять.
Просто используйте «ForEach(viewModel.data.keys.sorted() Если вы хотите изменить ky или содержимое данных, это работа ViewModel, и когда вы получаете его как ObservedObject, проблем не возникает.
@MainActor public class TestViewModel: ObservableObject {
@Published var data: [String: [String]]
init() {
self.data = ["a": ["a1", "a2"]]
}
// to update data
func updateData(for key: String, strings: [String]) {
self.data[key] = strings
}
// to add data
func addData(for key: String, strings: [String]) {
self.data[key] = strings
}
}
struct ContainerView: View {
@ObservedObject var viewModel: TestViewModel
var body: some View {
List() {
Button("add key b") {
viewModel.addData(for: "b", strings: ["b1", "b2"])
}
Button("update key b") {
viewModel.addData(for: "b", strings: ["b3", "b4"])
}
Button("add key c") {
viewModel.addData(for: "c", strings: ["c1", "c2"])
}
ForEach(viewModel.data.keys.sorted(), id: \.self){ key in
let strings = viewModel.data[key]!.joined(separator: "-")
Text(key + " = " + strings)
}
}
}
}
struct TestView: View {
@StateObject private var viewModel = TestViewModel()
var body: some View {
ContainerView(
viewModel: viewModel
)
}
}
viewModel.data
является изменяемым, ноviewModel.data.keys
доступен только для чтения. Когда вы передаете$viewModel.data.keys
, это Binding, и Binding ожидает записи в путь, в котором находится переменная, в данном случае viewModel.data.keys, который доступен только для чтения.