Zacznę trochę teorii . MVVM jest specjalizacją modelu Presentation Model (lub modelu aplikacji) dla Silverlight i WPF firmy Microsoft. Główne założenia tego wzoru architektonicznego interfejsu użytkownika to:
- Część widoku jest jedyną, która zależy od interfejsu GUI. Oznacza to, że w przypadku iOS kontroler widoku jest częścią widoku.
- Widok może rozmawiać tylko z modelem widoku. Nigdy do modelu.
- Model widoku zawiera stan widoku.Ten stan jest oferowany do widoku za pomocą właściwości modelu widoku. Te właściwości zawierają nie tylko wartość etykiet, ale także inne informacje związane z wyświetlaniem, takie jak włączenie przycisku zapisu lub kolor widoku oceny. Ale informacja o stanie musi być niezależna od struktury UI. Tak więc w przypadku iOS właściwość koloru powinna być na przykład wylicznikiem zamiast UIColor.
- Model widoku zapewnia również metody, które zajmą się działaniami interfejsu użytkownika. Te działania będą rozmawiać z modelem, ale nigdy nie zmieniają stanu widoku, który jest bezpośrednio związany z danymi. Zamiast tego rozmawia z modelem i prosi o wymagane zmiany.
- Model powinien być autonomiczny , tj. Powinieneś być w stanie używać tego samego kodu dla modelu dla aplikacji wiersza poleceń i interfejsu interfejsu użytkownika. Zajmie się całą logiką biznesową.
- Model nie wie o modelu widoku. Zatem zmiany w modelu widoku są propagowane za pomocą mechanizmu obserwacyjnego. W przypadku iOS i modelu z prostymi podklasami NSObject, a nawet Core Data, do tego można użyć KVO (także dla Swift).
- Gdy model widoku wie o zmianach w modelu, powinien zaktualizować stan, w którym się znajduje (jeśli używasz typów wartości, powinien utworzyć zaktualizowany i zastąpić go).
- Model widoku nie wie o widoku. W pierwotnej koncepcji wykorzystuje powiązanie danych, które nie jest dostępne dla systemu iOS. Zatem zmiany w modelu widoku są propagowane za pomocą mechanizmu obserwacyjnego. Możesz także użyć KVO tutaj, lub jak wspomniałeś w pytaniu, zrobi to prosty wzór delegacji, nawet lepszy w połączeniu z obserwatorami Swift. Niektórzy ludzie wolą reaktywne frameworki, takie jak RxSwift, ReactiveCocoa, a nawet Swift Bond.
Korzyści są jak wspomniano:
- Lepsze oddzielenie obawy.
- UI niezależność: łatwiejsza migracja do innych UI.
- Lepsza testowalność ze względu na rozdzielenie obaw i rozłączenie kodu.
Wracając do pytania, implementacja protokołu UITableViewDataSource
należy do części widoku architektury z powodu zależności od struktury interfejsu użytkownika. Zauważ, że aby użyć tego protokołu w twoim kodzie, ten plik musi zaimportować UIKit. Również metody, takie jak tableView(:cellForRowAt:)
, które zwracają widok, są silnie uzależnione od UIKit.
Następnie, Twój zestaw Contacts
, który jest rzeczywiście Twój model, nie może być obsługiwany lub zapytał przez widok (źródło danych lub w inny sposób). Zamiast tego przekazujesz model widoku do kontrolera widoku tabeli, który w najprostszym przypadku ma dwie właściwości (zalecam, aby były przechowywane, a nie obliczone). Jednym z nich jest liczba sekcji, a druga jest liczbą wierszy na odcinku:
var numberOfSections: Int = 0
var rowsPerSection: [Int] = []
Widok modelu jest inicjowany w odniesieniu do modelu i w ostatnim etapie w inicjalizacja ustawia wartość tych dwóch właściwości.
Źródłem danych w kontrolerze widoku wykorzystuje dane modelu Widok:
override func numberOfSections(in tableView: UITableView) -> Int {
return viewModel.numberOfSections
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return viewModel.rowsPerSection[section]
}
Wreszcie można mieć inny widok modelu struct dla każdej z komórek:
struct ContactCellViewModel {
let name: String
init(contact: Contact) {
name = contact.name ?? ""
}
}
A UITableViewCell
podklasa będzie wiedział jak użyć tego struct:
class ContactTableViewCell: UITableViewCell {
var viewModel: ContactCellViewModel!
func configure() {
textLabel!.text = viewModel.name
}
}
W celu uzyskania odpowiedniego modelu widoku dla każdej z komórek, model stołowy view zapewni metoda, która je wytwarza, a które mogą być wykorzystane do zapełniania tablicy widzenia modeli:
func viewModelForCell(at index: Int) -> ContactCellViewModel {
return ContactCellViewModel(contact: contacts[index])
}
Jak widać modele widoku tutaj są jedynymi osobami rozmawiającymi z modelem (twoja tablica Contacts
), a widoki tylko rozmawiają z modelami widoku.
Mam nadzieję, że to pomoże.
O ile nie korzystasz z dodatkowej platformy, takiej jak React, MVVM nie działa tak naprawdę w systemie iOS, ponieważ elementy sterujące iOS nie mają powiązania danych. – Paulw11
@ Paulw11 Używam schematu delegatów zamiast React, to jest więcej kodu, ale jest bardziej przejrzysty i debugowanie nie jest bólem – azimov