ios

Материалы по платформе iOS.

View on GitHub

Back

ViewController

ViewController - основа внутренней структуры iOS приложения. iOS приложение имеет как минимум один ViewController, который является рутовым контроллером окна(window). Однако, большинство приложений содержит большое количество контроллеров. Контроллер управляет частью пользовательского интерфейса, вместе с этим он управляет взаимодействием с этим интерфейсом и некими данными. Также контроллер обеспечивает перемещения между различными частями приложения. Так как контроллеры играют такую важную часть приложения, их можно назвать центром практически всего что вы делаете.

UIViewController

Наследуется от UIResponder

Класс определяет методы и свойства для управления вашими View, обработке событий, переходами от одного контроллера к другому и координацией с другими частями приложения. Вы можете создать подкласс UIViewController или одного из его подклассов, и добавлять необходимый код для реализации поведения нашего приложения. Все контроллеры можно разделить на два типа:

UIKit содержит готовые реализации контейнера контроллеров (Container view controllers), такие как: UINavigationController, UITabBarController и UISplitViewController. В приложении одновременно могут использоваться контроллеры обоих типов.


Схема MVC (model-view-controller):


Контроллер является посредником между представлением (View) которым он управляет и данными приложения (Model). Реализуя тем самым паттерн MVC (model-view-controller). К модели мы относим данные, с которыми работает приложение, к view - их отображение, с которыми работает контроллер. Методы и свойства UIViewController позволяют вам работать с визуальной частью приложения. Когда вы создаете подкласс UIViewController, вы создаете переменные и свойства, которые ссылаются на вашу модель. Через эти свойства вы можете управлять и взаимодействовать с данными. Необходимо четко разграничивать обязанности в MVC. Большая часть логики, обеспечивающей целостность и структуру данных, должна находиться в модели. Реализация модели должна быть закрыта за протоколом, в котором будут определены методы для взаимодействия с этой моделью (предоставление, обновление, изменения). Контроллер может проверять валидность данных, получаемых от view(например данные из текстового поля), после чего упаковать данные в необходимый для модели формат и отправить их в модель, а дальнейшую обработку модель проведет самостоятельно. Основная идея в том, что нужно максимально минимизировать обязанности контроллера по работе с данными.

Управление иерархией view:

Наиболее важная роль контроллера — это управление иерархией view. Каждый контроллер имеет единственную рутовую view, которая охватывает весь контент контроллера. Именно на эту рутовую view вы добавляете дочерние view, которые необходимо показать. Контроллер всегда имеет ссылку на рутовую view и каждая view имеет сильные ссылки на дочерние view. В зависимости от типа контроллера в отношении view используются подходы:

Использовать Xib и Storyboard можно для:

1) Добавления, упорядочения и настройки view. 2) Подключения IBOutlet и IBAction. 3) Создания отношений и переходов между вашими контроллерами. 4) Кастомизировать view для разных классов размеров (size classes). 5) Добавления распознавания жестов. *приставка IB - означает InterfaceBuilder

Storyboard удобен пока приложение небольшое, когда приложение имеет огромное количество контроллеров контролировать все в storyboard довольно таки сложно. В своем устройстве сториборд является XML файлом, и в случае если с ним одновременно работает несколько разработчиков, мерджить его - не самое приятное занятие. В меньшей мере это относится к Xib файлам, поэтому контроллеры можно полностью создавать в коде.

Листинг с примером обработки пользовательских действий:

- (IBAction)myButtonAction:(id)sender {
     // Some action handling
}
// Add target-action for button
[button addTarget:self action:@selector(myButtonAction:) forControlEvents:UIControlEventTouchUpInside]; // ...
- (void)myButtonAction:(id)sender {
    // Some action handling
}

Lifecycle

Жизненный цикл представляет собой набор методов, которые помогают нам знать, в каком состоянии находится ViewController.

Жизненный цикл начинается не с момента создания ViewController, а тогда, когда вы, или система впервые обратитесь к свойству view. Если значение свойства view == nil, тогда система автоматически запускает метод -(void)loadView. Для того чтобы проверить, загружена ли view, существует метод -(BOOL)isViewLoaded, которая вернет текущее состояние и не запустит жизненный цикл ViewController. Никогда не нужно вызывать loadView: напрямую из кода, этот метод может вызывать только контроллер, если view == nil. Если view определена в nib файле, то loadView: загружает view из nib файла. Если для создания view вы использовали InterfaceBuilder, то не нужно переопределять метод loadView, его стоит переопределять только с целью создания view вручную. Если вы решили переопределить этот метод, то создание view и его присвоение ViewController лежит на вас, также не стоит вызывать имплементацию суперкласса. Если вы хотите просто провести дополнительную инициализацию ваших view, это стоит сделать в методе:

Managing Memory Efficiently

У UIViewController есть список методов, где более предпочтительно выделять или освобождать память. Освобождение проходит за счет удаления сильных ссылок. Чтобы удалить сильную ссылку нужно свойству или переменной присвоить nil (при ARC) или вызвать release (при MRC). Некоторые правила: 1) Используйте инициализаторы для выделения памяти под структуры данных, 2) Используйте viewDidLoad для загрузки любых объектов данных, которые нужно отобразить. К вызову этого метода объекты гарантированно существуют и находятся в готовом состоянии. 3) Используйте метод didReceiveMemoryWarning для освобождения всех объектов, которые не являются критическими. Освободите столько памяти, сколько сможете. 4) Переопределяйте dealloc для освобождения свойств вашего контроллера и любых других данных при MRC, при ARC свойства освобождаются автоматически, поэтому явно этого делать не нужно.

Handling View Rotations

До iOS 8 у нас был следующий список методов для отслеживания ротации:


Custom Container VC

Для создания кастомного контейнера необходимо установить отношения между вашим контроллером и дочерними контроллерами. Отношение родитель-наследник должно быть установлено перед тем, как вы попытаетесь управлять дочерним контроллером. Это необходимо чтобы дать знать UIKit, что ваш контейнер контроллер управляет размером и позицией дочерних контроллеров. Такое отношение можно создать как программно, так и в Interface Builder. Если отношения делаются программно, необходимо явно, кодом указать на такие отношения. Процесс добавления дочерних контроллеров:

1) Вызвать addChildViewController: в вашем контейнере. Вызов этого метода говорит UIKit что ваш контейнер-контроллер сейчас управляет размером и положением вашего ViewController. 2) Добавить корневой view дочернего контроллера в иерархию view контейнер-контроллера. 3) Добавить ограничения по размерам и позиционированию для дочерних view(constraints). 4) Вызвать метод didMoveToParentViewController: у дочерних view контроллеров.

Листинг добавления дочернего ViewController:

[self addChildViewController:childVC];
childVC.view.frame = [self frameForContentController];
[self.view addSubview:childVC.view];
[childVC didMoveToParentViewController:self];

Стоит обратить внимание, что явно здесь вызывается только didMoveToParentViewController: но мы не вызываем willMoveToParentViewController:. Это связано с тем, что addChildViewController: вызывает его автоматически.

Процесс удаления дочерних контроллеров:

1) Вызвать у дочерних контроллеров метод willMoveToParentViewController: со значением nil 2) Удалить все правила и ограничения по размерам, которые связывают контейнер и дочерние элементы. 3) Удалить корневое view дочернего контроллера из иерархии представлений у контейнер контроллера. 4) Вызываем у дочернего контроллера метод removeFromParentViewController: чтобы разорвать отношение родитель-потомок

Листинг удаления дочернего ViewController:

[childVC willMoveToParentViewController:nil]; 
[childVC.view removeFromSuperview];
[childVC removeFromParentViewController];

Заметьте, в процессе удаления дочернего ViewController мы явно вызываем метод willMoveToParentViewController: и не вызываем метод didMoveToParentViewController. Это связано с тем, что didMoveToParentViewController вызывается внутри реализации removeFromParentViewController.


Useful materials 🤓

UIViewController Documentation

View Controller Programming Guide for iOS