Tehnografi.com - Технологические новости, обзоры и советы
[adinserter block="67"]

Целевое действие, объясненное на Swift

Следующая статья поможет вам: Целевое действие, объясненное на Swift

Вернуться в блог

Target-Action — это механизм iOS, который используется для вызова определенной функции для определенного объекта. Он широко используется во многих фреймворках и библиотеках iOS, хотя вы, возможно, до сих пор не задумывались об этом. Давайте узнаем, как это работает!

Из этого руководства по разработке приложений вы узнаете:

  • Что такое таргет-экшен и для чего он используется
  • Что означает «я» при использовании его с целевым действием
  • Как работать с селекторами и сигнатурами функций
  • Как совместимость Objective-C и Swift влияет на целевое действие
  • Использование целевого действия в практической разработке iOS

Рассмотрим идею о том, что программирование — это не что иное, как отдача команд вещам. Отдавайте команды многим вещам и реагируйте на взаимодействие с пользователем, и вы получите приложение (что-то вроде).

Вот одна из таких команд:

bob.kicks(мяч)

В приведенном выше коде функцияicks() вызывается для объекта bob. А функция Kicks() принимает один параметр ball. Боб бьет по мячу. Это довольно простое программирование, верно?

Далее, представьте, что мы создаем приложение с одной кнопкой. Имя его переменной — button, а тип — UIButton, который является частью платформы UIKit. Когда пользователь нажимает кнопку, мы хотим выполнить определенную функцию с именем fireworks().

Как мы собираемся сообщить кнопке, чтобы она вызывала функцию fireworks() при нажатии на нее? Вот тут-то и приходит на помощь целевое действие.

Давайте сделаем один шаг назад на секунду. Помните пример bob.kicks(ball)? То, что там происходит, на самом деле называется обменом сообщениями. Когда вы кодируете эту строку в Swift, вы на самом деле отправляете сообщение «пинки» с аргументом «мяч» объекту bob.

С целевым действием вы делаете то же самое. Отправка сообщения, действия, объекту, цели.

Давайте посмотрим на пример для UIButton. Сначала мы создаем кнопку и устанавливаем текст на кнопке:

кнопка let = UIButton (тип: .plain)
button.setTitle(“Запустить фейерверк!”, for: .normal)

Затем мы используем механизм целевого действия:

button.addTarget(self, действие: #selector(fireworks), для: .touchUpInside)

В этой одной строке кода происходит несколько вещей. Мы вызываем функцию addTarget(_:action:for:) для кнопки с тремя параметрами:

  1. Цель – я сам
  2. Действие — #selector(fireworks) (так называемый селектор)
  3. Событие — .touchUpInside (из UIControl.Event).

Это работает следующим образом: когда происходит событие .touchUpInside, отправьте сообщение самому себе для выполнения функции fireworks().

А вот эта функция fireworks() для полноты картины:

@objc func фейерверк() {
print(“БУУУМ!!! ПФИИИИУУУУ!!! ККККГГГРРРР!!!”)
}

Почему кнопка не может просто выполнить функцию fireworks()?

Что ж, учтите, что UIButton не знает вашего кода. Он не знает, что у вас есть эта функция, и более того, он не может получить прямой доступ к вашей функции! Вам нужен способ сообщить кнопке, что делать, и вы делаете это, задавая ей цель и действие. Вы можете думать об этом как о «ссылке» на функцию, которая сохраняется для вызова в более поздний момент времени.

Константа .touchUpInside происходит из структуры UIControl.Event, которая является частью класса UIControl. Целевое действие в основном используется в подклассах UIControl, таких как UIButton. Структура определяет множество событий управления, таких как .valueChanged, .editingDidEnd и .touchDown.

Событие .touchUpInside используется для взаимодействия с нажатием, скажем, кнопки. Когда вы снова поднимете палец после касания, появится .touchUpInside («прикосновение внутри [item]») событие запускается. Это событие очень распространено.

Далее, давайте поближе посмотрим на себя.

Ключевое слово self относится к текущему экземпляру класса внутри этого экземпляра класса.

В объектно-ориентированном программировании ваш код организован в классы. Вы можете создавать объекты из этих классов так же, как вы можете создавать здания по чертежам.

Давайте посмотрим на пример. Сначала мы создаем класс:

класс Транспортное средство {
вар колеса = 0
вар позиция = 0
}

Затем мы создаем экземпляр этого класса:

пусть автомобиль = Транспортное средство()
машина.колеса = 4

В любой функции класса Vehicle вы можете использовать self для ссылки на текущий экземпляр Vehicle. Так:

func диск(в позицию: Int) {
self.position = позиция
}

В приведенном выше коде вы используете self, чтобы различать позицию свойства и позицию параметра. Ключевое слово self — это значение, которое ссылается на текущий экземпляр класса, значение, с которым вы можете работать внутри класса.

Проверь это:

пусть машина =
car.drive(до: 999)

В приведенном выше коде self внутри функции Drive(to:) относится к тому же объекту, что и автомобиль. Внутри класса Vehicle self ссылается на текущий экземпляр класса.

Но есть и другое применение для себя. Вы можете передать его в своем коде, как любое другое значение или ссылку:

button.addTarget(self, действие: #selector(fireworks), для: .touchUpInside)

С помощью приведенного выше кода вы передаете self кнопке, поэтому кнопка теперь имеет ссылку на текущий экземпляр класса. Теперь он может использовать эту ссылку для вызова функции fireworks() для объекта, на который ссылается сам. Внутри функции addTarget() он, конечно, больше не называется self, а, вероятно, что-то вроде target.

Внутри и с помощью Objective-C вызов этого селектора выглядит примерно так:

если([target respondsToSelector: selector]) {
[target performSelector: selector];
}

Этот код Objective-C сначала проверяет, является ли данный селектор допустимой функцией для определенной цели, а затем вызывает этот селектор для цели. Фактически это то же самое, что:

цель.селектор()

За исключением того, что фактический селектор, то есть вызываемая функция, передается как значение!

Целевым объектом обычно является контроллер, например контроллер представления. Вы добавили кнопку в представление и функцию в его контроллер представления, и вы хотите, чтобы эта функция выполнялась при нажатии кнопки. В этом случае целевым объектом будет контроллер представления. И учитывая, что вы кодируете все это внутри класса контроллера представления, целью будет self.

Можете ли вы также передать что-то еще, кроме себя? Да! Вы можете передать любое значение в качестве целевого объекта. Контроллер маршрутизации может, например, передать другой объект контроллера в addTarget(_:action:for:) и позволить этому объекту контроллера обрабатывать события.

А что там с селекторами? Селектор используется для описания имени функции.

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

Swift не использует селекторы, а Objective-C использует. А поскольку многие платформы iOS написаны на Objective-C и вы используете их вместе со Swift, вы используете селекторы в Swift.

До Swift 2.2 в качестве селекторов можно было использовать строки. Так:

Селектор(“functionToExecute:”)

У этого есть серьезный недостаток: вы не можете проверить, действителен ли селектор во время компиляции. Это часто приводит к тому, что ужасный неопознанный селектор отправляется в экземпляр ошибки.

Итак, начиная с Swift 2.2+, вы можете использовать синтаксис #selector(…) следующим образом:

#selector(functionToExecute(_:))

Приведенный выше селектор является ссылкой на функцию, которую можно проверить во время компиляции. Перед запуском приложения Swift проверяет, действительно ли существует селектор («функция»). Если этого не произойдет, вы увидите сообщение об ошибке. Это уменьшает количество ошибок, поскольку их можно обнаружить на ранней стадии.

Что интересно в селекторах, так это то, что они используют специальный формат подписи для описания функций. Вы, вероятно, видели это раньше, потому что оно используется повсюду. Appleдокументация и руководства по iOS.

Короче говоря, в подписи используется имя функции и ноль, одна или несколько меток аргументов в круглых скобках. Несколько примеров:

  • Функция Fireworks() имеет фирменный фейерверк (без аргументов и круглых скобок).
  • Функция Fireworks(intensity: Int) имеет подпись Fireworks(intensity:) (круглые скобки, метка аргумента, за которой следует двоеточие)
  • Вызов функции fireworks(intensity: Int, size: Int) имеет подпись fireworks(intensity:size:) (круглые скобки, несколько меток с двоеточиями)
  • Если параметр не имеет имени, его метка — _, в вызове функции Fireworks(_ Intensity: Int, Color: UIColor), которая, например, имеет подпись Fireworks(_:color:).

Быстрая подсказка: Используйте функцию автозаполнения Xcode, чтобы получить правильную сигнатуру функции. Начните вводить #selector(, а затем нажмите клавишу Esc. Появится раскрывающийся список с возможными совпадениями функций. Выберите правильную функцию, и правильная подпись автоматически добавится в ваш код.

В зависимости от вашего вкуса и стиля кодирования, использование селекторов в коде Swift может оказаться довольно запутанным. Особенно, если сигнатура функции длинная. Чтобы решить эту проблему, вы можете создать расширение для структуры Selector. Так:

Селектор расширений {
статический let Fireworks = #selector(Effects.fireworks)
static let onButtonTapped = #selector(ViewController.onButtonTapped(_:))
}

Вы можете либо поместить каждый селектор в одно расширение (плохая идея), либо пометить расширение как fileprivate, чтобы сделать его доступным только в одном файле (и классе).

Теперь вы можете использовать селектор как выводимую константу, например:

button.addTarget(self, селектор: .onButtonTapped, for: .touchUpInside)

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

Не забудьте добавить атрибут @objc к функции, которую вы хотите использовать с целевым действием. Это сделает функцию доступной для Objective-C, что необходимо, поскольку UIControl (и целевое действие) является классом Objective-C.

До сих пор мы рассмотрели, что такое механизм целевого действия, как он работает, и зашли в обход селекторов. Остается, конечно, вопрос: как целевое действие влияет на практическую разработку iOS? Давайте погрузимся.

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

Рассмотрим UIButton, класс, который мы рассматривали ранее. Он использует целевое действие для реагирования на нажатия пользователем кнопки с помощью события управления .touchUpInside.

Аналогичным элементом пользовательского интерфейса является UISwitch, переключатель включения и выключения, который можно «перевернуть». Этот класс генерирует событие элемента управления .valueChanged при переключении переключателя. Вы можете отреагировать на это событие, используя target-action с функцией addTarget(_:action:for:).

Еще один класс, использующий механизм целевого действия, — NotificationCenter. С помощью NotificationCenter вы можете транслировать уведомления наблюдателям с помощью шаблона проектирования Observer-Observable. Он идеально подходит для трансляции событий между одним или несколькими несвязанными компонентами вашего приложения.

И угадайте, как вы сообщаете NotificationCenter выполнить определенную функцию при получении уведомления? С механизмом целевого действия. Так:

NotificationCenter.default.addObserver(self, селектор: #selector(onDidReceiveData(_:)), имя: .didReceiveData, объект: ноль)

В приведенном выше коде первый безымянный аргумент self является целью (или наблюдателем), а параметр селектора принимает значение селектора. Когда наблюдается уведомление с именем .didReceiveData, функция onDidReceiveData(_:) вызывается самостоятельно.

Другой компонент разработки iOS использует целевое действие: таймеры. В этом случае вы планируете таймер и сообщаете объекту Timer, какую функцию вызывать, когда таймер истечет и сработает. Его синтаксис уже должен показаться вам знакомым:

Timer.scheduledTimer(timeInterval: 1.0, цель: self, селектор: #selector(fire), userInfo: ноль, повторы: true)

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

Динамический способ, которым target-action соединяет части вашего кода с другими частями и инструктирует объект выполнить функцию, имеет недостаток: что, если эта функция не существует?

Вот тут-то и появляется проверка #selector(…) во время компиляции в Swift 2.2+. Прежде чем запускать код, во время его компиляции компилятор проверяет, действительно ли существуют ссылочные функции. Если функция отсутствует, вы увидите ошибку, и ваше приложение не запустится. И это поможет вам избежать ошибок, которые особенно трудно исправить!

Механизм целевого действия становится устаревшим компонентом iOS, остатком Objective-C. Он до сих пор широко используется, например, в UIKit, но мы видим, как его заменяют в iOS SDK более новыми подходами, такими как замыкания.

Механизм целевого действия — это не то, о чем вы зацикливаетесь каждый день, но стоит изучить, как он работает. Это неотъемлемая часть SDK для Objective-C, и вы не сможете обойтись без его использования в Swift.

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