>

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.

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

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.

Criando uma UITableView por código
1
2
3
4
5
6
7
- (void)viewDidLoad {
  [super viewDidLoad];
  UITableView *tableView = [[UITableView alloc] initWithFrame:[[self view] bounds]];
  tableView.dataSource = self;
  [[self view] addSubview:tableView];
  [tableView release];
}

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
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    return 10;
}

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
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    static NSString *cellId = @"CellId";

    UITableViewCell *cell = [aTableView dequeueReusableCellWithIdentifier:cellId];
    if (cell == nil) {
        cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellId] autorelease];
    }

    cell.textLabel.text = [NSString stringWithFormat:@"Linha %d", [indexPath row]];

    return cell;
}

A table view chama esse método para cada linha que será exibida. E é responsabilidade doUITableViewDataSource 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
tableView.delegate = self;

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
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
  NSLog(@"Linha %d foi selecionada pelo usuário.", [indexPath row]);
}

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
verticalButtonListView:(VerticalButtonListView *)verticalButtonListView

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
- (void)verticalButtonListView:(VerticalButtonListView *)verticalButtonListView buttonTappedAtIndex:(NSInteger)index;

Neste momemnto muitos podem se perguntar, porque não criar algo menor só com “o que importa”?

1
- (void)buttonTappedAtIndex:(NSInteger)index;

Problemas de ter um delegate com um método como o acima:

  1. Somente metade das informações está sendo passada pro delegate.
  2. Imagine uma tela que tem duas listas diferentes, como você vai saber de qual lista veio a ação?
  3. 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 devemser 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”.

VerticalButtonListView.h
1
2
3
4
5
6
7
8
@protocol VerticalButtonListViewDelegate <NSObject>
- (void)verticalButtonListView:(VerticalButtonListView *)verticalButtonListView buttonTappedAtIndex:(NSInteger)index;
@end

@interface VerticalButtonListView : NSObject
@property (nonatomic, assign) id<VerticalButtonListViewDelegate> delegate;
// ...
@end

O código acima mostra como declarar o delegate VerticalButtonListViewDelegate que é um Protocol que herda do protocol NSObject.

1
@protocol VerticalButtonListViewDelegate <NSObject>

É 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
@property (nonatomic, assign) id<VerticalButtonListViewDelegate> delegate;

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:

VerticalButtonListView.m
1
2
3
4
5
- (void)buttonTouchedUp:(id)sender {
  NSInteger index = [self indexForButton:sender];

  [delegate verticalButtonListView:self buttonTappedAtIndex:index];
}

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”.

VerticalButtonListView.h
1
2
3
4
5
6
7
@protocol VerticalButtonListViewDelegate <NSObject>
- (void)verticalButtonListView:(VerticalButtonListView *)verticalButtonListView buttonTappedAtIndex:(NSInteger)index;

@optional
- (BOOL)verticalButtonListView:(VerticalButtonListView *)verticalButtonListView canTouchButtonAtIndex:(NSInteger)index;

@end

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:.

VerticalButtonListView.m
1
2
3
4
5
6
7
8
9
10
11
12
- (void)buttonTouchedUp:(id)sender {
  NSInteger index = [self indexForButton:sender];

  BOOL canBeTapped = YES;
  if ([delegate respondsToSelector:@selector(verticalButtonListView:canTouchButtonAtIndex:)]) {
      canBeTapped = [delegate verticalButtonListView:self canTouchButtonAtIndex:index];
  }

  if (canBeTapped) {
      [delegate verticalButtonListView:self buttonTappedAtIndex:index];
  }
}

O método respondsToSelector: é implementado pela classe NSObject e retorna YEScaso o objeto tenha implementado o método. Caso o delegate não herdasse de NSObjecto compilador iria colocar um warning na linha do if dizendo que o objeto delegatepode 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.



Comentários

Vantagens em estudar na RL System

Conheça algumas das vantagens em estudar com a RL System, se tornar um desenvolvedor Full Stak ou DevOps e dar um UP na sua carreira!

Plataforma de estudo simples e intuitiva

Através do Painel do Aluno, você tem acesso de forma simples aos seus cursos, arquivos, certificados e muito mais.

Suporte 24 horas para tirar suas dúvidas

Você tem suporte com nossos instrutores e moderadores em um fórum exclusivo para você tirar suas dúvidas.

Certificado ao final do curso

Todos os cursos da RL System emitem certificado, apôs uma prova online, comprovando que você realmente aprendeu.

Satisfação Garantida

A RL System possui mais de 300 mil alunos e mais de 500 mil certificados emitidos.

Planos de Estudos

Não sabe por onde começar? Temos planos de estudos exclusivos para Membros Gold. Torne-se agora um desenvolvedor Full Stack!

LiveCast

LiveCast semanal, ao vivo e online para tira dúvidas e ensino de novas tecnologias!