
Qual é a diferença entre o primeiro e o segundo exemplo?
Qual é o alvo responsável?
Nesse caso, o método é chamado quando o botão é clicado?
TL; DR
Quando um botão é clicado, nosso método é chamado nos dois casos.
Somente no primeiro exemplo, o UIKit tentará chamar o método no destino atribuído (temos o ViewController
). Ele falhará se esse método não existir.
No segundo exemplo, a cadeia de resposta do iOS é usada, o UIKit
procurará o UIResponder
mais próximo -a, que possui esse método. Não haverá falha se o nosso método não for encontrado.
UIViewController, UIView, UIApplication
herdam do UIResponder
.
Cadeia de resposta do iOS e o que há por trás
UIKit
processo da cadeia de resposta do iOS é tratado pelo UIKit
, que trabalha dinamicamente com uma lista vinculada de UIResponder
. Essa lista do UIKit
criada a partir do primeiro respondedor (o primeiro UIResponder
que registrou o evento, temos o UIButton(UIView)
e suas subviews
.

O UIKit percorre a lista de UIResponder
e verifica com canPerformAction
a nossa função.
open func canPerformAction(_ action: Selector, withSender sender: Any?) -> Bool
Se o UIResponder
selecionado não puder trabalhar com um método específico,
UIKit
envia recursivamente ações para o próximo UIResponder
na lista usando o método de target
que retorna o próximo UIResponder
.
open func target(forAction action: Selector, withSender sender: Any?) -> Any?
Esse processo é repetido até que um dos UIResponder
possa trabalhar com nosso método ou a lista termine e o sistema ignore esse evento.
No exemplo do segundo clique, ele foi tratado pelo UIViewController
, mas o UIKit
primeiro enviou uma solicitação ao UIView
pois era o primeiro respondedor. Ele não tinha o método necessário; portanto, o UIKit
redirecionou as ações para o próximo UIResponder
em uma lista vinculada, que era o UIViewController
que possuía o método desejado.
Na maioria dos casos, a iOS Responder Chain
do iOS Responder Chain
é uma simples lista subviews
de subviews
, mas sua ordem pode ser alterada. Você pode fazer o UIResponder (becomeFirstResponder)
se tornar
primeiro UIResponder
e retorne à posição antiga usando resignFirstResponder
. Isso geralmente é usado com um UITextField
para mostrar o teclado que será chamado apenas quando o UITextField
for o first responder
.
Cadeia de resposta do iOS e UIEvent
A Cadeia de Respondentes também está envolvida em tocar na tela, movimentos, cliques. Quando o sistema detecta algum tipo de evento (toque, movimento, controle remoto, imprensa), um UIEvent
é criado sob o capô e enviado usando o método UIApplication.shared.sendEvent()
para o UIWindow
. Após receber o evento, o UIWindow
determina o uso do hitTest:withEvent
ao qual esse evento pertence ao UIResponder
e atribui a ele o first responder
. A seguir, é apresentado o trabalho com uma lista vinculada de UIResponder
descrita acima.
Para trabalhar com UIEvent
sistema, as subclasses de UIResponder (UIViewController, UIView, UIApplication)
podem substituir esses métodos:
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?)
Apesar do fato de a capacidade de herdar e chamar sendEvent
manualmente estar presente, o UIResponder
não se destina a isso. Isso pode criar muitos problemas com a operação de eventos personalizados, o que pode levar a ações incompreensíveis causadas por um first responeder
respondente aleatório que pode responder ao seu evento.
Por que é útil, onde usar
Apesar do iOS Responder Chain
totalmente controlado pelo UIKit
, ele pode ser usado para resolver o problema de delegação / comunicação. UIResponder
ações do UIResponder
são semelhantes ao NotificationCenter.default.post
único.
Vamos dar um exemplo: temos um UIViewController
, que está no fundo da pilha UINavigationController e precisamos contar o que aconteceu quando um botão foi clicado em outra tela. Você pode usar o padrão delagate ou NotificationCenter.default.post
, mas uma opção bastante simples é usar a iOS Responder Chain
do iOS Responder Chain
.
button.addTarget(nil, action: #selector(RootVC.doSomething), for: .touchUpInside)
Quando pressionado, o método na UIViewController
será chamado. #selector pode usar os seguintes parâmetros:
@objc func doSomething() @objc func doSomething(sender: Any?) @objc func doSomething(sender: Any?, event: UIEvent?)
remetente é o objeto que enviou o evento - UIButton, UITextField e assim por diante.
Recursos adicionais para aprendizado [eng]:
Boa descrição do UIEvent, UIResponder e alguns exemplos avançados (coordenador patern)
Leia mais sobre a cadeia de resposta do ios
Exemplo de cadeia de respondentes na prática
Off dock na cadeia de resposta do iOS
Off Dock por UIResponder