Небольшое вступление
Всем 404! Большинство IOS-разработчиков не понимают как работать с Apple-MVC. Из-за чего появляется необоснованная критика, маленькие проекты стараются писать на архитектурах для этого не предназначенных, ViewController'ы становятся огромными, не читаемыми и тд.
В этой статье я покажу простой способ как облегчить жизнь себе, жизнь себе, заглянувшему в проект через 3 месяца, и, конечно, всем ViewController'aм. Для кого он подойдет? Для начинающих разработчиков или разработчиков с небольшими проектами, которые в итоге получатся с чистой и красивой структурой.
Где же все тонут?
Для начала давайте посмотрим на всеми любимое стандартное красивое MVC.

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

Классно? Нет! Поэтому мы так делать не будем, а реализуем первый вариант.
Делаем структуру красоте
Создаем обычный Single View App. В поле выбора языка я ставлю Swift, больше ничего не включая: ни тестов, ни CoreDat'ы.
Я буду показывать на примере создания витрины магазина, т.к. нам нужно затронуть все части MVC, будем брать данные из model в controller распределять и показывать во view.
Окей, задача есть, пустой проект есть. Теперь нырнем в проект, я распределю все по папкам и создам пустые файлы, чтобы вы понимали где они должны лежать в дальнейшем. Также прикрепил storyboard структуру, чтобы не тратить время в дальнейшем. Не забудьте указать reuse id у cell, я написал ProductCell.

Красиво? Конечно красиво. Продолжаем…
Немного магии 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
- Легкая расширяемость нашего проекта
Заключение
Очень надеюсь что эта статья поможет новичкам держать свои проекты в чистоте, не теряться в коде, а концентрироваться на задачах. Всем добра и меньше багов ^^
