2016-06-25 45 views
16

Utknąłem na decyzji dotyczącej projektu przy tworzeniu modeli widoku dla komórek widoku tabeli. Dane dla każdej komórki są dostarczane przez klasę źródła danych (ma tablicę Contacts). W modelu MVVM tylko model widoku może komunikować się z modelem, ale nie ma sensu umieszczać źródła danych w widoku-modelu, ponieważ umożliwiłoby to dostęp do danych dla wszystkich komórek, także niewłaściwe jest umieszczanie źródła danych w widoku kontrolera, ponieważ nie może mieć odniesienia do danych. Istnieje kilka innych kluczowych momentów:Tworzenie modelu widoku dla każdego obiektu UITableViewCell

  • Każda komórka musi mieć swój własny przykład widzenia model nie podzielił jeden
  • cellForRowAtindexPath nie musi być umieszczony w widoku model bo nie powinien zawierać żadnych UI referencje
  • View/ViewController za widok model nie powinien wchodzić w interakcje z komórki widoku model

Jaka jest właściwa droga do „insert” źródło danych dla komórek w stosunku MVVM „s? Dzięki.

+0

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

+0

@ Paulw11 Używam schematu delegatów zamiast React, to jest więcej kodu, ale jest bardziej przejrzysty i debugowanie nie jest bólem – azimov

Odpowiedz

-1

Jeśli nie masz konkretnego problemu, który został rozwiązany za pomocą Model-View-ViewModel, próba przyjęcia go tylko dla "najlepszych praktyk" doprowadzi do wprowadzenia niepotrzebnej złożoności.

Twoje źródło danych odpowiada za zapełnienie Twojego stołu. Nic poza twoim źródłem danych nie wymaga odniesienia do contacts, ponieważ zaktualizuje twoją tabelę o te dane.

View Models wchodzi w grę tylko wtedy, gdy potrzebne są złożone interakcje i aktualizacje UI. VM jest odpowiedzialny za enkapsulacji stan swojego widzenia rzeczy jak ...

  1. wartości pól tekstowych
  2. Które pola wyboru/przyciski radiowe są wybrane
  3. Kolory elementów
  4. Animacja logicznych
  5. Zależności między elementami interfejsu użytkownika

Po wprowadzeniu zmian w widoku, Twój View Model jest odpowiedzialny za wykonanie aktualizacje do Model (jeśli to konieczne) w celu odzwierciedlenia zmian, które zostały wprowadzone do tego Model za pośrednictwem interfejsu użytkownika.

Ze wszystkim, co powiedział, widok modele nie mają sensu w iOS to dlatego IOS korzysta z View Controllers w metodologii projektowania o nazwie MVC (Model-View-Controller)

+0

Po pierwsze, używam MVVM nie dlatego, że jest fantazyjny, ale dlatego, że umożliwia łatwiejsze pisanie testów, oddziela logika prezentacji i logika biznesowa, których MVC nie ma (z powodu projektu kontrolera viewcontroller). Po drugie, MVVM działa dobrze na iOS, to tylko ulepszona wersja MVC. Po trzecie, ponieważ ViewController i View są tak ściśle powiązane, ta równość jest prawie w 100% prawdziwa: ViewController = View, wersja MVC firmy Apple wygląda bardziej tak: View/ViewController -> Model. Zatem View/ViewController może komunikować się z Modelem, a Model tylko powiadamia View/ViewController, kiedy muszą być wykonane aktualizacje. – azimov

+0

Stwierdzenie, że MVVM jest "ulepszoną wersją MVC", nie czyni tego prawdziwym, ponieważ tak nie jest. Są to całkowicie różne podejścia do tego samego problemu (Widoki i ich stan). W widokach systemu iOS maszyna wirtualna nie jest potrzebna, ponieważ mają już własny stan, który jest aktualizowany przez ViewController. – Literphor

52

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.

+0

miła odpowiedź dzięki! – Ronaldoh1

+0

@jorge: Czy możesz udostępnić przykładowy kod? – Invincible

+0

@jorge Czy podkomórki komórki mają być powiązane z modelem widoku komórki z jakimś mechanizmem obserwacyjnym? Lub po prostu ustawić model widoku w komórce dla wiersza i wywołanie 'configure()' zaraz po? – ocwang