iOS Responder Chain или Что спрашивают на собеседовании

Моя цель - предложение широкого ассортимента товаров и услуг на постоянно высоком качестве обслуживания по самым выгодным ценам.

Прежде чем перейти к статье, хочу вам представить, экономическую онлайн игру Brave Knights, в которой вы можете играть и зарабатывать. Регистируйтесь, играйте и зарабатывайте!

image


Какая разница между первым и вторым примером?

За что отвечает таргет?

В каком случае вызывается метод при нажатие кнопки?

TL;DR


При нажатии на кнопку наш метод вызывается в обоих случаях.


Только в первом примере UIKit попытается вызвать метод в назначенном таргете(у нас это ViewController). Будет краш, если этого метода не существует.


Во втором же примере используется iOS Responder Chain, UIKit будет искать самого ближнего UIResponder-a у которого есть данный метод. Краша не будет, если наш метод не найден.


UIViewController, UIView, UIApplication наследуют от UIResponder.


iOS Responder Chain и что под капотом


Всем процессом iOS Responder Chain занимается UIKit, который динамично работает со связным списком UIResponder-ов. Этот список UIKit создает из first responder(первый UIResponder который зарегистрировал событие, у нас это UIButton(UIView) и его subviews.


image


UIKit проходит через список UIResponder-ов и проверяет с помощью canPerformAction на наличие нашей функции.


open func canPerformAction(_ action: Selector, withSender sender: Any?) -> Bool

Если выбранный UIResponder не может работать с конкретным методом,
UIKit рекурсивно посылает действия к следующему UIResponder-у в списке с помощью метода target который возвращает следующего UIResponder-а.


open func target(forAction action: Selector, withSender sender: Any?) -> Any?

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


Во втором примере нажатия обработалось UIViewController-ом, но UIKit сначала отправил запрос к UIView так как он был first responder. У него не было нужного метода, поэтому UIKit перенаправил действия на следующего UIResponder-а в связном списке кем являлся UIViewController у которого был нужный метод.


В большинстве случаев iOS Responder Chain это простой массив subviews, но его очередность можно изменить. Можно заставить UIResponder (becomeFirstResponder) стать
первым UIResponder и вернуть его к старой позиции с помощью resignFirstResponder. Это часто используется с UITextField для показа клавиатуры которая будет вызвана, только когда UITextField является first responder-ом.


iOS Responder Chain и UIEvent


The Responder Chain так же участвует при касаниях экрана, движениях, нажатиях. Когда система определяет какое-то события(touch, motion, remote-control, press), под капотом создается UIEvent и отправляется с помощью метода UIApplication.shared.sendEvent() к UIWindow. После получения события UIWindow определяет с помощью метода hitTest:withEvent к какому UIResponder данное событие принадлежит и назначает его first responder-ом. Дальше идет работа с связным списком UIResponder-ов описанная выше.


Что бы работать с системными UIEvent-ами, сабклассы UIResponder (UIViewController, UIView, UIApplication) могут переопределить данные методы:


open func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?)
open func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?)
open func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?)
open func touchesCancelled(_ touches: Set<UITouch>, with event: UIEvent?)
open func pressesBegan(_ presses: Set<UIPress>, with event: UIPressesEvent?)
open func pressesChanged(_ presses: Set<UIPress>, with event: UIPressesEvent?)
open func pressesEnded(_ presses: Set<UIPress>, with event: UIPressesEvent?)
open func pressesCancelled(_ presses: Set<UIPress>, with event: UIPressesEvent?)
open func motionBegan(_ motion: UIEvent.EventSubtype, with event: UIEvent?)
open func motionEnded(_ motion: UIEvent.EventSubtype, with event: UIEvent?)
open func motionCancelled(_ motion: UIEvent.EventSubtype, with event: UIEvent?)
open func remoteControlReceived(with event: UIEvent?)

Не смотря что возможность наследовать и вызывать sendEvent в ручную присутствует, UIResponder не предназначен для этого. Это может создать много проблем с работой кастомных событий, которые могут привести к не понятным действиям вызванными случайным first responeder-ом который может отреагировать на ваше событие.


Чем это полезно, где использовать


Не взирая на то, что iOS Responder Chain полностью контролируется UIKit-ом, его можно использовать для решения проблемы делегирования/общения. UIResponder действия похоже на одноразовые NotificationCenter.default.post.


Возьмем пример, у нас есть рут UIViewController, который глубоко находится в стеке UINavigationController и нам нужно ему передать что произошло при нажатие кнопки на другом экране. Можно воспользоваться делагат паттерном или NotificationCenter.default.post, но довольно простой вариант это использования iOS Responder Chain.


button.addTarget(nil, action: #selector(RootVC.doSomething), for: .touchUpInside)

При нажатие будет вызываться метод в рут UIViewController. #selector может принимать следующие параметры:


func doSomething()
func doSomething(sender: Any?)
func doSomething(sender: Any?, event: UIEvent?)

sender это объект который отправил событие — UIButton, UITextField и так далее.


Дополнительные ресурсы для изучения [eng]:


Хорошое описание UIEvent, UIResponder и пару продвинутых примеров(координатор патерн)
Подробная статья о ios responder chain
Пример responder chain на практике
Офф дока по iOS responder chain
Офф дока по UIResponder

Источник: https://habr.com/ru/post/464463/


Интересные статьи

Интересные статьи

SWAP (своп) — это механизм виртуальной памяти, при котором часть данных из оперативной памяти (ОЗУ) перемещается на хранение на HDD (жёсткий диск), SSD (твёрдотельный накоп...
Часто от программистов PHP можно услышать: «О нет! Только не „Битрикс“!». Многие специалисты не хотят связываться фреймворком, считают его некрасивым и неудобным. Однако вакансий ...
Одна из самых отстойных вещей в технических собеседованиях — то, что это чёрный ящик. Кандидатам сообщают лишь то, прошли ли они на следующий этап без каких-либо подробностей, почему так вышло. ...
Периодически мне в разных вариантах задают вопрос, который «в среднем» звучит так: «что лучше: заказать интернет-магазин на бесплатной CMS или купить готовое решение на 1С-Битрикс и сделать магазин на...
На сегодняшний день у сервиса «Битрикс24» нет сотен гигабит трафика, нет огромного парка серверов (хотя и существующих, конечно, немало). Но для многих клиентов он является основным инструментом ...