Ao criar um app iOS com Swift, você provavelmente já se perguntou por que este controlador de visualização recebe o evento ao tocar em um botão.
Quando você rastreia por onde e como os eventos de toque percorrem a tela, encontra o padrão Chain of Responsibility na base.
Em resumo, a cadeia UIResponder é um exemplo representativo de como a Apple implementou, no nível do framework, o padrão Chain of Responsibility, um dos padrões de projeto GoF. Os responders passam a responsabilidade ao próximo responder até encontrar um objeto capaz de processar o evento.
Neste artigo, examino o código para explicar o que é Chain of Responsibility, como a cadeia UIResponder realmente funciona e como entendê-la ajuda no desenvolvimento prático.
Resumo dos pontos principais
- Chain of Responsibility é um padrão de projeto no qual vários objetos passam uma requisição como uma cadeia até encontrar um capaz de processá-la.
- A cadeia UIResponder do iOS implementa esse padrão no nível do framework.
- Um evento de toque sobe de UIView pelas superviews, pelo controlador de visualização, pela janela e pelo aplicativo.
- A propriedade next aponta para o próximo elo da cadeia; se ninguém processar o evento, ele será simplesmente descartado.
O que é o padrão Chain of Responsibility?
Chain of Responsibility é um padrão comportamental que desacopla o remetente de uma requisição do objeto que a processa.
É parecido com encaminhar uma aprovação em uma empresa. Se um assistente puder resolver, termina ali; caso contrário, passa ao gerente e, se ele também não puder, ao diretor.
Quem envia a requisição não precisa saber quem dará a aprovação final. Basta entregá-la ao primeiro elo, e a cadeia a encaminha automaticamente.
O ponto central é que cada objeto processador precisa saber apenas duas coisas: se consegue processar a requisição e quem é o próximo objeto caso não consiga.
Uma versão bem simples em Swift teria esta estrutura.
class Handler {
var next: Handler? // Próximo elo da cadeia
func handle(_ request: Int) {
// Passar a responsabilidade ao próximo objeto se não puder processar
next?.handle(request)
}
}
Essa estrutura, em que uma única propriedade next conecta a cadeia e o que não foi processado é passado para next, está incorporada diretamente ao UIResponder.
Como funciona a cadeia UIResponder?
No iOS, todo objeto que pode receber eventos de toque, movimento ou controle remoto herda de UIResponder. UIView, UIViewController, UIWindow e UIApplication são subclasses de UIResponder.
Por isso, cada um desses objetos possui uma propriedade cujo nome exato é next. Ela é definida em UIResponder.
// UIResponderPropriedade definida em
var next: UIResponder? { get }
Quando o usuário toca na tela, o sistema primeiro encontra a view mais interna que recebeu o toque. Isso é chamado de hit-testing e define a candidata a first responder.
Se essa view não processar o evento, o sistema segue next para cima. A ordem é aproximadamente esta.
- UIView (a view tocada)
- Suas superviews
- O UIViewController que gerencia a view
- UIWindow
- UIApplication
- UIApplicationDelegate
Se ninguém processar o evento depois que a cadeia for percorrida, ele será descartado silenciosamente. O app não trava nem gera erro; apenas ignora o evento.
O interessante é que next nem sempre é a superview.
Se a view for a view raiz de um controlador de visualização, next será o controlador, não uma superview.
Qual é a utilidade disso na prática?
Sinceramente, no começo o app funciona bem mesmo sem conhecer essa estrutura interna. Mas entendê-la muda claramente a forma de depurar.
Vou contar um caso que vivi: um gesto de toque não funcionava em uma view personalizada.
Ao rastrear em ordem onde o responder parava, encontrei rapidamente a causa: isUserInteractionEnabled estava false em uma superview, interrompendo a cadeia.
A propagação de eventos personalizados também é útil. Você pode definir mensagens que sobem pela cadeia UIResponder e entregar uma ação de uma view profunda ao controlador de visualização superior sem delegates.
endEditing(true), muito usado para ocultar o teclado, também utiliza essa cadeia. Ele encontra o first responder e chama resignFirstResponder.
Em resumo, entender a cadeia UIResponder permite rastrear logicamente onde um evento parou e projetar sua propagação para cima sem abusar de delegates.
Perguntas frequentes
P. first responder e a cadeia responder são coisas diferentes? R. first responder é um único objeto qualificado para receber um evento primeiro; a cadeia responder é toda a cadeia que sobe a partir dele.
P. next sempre aponta para uma superview? R. Não. Uma view comum aponta para sua superview, mas a view raiz de um controlador de visualização tem o controlador como next.
O nome Chain of Responsibility pode parecer difícil, mas tudo se resume a um princípio simples: se não puder processar, passe ao próximo. Espero que este artigo tenha ajudado você a entender a estrutura oculta do iOS chamada cadeia UIResponder. Quando encontrar um bug em que um evento não funciona, siga essa cadeia elo por elo.

