Адекватное MVC для начинающих и не только

—

от автора

Небольшое вступление

Всем 404! Большинство IOS-разработчиков не понимают как работать с Apple-MVC. Из-за чего появляется необоснованная критика, маленькие проекты стараются писать на архитектурах для этого не предназначенных, ViewController’ы становятся огромными, не читаемыми и тд.

В этой статье я покажу простой способ как облегчить жизнь себе, жизнь себе, заглянувшему в проект через 3 месяца, и, конечно, всем ViewController’aм. Для кого он подойдет? Для начинающих разработчиков или разработчиков с небольшими проектами, которые в итоге получатся с чистой и красивой структурой.

Где же все тонут?

Для начала давайте посмотрим на всеми любимое стандартное красивое MVC.

image

А теперь посмотрим на то, что осуществляется большинством.

image

Классно? Нет! Поэтому мы так делать не будем, а реализуем первый вариант.

Делаем структуру красоте

Создаем обычный Single View App. В поле выбора языка я ставлю Swift, больше ничего не включая ни тестов, ни CoreDat’ы.

Я буду показывать на примере создания витрины магазина, т.к. нам нужно затронуть все части MVC, будем брать данные из model в controller распределять и показывать во view.

Окей, задача есть, пустой проект есть. Теперь нырнем в проект, я распределю все по папкам и создам пустые файлы, чтобы вы понимали где они должны лежать в дальнейшем. Также прикрепил storyboard структуру, чтобы не тратить время в дальнейшем. Не забудьте указать reuse id у cell, я написал ProductCell.

image

Красиво? Конечно красиво. Продолжаем…

Немного магии swift

Сейчас можно и к реализации приступить. Начнем с простого, view. Нам нужно создать все IBOutlets, и настройки нашего представления.

// Начинаем с импорта библиотек, зачастую во view нам будет необходим только UIKit.  import UIKit  class ShopView: UIView{     // Подвязываем нашу переменную к объекту в storyboard.      @IBOutlet weak var tableView: UITableView!     // Создаем функции для настройки нашей view.      func configure(){         // Здесь могут быть любые параметры, как установка цвета текста, так и установка высоты строки таблицы...         tableView.estimatedRowHeight = 50         tableView.rowHeight = 100     } } 

Так будет выглядеть наша view, в configure() функции я лишь задаю настройки, но view нужно использовать и для анимаций, и для смены названия title’а кнопок и тд… Так примитивно скажете? Но почему тогда так мало используют это преимущество?! Вот так я например использую анимации в controller’e: view.hideBorders(self) и все, а view уже сделает красивую анимацию.

// Передаем ссылку на view из контроллера.  func hideBorders(view: UIView){     UIView.animate(withDuration: 0.3,                    delay: 0,                    options: .curveEaseInOut,                    animations: {            // И уже внутри анимации меняем высоту при помощи constraint.             self.createViewHeighConstraint.constant = view.bounds.height * 0.6            // Изменяем такие параметры, как округление краев, видимость тени и тд.             self.editButton.layer.cornerRadius = 20            self.postView.layer.shadowOpacity = 0            self.postView.layer.cornerRadius = 0            view.layoutIfNeeded()     }, completion: nil) } 

Огонь! Но вернемся к нашему проекту, время реализовать model. И так у нас будут товары, и пусть будут их группы.

// Хорошим тоном считается, что в model не должно быть никаких данных о View. // Да, не всегда это получается, но в нашем случае достаточно импортировать Foundation. import Foundation // Теперь нам нужно сделать наш продукт, воспользуемся структурой.  public struct Product {   public let name: String } // Подумаем немного о будущем, у нас могут быть разные продукты, создадим их группы.  public struct ProductGroup {   public let products: [Product]   public let title: String } // Магия swift позволяет нам декорировать(расширять) структуры с помощью extension.  extension ProductGroup{     // Создадим функцию, позволяющую нам получить массив овощей(структур Product).      public static func vegetables() -> ProductGroup{        // Собираем массив овощей.         let products = [             Product(name: "Огурцы"),             Product(name: "Помидоры"),             Product(name: "Капуста")         ]         // Возвращаем наш массив с названием "Овощи".          return ProductGroup(products: products, title: "Овощи")     } } 

Замечательно, наша модель готова, осталось собрать контроллер, давайте преступим. первым делом нужно будет создать две наших главных сущности модель и вью. Затем я вынесу настройки контроллера в private extension и передам данные из model в нашу таблицу.

// В controller'e у нас могут быть разные библиотеки, но сегодня нам потребуется только UIKit. import UIKit  class ShopController: UIViewController {     // Создаем переменную-модель и получаем в нее овощи.     private var shopModel = ProductGroup.vegetables()          // Дальше создаем вычисляемое свойство (объяснение длинновато для комментария смотри внизу под кодом)     private var shopView: ShopView! {         guard isViewLoaded else { return nil }         return (view as! ShopView)     }     // Помните мы делали функцию настройки view, вот здесь и применяем ее чтобы при загрузке controller'a наша view тоже настроилась.      override func viewDidLoad() {         super.viewDidLoad()         // Тем самым настройка view вообще не занимает места в контроллере.         configure()     } }  private extension ShopController{     // Но делегаты есть делегаты их присваиваем и оставляем в контроллере.     func configure(){         shopView.tableView.delegate = self         shopView.tableView.dataSource = self     } }  // Теперь самое время реализовать методы делегатов.  extension ShopController: UITableViewDataSource, UITableViewDelegate{      func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {         // Возвращаем кол-во ячеек по кол-ву элементов в модели         return shopModel.products.count     }          func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {         // Помните я говорил, не забудьте reuse Id установить, так вот он нужен именно здесь.         let cell = tableView.dequeueReusableCell(withIdentifier: "ProductCell")         // Создаем магическим образом label в cell и присваиваем ей название продукта.          cell?.textLabel?.text = shopModel.products[indexPath.row].name         return cell!     } } 

Обещал обьяснить про view, не забыли?) Наша shopView — это вычисляемое свойство. Здесь мы проверяем isViewLoaded, чтобы не вызвать непреднамеренную загрузку view при доступе к этому свойству. Если представление уже загружено, мы принудительно преобразуем его в ShopView.

Вот и все, наше mvc готово! Остается нажать command+R и увидеть три ячейки.

Что мы имеем в итоге?

  • Простая, понятная архитектура
  • Равномерная нагрузка на model, view, controller
  • Легкая переиспользуемость view
  • Легкая расширяемость нашего проекта

Заключение

Очень надеюсь что эта статья поможет новичкам держать свои проекты в чистоте, не теряться в коде, а концентрироваться на задачах. Всем добра и меньше багов ^^

ссылка на оригинал статьи https://habr.com/ru/post/500870/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *