@Опубликованный массив в нескольких моделях просмотра — SwiftUI

#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)
    }
}