Uma das maiores dificuldades de quem está começando a desenvolver para iOS é entender a ligação entre as várias partes do SDK. Como o primeiro contato costuma ser com o UIKit, enteder e saber usar os delegates é essencial, pois eles são a forma usada pelo UIKit para ligar os componentes da View ao Controller.
A documentação da Apple diz o seguinte sobre delegates:
Delegação é um padrão simples e poderoso em que um objeto age no lugar de, ou em coordenação com, outro objeto.
Ou seja, é uma forma de comunicação entre dois, e somente dois, objetos. Entender que somente dois objetos estão envolvidos é o que diferencia o uso de delegates das duas outras formas de comunicação no iOS: Notification e Key Value Observing (KVO).
Neste artigo falarei sobre como usar os delegates que vem no iOS e a partir desse conhecimento entender como e quando utilizar esse padrão nos seus próprios objetos.
Delegates no UIKit
Várias das classes do UIKit usam delegates, é a forma de ligação entre os componentes e os controllers. Normalmente a classe que possui um delegate tem uma propriedade chamada delegate e o nome do Protocol com sufixo “Delegate”. Alguns componentes mais complexos possuem dois delegates sendo um deles responsável pelos dados que serão exibidos, a propriedade deste delegate costuma ser chamada de dataSource e seu Protocol tem sufixo “DataSource”.
| Componente | Delegate(s) |
|---|---|
UITextField |
UITextFieldDelegate |
UIPicker |
UIPickerDelegate e UIPickerDataSource |
UIScrollView |
UIScrollViewDelegate |
UITableView |
UITableViewDelegate e UITableViewDataSource |
O UITableView é a forma padrão de listar dados no iOS, essa classe é responsável por exibir a lista na tela e interpretar as interações que o usuário faz com a lista, é isso que ela faz e faz bem. As outras duas importantes funções da lista são delegadas: obtenção dos dados da lista e tratamento de eventos. Essa classe é uma das que mais delega funções e é por isso que eu gosto de usá-la como exemplo e como inspiração na criação dos meus próprios delegates.

Delegação dos dados - UITableViewDataSource
Para não ficar preso a uma listas de algum determinado tipo de dado, como strings, o UITableView delega essa responsabilidade através do protocol UITableViewDataSource. Com isso é possível mostrar qualquer tipo de dado sem precisar criar uma subclasse para cada variação.
Normalmente UITableViewDataSource é implementado no controller, já que é dele a responsabilidade de ligar a view ao model. Mas nada impede que o delegate seja implementado em uma subclasse simples deNSObject.
1 2 3 4 5 6 7 | |
No exemplo acima após a criação da UITableView sua propriedade dataSource é setada para self, indicando que o UIViewController que a criou vai ser o objeto responsável por ser a origem dos dados da lista. Então toda vez que sua table view precisar de um dado ela irá chamar o metódo corresponde no objeto que foi setado a propriedade dataSource. Por exemplo, para informar quantas linhas a table view tem você deve implementar o seguinte método:
1 2 3 | |
Neste caso a table view terá sempre 10 linhas. No uso real você irá retornar o valor com base no tamanho de sua lista. Agora que a quantidade de linhas já está definida é preciso implementar o método que retorna o que vai ser exibido em cada linha.
1 2 3 4 5 6 7 8 9 10 11 12 | |
A table view chama esse método para cada linha que será exibida. E é responsabilidade do UITableViewDataSource o que será mostrado em cada linha, nesse exemplo simples somente uma lista de strings é exibida mas se quiser uma lista toda customizada é nesse método que você vai trabalhar.
Esses dois métodos são os únicos obrigatórios no UITableViewDataSource, mas existem outros muito úteis basta dar uma olhada na documentação.
Respondendo a eventos - UITableViewDelegate
Com a sua lista exibindo os dados é preciso fazê-la responder a eventos, como selecionar uma determinada linha. O resposável por fazer isso na UITableView é o UITableViewDelegate, esse delegate é muito completo e vale a pena dar uma boa olhada na documentação.
1
| |
Para setar o delegate basta incluir o código acima no método viewDidLoad criado anteriormente. Diferente do UITableViewDataSource todos os métodos do UITableViewDelegate são opcionais. O método abaixo é chamado sempre que uma linha da lista é selecionada:
1 2 3 | |
O uso dos delegates é muito similar e sem segredos.
Criando Seus Próprios Delegates
Saber usar delegates do SDK é somente a primeira parte, é essencial saber quando e como criar os seus próprios.
Nomenclatura
O primeiro ponto que considero muito importante ao criar seus delegates é utilizar corretamente o padrão de nomenclatura, que é bem simples. Supondo que você tenha criado uma classe chamada VerticalButtonListView que gera uma lista de botões na vertical com base em um array de nomes, é preciso delegar a ação de quando ocorrer um tap em dos botões.
O nome do delegate é o nome da classe com o sufixo “Delegate”: VerticalButtonListViewDelegate. O nome dos métodos devem ter sempre o primeiro parametro a instância que gerou a chamada. Logo o primeiro parâmetro de todos os métodos do delegate VerticalButtonListViewDelegate deve ser:
1
| |
A ação que precisamos delegar na view VerticalButtonListView é o que será feito quando houver um tap em um determinado botão. Como podem haver vários botões na lista é preciso informar qual botão gerou a chamada, faremos isso passando o índice do botão na lista:
1
| |
Neste momemnto muitos podem se perguntar, porque não criar algo menor só com “o que importa”?
1
| |
Problemas de ter um delegate com um método como o acima:
- Somente metade das informações está sendo passada pro delegate.
- Imagine uma tela que tem duas listas diferentes, como você vai saber de qual lista veio a ação?
- Se no futuro você criar o
HorizontalButtonListView, como vai diferenciar os métodos de dois delegates diferentes?
Resumindo, não deixe de passar o objeto que gerou a ação nos métodos do seu delegate utilizando o mesmo nome da classe do objeto.
Implementando
Delegates são implementados em Objective-C através de Protocols.
Protocols declaram métodos que podem ser implementados por qualquer classe.
Eu prefiro dizer que Protocols são definições de interfaces cujas implementações devem ser feitas por outras classes. O Protocol nunca implementa nenhum método. Então, o delegate é uma interface, ou conjunto de métodos, que outra classe irá implementar. Assim o objeto “delegador” sabe exatamento qual método chamar no objeto “delegado”.
1 2 3 4 5 6 7 8 | |
O código acima mostra como declarar o delegate VerticalButtonListViewDelegate que é um Protocol que herda do protocol NSObject.
1
| |
É importante fazer seus Protocols herdarem de NSObject pois na prática seus objetos vão mesmo serem subclasses de NSObject, definindo isso no Protocol faz com que o compilador não gere nenhum warning quando você precisar chamar um método de NSObject no delegate, como por exemplo description.
1
| |
Para que VerticalButtonListView consiga chamar os métodos do delegate é preciso criar uma propriedade que irá guardar a referência para o objeto que implementa os métodos. A propriedade tem que ser assign, pois não faz sentido que a instância de VerticalButtonListView tenha ‘ownership’ sobre seu delegate. Não entendeu nada sobre ownership e assign? Então leia e aprenda tudo sobre gerenciamento de memória, é essencial.
Sempre que houver tap em um botão é preciso chamar o método do delegate. Para fazer isso é simples basta colocar o seguinte código na action de cada botão:
1 2 3 4 5 | |
Com isso, todas vez que a ação do botão for executada a responsabilidade vai ser passada pro delegate para que ele decida o que fazer.
Há um recurso interessante no Protocol do Objective-C que é bem útil ao criar delegates: métodos opcionais. É possível definir métodos no delegate que são opcionais, como exemplo vamos criar um método que pergunta ao delegate se um botão pode ser “tocado”.
1 2 3 4 5 6 7 | |
Todos os métodos declarados a partir da diretiva @optional serão opcionais. Um método opcional é um método em que o compilador não gera nenhum warning caso ele não tenha sido implementado. Sendo assim, não podemos chama-lo diretamente como fizemos com o verticalButtonListView:buttonTappedAtIndex:.
1 2 3 4 5 6 7 8 9 10 11 12 | |
O método respondsToSelector: é implementado pela classe NSObject e retorna YES caso o objeto tenha implementado o método. Caso o delegate não herdasse de NSObject o compilador iria colocar um warning na linha do if dizendo que o objeto delegate pode não responder a respondsToSelector:.
Concluindo
Delegates são essenciais para ligar os objetos da View com o Controller. Entender como criá-los e usá-los é essencial para fazer qualquer projeto no iOS. Use o bom senso e não deixe seus controllers virarem uma macarronada de delegates, sempre faça delegates com nomes que são auto explicativos e não economize no tamanho dos nomes não há vantagem nenhuma nisso.