{"id":314366,"date":"2020-12-05T15:01:18","date_gmt":"2020-12-05T15:01:18","guid":{"rendered":"http:\/\/savepearlharbor.com\/?p=314366"},"modified":"-0001-11-30T00:00:00","modified_gmt":"-0001-11-29T21:00:00","slug":"","status":"publish","type":"post","link":"https:\/\/savepearlharbor.com\/?p=314366","title":{"rendered":"\u0410\u0434\u0430\u043f\u0442\u0438\u0440\u0443\u0435\u043c UITableView \u043f\u043e\u0434 MVVM"},"content":{"rendered":"\n<div class=\"post__text post__text_v2\" id=\"post-content-body\">\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/034\/926\/b37\/034926b3737cca767e4b0fd3cbbba96f.png\" width=\"1420\" height=\"1000\"><figcaption><\/figcaption><\/figure>\n<h2>\u0412\u0432\u0435\u0434\u0435\u043d\u0438\u0435<\/h2>\n<p><em>UITableView<\/em> \u043e\u0434\u0438\u043d \u0438\u0437 \u0441\u0430\u043c\u044b\u0445 \u0447\u0430\u0441\u0442\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c\u044b\u0445 \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u043e\u0432 <em>UIKit<\/em>. \u0422\u0430\u0431\u043b\u0438\u0447\u043d\u043e\u0435 \u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u0438\u0435 \u0437\u0430\u0440\u0435\u043a\u043e\u043c\u0435\u043d\u0434\u043e\u0432\u0430\u043b\u043e \u0441\u0435\u0431\u044f \u043a\u0430\u043a \u043e\u0434\u043d\u043e \u0438\u0437 \u0441\u0430\u043c\u044b\u0445 \u0443\u0434\u043e\u0431\u043d\u044b\u0445 \u0432\u0437\u0430\u0438\u043c\u043e\u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0439 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u0441 \u043a\u043e\u043d\u0442\u0435\u043d\u0442\u043e\u043c \u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u043d\u044b\u043c \u043d\u0430 \u044d\u043a\u0440\u0430\u043d\u0435 \u0441\u043c\u0430\u0440\u0442\u0444\u043e\u043d\u0430.<\/p>\n<p>\u041d\u0430 \u0441\u0435\u0433\u043e\u0434\u043d\u044f\u0448\u043d\u0438\u0439 \u0434\u0435\u043d\u044c, \u043a\u0430\u0436\u0434\u043e\u043c\u0443 iOS \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a\u0443 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e \u0432 \u0441\u043e\u0432\u0435\u0440\u0448\u0435\u043d\u0441\u0442\u0432\u0435 \u0432\u043b\u0430\u0434\u0435\u0442\u044c <em>UITableView, <\/em>\u0437\u043d\u0430\u0442\u044c \u0442\u043e\u043d\u043a\u043e\u0441\u0442\u0438 \u0438 \u043f\u043e\u043d\u0438\u043c\u0430\u0442\u044c \u043a\u0430\u043a \u0435\u0433\u043e \u0430\u0434\u0430\u043f\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u043f\u043e\u0434 \u0440\u0430\u0437\u043d\u044b\u0435 \u0430\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440\u044b, \u0447\u0442\u043e\u0431\u044b \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435 \u043d\u0435 \u0432\u044b\u0437\u044b\u0432\u0430\u043b\u043e \u043b\u0438\u0448\u043d\u0438\u0445 \u043f\u0440\u043e\u0431\u043b\u0435\u043c \u0438 \u0442\u0440\u0443\u0434\u043d\u043e\u0441\u0442\u0435\u0439.<\/p>\n<p>\u0412 \u044d\u0442\u043e\u0439 \u0441\u0442\u0430\u0442\u044c\u0435 \u043c\u044b \u043f\u043e\u0433\u043e\u0432\u043e\u0440\u0438\u043c \u043e \u0442\u043e\u043c, \u043a\u0430\u043a \u0430\u0434\u0430\u043f\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c <em>UITableView<\/em> \u043f\u043e\u0434 \u0430\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440\u0443 <em>Model-View-ViewModel (MVVM)<\/em>. \u041d\u0430\u0447\u043d\u0451\u043c.<\/p>\n<h2>\u0421\u043e\u0434\u0435\u0440\u0436\u0430\u043d\u0438\u0435<\/h2>\n<ol>\n<li>\n<p>\u0412\u0432\u0435\u0434\u0435\u043d\u0438\u0435<\/p>\n<\/li>\n<li>\n<p>\u041f\u0440\u0438\u043c\u0435\u0440<\/p>\n<\/li>\n<li>\n<p>\u0420\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f<\/p>\n<\/li>\n<li>\n<p>\u0418\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435<\/p>\n<\/li>\n<li>\n<p>\u0420\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442<\/p>\n<\/li>\n<li>\n<p>\u0412\u044b\u0432\u043e\u0434<\/p>\n<\/li>\n<\/ol>\n<h2>\u041f\u0440\u0438\u043c\u0435\u0440<\/h2>\n<p>\u0412 \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0435 \u043f\u0440\u0438\u043c\u0435\u0440\u0430 \u044f \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u043b \u044f\u0447\u0435\u0439\u043a\u0443 \u0441 \u043a\u043d\u043e\u043f\u043a\u043e\u0439, \u043a\u0430\u0440\u0442\u0438\u043d\u043a\u043e\u0439 \u0438 \u0442\u0435\u043a\u0441\u0442\u043e\u043c.<\/p>\n<figure class=\"bordered full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/f05\/5b6\/06b\/f055b606bbf7d7f2ec2443eeb69025c3.png\" width=\"1242\" height=\"1724\"><figcaption><\/figcaption><\/figure>\n<h2>\u0420\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f<\/h2>\n<p>\u041f\u0435\u0440\u0432\u044b\u043c \u0434\u0435\u043b\u043e\u043c \u0441\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u043f\u043e\u0434\u043a\u043b\u0430\u0441\u0441 \u043e\u0442 <em>UITableView<\/em> \u0438 \u043d\u0430\u0437\u043e\u0432\u0435\u043c \u0435\u0433\u043e <em>AdaptedTableView<\/em>. <\/p>\n<pre><code class=\"swift\">class AdaptedTableView: UITableView {      }<\/code><\/pre>\n<p>\u041e\u043f\u0440\u0435\u0434\u0435\u043b\u0438\u043c \u043c\u0435\u0442\u043e\u0434 <em>setup(). <\/em>\u041e\u043d \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c \u0434\u043b\u044f \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u0438 \u0442\u0430\u0431\u043b\u0438\u0446\u044b. \u0412\u0440\u0435\u043c\u0435\u043d\u043d\u043e \u0437\u0430\u043f\u043e\u043b\u043d\u0438\u043c \u043e\u0431\u044f\u0437\u0430\u0442\u0435\u043b\u044c\u043d\u044b\u0435 \u0434\u043b\u044f \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438 \u043c\u0435\u0442\u043e\u0434\u044b <em>UITableViewDataSource<\/em>.<\/p>\n<pre><code>class AdaptedTableView: UITableView {          \/\/ MARK: - Public methods          func setup() {         self.dataSource = self     }      }  \/\/ MARK: - UITableViewDataSource  extension AdaptedTableView: UITableViewDataSource {          func numberOfSections(in tableView: UITableView) -&gt; Int {         .zero     }          func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -&gt; Int {         .zero     }          func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -&gt; UITableViewCell {         UITableViewCell()     }      }<\/code><\/pre>\n<p>\u0421\u043e\u0433\u043b\u0430\u0441\u043d\u043e \u043f\u0430\u0442\u0442\u0435\u0440\u043d\u0443 <em>MVVM<\/em>, <em>view<\/em> \u0432\u043b\u0430\u0434\u0435\u0435\u0442 <em>viewModel.<\/em> \u0421\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u0430\u0431\u0441\u0442\u0440\u0430\u043a\u0446\u0438\u044e \u0434\u043b\u044f \u0432\u0445\u043e\u0434\u043d\u044b\u0445 \u0434\u0430\u043d\u043d\u044b\u0445 \u0438 \u043d\u0430\u0437\u043e\u0432\u0435\u043c \u0435\u0451 <em>AdaptedViewModelInputProtocol<\/em>. <em>AdaptedSectionViewModelProtocol<\/em> \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c \u0434\u043b\u044f \u043e\u043f\u0438\u0441\u0430\u043d\u0438\u044f <em>viewModel<\/em> \u0441\u0435\u043a\u0446\u0438\u0438. <em>AdaptedCellViewModelProtocol<\/em> \u0441\u043b\u0443\u0436\u0438\u0442 \u043b\u0438\u0448\u044c \u0434\u043b\u044f \u043f\u043e\u043b\u0438\u043c\u043e\u0440\u0444\u0438\u0437\u043c\u0430 \u043f\u043e\u0434\u0442\u0438\u043f\u043e\u0432 \u043d\u0430\u0448\u0438\u0445 <em>viewModels <\/em>\u0434\u043b\u044f \u044f\u0447\u0435\u0435\u043a.<\/p>\n<pre><code class=\"swift\">protocol AdaptedCellViewModelProtocol { }  protocol AdaptedSectionViewModelProtocol {     var cells: [AdaptedCellViewModelProtocol] { get } }  protocol AdaptedViewModelInputProtocol {     var sections: [AdaptedSectionViewModelProtocol] { get } }<\/code><\/pre>\n<p>\u0414\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u043c <em>viewModel<\/em>. \u0422\u0435\u043f\u0435\u0440\u044c \u0443 \u043d\u0430\u0441 \u0435\u0441\u0442\u044c \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u044c \u043a\u043e\u0440\u0440\u0435\u043a\u0442\u043d\u043e \u0437\u0430\u043f\u043e\u043b\u043d\u0438\u0442\u044c \u043c\u0435\u0442\u043e\u0434\u044b <em>UITableViewDataSource.<\/em><\/p>\n<pre><code class=\"swift\">class AdaptedTableView: UITableView {          \/\/ MARK: - Public properties          var viewModel: AdaptedViewModelInputProtocol?          \/\/ MARK: - Public methods          func setup() {         self.dataSource = self     }      }  \/\/ MARK: - UITableViewDataSource  extension AdaptedTableView: UITableViewDataSource {          func numberOfSections(in tableView: UITableView) -&gt; Int {         viewModel?.sections.count ?? .zero     }          func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -&gt; Int {         viewModel?.sections[section].cells.count ?? .zero     }          func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -&gt; UITableViewCell {         guard let cellViewModel = viewModel?.sections[indexPath.section].cells[indexPath.row] else {             return UITableViewCell()         }              \t\/\/ TO DO: - Register cell       \t\/\/ TO DO: - Create cell                  return UITableViewCell()     }      }<\/code><\/pre>\n<p>\u041d\u0430 \u0434\u0430\u043d\u043d\u043e\u043c \u044d\u0442\u0430\u043f\u0435 \u0441 <em>AdaptedTableView<\/em> \u043f\u043e\u0447\u0442\u0438 \u0432\u0441\u0435 \u0433\u043e\u0442\u043e\u0432, \u043e\u0434\u043d\u0430\u043a\u043e \u0435\u0441\u0442\u044c \u0435\u0449\u0435 \u043f\u0430\u0440\u0443 \u043d\u0435\u0440\u0435\u0448\u0435\u043d\u043d\u044b\u0445 \u0432\u043e\u043f\u0440\u043e\u0441\u043e\u0432. \u0420\u0435\u0433\u0438\u0441\u0442\u0440\u0430\u0446\u0438\u044f \u0438 \u043f\u0435\u0440\u0435\u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435 \u044f\u0447\u0435\u0435\u043a. \u0421\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u043f\u0440\u043e\u0442\u043e\u043a\u043e\u043b <em>AdaptedCellProtocol<\/em>, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0431\u0443\u0434\u0443\u0442 \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u044b\u0432\u0430\u0442\u044c \u0432\u0441\u0435 \u043d\u0430\u0448\u0438 \u043f\u043e\u0434\u043a\u043b\u0430\u0441\u0441\u044b <em>UITableViewCell<\/em>, \u0434\u043e\u0431\u0430\u0432\u0438\u043c \u043c\u0435\u0442\u043e\u0434 <em>register(_ tableView:) <\/em>\u0438 <em>reuse(_ tableView:, for indexPath:)<\/em>.<\/p>\n<pre><code class=\"swift\">protocol AdaptedCellProtocol {     static var identifier: String { get }     static var nib: UINib { get }     static func register(_ tableView: UITableView)     static func reuse(_ tableView: UITableView, for indexPath: IndexPath) -&gt; Self }  extension AdaptedCellProtocol {          static var identifier: String {         String(describing: self)     }          static var nib: UINib {         UINib(nibName: identifier, bundle: nil)     }          static func register(_ tableView: UITableView) {         tableView.register(nib, forCellReuseIdentifier: identifier)     }          static func reuse(_ tableView: UITableView, for indexPath: IndexPath) -&gt; Self {         tableView.dequeueReusableCell(withIdentifier: identifier, for: indexPath) as! Self     }      } <\/code><\/pre>\n<p>\u0414\u043b\u044f \u043f\u043e\u0440\u043e\u0436\u0434\u0435\u043d\u0438\u044f \u044f\u0447\u0435\u0435\u043a \u0441\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u043f\u0440\u043e\u0442\u043e\u043a\u043e\u043b \u0444\u0430\u0431\u0440\u0438\u0447\u043d\u043e\u0433\u043e \u043c\u0435\u0442\u043e\u0434\u0430 <em>AdaptedCellFactoryProtocol<\/em>.<\/p>\n<pre><code class=\"swift\">protocol AdaptedCellFactoryProtocol {     var cellTypes: [AdaptedCellProtocol.Type] { get }     func generateCell(viewModel: AdaptedCellViewModelProtocol, tableView: UITableView, for indexPath: IndexPath) -&gt; UITableViewCell }<\/code><\/pre>\n<p>\u0414\u043e\u0431\u0430\u0432\u0438\u043c \u043f\u043e\u043b\u0435 <em>cellFactory<\/em> \u0438 \u0432 <em>didSet<\/em> \u043f\u043e\u043c\u0435\u0441\u0442\u0438\u043c \u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0430\u0446\u0438\u044e \u0432\u0441\u0435\u0445 \u044f\u0447\u0435\u0435\u043a. <\/p>\n<pre><code class=\"swift\">class AdaptedTableView: UITableView {          \/\/ MARK: - Public properties          var viewModel: AdaptedViewModelInputProtocol?     var cellFactory: AdaptedCellFactoryProtocol? {         didSet {             cellFactory?.cellTypes.forEach({ $0.register(self)})         }     }          ...      }<\/code><\/pre>\n<p>\u0418\u0441\u043f\u0440\u0430\u0432\u0438\u043c \u043c\u0435\u0442\u043e\u0434 \u0434\u0435\u043b\u0435\u0433\u0430\u0442\u0430.<\/p>\n<pre><code class=\"swift\">extension AdaptedTableView: UITableViewDataSource {          ...          func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -&gt; UITableViewCell {         guard             let cellFactory = cellFactory,             let cellViewModel = viewModel?.sections[indexPath.section].cells[indexPath.row]         else {             return UITableViewCell()         }                  return cellFactory.generateCell(viewModel: cellViewModel, tableView: tableView, for: indexPath)     }      }<\/code><\/pre>\n<h2>\u0418\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435<\/h2>\n<p>\u0421 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u044b \u0430\u0431\u0441\u0442\u0440\u0430\u043a\u0446\u0438\u044f\u043c\u0438 \u043d\u0430 \u044d\u0442\u043e\u043c \u0432\u0441\u0435, \u043f\u043e\u0440\u0430 \u043f\u0435\u0440\u0435\u0439\u0442\u0438 \u043a \u043a\u043e\u043d\u043a\u0440\u0435\u0442\u043d\u044b\u043c \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f\u043c.<\/p>\n<p><strong>1. \u042f\u0447\u0435\u0439\u043a\u0430<\/strong><\/p>\n<p>\u0412 \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0435 \u043f\u0440\u0438\u043c\u0435\u0440\u0430 \u044f \u0441\u043e\u0437\u0434\u0430\u043c \u044f\u0447\u0435\u0439\u043a\u0443 \u0441 \u043b\u0435\u0439\u0431\u043b\u043e\u043c \u043f\u043e \u0446\u0435\u043d\u0442\u0440\u0443 \u0438 <em>viewModel<\/em> \u043a \u043d\u0435\u0439. \u0420\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f \u044f\u0447\u0435\u0439\u043a\u0438 \u0441 <a href=\"https:\/\/github.com\/3ai41k\/MVVM-UITableView\/tree\/master\/MVVM-UITableView\/Modules\/Main\/Cells\/ButtonCell\" rel=\"noopener noreferrer nofollow\">\u043a\u043d\u043e\u043f\u043a\u043e\u0439<\/a> \u0438 <a href=\"https:\/\/github.com\/3ai41k\/MVVM-UITableView\/tree\/master\/MVVM-UITableView\/Modules\/Main\/Cells\/ImageCell\" rel=\"noopener noreferrer nofollow\">\u043a\u0430\u0440\u0442\u0438\u043d\u043a\u043e\u0439<\/a>.<\/p>\n<pre><code class=\"swift\">protocol TextCellViewModelInputProtocol {     var text: String { get } }  typealias TextCellViewModelType = AdaptedCellViewModelProtocol &amp; TextCellViewModelInputProtocol  class TextCellViewModel: TextCellViewModelType {          var text: String          init(text: String) {         self.text = text     }      }  final class TextTableViewCell: UITableViewCell, AdaptedCellProtocol {          \/\/ MARK: - IBOutlets          @IBOutlet private weak var label: UILabel!          \/\/ MARK: - Public properties          var viewModel: TextCellViewModelInputProtocol? {         didSet {             bindViewModel()         }     }          \/\/ MARK: - Private methods          private func bindViewModel() {         label.text = viewModel?.text     }      }<\/code><\/pre>\n<p><strong>2.  C\u0435\u043a\u0446\u0438\u044f<\/strong><\/p>\n<pre><code class=\"swift\">class AdaptedSectionViewModel: AdaptedSectionViewModelProtocol {          \/\/ MARK: - Public properties        var cells: [AdaptedCellViewModelProtocol]          \/\/ MARK: - Init          init(cells: [AdaptedCellViewModelProtocol]) {         self.cells = cells     }      }<\/code><\/pre>\n<p><strong>3. \u0424\u0430\u0431\u0440\u0438\u043a\u0430<\/strong><\/p>\n<pre><code class=\"swift\">struct MainCellFactory: AdaptedSectionFactoryProtocol {          var cellTypes: [AdaptedCellProtocol.Type] = [         TextTableViewCell.self     ]          func generateCell(viewModel: AdaptedCellViewModelProtocol, tableView: UITableView, for indexPath: IndexPath) -&gt; UITableViewCell {         switch viewModel {         case let viewModel as TextCellViewModelType:             let view = TextTableViewCell.reuse(tableView, for: indexPath)             view.viewModel = viewModel             return view         default:             return UITableViewCell()         }     }      }<\/code><\/pre>\n<p>\u041d\u0443 \u0438 \u043d\u0430\u043f\u043e\u0441\u043b\u0435\u0434\u043e\u043a \u043d\u0430\u043c \u043f\u043e\u043d\u0430\u0434\u043e\u0431\u0438\u0442\u0441\u044f <em>viewModel<\/em> \u0441\u0430\u043c\u043e\u0433\u043e \u043c\u043e\u0434\u0443\u043b\u044f.<\/p>\n<pre><code class=\"swift\">final class MainViewModel: AdaptedSectionViewModelType {          \/\/ MARK: - Public properties          var sections: [AdaptedSectionViewModelProtocol]          \/\/ MARK: - Init          init() {         self.sections = []                  self.setupMainSection()     }          \/\/ MARK: - Private methods          private func setupMainSection() {         let section = AdaptedSectionViewModel(cells: [             TextCellViewModel(text: \"Hello!\"),             TextCellViewModel(text: \"It's UITableView with using MVVM\")         ])         sections.append(section)     }      }<\/code><\/pre>\n<p>\u0412\u0441\u0435 \u0433\u043e\u0442\u043e\u0432\u043e, \u043f\u043e\u0440\u0430 \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c <em>UITableView<\/em> \u043d\u0430 <em>ViewController<\/em>, \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0432 \u0432 \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0435 <em>custom class<\/em> \u043d\u0430\u0448 <em>AdaptedTableView<\/em>.<\/p>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/9df\/ce8\/ade\/9dfce8ade8278053e3f9ea466a27e34e.png\" width=\"783\" height=\"808\"><figcaption><\/figcaption><\/figure>\n<p>\u0412 \u0440\u0435\u0430\u043b\u044c\u043d\u043e\u043c \u043f\u0440\u043e\u0435\u043a\u0442\u0435, <em>MVVM<\/em> \u043e\u0447\u0435\u043d\u044c \u0447\u0430\u0441\u0442\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044e\u0442 \u0441 \u043a\u0430\u043a\u0438\u043c-\u0442\u043e \u043f\u0430\u0442\u0442\u0435\u0440\u043d\u043e\u043c \u043d\u0430\u0432\u0438\u0433\u0430\u0446\u0438\u0438, \u044d\u0442\u043e \u043c\u043e\u0436\u0435\u0442 \u0431\u044b\u0442\u044c \u043a\u043e\u043e\u0440\u0434\u0438\u043d\u0430\u0442\u043e\u0440 \u0438\u043b\u0438 \u0440\u043e\u0443\u0442\u0435\u0440. \u0412 \u0437\u043e\u043d\u0443 \u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0435\u043d\u043d\u043e\u0441\u0442\u0438 \u0442\u0430\u043a\u0438\u0445 \u043e\u0431\u044a\u0435\u043a\u0442\u043e\u0432 \u0432\u0445\u043e\u0434\u0438\u0442 <em>DI (Dependency Injection) <\/em>\u0432\u043d\u0435\u0434\u0440\u0435\u043d\u0438\u0435 \u0432\u0441\u0435\u0445 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u044b\u0445 \u043c\u043e\u0434\u0443\u043b\u044e \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0435\u0439. \u0422\u0430\u043a \u043a\u0430\u043a \u044d\u0442\u043e \u0442\u0435\u0441\u0442\u043e\u0432\u044b\u0439 \u043f\u0440\u043e\u0435\u043a\u0442, \u044f \u0437\u0430\u0445\u0430\u0440\u0434\u043a\u043e\u0434\u0438\u043b <em>viewModel<\/em> \u0438 <em>cellFactory <\/em>\u043f\u0440\u044f\u043c\u043e \u0432\u043e<em> ViewController<\/em>.<\/p>\n<pre><code class=\"swift\">class ViewController: UIViewController {          \/\/ MARK: - IBOutlets          @IBOutlet weak var tableView: AdaptedTableView! {         didSet {             tableView.viewModel = MainViewModel()             tableView.cellFactory = MainCellFactory()                          tableView.setup()         }     }      }<\/code><\/pre>\n<h2>\u0420\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442<\/h2>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/1dc\/07b\/6da\/1dc07b6dafbd5691e1d3a23840ead4d3\" width=\"1242\" height=\"681\"><figcaption><\/figcaption><\/figure>\n<h2>\u0412\u044b\u0432\u043e\u0434<\/h2>\n<p>\u0412 \u0438\u0442\u043e\u0433\u0435 \u043c\u044b \u043f\u043e\u043b\u0443\u0447\u0438\u043b\u0438 \u0440\u0435\u0448\u0435\u043d\u0438\u0435, \u043a\u043e\u0442\u043e\u0440\u043e\u0435 \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u0443\u0434\u043e\u0431\u043d\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c <em>UITableView<\/em> \u0441 <em>MVVM<\/em>. \u0421\u0442\u0430\u043b\u043e \u043e\u0447\u0435\u043d\u044c \u043f\u0440\u043e\u0441\u0442\u043e \u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c \u0441 \u0441\u0435\u043a\u0446\u0438\u044f\u043c\u0438, \u043d\u0430\u0441\u0442\u0440\u0430\u0438\u0432\u0430\u0442\u044c \u044f\u0447\u0435\u0439\u043a\u0438, \u043f\u0438\u0441\u0430\u0442\u044c \u043c\u0435\u043d\u044c\u0448\u0435 \u0448\u0430\u0431\u043b\u043e\u043d\u043d\u043e\u0433\u043e \u043a\u043e\u0434\u0430. \u0412 \u0442\u043e \u0436\u0435 \u0432\u0440\u0435\u043c\u044f \u043e\u0441\u0442\u0430\u043b\u0430\u0441\u044c \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u044c \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 \u0442\u0430\u0431\u043b\u0438\u0446\u044b \u0438 \u0440\u0430\u0441\u0448\u0438\u0440\u0435\u043d\u0438\u044f \u0444\u0443\u043d\u043a\u0446\u0438\u043e\u043d\u0430\u043b\u0430 \u043f\u0440\u0438 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e\u0441\u0442\u0438.<\/p>\n<hr>\n<p>\u0412\u0435\u0441\u044c \u043a\u043e\u0434 \u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u043d\u044b\u0439 \u0432 \u044d\u0442\u043e\u0439 \u0441\u0442\u0430\u0442\u044c\u0435 \u043c\u043e\u0436\u043d\u043e \u0441\u043a\u0430\u0447\u0430\u0442\u044c \u043f\u043e <a href=\"https:\/\/github.com\/3ai41k\/MVVM-UITableView\" rel=\"noopener noreferrer nofollow\">\u044d\u0442\u043e\u0439<\/a> \u0441\u0441\u044b\u043b\u043a\u0435.<\/p>\n<\/div>\n<p> \u0441\u0441\u044b\u043b\u043a\u0430 \u043d\u0430 \u043e\u0440\u0438\u0433\u0438\u043d\u0430\u043b \u0441\u0442\u0430\u0442\u044c\u0438 <a href=\"https:\/\/habr.com\/ru\/post\/531446\/\"> https:\/\/habr.com\/ru\/post\/531446\/<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"\n<div class=\"post__text post__text_v2\" id=\"post-content-body\">\n<figure class=\"full-width\"><figcaption><\/figcaption><\/figure>\n<h2>\u0412\u0432\u0435\u0434\u0435\u043d\u0438\u0435<\/h2>\n<p><em>UITableView<\/em> \u043e\u0434\u0438\u043d \u0438\u0437 \u0441\u0430\u043c\u044b\u0445 \u0447\u0430\u0441\u0442\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c\u044b\u0445 \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u043e\u0432 <em>UIKit<\/em>. \u0422\u0430\u0431\u043b\u0438\u0447\u043d\u043e\u0435 \u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u0438\u0435 \u0437\u0430\u0440\u0435\u043a\u043e\u043c\u0435\u043d\u0434\u043e\u0432\u0430\u043b\u043e \u0441\u0435\u0431\u044f \u043a\u0430\u043a \u043e\u0434\u043d\u043e \u0438\u0437 \u0441\u0430\u043c\u044b\u0445 \u0443\u0434\u043e\u0431\u043d\u044b\u0445 \u0432\u0437\u0430\u0438\u043c\u043e\u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0439 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u0441 \u043a\u043e\u043d\u0442\u0435\u043d\u0442\u043e\u043c \u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u043d\u044b\u043c \u043d\u0430 \u044d\u043a\u0440\u0430\u043d\u0435 \u0441\u043c\u0430\u0440\u0442\u0444\u043e\u043d\u0430.<\/p>\n<p>\u041d\u0430 \u0441\u0435\u0433\u043e\u0434\u043d\u044f\u0448\u043d\u0438\u0439 \u0434\u0435\u043d\u044c, \u043a\u0430\u0436\u0434\u043e\u043c\u0443 iOS \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a\u0443 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e \u0432 \u0441\u043e\u0432\u0435\u0440\u0448\u0435\u043d\u0441\u0442\u0432\u0435 \u0432\u043b\u0430\u0434\u0435\u0442\u044c <em>UITableView, <\/em>\u0437\u043d\u0430\u0442\u044c \u0442\u043e\u043d\u043a\u043e\u0441\u0442\u0438 \u0438 \u043f\u043e\u043d\u0438\u043c\u0430\u0442\u044c \u043a\u0430\u043a \u0435\u0433\u043e \u0430\u0434\u0430\u043f\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u043f\u043e\u0434 \u0440\u0430\u0437\u043d\u044b\u0435 \u0430\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440\u044b, \u0447\u0442\u043e\u0431\u044b \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435 \u043d\u0435 \u0432\u044b\u0437\u044b\u0432\u0430\u043b\u043e \u043b\u0438\u0448\u043d\u0438\u0445 \u043f\u0440\u043e\u0431\u043b\u0435\u043c \u0438 \u0442\u0440\u0443\u0434\u043d\u043e\u0441\u0442\u0435\u0439.<\/p>\n<p>\u0412 \u044d\u0442\u043e\u0439 \u0441\u0442\u0430\u0442\u044c\u0435 \u043c\u044b \u043f\u043e\u0433\u043e\u0432\u043e\u0440\u0438\u043c \u043e \u0442\u043e\u043c, \u043a\u0430\u043a \u0430\u0434\u0430\u043f\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c <em>UITableView<\/em> \u043f\u043e\u0434 \u0430\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440\u0443 <em>Model-View-ViewModel (MVVM)<\/em>. \u041d\u0430\u0447\u043d\u0451\u043c.<\/p>\n<h2>\u0421\u043e\u0434\u0435\u0440\u0436\u0430\u043d\u0438\u0435<\/h2>\n<ol>\n<li>\n<p>\u0412\u0432\u0435\u0434\u0435\u043d\u0438\u0435<\/p>\n<\/li>\n<li>\n<p>\u041f\u0440\u0438\u043c\u0435\u0440<\/p>\n<\/li>\n<li>\n<p>\u0420\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f<\/p>\n<\/li>\n<li>\n<p>\u0418\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435<\/p>\n<\/li>\n<li>\n<p>\u0420\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442<\/p>\n<\/li>\n<li>\n<p>\u0412\u044b\u0432\u043e\u0434<\/p>\n<\/li>\n<\/ol>\n<h2>\u041f\u0440\u0438\u043c\u0435\u0440<\/h2>\n<p>\u0412 \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0435 \u043f\u0440\u0438\u043c\u0435\u0440\u0430 \u044f \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u043b \u044f\u0447\u0435\u0439\u043a\u0443 \u0441 \u043a\u043d\u043e\u043f\u043a\u043e\u0439, \u043a\u0430\u0440\u0442\u0438\u043d\u043a\u043e\u0439 \u0438 \u0442\u0435\u043a\u0441\u0442\u043e\u043c.<\/p>\n<figure class=\"bordered full-width\"><figcaption><\/figcaption><\/figure>\n<h2>\u0420\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f<\/h2>\n<p>\u041f\u0435\u0440\u0432\u044b\u043c \u0434\u0435\u043b\u043e\u043c \u0441\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u043f\u043e\u0434\u043a\u043b\u0430\u0441\u0441 \u043e\u0442 <em>UITableView<\/em> \u0438 \u043d\u0430\u0437\u043e\u0432\u0435\u043c \u0435\u0433\u043e <em>AdaptedTableView<\/em>. <\/p>\n<pre><code class=\"swift\">class AdaptedTableView: UITableView {      }<\/code><\/pre>\n<p>\u041e\u043f\u0440\u0435\u0434\u0435\u043b\u0438\u043c \u043c\u0435\u0442\u043e\u0434 <em>setup(). <\/em>\u041e\u043d \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c \u0434\u043b\u044f \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u0438 \u0442\u0430\u0431\u043b\u0438\u0446\u044b. \u0412\u0440\u0435\u043c\u0435\u043d\u043d\u043e \u0437\u0430\u043f\u043e\u043b\u043d\u0438\u043c \u043e\u0431\u044f\u0437\u0430\u0442\u0435\u043b\u044c\u043d\u044b\u0435 \u0434\u043b\u044f \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438 \u043c\u0435\u0442\u043e\u0434\u044b <em>UITableViewDataSource<\/em>.<\/p>\n<pre><code>class AdaptedTableView: UITableView {          \/\/ MARK: - Public methods          func setup() {         self.dataSource = self     }      }  \/\/ MARK: - UITableViewDataSource  extension AdaptedTableView: UITableViewDataSource {          func numberOfSections(in tableView: UITableView) -&gt; Int {         .zero     }          func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -&gt; Int {         .zero     }          func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -&gt; UITableViewCell {         UITableViewCell()     }      }<\/code><\/pre>\n<p>\u0421\u043e\u0433\u043b\u0430\u0441\u043d\u043e \u043f\u0430\u0442\u0442\u0435\u0440\u043d\u0443 <em>MVVM<\/em>, <em>view<\/em> \u0432\u043b\u0430\u0434\u0435\u0435\u0442 <em>viewModel.<\/em> \u0421\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u0430\u0431\u0441\u0442\u0440\u0430\u043a\u0446\u0438\u044e \u0434\u043b\u044f \u0432\u0445\u043e\u0434\u043d\u044b\u0445 \u0434\u0430\u043d\u043d\u044b\u0445 \u0438 \u043d\u0430\u0437\u043e\u0432\u0435\u043c \u0435\u0451 <em>AdaptedViewModelInputProtocol<\/em>. <em>AdaptedSectionViewModelProtocol<\/em> \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c \u0434\u043b\u044f \u043e\u043f\u0438\u0441\u0430\u043d\u0438\u044f <em>viewModel<\/em> \u0441\u0435\u043a\u0446\u0438\u0438. <em>AdaptedCellViewModelProtocol<\/em> \u0441\u043b\u0443\u0436\u0438\u0442 \u043b\u0438\u0448\u044c \u0434\u043b\u044f \u043f\u043e\u043b\u0438\u043c\u043e\u0440\u0444\u0438\u0437\u043c\u0430 \u043f\u043e\u0434\u0442\u0438\u043f\u043e\u0432 \u043d\u0430\u0448\u0438\u0445 <em>viewModels <\/em>\u0434\u043b\u044f \u044f\u0447\u0435\u0435\u043a.<\/p>\n<pre><code class=\"swift\">protocol AdaptedCellViewModelProtocol { }  protocol AdaptedSectionViewModelProtocol {     var cells: [AdaptedCellViewModelProtocol] { get } }  protocol AdaptedViewModelInputProtocol {     var sections: [AdaptedSectionViewModelProtocol] { get } }<\/code><\/pre>\n<p>\u0414\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u043c <em>viewModel<\/em>. \u0422\u0435\u043f\u0435\u0440\u044c \u0443 \u043d\u0430\u0441 \u0435\u0441\u0442\u044c \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u044c \u043a\u043e\u0440\u0440\u0435\u043a\u0442\u043d\u043e \u0437\u0430\u043f\u043e\u043b\u043d\u0438\u0442\u044c \u043c\u0435\u0442\u043e\u0434\u044b <em>UITableViewDataSource.<\/em><\/p>\n<pre><code class=\"swift\">class AdaptedTableView: UITableView {          \/\/ MARK: - Public properties          var viewModel: AdaptedViewModelInputProtocol?          \/\/ MARK: - Public methods          func setup() {         self.dataSource = self     }      }  \/\/ MARK: - UITableViewDataSource  extension AdaptedTableView: UITableViewDataSource {          func numberOfSections(in tableView: UITableView) -&gt; Int {         viewModel?.sections.count ?? .zero     }          func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -&gt; Int {         viewModel?.sections[section].cells.count ?? .zero     }          func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -&gt; UITableViewCell {         guard let cellViewModel = viewModel?.sections[indexPath.section].cells[indexPath.row] else {             return UITableViewCell()         }              \t\/\/ TO DO: - Register cell       \t\/\/ TO DO: - Create cell                  return UITableViewCell()     }      }<\/code><\/pre>\n<p>\u041d\u0430 \u0434\u0430\u043d\u043d\u043e\u043c \u044d\u0442\u0430\u043f\u0435 \u0441 <em>AdaptedTableView<\/em> \u043f\u043e\u0447\u0442\u0438 \u0432\u0441\u0435 \u0433\u043e\u0442\u043e\u0432, \u043e\u0434\u043d\u0430\u043a\u043e \u0435\u0441\u0442\u044c \u0435\u0449\u0435 \u043f\u0430\u0440\u0443 \u043d\u0435\u0440\u0435\u0448\u0435\u043d\u043d\u044b\u0445 \u0432\u043e\u043f\u0440\u043e\u0441\u043e\u0432. \u0420\u0435\u0433\u0438\u0441\u0442\u0440\u0430\u0446\u0438\u044f \u0438 \u043f\u0435\u0440\u0435\u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435 \u044f\u0447\u0435\u0435\u043a. \u0421\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u043f\u0440\u043e\u0442\u043e\u043a\u043e\u043b <em>AdaptedCellProtocol<\/em>, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0431\u0443\u0434\u0443\u0442 \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u044b\u0432\u0430\u0442\u044c \u0432\u0441\u0435 \u043d\u0430\u0448\u0438 \u043f\u043e\u0434\u043a\u043b\u0430\u0441\u0441\u044b <em>UITableViewCell<\/em>, \u0434\u043e\u0431\u0430\u0432\u0438\u043c \u043c\u0435\u0442\u043e\u0434 <em>register(_ tableView:) <\/em>\u0438 <em>reuse(_ tableView:, for indexPath:)<\/em>.<\/p>\n<pre><code class=\"swift\">protocol AdaptedCellProtocol {     static var identifier: String { get }     static var nib: UINib { get }     static func register(_ tableView: UITableView)     static func reuse(_ tableView: UITableView, for indexPath: IndexPath) -&gt; Self }  extension AdaptedCellProtocol {          static var identifier: String {         String(describing: self)     }          static var nib: UINib {         UINib(nibName: identifier, bundle: nil)     }          static func register(_ tableView: UITableView) {         tableView.register(nib, forCellReuseIdentifier: identifier)     }          static func reuse(_ tableView: UITableView, for indexPath: IndexPath) -&gt; Self {         tableView.dequeueReusableCell(withIdentifier: identifier, for: indexPath) as! Self     }      } <\/code><\/pre>\n<p>\u0414\u043b\u044f \u043f\u043e\u0440\u043e\u0436\u0434\u0435\u043d\u0438\u044f \u044f\u0447\u0435\u0435\u043a \u0441\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u043f\u0440\u043e\u0442\u043e\u043a\u043e\u043b \u0444\u0430\u0431\u0440\u0438\u0447\u043d\u043e\u0433\u043e \u043c\u0435\u0442\u043e\u0434\u0430 <em>AdaptedCellFactoryProtocol<\/em>.<\/p>\n<pre><code class=\"swift\">protocol AdaptedCellFactoryProtocol {     var cellTypes: [AdaptedCellProtocol.Type] { get }     func generateCell(viewModel: AdaptedCellViewModelProtocol, tableView: UITableView, for indexPath: IndexPath) -&gt; UITableViewCell }<\/code><\/pre>\n<p>\u0414\u043e\u0431\u0430\u0432\u0438\u043c \u043f\u043e\u043b\u0435 <em>cellFactory<\/em> \u0438 \u0432 <em>didSet<\/em> \u043f\u043e\u043c\u0435\u0441\u0442\u0438\u043c \u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0430\u0446\u0438\u044e \u0432\u0441\u0435\u0445 \u044f\u0447\u0435\u0435\u043a. <\/p>\n<pre><code class=\"swift\">class AdaptedTableView: UITableView {          \/\/ MARK: - Public properties          var viewModel: AdaptedViewModelInputProtocol?     var cellFactory: AdaptedCellFactoryProtocol? {         didSet {             cellFactory?.cellTypes.forEach({ $0.register(self)})         }     }          ...      }<\/code><\/pre>\n<p>\u0418\u0441\u043f\u0440\u0430\u0432\u0438\u043c \u043c\u0435\u0442\u043e\u0434 \u0434\u0435\u043b\u0435\u0433\u0430\u0442\u0430.<\/p>\n<pre><code class=\"swift\">extension AdaptedTableView: UITableViewDataSource {          ...          func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -&gt; UITableViewCell {         guard             let cellFactory = cellFactory,             let cellViewModel = viewModel?.sections[indexPath.section].cells[indexPath.row]         else {             return UITableViewCell()         }                  return cellFactory.generateCell(viewModel: cellViewModel, tableView: tableView, for: indexPath)     }      }<\/code><\/pre>\n<h2>\u0418\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435<\/h2>\n<p>\u0421 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u044b \u0430\u0431\u0441\u0442\u0440\u0430\u043a\u0446\u0438\u044f\u043c\u0438 \u043d\u0430 \u044d\u0442\u043e\u043c \u0432\u0441\u0435, \u043f\u043e\u0440\u0430 \u043f\u0435\u0440\u0435\u0439\u0442\u0438 \u043a \u043a\u043e\u043d\u043a\u0440\u0435\u0442\u043d\u044b\u043c \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f\u043c.<\/p>\n<p><strong>1. \u042f\u0447\u0435\u0439\u043a\u0430<\/strong><\/p>\n<p>\u0412 \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0435 \u043f\u0440\u0438\u043c\u0435\u0440\u0430 \u044f \u0441\u043e\u0437\u0434\u0430\u043c \u044f\u0447\u0435\u0439\u043a\u0443 \u0441 \u043b\u0435\u0439\u0431\u043b\u043e\u043c \u043f\u043e \u0446\u0435\u043d\u0442\u0440\u0443 \u0438 <em>viewModel<\/em> \u043a \u043d\u0435\u0439. \u0420\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f \u044f\u0447\u0435\u0439\u043a\u0438 \u0441 <a href=\"https:\/\/github.com\/3ai41k\/MVVM-UITableView\/tree\/master\/MVVM-UITableView\/Modules\/Main\/Cells\/ButtonCell\" rel=\"noopener noreferrer nofollow\">\u043a\u043d\u043e\u043f\u043a\u043e\u0439<\/a> \u0438 <a href=\"https:\/\/github.com\/3ai41k\/MVVM-UITableView\/tree\/master\/MVVM-UITableView\/Modules\/Main\/Cells\/ImageCell\" rel=\"noopener noreferrer nofollow\">\u043a\u0430\u0440\u0442\u0438\u043d\u043a\u043e\u0439<\/a>.<\/p>\n<pre><code class=\"swift\">protocol TextCellViewModelInputProtocol {     var text: String { get } }  typealias TextCellViewModelType = AdaptedCellViewModelProtocol &amp; TextCellViewModelInputProtocol  class TextCellViewModel: TextCellViewModelType {          var text: String          init(text: String) {         self.text = text     }      }  final class TextTableViewCell: UITableViewCell, AdaptedCellProtocol {          \/\/ MARK: - IBOutlets          @IBOutlet private weak var label: UILabel!          \/\/ MARK: - Public properties          var viewModel: TextCellViewModelInputProtocol? {         didSet {             bindViewModel()         }     }          \/\/ MARK: - Private methods          private func bindViewModel() {         label.text = viewModel?.text     }      }<\/code><\/pre>\n<p><strong>2.  C\u0435\u043a\u0446\u0438\u044f<\/strong><\/p>\n<pre><code class=\"swift\">class AdaptedSectionViewModel: AdaptedSectionViewModelProtocol {          \/\/ MARK: - Public properties        var cells: [AdaptedCellViewModelProtocol]          \/\/ MARK: - Init          init(cells: [AdaptedCellViewModelProtocol]) {         self.cells = cells     }      }<\/code><\/pre>\n<p><strong>3. \u0424\u0430\u0431\u0440\u0438\u043a\u0430<\/strong><\/p>\n<pre><code class=\"swift\">struct MainCellFactory: AdaptedSectionFactoryProtocol {          var cellTypes: [AdaptedCellProtocol.Type] = [         TextTableViewCell.self     ]          func generateCell(viewModel: AdaptedCellViewModelProtocol, tableView: UITableView, for indexPath: IndexPath) -&gt; UITableViewCell {         switch viewModel {         case let viewModel as TextCellViewModelType:             let view = TextTableViewCell.reuse(tableView, for: indexPath)             view.viewModel = viewModel             return view         default:             return UITableViewCell()         }     }      }<\/code><\/pre>\n<p>\u041d\u0443 \u0438 \u043d\u0430\u043f\u043e\u0441\u043b\u0435\u0434\u043e\u043a \u043d\u0430\u043c \u043f\u043e\u043d\u0430\u0434\u043e\u0431\u0438\u0442\u0441\u044f <em>viewModel<\/em> \u0441\u0430\u043c\u043e\u0433\u043e \u043c\u043e\u0434\u0443\u043b\u044f.<\/p>\n<pre><code class=\"swift\">final class MainViewModel: AdaptedSectionViewModelType {          \/\/ MARK: - Public properties          var sections: [AdaptedSectionViewModelProtocol]          \/\/ MARK: - Init          init() {         self.sections = []                  self.setupMainSection()     }          \/\/ MARK: - Private methods          private func setupMainSection() {         let section = AdaptedSectionViewModel(cells: [             TextCellViewModel(text: \"Hello!\"),             TextCellViewModel(text: \"It's UITableView with using MVVM\")         ])         sections.append(section)     }      }<\/code><\/pre>\n<p>\u0412\u0441\u0435 \u0433\u043e\u0442\u043e\u0432\u043e, \u043f\u043e\u0440\u0430 \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c <em>UITableView<\/em> \u043d\u0430 <em>ViewController<\/em>, \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0432 \u0432 \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0435 <em>custom class<\/em> \u043d\u0430\u0448 <em>AdaptedTableView<\/em>.<\/p>\n<figure class=\"full-width\"><figcaption><\/figcaption><\/figure>\n<p>\u0412 \u0440\u0435\u0430\u043b\u044c\u043d\u043e\u043c \u043f\u0440\u043e\u0435\u043a\u0442\u0435, <em>MVVM<\/em> \u043e\u0447\u0435\u043d\u044c \u0447\u0430\u0441\u0442\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044e\u0442 \u0441 \u043a\u0430\u043a\u0438\u043c-\u0442\u043e \u043f\u0430\u0442\u0442\u0435\u0440\u043d\u043e\u043c \u043d\u0430\u0432\u0438\u0433\u0430\u0446\u0438\u0438, \u044d\u0442\u043e \u043c\u043e\u0436\u0435\u0442 \u0431\u044b\u0442\u044c \u043a\u043e\u043e\u0440\u0434\u0438\u043d\u0430\u0442\u043e\u0440 \u0438\u043b\u0438 \u0440\u043e\u0443\u0442\u0435\u0440. \u0412 \u0437\u043e\u043d\u0443 \u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0435\u043d\u043d\u043e\u0441\u0442\u0438 \u0442\u0430\u043a\u0438\u0445 \u043e\u0431\u044a\u0435\u043a\u0442\u043e\u0432 \u0432\u0445\u043e\u0434\u0438\u0442 <em>DI (Dependency Injection) <\/em>\u0432\u043d\u0435\u0434\u0440\u0435\u043d\u0438\u0435 \u0432\u0441\u0435\u0445 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u044b\u0445 \u043c\u043e\u0434\u0443\u043b\u044e \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0435\u0439. \u0422\u0430\u043a \u043a\u0430\u043a \u044d\u0442\u043e \u0442\u0435\u0441\u0442\u043e\u0432\u044b\u0439 \u043f\u0440\u043e\u0435\u043a\u0442, \u044f \u0437\u0430\u0445\u0430\u0440\u0434\u043a\u043e\u0434\u0438\u043b <em>viewModel<\/em> \u0438 <em>cellFactory <\/em>\u043f\u0440\u044f\u043c\u043e \u0432\u043e<em> ViewController<\/em>.<\/p>\n<pre><code class=\"swift\">class ViewController: UIViewController {          \/\/ MARK: - IBOutlets          @IBOutlet weak var tableView: AdaptedTableView! {         didSet {             tableView.viewModel = MainViewModel()             tableView.cellFactory = MainCellFactory()                          tableView.setup()         }     }      }<\/code><\/pre>\n<h2>\u0420\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442<\/h2>\n<figure class=\"full-width\"><figcaption><\/figcaption><\/figure>\n<h2>\u0412\u044b\u0432\u043e\u0434<\/h2>\n<p>\u0412 \u0438\u0442\u043e\u0433\u0435 \u043c\u044b \u043f\u043e\u043b\u0443\u0447\u0438\u043b\u0438 \u0440\u0435\u0448\u0435\u043d\u0438\u0435, \u043a\u043e\u0442\u043e\u0440\u043e\u0435 \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u0443\u0434\u043e\u0431\u043d\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c <em>UITableView<\/em> \u0441 <em>MVVM<\/em>. \u0421\u0442\u0430\u043b\u043e \u043e\u0447\u0435\u043d\u044c \u043f\u0440\u043e\u0441\u0442\u043e \u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c \u0441 \u0441\u0435\u043a\u0446\u0438\u044f\u043c\u0438, \u043d\u0430\u0441\u0442\u0440\u0430\u0438\u0432\u0430\u0442\u044c \u044f\u0447\u0435\u0439\u043a\u0438, \u043f\u0438\u0441\u0430\u0442\u044c \u043c\u0435\u043d\u044c\u0448\u0435 \u0448\u0430\u0431\u043b\u043e\u043d\u043d\u043e\u0433\u043e \u043a\u043e\u0434\u0430. \u0412 \u0442\u043e \u0436\u0435 \u0432\u0440\u0435\u043c\u044f \u043e\u0441\u0442\u0430\u043b\u0430\u0441\u044c \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u044c \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 \u0442\u0430\u0431\u043b\u0438\u0446\u044b \u0438 \u0440\u0430\u0441\u0448\u0438\u0440\u0435\u043d\u0438\u044f \u0444\u0443\u043d\u043a\u0446\u0438\u043e\u043d\u0430\u043b\u0430 \u043f\u0440\u0438 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e\u0441\u0442\u0438.<\/p>\n<hr>\n<p>\u0412\u0435\u0441\u044c \u043a\u043e\u0434 \u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u043d\u044b\u0439 \u0432 \u044d\u0442\u043e\u0439 \u0441\u0442\u0430\u0442\u044c\u0435 \u043c\u043e\u0436\u043d\u043e \u0441\u043a\u0430\u0447\u0430\u0442\u044c \u043f\u043e <a href=\"https:\/\/github.com\/3ai41k\/MVVM-UITableView\" rel=\"noopener noreferrer nofollow\">\u044d\u0442\u043e\u0439<\/a> \u0441\u0441\u044b\u043b\u043a\u0435.<\/p>\n<\/div>\n<p> \u0441\u0441\u044b\u043b\u043a\u0430 \u043d\u0430 \u043e\u0440\u0438\u0433\u0438\u043d\u0430\u043b \u0441\u0442\u0430\u0442\u044c\u0438 <a href=\"https:\/\/habr.com\/ru\/post\/531446\/\"> https:\/\/habr.com\/ru\/post\/531446\/<\/a><br \/><\/br><\/br><\/div>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[],"tags":[],"class_list":["post-314366","post","type-post","status-publish","format-standard","hentry"],"_links":{"self":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/314366","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=314366"}],"version-history":[{"count":0,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/314366\/revisions"}],"wp:attachment":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=314366"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=314366"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=314366"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}