Vorrei iniziare con qualche teoria . MVVM è una specializzazione di Presentation Model (o modello di applicazione) per Silverlight e WPF di Microsoft. Le idee principali alla base di questo modello architettonico di interfaccia utente sono:
- La parte vista è l'unico che dipende dal quadro GUI. Ciò significa che per iOS, il controller di visualizzazione è parte della visualizzazione.
- La vista può solo parlare con il modello di vista. Mai per il modello.
- Il modello di vista mantiene lo stato della vista.Questo stato viene offerto alla vista tramite le proprietà del modello di visualizzazione. Queste proprietà contengono non solo il valore delle etichette, ma anche altre informazioni relative alla vista come se il pulsante Salva sia abilitato o il colore per una vista di valutazione. Ma l'informazione dello stato deve essere indipendente dalla struttura dell'interfaccia utente. Quindi, nel caso di iOS, la proprietà per il colore dovrebbe essere un enum, ad esempio, anziché un UIColor.
- Il modello di vista fornisce anche metodi che si occuperanno delle azioni dell'interfaccia utente. Queste azioni parleranno con il modello, ma non cambiano mai lo stato della vista direttamente correlato ai dati. Invece, parla al modello e chiede i cambiamenti richiesti.
- Il modello deve essere autonomo, vale a dire che si dovrebbe essere in grado di utilizzare lo stesso codice per il modello per un'applicazione della riga di comando e un'interfaccia utente. Si prenderà cura di tutta la logica aziendale.
- Il modello non conosce il modello di vista. Quindi le modifiche al modello di vista vengono propagate attraverso un meccanismo di osservazione. Per iOS e un modello con sottoclassi NSObject semplici o anche Core Data, KVO può essere usato per questo (anche per Swift).
- Una volta che il modello di visualizzazione è a conoscenza delle modifiche nel modello, deve aggiornare lo stato che mantiene (se si utilizzano i tipi di valore, è necessario crearne uno aggiornato e sostituirlo).
- Il modello di vista non conosce la vista. Nella sua concezione originale utilizza l'associazione dati, che non è disponibile per iOS. Quindi i cambiamenti nel modello di vista sono propagati attraverso un meccanismo di osservazione. Puoi anche usare KVO qui o, come hai detto nella domanda, un semplice schema di delega, anche meglio se combinato con gli osservatori di proprietà Swift, lo farà. Alcune persone preferiscono quadri reattivi, come RxSwift, ReactiveCocoa o persino Swift Bond.
I vantaggi sono come lei ha ricordato:
- migliore separazione degli interessi.
- Interfaccia utente: migrazione più semplice verso altre interfacce utente.
- Testabilità migliore a causa della separazione delle preoccupazioni e della natura disaccoppiata del codice.
Tornando alla tua domanda, l'implementazione del protocollo UITableViewDataSource
appartiene alla parte vista dell'architettura, a causa delle sue dipendenze sul framework dell'interfaccia utente. Si noti che per utilizzare quel protocollo nel proprio codice, quel file deve importare UIKit. Anche metodi come tableView(:cellForRowAt:)
che restituiscono una vista dipendono fortemente da UIKit.
Quindi, l'array di Contacts
, che è effettivamente il modello, non può essere gestito o interrogato tramite la vista (origine dati o altro). Invece si passa un modello di vista al proprio controller di visualizzazione tabella, che, nel caso più semplice, ha due proprietà (raccomando che siano archiviate, non proprietà calcolate). Uno di questi è il numero di sezioni e l'altro è il numero di righe per girone:
var numberOfSections: Int = 0
var rowsPerSection: [Int] = []
Il modello vista viene inizializzato con un riferimento al modello e come ultimo passo l'inizializzazione imposta il valore di queste due proprietà.
L'origine dati nel controller della vista utilizza i dati del modello di vista:
override func numberOfSections(in tableView: UITableView) -> Int {
return viewModel.numberOfSections
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return viewModel.rowsPerSection[section]
}
Infine si può avere un diverso struct vista del modello per ciascuna delle cellule:
struct ContactCellViewModel {
let name: String
init(contact: Contact) {
name = contact.name ?? ""
}
}
E sottoclasse UITableViewCell
saprà come utilizzare tale struct:
class ContactTableViewCell: UITableViewCell {
var viewModel: ContactCellViewModel!
func configure() {
textLabel!.text = viewModel.name
}
}
Per avere il modello vista corrispondente per ciascuna cella, il modello di tabella vista vista fornirà un metodo che li genera e che può essere usata per compilare la matrice di modelli immagine:
func viewModelForCell(at index: Int) -> ContactCellViewModel {
return ContactCellViewModel(contact: contacts[index])
}
Come si può vedere i modelli di vista qui sono gli unici a parlare con il modello (l'array Contacts
) e le viste parlano solo con i modelli di visualizzazione.
Spero che questo aiuti.
fonte
2016-08-15 15:06:27
A meno che non si stia utilizzando un framework aggiuntivo come React, MVVM non funziona in realtà in iOS poiché i controlli iOS non hanno l'associazione dati. – Paulw11
@ Paulw11 Sto usando il pattern delegato invece di React, è più codice ma è più chiaro e il debug non è un dolore – azimov