2016-06-25 48 views
16

Sono bloccato su una decisione di progettazione con la creazione di modelli di visualizzazione per le celle di visualizzazione tabella. I dati per ogni cella sono forniti da una classe di origine dati (ha una matrice di Contacts). Nel modello MVVM solo il modello di visualizzazione può dialogare con il modello, ma non ha senso inserire l'origine dati nel modello di visualizzazione perché renderebbe possibile l'accesso ai dati per tutte le celle, inoltre è sbagliato inserire l'origine dati nel controller di visualizzazione in quanto non deve avere riferimento ai dati. Ci sono alcuni altri momenti chiave:Creazione del modello di visualizzazione per ogni UITableViewCell

  • ogni cella deve avere una sua propria istanza di vista-modella non condiviso una
  • cellForRowAtindexPath non deve essere posto in un modello vista perché non dovrebbe contenere alcuna interfaccia utente riferimenti
  • vista/vista-il modello di ViewController non dovrebbe interagire con vista-il modello di cellulare

Qual è il modo giusto per "inserire" fonte di dati per le cellule in relazione MVVM s'? Grazie.

+0

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

+0

@ Paulw11 Sto usando il pattern delegato invece di React, è più codice ma è più chiaro e il debug non è un dolore – azimov

risposta

-1

A meno che non si abbia un problema specifico risolto con Model-View-ViewModel, tentare di adottarlo solo per le "migliori pratiche" finirà per introdurre un sacco di complessità non necessaria.

La fonte dei dati è ciò che è responsabile per il popolamento della tabella. Nient'altro che la tua origine dati necessita di un riferimento a contacts in quanto aggiornerà la tua tabella con questi dati.

View Models entra in gioco solo quando è necessario eseguire interazioni e aggiornamenti complessi dell'interfaccia utente. La VM è responsabile per incapsulare lo stato della vostra vista, le cose come ...

  1. Valori di textfields
  2. Quali/vengono selezionate le caselle di controllo pulsanti di opzione
  3. Colori di elementi
  4. Animazione logiche
  5. Dipendenze tra gli elementi dell'interfaccia utente

Quando vengono apportate modifiche alla vista, il tuo View Model è responsabile della creazione aggiornamenti al tuo Model (se necessario) al fine di riflettere le modifiche apportate a tale Model attraverso l'interfaccia utente.

Con tutto ciò che ha detto, visualizzare i modelli non hanno senso in IOS questo perché IOS fa uso di View Controllers nella metodologia di progettazione denominato MVC (Model-View-Controller)

+0

Innanzitutto, sto usando MVVM non perché è di fantasia ma perché rende possibile scrivere test più facilmente, si separa logica di presentazione e logica di business che MVC non possiede (a causa del design del viewcontroller). Secondo, MVVM va bene su iOS, è solo una versione aggiornata di MVC. In terzo luogo, poiché ViewController e View sono così strettamente accoppiati, questa uguaglianza è quasi vera al 100%: ViewController = View, la versione di MVC di Apple è più simile a questa: View/ViewController -> Model. Quindi View/ViewController può dialogare con Model e Model invia notifiche a View/ViewController solo quando è necessario eseguire gli aggiornamenti. – azimov

+0

Affermare che MVVM è una "versione aggiornata di MVC" non lo rende vero, perché non lo è. Sono approcci completamente diversi allo stesso problema (viste e il loro stato). Nelle viste iOS non è necessaria una VM perché hanno già il proprio stato, che viene aggiornato dal ViewController. – Literphor

52

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.

+0

bella risposta, grazie! – Ronaldoh1

+0

@jorge: puoi condividere alcuni esempi di codice – Invincible

+0

@jorge Le versioni secondarie della cella devono essere associate al modello di visualizzazione delle celle con una sorta di meccanismo di osservazione? O imposti il ​​modello di vista nella cella per riga e chiama 'configure()' subito dopo? – ocwang