Je suis coincé dans une décision de conception avec la création de modèles de vue pour les cellules de la vue tableau. Les données de chaque cellule sont fournies par une classe de source de données (a un tableau de Contacts
). Dans MVVM
seul view-model peut parler au modèle, mais cela n'a pas de sens de mettre la source de données dans view-model car cela rendrait possible l'accès aux données pour toutes les cellules, il est également faux de mettre la source de données dans le contrôleur de vue car il ne doit pas avoir de référence aux données. Il y a d'autres moments clés:
cellForRowAtindexPath
ne doit pas être placé dans un modèle de vue car il ne doit contenir aucune référence d'interface utilisateurQuelle est la bonne façon "d'insérer" la source de données pour les cellules dans la relation de MVVM
? Merci.
Permettez-moi de commencer par une théorie. MVVM est une spécialisation du modèle de présentation (ou modèle d'application) pour Silverlight et WPF de Microsoft. Les principales idées derrière ce modèle architectural de l'interface utilisateur sont:
Les avantages sont comme vous l'avez mentionné:
Pour en revenir à votre question, l'implémentation du protocole UITableViewDataSource
appartient à la partie vue de l'architecture, en raison de ses dépendances sur le framework UI. Notez que pour utiliser ce protocole dans votre code, ce fichier doit importer UIKit. De plus, des méthodes comme tableView(:cellForRowAt:)
qui renvoie une vue dépendent fortement d'UIKit.
Ensuite, votre tableau de Contacts
, qui est en effet votre modèle, ne peut pas être exploité ou interrogé via la vue (source de données ou autre). Au lieu de cela, vous passez un modèle de vue à votre contrôleur de vue de table, qui, dans le cas le plus simple, possède deux propriétés (je recommande qu'elles soient stockées, et non des propriétés calculées). L'un d'eux est le nombre de sections et l'autre est le nombre de lignes par section:
var numberOfSections: Int = 0
var rowsPerSection: [Int] = []
Le modèle de vue est initialisé avec une référence au modèle et comme dernière étape de l'initialisation, il définit la valeur de ces deux propriétés.
La source de données dans le contrôleur de vue utilise les données du modèle de vue:
override func numberOfSections(in tableView: UITableView) -> Int {
return viewModel.numberOfSections
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return viewModel.rowsPerSection[section]
}
Enfin, vous pouvez avoir une structure de modèle de vue différente pour chacune des cellules:
struct ContactCellViewModel {
let name: String
init(contact: Contact) {
name = contact.name ?? ""
}
}
Et la sous-classe UITableViewCell
saura comment utiliser cette structure:
class ContactTableViewCell: UITableViewCell {
var viewModel: ContactCellViewModel!
func configure() {
textLabel!.text = viewModel.name
}
}
Afin d'avoir le modèle de vue correspondant pour chacune des cellules, le modèle de vue de vue de table fournira une méthode qui les génère et qui peut être utilisée pour remplir le tableau de modèles de vue:
func viewModelForCell(at index: Int) -> ContactCellViewModel {
return ContactCellViewModel(contact: contacts[index])
}
Comme vous pouvez le voir, les modèles de vue sont les seuls à parler au modèle (votre tableau Contacts
), et les vues ne parlent qu'aux modèles de vue.
J'espère que cela t'aides.