Статьи

Swift From Scratch: Делегирование и Свойства

На предыдущем уроке вы создали простой проект Swift в Xcode, базовом приложении для ведения дел. В этой части Swift From Scratch мы собираемся добавить возможность создавать задачи. По пути вы узнаете о действиях, делегировании и свойствах.

Если вы хотите следовать за мной, убедитесь, что на вашем компьютере установлен Xcode 8.3.2 или выше. Вы можете скачать Xcode 8.3.2 из Apple App Store .

В конце этого урока пользователь сможет добавлять новые задачи, нажав кнопку на панели навигации, представив представление с текстовым полем и кнопкой. Давайте начнем с создания контроллера представления, который будет обрабатывать добавление новых дел, класса AddItemViewController .

Выберите « Создать»> «Файл …» в меню « Файл» Xcode и выберите шаблон « Какао-класс касания» из списка iOS> Шаблоны источников .

Создать Какао Touch Touch Class

Назовите класс AddItemViewController и убедитесь, что он наследуется от UIViewController . Дважды проверьте, что для языка установлено значение Swift и что также не создается файл XIB .

Опции Класса касания Какао

Укажите Xcode, где вы хотите сохранить файл для класса AddItemViewController и нажмите « Создать» .

Прежде чем мы создадим пользовательский интерфейс класса AddItemViewController , нам нужно создать выход для текстового поля и два действия: одно для кнопки отмены на панели навигации и другое для кнопки создания под текстовым полем.

Добавление розетки должно быть уже знакомо. Создайте выход в классе AddItemViewController и назовите его textField как показано ниже.

01
02
03
04
05
06
07
08
09
10
11
12
13
class AddItemViewController: UIViewController {
 
    @IBOutlet var textField: UITextField!
 
    override func viewDidLoad() {
        super.viewDidLoad()
    }
 
    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
    }
 
}

Создание действия очень похоже на создание метода экземпляра. Фактически, атрибут @IBAction является не более чем подсказкой для Interface Builder. @IBAction метода с атрибутом @IBAction гарантирует, что Interface Builder знает о методе, что позволяет нам подключать его в раскадровке. Мы пока оставим тела обоих действий пустыми.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
class AddItemViewController: UIViewController {
 
    @IBOutlet var textField: UITextField!
 
    override func viewDidLoad() {
        super.viewDidLoad()
    }
 
    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
    }
 
    @IBAction func cancel(_ sender: Any) {
 
    }
 
    @IBAction func create(_ sender: Any) {
 
    }
 
}

Откройте Main.storyboard в Навигаторе проекта и перетащите View Controller из Библиотеки объектов справа. С выбранным контроллером представления, откройте Identity Inspector справа и установите Custom Class> Class в AddItemViewController .

Чтобы добавить панель навигации к представлению добавления элемента, выберите контроллер представления « Добавить элемент» и выберите « Встроить»> «Контроллер навигации» в меню « Редактор» . Это сделает Add View View Controller корневым контроллером представления навигационного контроллера.

Следующим шагом является добавление элемента кнопки панели на панель навигации контроллера представления, а не добавления контроллера представления элемента, и установка его идентификатора на « Добавить» в инспекторе атрибутов .

Добавить панель кнопок

Когда пользователь нажимает кнопку « Добавить», контроллер представления элемента должен быть представлен модально. Для этого нажмите клавишу « Control» и перетащите кнопку « Добавить» на контроллер навигации , выбрав « Представить модально» из всплывающего меню. Это создаст переход от контроллера представления элемента к новому контроллеру навигации .

Элемент кнопки «Подключить панель»

Перетащите текстовое поле и кнопку из библиотеки объектов и добавьте их в сцену « Добавить элемент» . Выберите Add View View Controller и соедините выход textField с текстовым полем и действие create(_:) с кнопкой. Действие create(_:) должно запускаться при возникновении события Touch Up Inside . Измените заголовок кнопки на « Создать» и добавьте необходимые ограничения макета в текстовое поле и кнопку.

Подключить текстовое поле

Чтобы завершить работу с пользовательским интерфейсом, добавьте элемент кнопки панели в левом верхнем углу панели навигации контроллера представления элемента и установите для его идентификатора значение « Отмена» . При выбранном контроллере « Добавить элемент» откройте инспектор подключений и подключите cancel(_:) действие на кнопку Отмена .

Создайте и запустите приложение, нажав Command-R, чтобы убедиться, что все правильно подключено.

Когда пользователь нажимает кнопку « Создать» , чтобы добавить элемент списка дел, контроллер представления добавления элемента должен уведомить контроллер представления. Делегирование является идеальным решением для этого сценария. Процесс довольно прост.

Мы создаем протокол делегата, ViewController соответствует класс ViewController . Когда AddItemViewController экземпляр AddItemViewController когда пользователь нажимает кнопку « ViewController объект ViewController устанавливается как делегат экземпляра AddItemViewController , что позволяет последнему уведомлять экземпляр ViewController при создании нового элемента ViewController . Давайте разберемся, чтобы лучше понять этот процесс.

Откройте AddItemViewController.swift и объявите протокол AddItemViewControllerDelegate под оператором импорта в верхней части. Объявление протокола выглядит как объявление класса. За ключевым словом protocol следует имя протокола.

1
2
3
4
5
6
7
import UIKit
 
protocol AddItemViewControllerDelegate {
 
    func controller(_ controller: AddItemViewController, didAddItem: String)
 
}

Концепция очень похожа на протоколы в Objective-C. Название протокола AddItemViewControllerDelegate и определяет один метод, controller(_:didAddItem:) .

Объект, который должен реализовать протокол делегата, является делегатом AddItemViewController . Сначала нам нужно создать свойство для делегата, как показано во фрагменте ниже.

1
2
3
4
5
6
7
8
class AddItemViewController: UIViewController {
 
    @IBOutlet var textField: UITextField!
 
    var delegate: AddItemViewControllerDelegate?
     
    …
}

Свойство delegate имеет тип AddItemViewControllerDelegate? необязательный тип, поскольку мы не можем быть уверены, что свойство delegate не равно nil . Обратите внимание, что имя протокола не заключено в угловые скобки, как в Objective-C.

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

Мы используем необязательную цепочку для вызова метода делегата на объекте делегата, что означает, что метод делегата вызывается только в том случае, если установлено свойство delegate . Значение текстового поля временно сохраняется в константе, item .

1
2
3
4
5
@IBAction func create(_ sender: Any) {
    if let item = textField.text {
        delegate?.controller(self, didAddItem: item)
    }
}

Реализация действия cancel(_:) очень проста. Все, что мы делаем, — AddItemViewController экземпляр AddItemViewController .

1
2
3
@IBAction func cancel(_ sender: Any) {
    dismiss(animated: true)
}

Там один кусок головоломки отсутствует, хотя. Свойство delegate экземпляра AddItemViewController в данный момент не устанавливается. Мы можем решить эту проблему, реализовав метод prepare(for:sender:) в классе ViewController . Однако сначала нам нужно вернуться к раскадровке.

Откройте Main.storyboard и выберите переход, соединяющий кнопку Add с навигационным контроллером . Откройте Инспектор Атрибутов и установите Идентификатор перехода в   AddItemViewController .

Затем реализуйте метод prepare(for:sender:) в классе ViewController как показано ниже. Обратите внимание на ключевое слово override префикс метода. Это должно быть уже знакомо.

01
02
03
04
05
06
07
08
09
10
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
    if segue.identifier == «AddItemViewController» {
        let navigationController = segue.destination as?
        let addItemViewController = navigationController?.topViewController as?
 
        if let viewController = addItemViewController {
            viewController.delegate = self
        }
    }
}

Мы начнем с проверки идентификатора перехода, чтобы убедиться, что мы готовимся к правильному переходу. Затем мы просим segue о его контроллере представления назначения. Вы можете ожидать, что это будет экземпляр AddItemViewController , но помните, что мы сделали контроллер представления корневым контроллером представления контроллера навигации. Это означает, что нам нужно запросить у контроллера навигации, контроллера представления назначения segue, его контроллер вида сверху.

Константа addItemViewController имеет тип AddItemViewController? из-за использования as? ключевое слово. Другими словами, используя as? мы topViewController свойства topViewController до необязательного типа.

В операторе if мы разворачиваем необязательное и устанавливаем свойство delegate для экземпляра ViewController .

Я уверен, что вы заметили использование нескольких опций в реализации метода prepare(for:sender:) . При взаимодействии с API Objective-C всегда лучше не рисковать. Хотя отправка сообщений на nil в Objective-C вполне возможна, в Swift — нет. Из-за этого ключевого различия вы всегда должны быть осторожны при взаимодействии с Objective-C API в Swift. Приведенный выше пример хорошо иллюстрирует это.

Реализация протокола AddItemViewControllerDelegate аналогична реализации протокола UITableViewDataSource . Мы начнем с согласования класса ViewController с протоколом, как показано ниже.

1
2
3
class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate, AddItemViewControllerDelegate {
    …
}

Затем мы реализуем методы протокола AddItemViewControllerDelegate , который сводится к реализации метода controller(_:didAddItem:) . Мы добавляем новый элемент в свойство items контроллера представления, перезагружаем табличное представление и отклоняем контроллер представления add item.

01
02
03
04
05
06
07
08
09
10
11
12
// MARK: Add Item View Controller Delegate Methods
 
func controller(_ controller: AddItemViewController, didAddItem: String) {
    // Update Data Source
    items.append(didAddItem)
 
    // Reload Table View
    tableView.reloadData()
 
    // Dismiss Add Item View Controller
    dismiss(animated: true)
}

Создайте и запустите приложение, чтобы проверить, можете ли вы добавлять новые элементы в список дел. В настоящее время мы не проверяем ввод пользователя. В качестве упражнения, покажите пользователю предупреждение, если он нажал кнопку « Создать» , а текстовое поле пустое. Добавление пустого списка дел не очень полезно. Правильно?

На этом уроке вы узнали, как объявлять и реализовывать собственный протокол. Вы также узнали, как создавать действия и подключать их в Интерфейсном Разработчике. На следующем уроке мы собираемся завершить наше приложение, добавив возможность удалять элементы, и мы также улучшим взаимодействие с приложением.

А пока ознакомьтесь с некоторыми другими нашими курсами и учебными пособиями по разработке iOS на языке Swift!

  • стриж
    Создание приложений для iOS с Swift 3
    Маркус Мюльбергер
  • IOS
    Идите дальше со Swift: анимация, работа в сети и пользовательские элементы управления
    Маркус Мюльбергер
  • стриж
    Запрограммируйте игру с боковой прокруткой с помощью Swift 3 и SpriteKit
    Дерек Дженсен
  • iOS SDK
    Правильный способ поделиться состоянием между контроллерами Swift View
    Маттео Манфердини