Как стать автором
Обновить

Делегаты в Swift на простом примере

Время на прочтение5 мин
Количество просмотров7.8K

Эта статья для уровня trainee, а значит для совсем начинающих великолепных разработчиков

Основная цель статьи - рассказать просто, на примере, как можно использовать паттерн делегирования в Swift.

Статья состоит из двух частей. Первая - удалим из проекта Storyboard и напишем кодом простой интерфейс. Вторая - разберем, как с помощью делегата передать данные на предыдущий контроллер.

Часть 1

Создаем новый проект, назовем его DelegatePattern:

Галочки не понадобятся, Interface - Storyboard, Language - Swift.
Галочки не понадобятся, Interface - Storyboard, Language - Swift.

Размещать элементы будем кодом, поэтому удаляем Storyboard из проекта:

Удаляем Main.storyboard
Удаляем Main.storyboard
Выбираем Move to Trash - удалить в корзину
Выбираем Move to Trash - удалить в корзину

Выбираем проект (стрелка 1), вкладка General и в разделе Deployment Info выделяем и удаляем Main(стрелка 2)

Поле Main Interface должно остаться пустым
Поле Main Interface должно остаться пустым

Переходим в файл info.plist (стрелка 1) и удаляем строку Application Scene Manifest

В итоге останется только строка Information Property List
В итоге останется только строка Information Property List

У нас не будет поддержки IPad - файл SceneDelegate можно тоже удалить:

Вот так теперь выглядит проект
Вот так теперь выглядит проект

Так как мы удалили Storyboard, в файле AppDelegate объявим свойство window, а в методе application didFinishLaunchingWithOptions, нужно добавить код ниже, чтобы указать стартовый контороллер.  Остальные методы удалим, в нашем проекте они использоваться не будут.

var window: UIWindow?

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
        
        window = UIWindow(frame: UIScreen.main.bounds)
        let rootVC = ViewController()
        window?.rootViewController = rootVC
        window?.makeKeyAndVisible()
        
        return true
    }

Теперь AppDelegate выглядит вот так:

Переходим во ViewController, в методе viewDidLoad добавляем фиолетовый цвет для бэкграунда view контроллера:

view.backgroundColor = .purple
view.backgroundColor = .purple

Запускаем проект, и если все сделали верно, запустится симулятор с фиолетовым контроллером:

Отлично, теперь мы можем продолжать писать интерфейс в коде. Для начала создадим еще один ViewController. Нажимаем Command + N и выбираем Cocoa Touch Class:

Назовем его SecondViewController:

После создания удалите весь закоментированый код из SecondViewController.
После создания удалите весь закоментированый код из SecondViewController.

Поработаем с ViewController, добавим на него кнопку перехода на SecondViewController:

Строки 12 - 17
Строки 12 - 17

Объявим новый метод makeConstraints, в нем добавим кнопку на view и создадим констрейнты.

    private func makeConstraints() {
        
        view.addSubview(toSecondViewControllerButton) // добавляем кнопку на view
        NSLayoutConstraint.activate([
            toSecondViewControllerButton.centerXAnchor.constraint(equalTo: view.centerXAnchor), // центр по оси Х
            toSecondViewControllerButton.centerYAnchor.constraint(equalTo: view.centerYAnchor) // центр по оси Y
        ])
    }

И обязательно, нужно установить значение свойстваbutton.translatesAutoresizingMaskIntoConstraints = false в клоужере создания кнопки. Вот как теперь выглядит ViewController:

Что бы все заработало не забудьте добавить вызов makeConstraints() во viewDidLoad(). Строка 27.
Что бы все заработало не забудьте добавить вызов makeConstraints() во viewDidLoad(). Строка 27.

Проверим симулятор:

Кнопка есть, все отлично.
Кнопка есть, все отлично.

Теперь аналогично добавим UILabel, в нем в последующем и будем менять текст при возвращении из следующего контроллера.

Можете сами потренироваться и создать лейбл. Если возникнут проблемы, подглядите здесь :)

Первым делом добавим сам элемент UILabel:

строка 21 - 26. Сразу зададим текст лейбла "Standart text"
строка 21 - 26. Сразу зададим текст лейбла "Standart text"

Добавляем лейбл на view и, создадаем констрейнты, расширив метод makeConstraints:

    private func makeConstraints() {
        
        view.addSubview(toSecondViewControllerButton)
        view.addSubview(someLabel)  // добавляем на экран
        NSLayoutConstraint.activate([
            toSecondViewControllerButton.centerXAnchor.constraint(equalTo: view.centerXAnchor),
            toSecondViewControllerButton.centerYAnchor.constraint(equalTo: view.centerYAnchor),
            
            someLabel.centerXAnchor.constraint(equalTo: toSecondViewControllerButton.centerXAnchor), // центр по оси X
            someLabel.centerYAnchor.constraint(equalTo: toSecondViewControllerButton.centerYAnchor, constant: 40) // центр по оси Y
        ])
    }

Отлично, теперь на view есть кнопка и лейбл. Симулятор выглядят так:

Чтобы кнопка заработала и при ее нажатии открывался следующий экран, добавим ей свойство button.addTarget():

Строка 18
Строка 18

Объявим методtoSecondVCButtonPressed(), который будет отрабатывать по нажатию на кнопку:

Перейдем в SecondViewController и в методе viewDidLoad() добавим цвет бэкграунда view второго контроллера:

Строка 15.
Строка 15.

Проверяем, что получилось, собираем проект. Если все сделано верно, по нажатию на кнопку Go to second VC, откроется второй контроллер с серым фоном.

Часть 2

Пора приступать к передаче данных при закрытии "серого" контроллера. Как вы видели в названии статьи, будем использовать делегат :)

Объявим протокол SecondViewControllerDelegate в файле класса ViewController и обязательно укажем тип AnyObject. Это нужно для того, чтобы протокол работал с классами, а это, в свою очередь, позволит создавать слабые ссылки и избежать retain cycle между контроллерами. Наш протокол будет содержать только один метод - для замены текста в лейбле ViewController'a.

Реализовывать метод протокола будет ViewController, подпишем его под протокол в extension'е и напишем логику для метода, которая будет менять текст:

В аргумент text, находящийся на 69 строке, придет новый текст с другого контроллера, а на 70 строке мы заменим стандартный текст лейбла.

Если навести курсор на аргумент text, Xcode подсветит какой text к какому относится.

Переходим в SecondViewController. Помните мы создали протокол и объявили его anyObject? Теперь пора создать слабую ссылку, которая будет иметь тип делегата, она будет жить в SecondViewController и через нее мы сможем добраться до методов делегата.

Строка 12
Строка 12

Когда мы закрываем SecondViewController, смахивая его вниз, срабатывает метод deinit. В нашем примере это отличное место, чтобы передать новый текст в лейбл ViewController'a. Добираемся через переменную delegate до метода newTextForLabel и передаем в него новый текст для лейбла на первом контроллере "New text". (*PS: метод deinit() в статье используется только для примера, поскольку мы точно уверены в том, что контроллер выгрузится из памяти.)

Строка 20 - 22
Строка 20 - 22

Как пример, если создать кнопку закрытия второго экрана, тогда self.delegate?.newTextForLabel(text: "New text") поселился бы в методе, срабатывающем по нажатию на кнопку закрытия.

Все почти готово, осталось дело за малой деталью, о которой лично я всегда забываю :) нужно сообщить нашему SecondViewController, кто будет его делегатом (то есть кто будет что-то делать с теми данными которые, он отправил).

Возвращаемся в ViewController, и там, где мы создавали для кнопки метод перехода на SecondViewController подпишемся под делегата:

Строка 62
Строка 62

Собираем проект и проверяем:

PS: Почему текст обновляется с задержкой? Дело в том, что deinit выгружает контроллер из памяти, поэтому проходит какое-то количество времени, пока контроллер выгрузится и текст сменится. Если вы будете использовать делегат, например, в методе UIViewController'a - dismiss() или в вашем методе кнопки, то никаких задержек не будет.

GitHub с финальным проектом - ссылка.

Спасибо, что дочитали :)

Теги:
Хабы:
Всего голосов 8: ↑5 и ↓3+2
Комментарии11

Публикации

Истории

Работа

Swift разработчик
32 вакансии
iOS разработчик
24 вакансии

Ближайшие события