#arrays #swiftui #viewmodel #shared
#массивы #swiftui #модель представления #общий доступ
Вопрос:
У меня есть приложение только с двумя представлениями, оба представления имеют собственную ViewModel. ViewA показывает объекты из массива selectedNumbers и управляет ими. ViewB владеет всеми доступными объектами (числами) — в этом представлении я хочу манипулировать массивом selectedNumbers, который используется ViewA.
Я пытаюсь выяснить, как разделить этот массив selectedNumbers между этими двумя ViewModels. Я пытался использовать EnvironmentObject, StaticObject и т.д. Но ничто не работает так, как мне нужно. Какой подход я должен использовать для достижения желаемого результата? Спасибо за помощь!
import SwiftUI
struct ViewA: View {
@ObservedObject var viewModel = ViewModelA()
var body: some View {
VStack {
Text("(viewModel.number)")
.font(.largeTitle)
.padding()
.onTapGesture {
viewModel.showNext()
}
ViewB()
}
}
}
class ViewModelA: ObservableObject {
var numbers: [Int] = []
@Published var number: Int
var index = 0
init() {
number = numbers.isEmpty ? 0 : numbers[index]
}
func showNext() {
guard !numbers.isEmpty else { return }
if index < numbers.count - 1 {
index = 1
} else {
index = 0
}
number = numbers[index]
}
}
struct ViewB: View {
@ObservedObject var viewModel = ViewModelB()
var body: some View {
HStack {
ForEach(viewModel.numbers, id: .self) { number in
Text("(number)")
.foregroundColor(viewModel.selectedNumbers.contains(number) ? .red : .black)
.onTapGesture {
viewModel.updateSelection(number)
}
}
}
}
}
class ViewModelB: ObservableObject {
@Published var numbers: [Int] = []
@Published var selectedNumbers: [Int] = []
init() {
numbers.append(contentsOf: [1,2,3,4,5,6,7,8])
}
func updateSelection(_ number: Int) {
if selectedNumbers.contains(number) {
selectedNumbers.remove(number)
} else {
selectedNumbers.append(number)
}
}
}
extension Array where Element: Equatable {
mutating func remove(_ object: Element) {
guard let index = firstIndex(of: object) else {return}
remove(at: index)
}
}
Комментарии:
1. У вас есть два разных источника истины, вам следует переосмыслить дизайн своей модели с использованием привязок и сохранить единый источник истины
2. Что вы имеете в виду? Использовать только одну ViewModel -> ViewModelA и привязать ее к ViewB? Я попытался разделить логику этих двух взглядов..
Ответ №1:
Вы все равно можете сохранить логику отдельно, но вам нужно сохранить единый источник истины, и если вы хотите поделиться данными между представлениями, вам нужно либо передать Bindings
, либо вы также можете поделиться @ObservedObject
между подвидами.
import SwiftUI
struct ViewA: View {
@ObservedObject var viewModel = ViewModelA(modelB: ViewModelB())
var body: some View {
VStack {
Text("(viewModel.number)")
.font(.largeTitle)
.padding()
.onTapGesture {
viewModel.showNext()
}
ViewB(model: viewModel)
}
}
}
class ViewModelA: ObservableObject {
var numbers: [Int] = []
@Published var number: Int
@Published var modelB:ViewModelB
var index = 0
init(modelB:ViewModelB) {
self.modelB = modelB
number = numbers.isEmpty ? 0 : modelB.selectedNumbers[index]
}
func showNext() {
guard !modelB.selectedNumbers.isEmpty else { return }
if index < modelB.selectedNumbers.count - 1 {
index = 1
} else {
index = 0
}
number = modelB.selectedNumbers[index]
}
}
struct ViewB: View {
@ObservedObject var model : ViewModelA
var body: some View {
HStack {
ForEach(model.modelB.selectedNumbers, id: .self) { number in
Text("(number)")
.foregroundColor(model.modelB.selectedNumbers.contains(number) ? .red : .black)
.onTapGesture {
model.modelB.updateSelection(number)
}
}
}
}
}
struct ViewModelB {
var selectedNumbers: [Int] = []
init() {
selectedNumbers.append(contentsOf: [1,2,3,4,5,6,7,8])
}
mutating func updateSelection(_ number: Int) {
if selectedNumbers.contains(number) {
selectedNumbers.remove(number)
} else {
selectedNumbers.append(number)
}
}
}
extension Array where Element: Equatable {
mutating func remove(_ object: Element) {
guard let index = firstIndex(of: object) else {return}
remove(at: index)
}
}