Pertanyaan Membuat tampilan-model untuk setiap UITableViewCell


Saya terjebak pada keputusan desain dengan membuat tampilan-model untuk sel tampilan tabel. Data untuk setiap sel disediakan oleh kelas sumber data (memiliki larik Contacts). Di MVVM hanya view-model yang dapat berbicara dengan model, tetapi tidak masuk akal untuk menempatkan sumber data dalam view-model karena itu akan memungkinkan untuk mengakses data untuk semua sel, juga salah menempatkan sumber data dalam pengontrol tampilan karena tidak boleh memiliki referensi ke data. Ada beberapa momen penting lainnya:

  • Setiap sel harus memiliki contoh tampilan-model sendiri bukan yang dibagikan
  • cellForRowAtindexPath tidak boleh ditempatkan dalam model tampilan karena seharusnya tidak berisi referensi UI apa pun
  • View / ViewController's view-model seharusnya tidak berinteraksi dengan model tampilan sel

Apa cara yang tepat untuk "menyisipkan" sumber data untuk sel di MVVMhubungan? Terima kasih.


21
2018-06-25 22:45


asal


Jawaban:


Mari saya mulai dengan beberapa teori. MVVM adalah spesialisasi dari Model Presentasi (atau Model Aplikasi) untuk Silverlight dan WPF Microsoft. Ide utama di balik pola arsitektur UI ini adalah:

  • Bagian tampilan adalah satu-satunya yang bergantung pada kerangka kerja GUI. Ini berarti bahwa untuk iOS, pengontrol tampilan adalah bagian dari tampilan.
  • Tampilan hanya dapat berbicara dengan model tampilan. Tak pernah ke model.
  • Model tampilan memegang status tampilan. Status ini ditawarkan ke tampilan melalui properti model tampilan. Properti ini tidak hanya berisi nilai label, tetapi juga informasi terkait tampilan lainnya seperti jika tombol simpan diaktifkan atau warna untuk tampilan peringkat. Tetapi informasi dari negara harus kerangka UI independen. Jadi dalam kasus iOS, properti untuk warna harus berupa enum, misalnya, bukan UIColor.
  • Model tampilan juga menyediakan metode yang akan menangani tindakan UI. Tindakan ini akan berbicara dengan model, tetapi mereka tidak pernah mengubah keadaan tampilan yang terkait langsung dengan data. Sebaliknya, ia berbicara kepada model dan meminta perubahan yang diperlukan.
  • Modelnya seharusnya otonom, Anda harus dapat menggunakan kode yang sama untuk model untuk aplikasi baris perintah dan antarmuka UI. Ini akan mengurus semua logika bisnis.
  • Model tidak tahu tentang model tampilan. Jadi perubahan pada model tampilan disebarkan melalui mekanisme observasi. Untuk iOS dan model dengan subclass NSObject biasa atau bahkan Data Inti, KVO dapat digunakan untuk itu (juga untuk Swift).
  • Setelah model tampilan tahu tentang perubahan dalam model, itu harus memperbarui keadaan yang dimilikinya (jika Anda menggunakan jenis nilai, maka harus membuat yang diperbarui dan menggantinya).
  • Model tampilan tidak tahu tentang tampilan. Dalam konsep aslinya menggunakan data yang mengikat, yang tidak tersedia untuk iOS. Jadi perubahan dalam model tampilan disebarkan melalui mekanisme observasi. Anda juga dapat menggunakan KVO di sini, atau seperti yang Anda sebutkan dalam pertanyaan, pola delegasi sederhana, bahkan lebih baik jika dikombinasikan dengan pengamat properti Swift, akan melakukannya. Beberapa orang lebih memilih kerangka kerja yang reaktif, seperti RxSwift, ReactiveCocoa, atau bahkan Swift Bond.

Manfaatnya seperti yang Anda sebutkan:

  • Pemisahan kekhawatiran yang lebih baik.
  • Kemandirian UI: migrasi lebih mudah ke UI lain.
  • Testability yang lebih baik karena pemisahan kekhawatiran dan sifat kode yang dipisahkan.

Jadi kembali ke pertanyaan Anda, implementasi dari UITableViewDataSource protokol milik bagian tampilan arsitektur, karena dependensinya pada kerangka UI. Perhatikan bahwa untuk menggunakan protokol itu dalam kode Anda, file itu harus mengimpor UIKit. Juga metode seperti tableView(:cellForRowAt:) yang mengembalikan tampilan sangat bergantung pada UIKit.

Kemudian, array Anda Contacts, itu memang model Anda, tidak dapat dioperasikan atau dipertanyakan melalui tampilan (sumber data atau sebaliknya). Sebagai gantinya, Anda menyampaikan model tampilan ke pengontrol tampilan tabel Anda, bahwa, dalam kasus yang paling sederhana, memiliki dua properti (saya menyarankan agar mereka disimpan, bukan properti yang dihitung). Salah satunya adalah jumlah bagian dan yang lainnya adalah jumlah baris per bagian:

var numberOfSections: Int = 0
var rowsPerSection: [Int] = []

Model tampilan diinisialisasi dengan referensi ke model dan sebagai langkah terakhir dalam inisialisasi itu menetapkan nilai dari dua properti tersebut.

Sumber data dalam pengontrol tampilan menggunakan data model tampilan:

override func numberOfSections(in tableView: UITableView) -> Int {
    return viewModel.numberOfSections
}

override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    return viewModel.rowsPerSection[section]
}

Akhirnya Anda dapat memiliki struktur tampilan model yang berbeda untuk masing-masing sel:

struct ContactCellViewModel {
    let name: String

    init(contact: Contact) {
        name = contact.name ?? ""
    }
}

Dan itu UITableViewCell subclass akan tahu cara menggunakan struct itu:

class ContactTableViewCell: UITableViewCell {
    
    var viewModel: ContactCellViewModel!

    func configure() {
        textLabel!.text = viewModel.name
    }
}

Untuk memiliki model tampilan yang sesuai untuk masing-masing sel, model tampilan tampilan tabel akan menyediakan metode yang menghasilkannya, dan yang dapat digunakan untuk mengisi susunan model tampilan:

func viewModelForCell(at index: Int) -> ContactCellViewModel {
    return ContactCellViewModel(contact: contacts[index])
}

Seperti yang Anda lihat model tampilan di sini adalah satu-satunya yang berbicara dengan model (Anda Contacts larik), dan tampilan hanya berbicara dengan model tampilan.

Semoga ini membantu.


63
2017-08-15 15:06



Kecuali Anda memiliki masalah khusus yang terpecahkan Model-View-ViewModel kemudian mencoba untuk mengadopsinya hanya untuk 'praktik terbaik' akan berakhir dengan memperkenalkan banyak kerumitan yang tidak perlu.

Sumber data Anda adalah apa yang bertanggung jawab untuk mengisi tabel Anda. Tidak ada yang lain selain sumber data Anda perlu referensi contacts karena akan memperbarui tabel Anda dengan data ini.

View Models hanya ikut bermain ketika Anda perlu melakukan interaksi dan pembaruan UI yang kompleks. VM bertanggung jawab untuk merangkum keadaan pandangan Anda, hal-hal seperti ...

  1. Nilai dari textfields
  2. Kotak centang / tombol radio mana yang dipilih
  3. Warna elemen
  4. Logika animasi
  5. Dependensi antar elemen UI

Ketika perubahan dilakukan pada Tampilan Anda, Anda View Model Bertanggung jawab untuk membuat pembaruan untuk Anda Model (bila perlu) untuk mencerminkan perubahan yang telah dibuat untuk itu Model melalui UI.

Dengan semua yang dikatakan, Lihat Model tidak masuk akal di iOS itu karena iOS memanfaatkan View Controllers dalam metodologi desain yang disebut MVC (Model-View-Controller)


-3
2018-06-25 23:43