Você está na página 1de 179

como funciona?

Fundamentos

Apostila do Aluno
Fundamentos

Apostila do Aluno
Copyright © Instituto de Artes Interativas
Todos os direitos reservados. Esse material não pode ser copiado, fotocopiado, reproduzido,
traduzido ou convertido em qualquer forma eletrônica ou legível por meio, em parte ou no todo,
sem a aprovação prévia por escrito do Instituto de Artes Interativas.

Elaboração do Conteúdo
André Baldi

Elaboração dos Exercícios


André Baldi

Diagramação
Samuel Sanção de Moura

Edição nº1
Novembro /2011

iai? Instituto de Artes Interativas


Rua Amauri, 352 - Itaim Bibi
CEP: 01448-000 - São Paulo, SP, Brasil
(11) 3071-4017 | contato@iai.art.br
http://iai.art.br

iOS Fundamentos 3
iOS: Fundamentos

Sobre o iai?
A história do iai?

O iai? Instituto de Artes Interativas foi fundada em março de 2009 com cursos de desenvolvimento
de aplicativos para iOS contando com uma proposta inovadora e atualizada, nestes últimos 3 anos
tivemos mais de 2000 alunos em nossos cursos.

A produtora já trabalha a 2 anos atendendo a demanda do mercado por aplicativos para


plataformas móveis como o iOS e Android.

Dessa forma, procuramos uma equipe coesa, criativa, e eficiente para desenvolver soluções e
inovações no mundo das artes interativas e educação.

Galeria
Exposição de artes e locação
para eventos corporativos

Escola Produtora
Cursos práticos, rápidos Desenvolvimento de
e objetivos aplicativos móveis

Na área educacional, O Iai? se destaca na oferta cursos com aprendizagem rápida, presencial e a
distância, contando com know how em sistemas móveis como iPhone SDK, Android SDK e Windows
phone 7, além de Lógica de programação, design, interfaces entre outros. Os cursos do iai? são
voltados para profissionais que buscam a capacitação por meio do conhecimento e domínio de
novas ferramentas tecnológica com cursos práticos e objetivos aonde aprendem produzindo.

A Produtora atua no mercado no desenvolvimento de projetos em diversos clientes em vários


mercados. Aplicativos móveis, peças de marketing e publicidade nesses dispositivos,

Como um espaço de exposição de novas idéias, a Galeria…


Nossa força está nos profissionais qualificados, com professores que também atuam do mercado
em diversos projetos, essa é forma de garanti além do conhecimento técnico, dicas, conceitos e
informações sobre o mercado.

iOS Fundamentos 5
Como encontrar o iai?

O Iai? conta com várias formas de se comunicar com o seus clientes e amigos, use a forma mais
fácil e prática para você.

iai.art.br
instituto de artes interativas

@iaibrasil

iai? instituto de artes interativas

iai.art.br/clube-iai.html

iOS Fundamentos 6
Outros cursos do iai?

IOS SDK
Programação para o sistema operacional da Apple

ANDROID SDK
Programação para o sistema operacional da Google

Lógica de programação
Conceitos Básicos de programação

Orientação a objeto e Objective C


Conceitos de desenvolvimento de sistemas e Linguagem de programação base para o IOS.

Orientação a objeto e Java


Conceitos de desenvolvimento de sistemas e Linguagem de programação base para o Android.

Design
O objetivo do curso é ensinar a montar um projeto de design utilizando como plataforma um
aplicativo para iPhone.

Interface
O curso é focado nas guias da Apple e pretende discutir as melhores formas de interface.

Games
Focamos em estratégias e técnicas para desenvolvimento de jogos para o Iphone, demonstrando o
funcionamento do Cocos2D. 

Windows Phone 7
Venha desenvolver para o mais novo sistema operacional da Micrsoft que promete mexer com
Mercado de tecnologia. 

Maiores informações acesse: WWW. IAI.ART.BR

iOS Fundamentos 7
iOS: Fundamentos

Sumário
Sumário

Capítulo 1
1.1 NSObject 14
1.2 NSString 14
1.3 NSLog 15
1.4 Hello World! 16
1.5 UIView 21
1.6 Exemplo de UIView 23
1.7 UILabel 25
1.8 Exemplo de UILabel 26
1.9 UIButton 28
1.10 Exemplo de UIButton 31
Capítulo 2
2.1 UIViewController 36
2.2 Exemplo de UIViewController 37
2.3 XIB e Storyboard 42
2.4 Exemplo de Storyboard 43
Capítulo 3
3.1 IBOutlet 47
3.2 Exemplo de IBOutlet 47
3.3 IBAction 52

iOS Fundamentos 9
Sumário

3.4 Exemplo de IBAction 53


3.5 UISlider 56
3.6 Exemplo de UISlider 56
3.7 UISwitch 58
3.8 Exemplo do UISwitch 59
Capítulo 4
4.1 UIImage e UIImageView 63
4.2 Exemplo de UIImageView 63
4.3 UISegmentedControl 67
4.4 Exemplo de UISegmentedControl 67

4.5 Delegate 71

4.6 UITextField e UITextFieldProtocol 72

4.7 Exemplo de UITextField 73

Capítulo 5
5.1 DataSource 78

5.2 UIPickerView 78

5.3 NSArray 80

5.4 Exemplo de UIPickerView 80

Capítulo 6
6.1 UIScrollView 86

iOS Fundamentos 10
Sumário

6.2 UIPageControl 87
6.3 Exemplo 87
6.4 Zoom com UIScrollView 93
6.5 Exemplo de UIScrollView com zoom 93
Capítulo 7
7.1 Modal View Controller 98
7.2 Exemplo 98
7.3 Modal View Controller via código 103
7.4 Exemplo de Modal View Controller via código 104
7.5 @property e @synthesize 108
7.6 Exemplo de @property 110
Capítulo 8
8.1 UINavigationController 116
8.2 Exemplo de UINavigationController 117
8.3 Exemplo de UINavigationController no Storyboard 124
8.4 UITableView e UITableViewCell 128
8.5 Exemplo de UITableView 129
8.6 UITableViewController 136
8.7 Master Detail Application 137

iOS Fundamentos 11
Sumário

Capítulo 9
9.1 NSDictionary 139
9.2 Exemplo de NSDicitonary 139
9.3 Arquivos de Property List 147
9.4 Exemplo de plist 147
9.5 Objetos mutáveis 152
9.6 Edição de tabelas 154
9.7 Exemplo de edição de tabela 155
9.8 Persistência de dados 160
9.9 Exemplo de persistência de dados 160
Capítulo 10
10.1 UITabBarController 166
10.2 Exemplo de UITabBarController 168
10.3 Tabbed Application 173
10.4 Imagens padrões e Retina Display 173
10.5 Exemplo 175
10.6 Arquivo Info.plist 178

iOS Fundamentos 12
iOS: Fundamentos

Capítulo 1
1

1.1 NSObject

NSObject é a classe raiz da maioria das classes do Objective-C e por isso


fornece uma interface básica para as classes que a estendem. Seus principais
métodos são:

• alloc: cria uma nova instância da classe, mas que ainda não pode ser usada.

• init: inicializa um novo objeto criado pelo método alloc. Subclasses do


NSObject devem implementar esse método.

• description: retorna uma string que descreve o objeto. As subclasses de


NSObject podem sobrescrever esse método caso seja necessário imprimir
informações relevantes do objeto.

• class: retorna o objeto do tipo Class.

1.2 NSString
A classe NSString estende a classe NSObject e define um array de caracteres,
ou seja, uma string. Pode parecer um pouco semelhante com as strings de C,
mas como ela é um objeto (em C ela classe string é um tipo literal), ela pode
receber mensagens. Isso significa que a classe NSString possui métodos, e com
eles é possível concatenar, dividir, buscar por substrings, comparar, transformar,
entre outros.
A forma mais simples de se criar um objeto do tipo NSString é utilizando o
construtor do Objective-C @””, por exemplo NSString *umaString = @”Primeiro

exemplo de string”;. Para enviarmos uma mensagem para o objeto, fazemos da

iOS Fundamentos 14
1

seguinte forma:

[umaString length];

Como toda instância da classe NSString é um objeto, podemos enviar uma


mensagem diretamente para o construtor:

[@”Primeiro exemplo de string” length];

1.3 NSLog

A Apple possui um sistema de logs chamado Apple System Logger (ASL), no


qual mensagens são registradas. No Objective-C o método NSLog(), que recebe
um formato de string como parâmetro, adiciona um registro a ele. O console da
IDE vai exibir os logs registrados pelo aplicativo que está executado. A forma
simples de utilizar o NSLog() é passando um NSString como parâmetro:

NSLog(@”Exemplo simples de log.”);

No método NSLog() é possível também passar informações dinâmicas através de


diretivas. Existem três tipos mais comuns de diretivas:

%@: imprime objetos do Objective-C. Implicitamente é chamado o método

iOS Fundamentos 15
description do objeto.
%i: imprime números inteiros.
%f: imprime números fracionários. Nesse caso é possível definir o número de
casas decimais a serem impressas. Por exemplo, se utilizarmos a diretiva alterada
para %.3f, vai fazer com que a parte fracionária do número seja arredondada
para exibir somente três casas decimais.

Portanto, para utilizarmos as diretivas no NSLog() fazemos da seguinte forma:

NSLog(@”O NSString: %@, o inteiro: %i e o decimal: %.2f”, umaString, umInteiro,


umDecimal);

O resultado seria:

O NSString: Primeiro exemplo de string, o inteiro: 10 e o decimal: 23.25

1.4) Hello World!


Como toda linguagem de programação, nada melhor que a prática para aprender
os novos conceitos. Vamos criar um novo projeto do tipo “Empty Application”
chamado HelloWorld, para iPhone e utilizando Automatic Reference Counting
(ARC).

iOS Fundamentos 16
1

Figura 1.1: Novo projeto do tipo “Empty Application”

Figura 1.2: Projeto HelloWorld utilizando ARC

Primeiro vamos alterar os métodos utilizados no ciclo de vida do aplicativo. São


eles:

iOS Fundamentos 17
1

• application: didFinishWithLaunchingOptions: é chamado quando o sistema


terminou de carregar os recursos necessários para inicar o aplicativo.

• applicationWillResignActive: é chamado quando o aplicativo vai para de ser


executado e enviado para o background.

• applicationDidEnterBackground: é chamado quando o aplicativo entra


em background. Depois que esse método é chamado, o aplicativo tem
aproximadamente cinco segundos para salvar informações relevantes para
o resumo do aplicativo. Caso o método não terminar nesse tempo, a
execução é parada.

• applicationWillEnterForeground: é chamado quando o aplicativo vai voltar


para a ativa, e é importante carregar as informações salvas durante o
método anterior.

• applicationDidBecomeActive: é chamado quando o aplicativo volta a ativa.

• applicationWillTerminate: é chamado quando iOS precisa remover o


aplicativo da memória por algum motivo. Depois que esse método é
chamado, o aplicativo tem aproximadamente cinco segundos para salvar
informações relevantes do aplicativo. Caso o método não terminar nesse
tempo, o iOS mata o aplicativo.

Vamos alterar o arquivo AppDelegate.m para que ele fique assim:

#import “AppDelegate.h”
@implementation AppDelegate
@synthesize window = window;
_
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(
NSDictionary *)launchOptions
{

iOS Fundamentos 18
1

NSLog(@”O aplicativo terminou de ser carregado.”);


self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen]
bounds]];
// Override point for customization after application launch.
self.window.backgroundColor = [UIColor whiteColor];
[self.window makeKeyAndVisible];
return YES;
}
- (void)applicationWillResignActive:(UIApplication *)application
{
NSLog(@”O aplicativo vai sair da ativa.”);
}
- (void)applicationDidEnterBackground:(UIApplication *)application
{
NSLog(@”O aplicativo entrou em background.”);
}
- (void)applicationWillEnterForeground:(UIApplication *)application
{
NSLog(@”O aplicativo vai ficar ativo.”);
}
- (void)applicationDidBecomeActive:(UIApplication *)application
{
NSLog(@”O aplicativo está ativo.”);
}

iOS Fundamentos 19
1

- (void)applicationWillTerminate:(UIApplication *)application
{
NSLog(@”O aplicativo vai fechar.”);
}
@end

No código acima podemos perceber a presença do termo self. Ele é uma


variável local que sempre é criada automaticamente para referenciar o objeto
que recebe a mensagem, que no caso é a instância da classe AppDelegate.

Ao executarmos o aplicativo, temos o seguinte resultado no console:

HelloWorld[9501:f803] O aplicativo terminou de ser carregado.


HelloWorld[9501:f803] O aplicativo está ativo.
Se apertarmos o botão home do dispositivo:
HelloWorld[9501:f803] O aplicativo vai sair da ativa.
HelloWorld[9501:f803] O aplicativo entrou em background.
E se abrirmos o aplicativo ainda na mesma execução:
HelloWorld[9501:f803] O aplicativo vai ficar ativo.
HelloWorld[9501:f803] O aplicativo está ativo.

iOS Fundamentos 20
1

No simulador o resultado será o seguinte:

Figura 1.3: Execução do aplicativo

1.5 UIView
A classe UIView definida no framework UIKit é a base de praticamente todos os
componentes do iOS. Ela define uma área retangular na tela e as informações
necessárias para controlar o conteúdo dentro dela. Uma view pode conter
outras views dentro dela, chamadas subviews, podendo assim criar uma
hierarquia de views mais sofisticada. Uma view pode conter quantas subviews
forem necessárias, mas ela pode ser filha de apenas uma view. A view pai é
conhecida como superview.
Uma view é definida por sua propriedade chamada frame. Um frame é
constituído pelo ponto de origem da view em relação à superview (x e y) e pelo
tamanho do retângulo (largura e altura).

iOS Fundamentos 21
1

As propriedades mais importantes de uma view são:

• backgroundColor: cor de fundo da view.

• hidden: define se a view está escondida (afeta as subsviews).

• alpha: define a transparência da view (afeta as subviews).

• frame: posição e tamanho da view em relação à supervew.

• bounds: posição e tamanho da view em relação ao seu próprio sistema de


coordenadas, por padrão a origem é (0,0) e o tamanho é o mesmo que do
frame.

• clipsToBounds: define se o conteúdo fora do bounds deve ser desenhado.

• userInteractionEnabled: defie se a view pode receber interações.

• center: o ponto central da view em relação à superview.

• superview: view pai.

• subviews: array de views filhas.

Os métodos mais comuns para adicionar uma subview ou remover uma view são:

• addSubview: : esse método é chamado da view pai e recebe a view filha


como parâmetro.

• removeFromSuperView: esse método é chamado da view filha para


removê-la da view pai.

iOS Fundamentos 22
1

1.6 Exemplo de UIView


Vamos alterar o arquivo AppDelegate.m do projeto HelloWorld para adicionar
uma view dentro na tela. Para isso, vamos modificar o método application:
didFinishLaunchingWithOptions: para o seguinte:

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(


NSDictionary *)launchOptions
{
NSLog(@”O aplicativo terminou de ser carregado.”);
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen]
bounds]];
// Override point for customization after application launch.
self.window.backgroundColor = [UIColor whiteColor];
[self.window makeKeyAndVisible];

UIView *primeiraView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 320,


480)];
primeiraView.backgroundColor = [UIColor blueColor];
[self.window addSubview:primeiraView];

return YES;
}

iOS Fundamentos 23
1

A classe UIView possui um método próprio para inicialização chamado


initWithFrame:. Esse método vai inicializar a instância da classe UIView e atribuir
o valor passado como parâmetro à propriedade frame. Essa propriedade é do
tipo CGRect, que é uma estrutura que contém os pontos de origem e tamanho
da view e para criar essa estrutura é utilizado o método CGRectMake(), que
recebe como parâmetros o x, o y, a largura e a altura.
Depois mudamos o valor da propriedade backgroundColor da view para azul.
Essa propriedade é do tipo UIColor, uma classe que representa uma cor junto
de sua opacidade (alpha). Por fim, adicionamos a view que criamos à tela.
Ao executarmos o aplicativo, obteremos o seguinte resultado, comparado à
execução anterior:

Figura 1.4: Execução do aplicativo comparado à execução anterior

iOS Fundamentos 24
1

1.7) UILabel
A classe UILabel estende a classe UIView para exibir um texto estático na
tela e fornece controle para modificar a aparência do texto. Suas principais
propriedades são:

• text: texto a ser exibido.

• font: estilo da fonte do label. Importante notar que o iOS não possui
todas as fontes e se não encontrada a fonte desejada, a fonte padrão é
utilizada.

• textColor: cor do texto.

• textAlignment: alinhamento do texto. Pode receber os valores definidos


na enum UITextAlignment da classe NSString, que são UITextAlignmentLeft,
UITextAlignmentCenter e UITextAlignmentRight.

• numberOfLines: número de linhas do texto.

• minimumFontSize: tamanho mínimo da fonte.

• adjustsFontSizeToFitWidth: define se o tamanho do texto deve ser ajustado


para ser exibido por completo dentro do label.

• lineBreakMode: técnica utilizada quando o texto não cabe dentro do label.


Os possíveis valores estão definidos na enum UILineBreakMode da classe
NSString.

iOS Fundamentos 25
1

1.8 Exemplo de UILabel


Vamos alterar o arquivo AppDelegate.m do projeto HelloWorld para
adicionarmos um label na view que havíamos criado. Portanto o método
application: didFinishLaunchingWithOptions: ficará assim:

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(


NSDictionary *)launchOptions
{
NSLog(@”O aplicativo terminou de ser carregado.”);
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen]
bounds]];
// Override point for customization after application launch.
self.window.backgroundColor = [UIColor whiteColor];
[self.window makeKeyAndVisible];

UIView *primeiraView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 320,


480)];
primeiraView.backgroundColor = [UIColor blueColor];
[self.window addSubview:primeiraView];

UILabel *helloLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, 160, 320,


40)];

iOS Fundamentos 26
1

helloLabel.backgroundColor = [UIColor clearColor];


helloLabel.textColor = [UIColor whiteColor];
helloLabel.textAlignment = UITextAlignmentCenter;
helloLabel.font = [UIFont systemFontOfSize:28.0];
helloLabel.text = @”Hello World!”;
[primeiraView addSubview:helloLabel];
return YES;
}

Como a classe UILabel estende a classe UIView, podemos acessar os métodos


e propriedades mostrados anteriormente, como o método de inicialização
initWithFrame: e a propriedade backgroundColor. Depois alteramos algumas
propriedades do label e o adicionamos na view.
Ao executarmos o aplicativo, teremos o seguinte resultado:

Figura 1.5: Execução do aplicativo com o label

iOS Fundamentos 27
1

1.9 UIButton
A classe UIButton implementa um botão, que recebe eventos de toque e envia
mensagens para a classe que responde a elas. A UIButton estende a classe
UIControl, que é a classe base para os componentes que recebem interações.
A classe UIControl não pode ser instanciada diretamente, ela apenas define
uma estrutura comum para suas subclasses, e seu papel fundamental é atribuir
mensagens para ações recebidas por componentes e distribuí-las para os
devidos objetos. Suas principais propriedades são:

• enabled: define se o componente deve receber interações ou não.

• selected: define o estado do componente (nem todos os componentes que


estendem o UIControl respondem à essa propriedade)

O principal método dessa classe é o addTarget: action: forControlEvents:,


que mapeia uma mensagem para eventos do componente. Ele recebe como
parâmetros o objeto (target) que vai responder à mensagem (action) e os
eventos (controlEvents) do tipo UIControlEvent. Os principais eventos são:

• UIControlEventTouchUpInside: ocorre quando o usuário aperta o botão e


levanta o dedo com ele dentro do botão.

• UIControlEventValueChanged: ocorre quando é alterado o valor do


componente (válido para os componentes que possuem a propriedade
value).

iOS Fundamentos 28
1

O parâmetro action do método acima é do tipo SEL, que é um identificador


único que o compilador gera para cada método, ou seja, todo id SEL (ou
selector) é diferente. Entretanto, métodos com o mesmo nome possuem o
mesmo id, o que é essencial para o polimorfismo, permitindo que a mesma
mensagem seja enviada para objetos de diferentes classes. Para recuperar o
selector de um método, utilizamos a diretiva @seletor() e passamos o nome do
método como parâmetro.
Para criarmos um botão utilizamos um método da classe buttonWithType:, que
recebe como parâmetro o tipo do botão que vai ser criado. Os tipos de botões
existentes estão definidos na enum UIButtonType e são os seguintes:

• UIButtonTypeCustom: um botão sem estilo, apenas define o espaço de


toque do botão.

• UIButtonTypeRoundRect: um botão retangular com bordas arredondadas


com tamanho variável.

• UIButtonTypeDetailDisclosure: um botão circular, azul com uma seta


dentro e de tamanho fixo.

• UIButtonTypeInfoLight: um botão circular, de fundo branco com um i


marcado e de tamanho fixo.

• UIButtonTypeInfoDark: um botão circular, de fundo escuro com um i


marcado e de tamanho fixo.

• UIButtonTypeContactAdd: um botão circular, azul com um + no meio e de


tamanho fixo.

iOS Fundamentos 29
1

Os tipos de botões estão exemplificados na imagem abaixo, na ordem acima (o


tipo custom está marcado para poder ser visualizado na imagem):

Figura 1.6: Diferentes tipos de UIButton (UIButtonTypes)

Para customizar o botão utilizamos dois métodos:

• setTitle: forState: altera o texto do botão para um determinado estado.

• setImage: forState: altera a imagem do botão para um determinado estado.

Os possíveis estados de um botão estão definidos na classe UIControl, e são


eles:

• UIContolStateNormal: o estado normal do botão.

• UIControlStateHighlighted: quando o botão está pressionado.

• UIControlStateDisabled: quando o botão não está habilitado (definido pela


propriedade enabled)

Caso não seja especificado uma imagem ou um texto para o botão nos estados
pressionado ou desabilitado, será usado o valor definido para o estado normal.

iOS Fundamentos 30
1

1.10 Exemplo de UIButton


Vamos alterar o projeto HelloWorld para adicionarmos um botão na tela.
Para isso, primeiro é preciso definir um método no arquivo de interface, o
AppDelegate.h:

#import <UIKit/UIKit.h>

@interface AppDelegate : UIResponder <UIApplicationDelegate>

@property (strong, nonatomic) UIWindow *window;

-(void)apertouBotao:(id)sender;

@end

Toda mensagem chamada a partir de um evento de toque da tela pode receber


como parâmetro uma referência ao objeto que recebeu o evento, no exemplo
acima é o parâmetro sender. Caso não seja necessário, podemos definir o
método da seguinte forma: -(void)apertouBotao;.
Vamos alterar o método application: didFinishLaunchingWithOptions do arquivo
AppDelegate.m para adicionar o botão na tela:

iOS Fundamentos 31
1

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(


NSDictionary *)launchOptions
{
NSLog(@”O aplicativo terminou de ser carregado.”);
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen]
bounds]];
// Override point for customization after application launch.
self.window.backgroundColor = [UIColor whiteColor];
[self.window makeKeyAndVisible];

UIView *primeiraView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 320,


480)];
primeiraView.backgroundColor = [UIColor blueColor];
[self.window addSubview:primeiraView];

UILabel *helloLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, 160, 320,


40)];
helloLabel.backgroundColor = [UIColor clearColor];
helloLabel.textColor = [UIColor whiteColor];
helloLabel.textAlignment = UITextAlignmentCenter;
helloLabel.font = [UIFont systemFontOfSize:28.0];
helloLabel.text = @”Hello World!”;
[primeiraView addSubview:helloLabel];

iOS Fundamentos 32
1

UIButton *umBotao = [UIButton buttonWithType:UIButtonTypeRoundedRect];


umBotao.frame = CGRectMake(50, 240, 220, 40);
[umBotao setTitle:@”Meu Primeiro Botão” forState:UIControlStateNormal];
[umBotao setTitle:@”Apertado!” forState:UIControlStateHighlighted];
[umBotao addTarget:self action:@selector(apertouBotao:) forControlEvents:UI
ControlEventTouchUpInside];
[primeiraView addSubview:umBotao];

return YES;
}

Criamos um botão do tipo round rect utilizando o método buttonWithType:.


Como não foi definido um frame para o botão durante sua inicialização (não foi
chamado o método initWithFrame: da classe UIView) atribuímos um valor para
a propriedade frame do botão. Alteramos o texto do botão para os estados
normal e apertado, adicionamos a chamada do método apertouBotao: para o
evento de touch up inside e adicionamos o botão na view.
Precisamos também implementar o método apertouBotao: caso contrário
o aplicativo vai travar quando apertarmos o botão. Portanto vamos apenas
imprimir uma mensagem no console:

-(void)apertouBotao:(id)sender {
NSLog(@”Apertou o botão!”);
}

iOS Fundamentos 33
1

Ao executarmos o aplicativo, teremos o seguinte resultado:

Figura 1.7: Execução do aplicativo com UIButton

iOS Fundamentos 34
iOS: Fundamentos

Capítulo 2
2

2.1 UIViewController
A classe UIViewController fornece o modelo fundamental para controle das
views de um aplicativo para iOS. Toda view controller possui uma view associada
(acessada pela propriedade view) e é responsável por controlar as demais
subviews e responder aos eventos gerados por elas.
Normalmente não é criado instâncias da classe UIViewController, mas sim de
classes que a reimplementam, fazendo com que elas tenham o comportamento
adequado para o aplicativo. Quando estendemos essa classe, alguns métodos
são importantes e merecem uma atenção especial:

• loadView: é chamado automaticamente pelo view controller para carregar


a view pela primeira vez.

• viewDidLoad: é chamado logo após o view controller ter carregado a view


para a memória.

• viewWillAppear: é chamado para notificar que a view vai ser apresentada


na tela.

• viewDidAppear: é chamado para notificar que a view está na tela.

• viewWillDisappear: é chamado para notificar que a view vai ser removida


da tela ou escondida.

• viewDidDesappear: é chamado para notificar que a view foi removida ou


escondida.

• viewDidUnload: é chamado quando a view foi removida da memória.

iOS Fundamentos 36
2

2.2 Exemplo de UIViewController


Vamos criar um projeto do tipo “Empty Application” chamado “ViewControllers”
para iPhone e utilizando ARC. Vamos também criar um novo arquivo (atalho ⌘N)
chamado “MinhaViewController” que estende a classe UIViewController sem
utilizar XIB nem ser para iPad.

iOS Fundamentos 37
2

Figura 2.1: Menu para criar um novo arquivo

Figura 2.2: Subclasse de UIViewController

Figura 2.3: Arquivo chamado MinhaViewController sem XIB

iOS Fundamentos 38
2

No arquivo AppDelegate.h vamos importar a classe MinhaViewController e


adicionar uma varável de classe para ela:

#import <UIKit/UIKit.h>
#import “MinhaViewController.h”
@interface AppDelegate : UIResponder <UIApplicationDelegate> {
MinhaViewController *minhaViewController;
}
@property (strong, nonatomic) UIWindow *window;
@end
Depois de importado, vamos alterar o método application:
didFinishLaunchingWithOptions: para inicializar a vairável e atribuí-la como
rottViewController do aplicativo:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(
NSDictionary *)launchOptions
{
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen]
bounds]];
// Override point for customization after application launch.
self.window.backgroundColor = [UIColor greenColor];
[self.window makeKeyAndVisible];
minhaViewController = [[MinhaViewController alloc] init];
[self.window setRootViewController:minhaViewController];

iOS Fundamentos 39
2

return YES;
}
Vamos agora recriar o exemplo do projeto HelloWorld utilizando a classe
MinhaViewController para carregar os componentes. Portanto no arquivo
MinhaViewController.h vamos adicionar o método para o botão:
#import <UIKit/UIKit.h>
@interface MinhaViewController : UIViewController

-(void)apertouBotao:(id)sender;
@end
E no arquivo MinhaViewController.m vamos implementar o método para o
botão e o método loadView:
#pragma mark - MinhaViewController methods
-(void)apertouBotao:(id)sender {
NSLog(@”Apertou o botão na MinhaViewController!”);
}
#pragma mark - View lifecycle
// Implement loadView to create a view hierarchy programmatically, without
using a nib.
- (void)loadView
{
[super loadView];
UIView *primeiraView = [[UIView alloc] initWithFrame:CGRectMake(0, 0,
320, 480)];

iOS Fundamentos 40
2

primeiraView.backgroundColor = [UIColor blueColor];


[self.view addSubview:primeiraView];
UILabel *helloLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, 160, 320,
40)];
helloLabel.backgroundColor = [UIColor clearColor];
helloLabel.textColor = [UIColor whiteColor];
helloLabel.textAlignment = UITextAlignmentCenter;
helloLabel.font = [UIFont systemFontOfSize:28.0];
helloLabel.text = @”Hello World!”;
[primeiraView addSubview:helloLabel];
UIButton *umBotao = [UIButton buttonWithType:UIButtonTypeRoundedRect];
umBotao.frame = CGRectMake(50, 240, 220, 40);
[umBotao setTitle:@”Meu Primeiro Botão” forState:UIControlStateNormal];
[umBotao setTitle:@”Apertado!” forState:UIControlStateHighlighted];
[umBotao addTarget:self action:@selector(apertouBotao:) forControlEvents:UI
ControlEventTouchUpInside];
[primeiraView addSubview:umBotao];
}
No momento que o método loadView é chamado, a propriedade view ainda é
nil e precisa ser inicializada portanto temos que chamar o método loadView
da classe pai, ou seja, da classe UIViewController, utilizando o termo super.
Portanto chamamos [super loadView];.

Ao executarmos o aplicativo, obteremos o mesmo resultado que no projeto


HelloWorld:

iOS Fundamentos 41
2

Figura 2.4: Execução do aplicativo

E no console:
ViewControllers[1008:f803] Apertou o botão na MinhaViewController!

2.3 XIB e Storyboard


Arquivos do tipo XIB são arquivos XML utilizados para definir elementos
de interface, geralmente associados com uma classe que estende a classe
UIViewController para criar a interface da view. A partir do iOS 5 foi introduzido
o conceito de Storyboard, um novo jeito de definir a interface do seu aplicativo.
O arquivo XIB defini a interface de um view controller apenas, entretanto, com
o Storyboard é possível definir a interface de todos os view controllers em um
único arquivo, assim como também a transição entre eles, ou seja, storyboards
guardam uma visão geral do aplicativo.

iOS Fundamentos 42
2

2.4 Exemplo de Storyboard


No projeto ViewControllers, vamos criar um novo arquivo do tipo Storyboard,
para iPhone, chamado “MainStoryboard”.

Figura 2.5: Novo arquivo do tipo Storyboard

Precisaremos também editar a propriedade “Main Storyboard” nas definições do


projeto:

Figura 2.6: Edição da propriedade “Main Storyboard”

iOS Fundamentos 43
2

Em seguida, alteramos o método application: didFinishLaunchingWithOptions: do


arquivo AppDelegate.m para o seguinte:

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(


NSDictionary *)launchOptions
{
return YES;
}
Agora precisamos adicionar no Storyboard um componente que represente
a classe MinhaViewController. Para isso, vamos arrastar para dentro do
Storyboard um componente do tipo UIViewController e adicionar um
componente do tipo UILabel. Esse view controller é uma Scene dentro do
Storyboard.

Figura 2.7: Adição do UIViewController no Storyboard

Reparem na seta à esquerda da representação da view do view controller, ela

iOS Fundamentos 44
2

indica que esse view controller é o root view controller e possui a mesma
função que a linha [self.window setRootViewController:minhaViewController]; que
utilizamos no AppDelegate.m. Precisamos também alterar o tipo da classe no
campo “Custom Class” de UIViewController para MinhaViewController.
No arquivo MinhaViewController.m, vamos comentar o método loadView, pois
ele deve ser implementado somente quando o carregamento da view não seja
feito por Storyboard ou XIB.
Ao executarmos o aplicativo, teremos o seguinte resultado:

Figura 2.8: Execução do aplicativo utilizando Storyboard

Na grande maioria dos aplicativos para iOS, esse é o ponto inicial para a criação
do aplicativo: um AppDelegate, um ViewController e o Storyboard configurado
para utilizá-lo como root view controller. Para esse tipo de configuração inicial,
o Xcode fornece o template chamado “Single View Application”.

iOS Fundamentos 45
iOS: Fundamentos

Capítulo 3
3

3.1 IBOutlet
O editor de interface do Xcode é uma ferramenta muito completa e possui
todos os componentes básicos para a implementação de um aplicativo, mas
uma interface precisa também de uma ligação com seu controlador. Para que
isso seja possível, utilizamos a constante IBOutlet na definição do componente.
Isso faz com que o editor de interface reconheça aquele componente e
permita fazer a ligação entre o que foi definido no arquivo .h e o Storyboard.
Adicionando essa constante antes do tipo do componente na sua definição no
arquivo .h permite que o editor de interface o reconheça:

IBOutlet UILabel *labelOutlet;

3. Exemplo de IBOutlet
Vamos criar um novo projeto do tipo “Single View Application” chamado
“Componentes” utilizando Storyboard e ARC.

iOS Fundamentos 47
3

Figura 3.1: Configuração do projeto

No arquivo de definição de interface do ViewController, vamos adicionar um


IBOutlet do tipo UILabel. O arquivo ViewController.h ficará assim:

#import <UIKit/UIKit.h>
@interface ViewController : UIViewController {
IBOutlet UILabel *labelOutlet;
}
@end

No Storyboard vamos adicionar um componente do tipo UILabel que vai ser


ligado com o IBOutlet (ou somente outlet) que adicionamos. Vamos arrastar
o componente para dentro do Storyboard e fazer as edições customizações
desejadas:

iOS Fundamentos 48
3

Figura 3.2: Adição do UILabel no Storyboard

Para ligarmos a variável labelOutlet com o componente que acabamos de


adicionar, selecionamos o componente do tipo view controller na barra de
representação das Scenes do Storyboard (barra da esquerda) e selecionamos a
aba de conexões da área de utilidades (última aba da barra da direita).

Figura 3.3: Conexões da Scene do view controller

iOS Fundamentos 49
3

A aba de conexões vai exibir todos os outlets definidos no arquivo de definição


da interface do ViewController, na parte de Outlets. Para efetuarmos de fato
a conexão, vamos apertar no círculo que fica a frente do nome “labelOutlet”
e arrastar até o componente que adicionamos na tela. Para nos ajudar, o Xcode
não permite a conexão de componentes de tipos diferentes, por exemplo, ele
não permitirá a ligação entre o outlet do tipo UILabel com um componente do
tipo UIView, além de destacar os componentes que podem ser ligados.

Figura 3.4: Ligando o outlet ao componente

iOS Fundamentos 50
3

Figura 3.5: Outlet ligado ao componente

Reparem no outlet “view” da lista de outlets, ele é a representação da


propriedade view da classe UIViewController e está ligada à view pai da Scene.
Sempre que for adicionado uma nova Scene ao Storyboard, esse outlet vai estar
ligado à view pai.
Agora no arquivo ViewController.m, vamos modificar o método viewDidLoad
para alterarmos a cor de fundo do label para verificarmos a ligação entre
o outlet e o componente. Importante notar que o template “Single View
Controller” não traz a implementação do método loadView na classe
ViewController, pois o carregamento dos componentes da view é feito pelo
Storyboard.

#pragma mark - View lifecycle


- (void)viewDidLoad
{

iOS Fundamentos 51
3

[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
labelOutlet.backgroundColor = [UIColor yellowColor];
}
Como o componente está definido na Scene do Storyboard, não precisamos
inicializá-lo, pois ele ocorre automaticamente ao carregar a view e suas
subviews.
Ao executarmos o aplicativo, obtemos o seguinte resultado:
Figura 3.6: Execução do aplicativo

3.3 IBAction
A constante IBAction funciona de uma forma semelhante a IBOutlet, mas para
mensagens enviadas para os componentes. Utilizamos essa constante como
retorno do método que queremos utilizar, que possui o mesmo valor de void, e
o editor de interface passa a reconhecer esse método na aba de conexões do
view controller.

iOS Fundamentos 52
3

3.4 Exemplo de IBAction


No projeto anterior, vamos definir um novo método no arquivo de definição de
interface da classe ViewController. Portanto o arquivo ViewContoller.h vai
ficar assim:

#import <UIKit/UIKit.h>

@interface ViewController : UIViewController {


IBOutlet UILabel *labelOutlet;
}

-(IBAction)apertouBotao:(id)sender;

@end

Na Scene do Storyboard, vamos adicionar um novo botão e associar esse


IBAction ao evento de touch up inside. Na aba de conexões do view controller
vai aparecer o método apertouBotao: na área “Received Actions” e para fazer
a ligação é só apertar no círculo correspondente ao método e arrastar até o
botão. Feito isso, irá aparecer uma lista de eventos existentes e é só escolher o
desejado, que no caso será o “Touch Up Inside”.

iOS Fundamentos 53
3

Figura 3.7: Ligando o IBAction ao botão

Figura 3.8: Escolhendo o evento desejado

Feita a ligação, o método na parte de Received Actions vai exibir uma


identificação do componente e o evento com o qual está associado.

iOS Fundamentos 54
3

Figura 3.9: Identificação do método

Por último precisamos implementar o método apertouBotao: para alterar o


texto do label.

#pragma mark - IBActions


-(IBAction)apertouBotao:(id)sender {
labelOutlet.text = @”Apertou o botão!!”;
}
Ao executarmos o aplicativo, obtemos o seguinte resultado:

Figura 3.10: Execução do aplicativo com IBAction

iOS Fundamentos 55
3

3.5 UISlider
Um objeto do tipo UISlider é um componente utilizado para selecionar valores
contínuos de um determinado intervalo. Diferente do botão, o slider é baseado
valor atual, e por isso os métodos são chamados com evento do tipo “Value
Changed”. Suas principais propriedades são:

value: o valor atual (valor padrão é 0,5).

minimumValue: o valor mínimo (valor padrão é 0).

maximumValue: o valor máximo (valor padrão é 1).

continuous: define se o método deve ser chamado continuamente ou somente


quando terminar o evento (o padrão é contínuo).

minimumTrackTintColor: cor do fundo do slider.

maximumTrackTintColor: cor do preenchimento do slider.

3.6 Exemplo de UISlider


Vamos alterar o projeto anterior e adicionar um componente do tipo UISlider.
Primeiro vamos alterar o arquivo ViewController.h para adicionarmos um
IBAction para seu evento:

#import <UIKit/UIKit.h>

iOS Fundamentos 56
3

@interface ViewController : UIViewController {


IBOutlet UILabel *labelOutlet;
}

-(IBAction)apertouBotao:(id)sender;
-(IBAction)alterouSlider:(id)sender;

@end

Em seguida, vamos adicionar um UISlider na Scene do Storyboard e ligar o


IBAction ao evento “Value Changed”. A Scene deve ficar assim:

Figura 3.11: Scene com UISlider

Por fim vamos implementar o método alterouSlider: no arquivo


ViewController.m:

iOS Fundamentos 57
3

-(IBAction)alterouSlider:(id)sender {
UISlider *senderSlider = (UISlider *) sender;
labelOutlet.text = [NSString stringWithFormat:@”Novo valor do slider: %.2f”,
senderSlider.value];
}

Para recuperar o valor do slider, primeiro precisamos fazer um cast do


parâmetro sender para o tipo UISlider. Em seguida utilizamos o método
stringWithFormat: da classe NSString para criar uma string com o novo valor do
slider e a atribuímos ao texto do label.
Ao rodarmos o aplicativo, teremos o seguinte resultado:

Figura 3.12: Execução do aplicativo com UISlider

3.7 UISwitch
Um objeto do tipo UISwitch é utilizado para selecionar dois tipos valores: ligado
ou desligado. É baseado também nos valores, e portanto uma ação deve ser

iOS Fundamentos 58
3

associada ao evento de “Value Changed”. Suas propriedades principais são:

on: valor do switch.

onTintColor: cor do valor “ligado” do switch.

3.8 Exemplo do UISwitch


Vamos alterar o projeto anterior e adicionar um switch, que será responsável
por habilitar ou desabilitar os componentes da tela. Portanto precisaremos de
dois IBOutlets para os componentes (UIButton e UISlider) e um IBAction para o
switch. Portanto o ViewController.h ficará assim:

#import <UIKit/UIKit.h>
@interface ViewController : UIViewController {
IBOutlet UILabel *labelOutlet;
IBOutlet UIButton *botao;
IBOutlet UISlider *slider;
}
-(IBAction)apertouBotao:(id)sender;
-(IBAction)alterouSlider:(id)sender;
-(IBAction)alterouSwitch:(id)sender;
@end

iOS Fundamentos 59
3

Na Scene do Storyboard, vamos adicionar o componente na tela e fazer a


ligação dos outlets e do método alterouSwitch: ao evento de “Value Changed”.

Figura 3.13: Scene do Storyboard com o switch

Por último vamos implementar o método alterouSwitch: no arquivo


ViewController.m:

-(IBAction)alterouSwitch:(id)sender {
UISwitch *senderSwitch = (UISwitch *)sender;
botao.enabled = senderSwitch.on;
slider.enabled = senderSwitch.on;
if (senderSwitch.on) {
labelOutlet.text = @”Componentes habilitados”;
} else {
labelOutlet.text = @”Componentes desabilitados”;
iOS Fundamentos 60
3

}
}

Para habilitar os componentes utilizamos a propriedade enabled, que eles


recebem por herança do pai UIControl, com o valor da propriedade on do
switch, além de alter também o texto do label. Ao executarmos o aplicativo,
obtemos o seguinte resultado:

Figura 3.14: Execução do aplicativo com UISwitch

iOS Fundamentos 61
iOS: Fundamentos

Capítulo 4
4

4.1 UIImage e UIImageView


Um objeto do tipo UIImage é a representação de uma imagem, que pode ser
carregada de um arquivo local ou da internet e suporta os diversos tipos de
imagens existentes, como PNG, JPG e JPEG, BMP, GIF, TIFF, entre outros. Esse
objeto guarda o conteúdo da imagem no objeto e suas propriedades geralmente
são definidas pelos metadados da imagem.
Para exibir essa imagem na tela, utilizamos o componente definido pela classe
UIImageView, que pode exibir uma imagem estática ou uma série de imagens
(como uma animação). No segundo caso, é possível controlar a frequência e a
duração da animação, além de poder iniciar e parar a animação.
Sua principal propriedade é a image, que guarda um objeto do tipo UIImage para
mostrar na tela.

4.2 Exemplo de UIImageView


Vamos criar um novo projeto do tipo “Single View Application” chamado
“Imagens” utilizando Storyboard e ARC. Vamos precisar adicionar imagens ao
projeto, para isso, basta arrastar uma imagem qualquer do Finder para dentro do
projeto.

iOS Fundamentos 63
4

Figura 4.1: Arrastando novo arquivo para dentro do projeto

Em seguida, uma janela aparecerá com algumas opções. No campo “Destination”


marque a opção para copiar o arquivo para dentro da pasta destino, o que
vai fazer com que seja criada uma copia do arquivo dentro do projeto, caso
contrário será guardada uma referência para o arquivo no Finder. No campo
“Folders” marque a primeira opção para criar grupos para as pastas adicionadas,
isso faz com que o Xcode mantenha a mesma hierarquia de pastas, caso exista
alguma.

iOS Fundamentos 64
4

Figura 4.2: Configurações da adição do arquivo

Outra forma de adicionar imagens é utilizando a opção “Add files to


“NomeDoProjeto” ...” no menu File.

Figura 4.3: Adicionando arquivos pelo menu File

iOS Fundamentos 65
4

Na Scene do Storyboard, vamos adicionar um componente do tipo UIImageView


e atribuir a imagem na propriedade “Image”.

Figura 4.4: Scene com UIImageView

Ao rodarmos o aplicativo, teremos o seguinte resultado:

Figura 4.5: Execução do aplicativo

iOS Fundamentos 66
4

4.3 UISegmentedControl
Um objeto do tipo UISegmentedControl é um componente horizontal com
segmentos que funcionam como botões, portanto somente um segmento pode
estar selecionado. Suas principais propriedades são:

numberOfSegments: número de segmentos.

selectedSegmentIndex: segmento seleciado.

tintColor: cor do componente.

4.4 Exemplo de UISegmentedControl


No projeto anterior, vamos adicionar mais algumas imagens, e utilizando um
componente do tipo UISegmentedControl vamos alterar a imagem exibida pelo
UIImageView.
Vamos alterar o arquivo de definição de interface da classe ViewController
para adicionarmos um IBOutlet para o image view e um IBAction para o evento
do segmented control. O arquivo ViewController.h ficará assim:

#import <UIKit/UIKit.h>

@interface ViewController : UIViewController {


IBOutlet UIImageView *imageView;
iOS Fundamentos 67
4

}
-(IBAction)mudarImagem:(id)sender;

@end

No Storyboard, vamos adicionar um segmented control na view principal


da Scene e alterar o número de segmentos e o texto de cada segmento.
Precisamos também fazer as ligações do IBOutlet com a imagem e do IBAction
com o evento “Value Changed” do segmented control.

Figura 4.6: Configuração do segmented control

iOS Fundamentos 68
4

Figura 4.7: Scene do Storyboard

Por fim, vamos implementar o método mudarImagem: para quando for alterado
o segmento selecionado, trocar para a imagem correspondente. Portanto o
método ficará assim no arquivo ViewController.m:

#pragma mark - IBAction methods


-(IBAction)mudarImagem:(id)sender {
UISegmentedControl *segControl = (UISegmentedControl *) sender;
NSString *nomeImagem = @””;
switch (segControl.selectedSegmentIndex) {
case 0:
nomeImagem = @”Eagle.tif”;
break;
case 1:
nomeImagem = @”Owl.tif”;

iOS Fundamentos 69
4

break;
case 2:
nomeImagem = @”Parrot.tif”;
break;
default:
break;
}
UIImage *novaImagem = [UIImage imageNamed:nomeImagem];
imageView.image = novaImagem;
}

Fazemos um cast do parâmetro sender para UISegmentedControl para


conseguirmos acessar qual segmento está selecionado e assim escolhermos o
nome correto para a imagem. Com o nome escolhido, criamos um objeto do
tipo UIImage e o atribuímos para a variável image do objeto imageView.
Ao executarmos o aplicativo, teremos o seguinte resultado:

Figura 4.8: Execução do aplicativo com UISegmentedControl

iOS Fundamentos 70
4

4.5 Delegate
O Objective-C possui um mecanismo no qual um objeto guarda uma referência
para outro objeto que responde mensagens definidas em um protocolo. Esse
mecanismo é chamado “Delegation” e o objeto que recebe as mensagens é
chamado “delegate”. De uma forma geral, um objeto delega uma tarefa para
outro objeto, o delegate. E como isso pode ser útil? Com o delegate, é possível
customizar o comportamento dos componentes sem precisar criar uma nova
classe customizada que estende o componente. Portanto o objeto que envia a
mensagem é, geralmente, um componente já existente e o delegate é o objeto
do view controller customizado e que implementa o protocolo.
Os protocolos definem as possíveis mensagens que o delegate pode receber
do outro objeto. Para a classe que vai implementar os métodos, eles são
classificados de duas maneiras: ou sua implementação é obrigatória (@required)
ou é opcional (@optional).
Para uma classe implementar um protocolo, é necessário adicionar o protocolo
na definição da interface da classe, no arquivo .h. Por exemplo:

@interface ViewController : UIViewController <UITextFieldDelegate>

Uma classe pode implementar quantos protocolos forem necessários, mas os


componentes devem possuir apenas um delegate, ou seja, um objeto pode ser
delegate de N outros objetos, mas um objeto pode ter somente um delegate.

iOS Fundamentos 71
4

4.6 UITextField e UITextFieldProtocol


Um objeto do tipo UITextField exibe uma caixa de texto editável que envia uma
mensagem quando pressionado o botão de retornar do teclado. Suas principais
propriedades são:

text: o texto da caixa de texto.

placeholder: texto exibido quando o não há texto digitado na caixa de texto.

delegate: referência ao objeto que responde as mensagens geradas pelo


componente.
A propriedade delegate é do tipo id<UITextFieldDelegate>, o que significa que
ele pode ser um objeto de qualquer tipo, contanto que ele implemente o
protocolo UITextFieldDelegate, que define as mensagens geradas pela caixa
de texto durante todo o processo de edição de seu texto. Todos os métodos
desse protocolo são opcionais.
O método mais importante é o textFieldShouldReturn: que, apesar de ser
opcional, é sempre importante ser implementado, pois, quando o usuário
aperta na caixa de texto para editá-lo, automaticamente ele mostra o
teclado, entretanto quando o usuário aperta no botão de retornar do teclado,
ele não some automaticamente, a caixa de texto vai enviar a mensagem
textFieldShouldReturn: para o delegate, e ele é o responsável de remover o
teclado da tela. Dessa forma é possível fazer alguma verificação se o texto é
válido antes de remover o teclado.

iOS Fundamentos 72
4

No iOS existem vários tipos de teclados para as diferentes situações. Para


alterar o tipo de teclado exibido. Suas propriedades estão definidas no
protocolo UITextInputTraits que é implementado pela classe UITextField. Esse
protocolo define algumas customizações para o teclado, como capitalização
automática das palavras, autocorreção, o tipo de teclado e o botão de retorno.
A maioria dessas customizações podem ser acessadas diretamente na aba de
atributos do componente no editor de interfaces.

4.7 Exemplo de UITextField


No projeto anterior, vamos adicionar uma caixa de texto para trocar a imagem
digitando o seu nome. Para isso vamos alterar o arquivo ViewController.h para
implementar o protocolo UITextFieldDelegate utilizado pela caixa de texto
e criar um IBOutlet para o segmented control, pois ao alterarmos a imagem,
precisamos alterar também o segmento selecionado e manter as informações
na tela consistentes:

#import <UIKit/UIKit.h>

@interface ViewController : UIViewController <UITextFieldDelegate> {


IBOutlet UIImageView *imageView;
IBOutlet UISegmentedControl *segmentedControl;
}

iOS Fundamentos 73
4

-(IBAction)mudarImagem:(id)sender;
@end

Na Scene do Storyboard, precisamos ligar o IBOutlet ao segmented control,


adicionar uma caixa de texto e ligar também sua propriedade delegate ao view
controller. Para isso, na aba de conexões da caixa de texto vamos encontrar um
Outlet chamado delegate e, da mesma forma que ligamos um outlet do view
controller a um componente da tela, vamos fazer o caminho inverso e ligar o
outlet do componente ao view controller.

Figura 4.9: Ligando o delegate ao view controller

iOS Fundamentos 74
4

Figura 4.10: Scene do Storboard

No arquivo ViewController.m vamos implementar o método do protocolo


UITextFieldDelegate textFieldShouldReturn: para verificar se o nome da imagem
está correto, trocar a imagem e sumir com o teclado.

#pragma mark - UITextFieldDelegate methods


-(BOOL)textFieldShouldReturn:(UITextField *)textField {
textField.backgroundColor = [UIColor whiteColor];
if ([textField.text isEqualToString:@”Eagle.tif”]) {
[segmentedControl setSelectedSegmentIndex:0];
} else if ([textField.text isEqualToString:@”Owl.tif”]) {
[segmentedControl setSelectedSegmentIndex:1];
} else if ([textField.text isEqualToString:@”Parrot.tif”]) {
[segmentedControl setSelectedSegmentIndex:2];
} else {

iOS Fundamentos 75
4

textField.backgroundColor = [UIColor redColor];


}
[self mudarImagem:segmentedControl];
[textField resignFirstResponder];
return YES;
}
Quando o texto digitado é diferente do nome de alguma imagem que temos
no projeto, mudamos para vermelho a cor de fundo da caixa de texto, caso
contrário deixamos branco. Para comparar textos, a classe NSString fornece o
método isEqualToString: que recebe como parâmetro a string com a qual deve
ser comparada. Se o texto estiver correto, mudamos o segmento selecionado
do segmented control e chamamos o método mudarImagem:.
Para removermos o teclado da tela, precisamos avisar o componente que está
em foco que ele não será mais utilizado no momento. No exemplo isso implica
que a caixa de texto saia de foco e o teclado seja removido da tela.
Ao executarmos o aplicativo, obtemos o seguinte resultado:

Figura 4.11: Execução do aplicativo com caixa de texto

iOS Fundamentos 76
iOS: Fundamentos

Capítulo 5
5

5.1 DataSource
Assim como nos delegates, algumas classes enviam mensagens para seus
datasources. O datasource é muito semelhantes ao delegate, ele permite
que a classe seja customizada sem precisar estendê-la. Entretanto, a função
de um datasource (no português fonte de dados) é fornecer as informações
necessárias e os dados para popular seu objeto. Assim como o delegate, os
métodos do datasource podem ser opcionais ou obrigatórios.

5.2 UIPickerView
A classe UIPickerView implementa um componente giratório, como uma
máquina de caça-níqueis, para selecionar um valor dentro de uma lista de
possibilidades alinhando o valor desejado no centro do componente. Um picker
view possui colunas e cada coluna possui valores diferentes para suas linhas e
ambas são acessadas por índices.
Quando utilizamos um picker view, precisamos implementar os
protocolos UIPickerViewDelegate (para construir o componente) e o
UIPickerViewDataSource (para prover o número de colunas e linhas do
componente).
As principais propriedades do UIPickerView são:

numberOfComponents: número de colunas.

iOS Fundamentos 78
5

delegate: referência ao delegate.

datasource: referência ao datasource.

Os principais métodos do picker view são:

numberOfRowsInComponent: retorna o número de linhas da coluna passada


como parâmetro.

selectedRowInComponent: retorna o índice da linha que está selecionada na


coluna passada como parâmetro

O protocolo UIPickerViewDelegate possui dois métodos principais:

pickerView: titleForRow: forComponent: chamado quando o componente


está sendo carregado para saber texto da linha para a coluna passados como
parâmetro.
pickerView: didSelectRow: inComponent: chamado quando é selecionada uma
linha de uma coluna passados como parâmetro.

O protocolo UIPickerViewDataSource possui apenas dois métodos:

numberOfComponentsInPickerView: chamado quando o componente está


sendo carregado para saber o número de colunas no picker view.
pickerView: numberOfRowsInComponent: chamado quando o compoenente
está sendo carregado para saber o número de linhas para o componente
passado como parâmetro.

iOS Fundamentos 79
5

5.3 NSArray
A classe NSArray guarda uma lista ordenada de objetos, chamada de array.
Um objeto dessa classe pode conter qualquer objeto que derive da classe
NSObject, seja direta ou indiretamente e não precisa ser de um tipo específico,
pode conter qualquer objeto, desde que seja tratado corretamente.
Um objeto da classe NSArray é imutável e deve ser construído no momento da
sua inicialização. Seus principais métodos são:

arrayWithObjects: cria um array com os objetos passados como parâmetro,


onde último valor deve ser nil para indicar o término do array.

count: retorna a quantidade de objetos do array.

objectAtIndex: retorna o objeto no índice passado como parâmetro.

containsObject: verifica se array contém o objeto passado como parâmetro.

5.4 Exemplo de UIPickerView


Vamos criar um novo projeto do tipo “Single View Application” chamado “Picker”,
utilizando Storyboard e ARC. Vamos também adicionar algumas imagens ao
projeto.
Vamos utilizar um picker view para selecionar a imagem que vai ser exibida na
tela. Se vamos utilizar um picker view, precisamos implementar os protocolos
UIPickerViewDelegate e o UIPickerViewDataSource. Portanto vamos alterar o

iOS Fundamentos 80
5

arquivo ViewController.h para o seguinte:

#import <UIKit/UIKit.h>

@interface ViewController : UIViewController <UIPickerViewDelegate,


UIPickerViewDataSource> {
IBOutlet UIImageView *imageView;

NSArray *info;
}

@end

Definimos também uma variável de classe chamada info, que vai guardar o nome
das imagens.
Vamos adicionar agora um UIImageView na Scene do Storyboard e um
UIPickerView. Temos que fazer a ligação do IBOutlet à imagem e do delegate e
datasource ao view controller. A Scene vai ficar assim:

iOS Fundamentos 81
5

Figura 5.1: Scene do Storyboard

No arquivo ViewController.m vamos implementar os métodos necessários para


que o picker view seja carregado corretamente. Primeiro vamos criar o array com
o nome das imagens que temos no projeto no método viewDidLoad:

#pragma mark - View lifecycle


- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
info = [NSArray arrayWithObjects:@”Drum.tif”, @”Guitar.tif”, @”Piano.tif”,
nil];
}

Os métodos do datasource do picker view são simples, teremos somente uma

iOS Fundamentos 82
5

coluna e o número de linhas depende da quantidade de elementos do array,


que é recuperado utilizando o método count:

#pragma mark - UIPickerViewDataSource methods

-(NSInteger)numberOfComponentsInPickerView:(UIPickerView *)pickerView {
return 1;
}

-(NSInteger)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent:(


NSInteger)component {
return [info count];
}

Para o delegate, implementaremos dois métodos, um para informar o texto da


linha e outro para mudarmos a imagem quando for trocado o valor do picker
view:

#pragma mark - UIPickerViewDelegate methods

-(NSString *)pickerView:(UIPickerView *)pickerView titleForRow:(NSInteger)row


forComponent:(NSInteger)component {
return [info objectAtIndex:row];
}

iOS Fundamentos 83
5

-(void)pickerView:(UIPickerView *)pickerView didSelectRow:(NSInteger)row


inComponent:(NSInteger)component {
imageView.image = [UIImage imageNamed:[info objectAtIndex:row]];
}

Utilizamos o método objectAtIndex: para recuperar o nome da imagem a ser


exibida, e também para informar o texto da linha do picker view.
Ao executarmos o aplicativo obteremos o seguinte resultado:

Figura 5.2: Execução do aplicativo

iOS Fundamentos 84
iOS: Fundamentos

Capítulo 6
6

6.1 UIScrollView
A classe UIScrollView fornece o suporte para exibir um conteúdo que é maior
que a tela, suportando a navegação do usuário pelo conteúdo do componente
e também o zoom fazendo gestos de pinça. O conteúdo do scroll view pode
ter qualquer tamanho, mas somente será exibido a parte que está dentro
do frame. Um scroll view pode ter um delegate, que deve obrigatoriamente
implementar o protocolo UIScrollViewDelegate.
As principais propriedades do UIScrollView são:

contentSize: o tamanho do conteúdo.

contentOffset: ponto do conteúdo que deve estar no ponto (0,0) do frame.

pagingEnabled: permite que a navegação do conteúdo seja feita de frame em


frame.

delegate: referência ao delegate.

O protocolo UIScrollViewDelegate permite que o delegate responda a


mensagens do scroll view para navegação e zoom. Os principais métodos são:

scrollViewDidScroll: é chamado quando o scroll view é movimentado.

scrollViewDidEndDecelerating: é chamado quando o scroll view termina de se


movimentar.

iOS Fundamentos 86
6

6.2 UIPageControl
A classe UIPageControl é um componente controlador de páginas, que
geralmente é exibido junto de um scroll view. Ele é um conjunto de bolinhas
centralizadas no componente que correspondem aos elementos do scroll e
uma delas para o elemento que está sendo exibido. Suas principais propriedades
são:

currentPage: o elemento atual.

numberOfPages: o número de elementos.

6.3 Exemplo
Vamos criar um novo projeto do tipo “Single View Application”chamado
“Slideshow”, utilizando Storyboard e ARC, e vamos adicionar algumas imagens
ao projeto. Vamos alterar o arquivo ViewController.h para ele implementar o
protocolo UIScrollViewDelegate e adicionar dois IBOutlets (um para o scroll
view e outro para o page control) e um IBAction, que será chamado quando
tocarmos em cima do page control:
#import <UIKit/UIKit.h>
@interface ViewController : UIViewController <UIScrollViewDelegate> {
IBOutlet UIScrollView *scroll;
IBOutlet UIPageControl *page;
}

iOS Fundamentos 87
6

-(IBAction)mudarPagina:(id)sender;

@end

Na Scene do Storyboard, vamos adicionar os componentes na tela, ligar os


IBOutlets e delegate do scroll view e ligar o IBAction ao evento de “Value
Changed” do page control.

Figura 6.1: Scene do Storyboard

Nos atributos do scroll view, vamos selecionar a propriedade “Paging Enabled”


para que sua navegação seja sempre paginada.

iOS Fundamentos 88
6

Figura 6.2: Atributos do UIScrollView

Vamos alterar o método viewDidLoad do arquivo ViewController.m para


carregarmos as imagens dentro do scroll view e configurarmos o page control:

#pragma mark - View lifecycle

- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.

NSArray *info = [NSArray arrayWithObjects:@”Earth.tif”, @”Lightning.tif”,


@”Snowflake.tif”, @”Zen.tif”, nil];

page.numberOfPages = [info count];

iOS Fundamentos 89
6

for (int i = 0; i < page.numberOfPages; i++) {


NSString *nome = [info objectAtIndex:i];
UIImageView *imagem = [[UIImageView alloc] initWithImage:[UIImage
imageNamed:nome]];
imagem.frame = CGRectMake(scroll.frame.size.width*i, 0, scroll.frame.size.
width, scroll.frame.size.height);
[scroll addSubview:imagem];
}
scroll.contentSize = CGSizeMake(scroll.frame.size.width*page.
numberOfPages, scroll.frame.size.height);
}
Primeiro criamos um array com o nome das imagens. Em seguida, vamos atribuir
a quantidade de imagens à propriedade numberOfPages do page control, pois
teremos uma página para cada imagem. Em seguida, iteramos por todo o array
com o nome das imagens para criá-las e atribuímos o valor do seu frame. Os
valores da largura e altura da imagem serão iguais aos do scroll, para que ela
encaixe perfeitamente dentro dele. O valor do ponto X é o mais importante
nesse caso, pois vamos colocar as imagens lado a lado dentro do scroll view.
Seu valor vai depender do tamanho do frame do scroll e do índice da imagem
que está sendo adicionada.
Por último temos que informar o tamanho do conteúdo do scroll view setando
o valor da propriedade contentSize. Ela é do tipo CGSize, uma estrutura que
guarda uma largura e uma altura e para criar uma estrutura dessas utilizamos a
função CGSizeMake().

iOS Fundamentos 90
6

Em seguida precisamos alterar a página atual exibida pelo page control quando
navegamos pelo scroll view. Para isso temos duas opções de implementação e
varia de caso para caso para saber qual é a mais interessante de se utilizar no
aplicativo, dependendo de sua funcionalidade. O método scrollViewDidScroll:
é chamado toda vez que é alterado a posição dentro do scroll, enquanto o
método scrollViewDidEndDecelerating: é chamado somente quando o scroll
estiver parando. É recomendado utilizar o segundo método pois ele é suficiente
para a maioria dos casos e, principalmente, quando tarefas muito pesadas
precisam ser executadas, ele obtém melhor performance que o primeiro.
Portanto no exemplo vamos implementar o segundo método para alterar a
página atual do page control:

#pragma mark - UIScrollViewDelegate methods


-(void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView {
page.currentPage = scroll.contentOffset.x/scroll.frame.size.width;
}
Para saber qual a página atual a partir da posição do conteúdo do scroll view,
utilizamos sua propriedade contentOffset para saber qual a posição do scroll
view está sendo exibida no ponto (0,0) de seu frame e dividimos pelo tamanho
do seu frame.
Por último precisamos implementar o método que é chamado quando alteramos
o valor do page control:
#pragma mark - IBActions

iOS Fundamentos 91
6

-(IBAction)mudarPagina:(id)sender {
[scroll scrollRectToVisible:CGRectMake(scroll.frame.size.width*page.
currentPage, 0, scroll.frame.size.width, scroll.frame.size.height) animated:YES];
}

Quando esse método é chamado, o valor da página atual acabou de ser


alterada, dependendo da posição do toque (ou mais a esquerda ou mais a direita
do componente). A classe UIScrollView define o método scrollRectToVisible:
animated: que recebe como parâmetro o CGRect que desejamos mostrar dentro
do frame do scroll view e se desejamos que esse movimento seja animado ou
não. Para montar o retângulo utilizamos os mesmo valores do frame do scroll
view para o tamanho, e no ponto X multiplicamos o tamanho do frame do
scroll view pela página atual do page control.
Dessa maneira, teremos o seguinte resultado ao executarmos o aplicativo:

Figura 6.3: Execução do aplicativo

iOS Fundamentos 92
6

6.4 Zoom com UIScrollView


Como dito anteriormente, é possível também fazer zoom de um conteúdo do
scroll view. Para isso precisamos necessariamente implementar o delegate e
prestar atenção nas seguintes propriedades da classe UIScrollView:

zoomScale: zoom atual do conteúdo.

minimumZoomScale: zoom mínimo permitido.

maximumZoomScale: zoom máximo permitido.

Quando implementamos o delegate, os métodos mais relevantes são:

viewForZoomingInScrollView: é chamado quando está para acontecer o zoom


do scroll view e é necessário retornar a view na qual vai ser dado o zoom.

scrollViewDidZoom: é chamado quando foi feito o zoom no scroll view.

6.5 Exemplo de UIScrollView com zoom


Vamos criar um novo projeto do tipo “Single View Application” chamado
“ZoomImagem”, utilizando ARC e Storyboard, e vamos adicionar uma imagem ao
projeto.
Vamos alterar o arquivo ViewController.h para que ele implemente o protocolo
UIScrollViewDelegate e tenha um IBOutlet para um UIImageView:
iOS Fundamentos 93
6

#import <UIKit/UIKit.h>

@interface ViewController : UIViewController <UIScrollViewDelegate> {

IBOutlet UIImageView *imagem;

@end

Na Scene do Storyboard vamos adicionar um scroll view e ligar seu delegate


ao view controller. Vamos também alterar as propriedades do zoom do scroll
view para que o valor mínimo seja 1 e o máximo 3.

Figura 6.4: Propriedades do scroll view

Em seguida vamos adicionar um UIImageView dentro do scroll view, para isso


basta arrastar o componente e colocá-lo por cima do outro. Se esperar um
pouco para soltar o image view, ele automaticamente vai se ajustar ao tamanho
do scroll view. Em seguida alteramos sua propriedade Image para a imagem que
adicionamos no projeto e ligamos o IBOutlet à ela. A Scene deve ficar assim:

iOS Fundamentos 94
6

Figura 6.5: Scene do Storyboard

Reparem na hierarquia de views dentro do view controller na parte a esquerda:


o view controller possui uma view, que possui um scroll view e que por sua vez
possui um image view.
Por último o que precisamos fazer é muito simples, no arquivo ViewController.m
implementamos apenas o método viewForZoomingInScrollView: para retornar
sempre a imagem que está dentro do scroll view e que temos o IBOutlet
ligado:

#pragma mark - UIScrollViewDelegate methods


-(UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView {
return imagem;
}

iOS Fundamentos 95
6

Ao executarmos o aplicativo, teremos o seguinte resultado:

Figura 6.6: Execução do aplicativo com zoom

Para conseguirmos fazer o gesto de pinça no simulador, deixamos apertado a


tecla alt/option (⌥) do teclado e duas bolinhas cinzas aparecerão como guias.

iOS Fundamentos 96
iOS: Fundamentos

Capítulo 7
7

7.1 Modal View Controller


Modal view controllers fornecem diferentes maneiras de controlar a
navegação do aplicativo. Geralmente elas são utilizadas para interromper
algum fluxo e obter alguma informação do usuário ou para mostrar alguma
informação temporária. Uma modal view controller não é nenhuma subclasse
do UIViewController, é apenas uma forma de exibir uma outra view controller.
Uma vez que não é mais necessário mostrar a modal view controller, a
removemos da tela e o fluxo inicial é retomado. Qualquer view controller pode
apresentar uma modal view controller, inclusive a view controller que acabou
de ser apresentada de forma modal.
Sempre que apresentada uma modal view controller, é criado uma hierarquia
entre as duas view controllers: a antiga passa a ter uma referência à nova e a
nova possui uma referência à antiga.

7.2 Exemplo
Vamos criar um novo projeto do tipo “Single View Application” chamado
“ModalViews”, utilizando Storyboard e ARC. Em seguida vamos criar um
novo view controller chamado “NovoViewController”, subclasse de
UIViewController e sem XIB. Depois de criado, vamos adicionar um novo
UIViewController ao Storyboard e alterar o campo Custom Class da aba de
Identidade para a classe NovoViewController.

iOS Fundamentos 98
7

Em seguida vamos adicionar um botão na view da classe ViewController e


mudar a cor de fundo da view das duas classes para diferenciarmos uma da
outra. Por enquanto a visão geral do Storyboard é essa:

Figura 7.1: Visão geral do Storyboard

Em seguida, vamos criar um Segue, que é uma transição de um view controller


para outro. Para isso, na aba de conexões do botão na parte de “Storyboard
Segues”, vamos ligar o segue “Modal” ao view controller que vai ser exibido, no
caso o NovoViewController.

iOS Fundamentos 99
7

Figura 7.2: Ligação do Segue Modal com o NovoViewController

No final, o Storyboard vai ficar assim:

Figura 7.3: Storyboard completo

iOS Fundamentos 100


7

Ao executarmos o aplicativo, teremos o seguinte resultado:

Figura 7.4: Execução do aplicativo

Depois de exibida o NovoViewController, não é possível voltar para a


tela anterior. Para isso temos que implementar um novo botão na classe
NovoViewController que vai fazer sumir o modal view controller da tela. Para
isso vamos alterar o arquivo NovoViewController.h para criar um novo IBAction:

#import <UIKit/UIKit.h>

@interface NovoViewController : UIViewController

-(IBAction)voltar:(id)sender;

@end

iOS Fundamentos 101


7

Em seguida vamos adicionar um novo botão na Scene do NovoViewController e


ligar o IBAction ao evento “Touch Up Inside”:

Figura 7.5: Nova configuração do NovoViewController

E no arquivo NovoViewController.m vamos implementar o método voltar:

#pragma mark - IBActions


-(IBAction)voltar:(id)sender {
[self dismissModalViewControllerAnimated:YES];
}
O método dismissModalViewControllerAmnimated: vai fazer com que o view
controller exibido de forma modal seja removido da tela. O responsável
por removar o modal view controller é a view que o exibiu, entretanto
podemos chamar o método a partir do view controller atual (self) e ele
automaticamente repassa a chamada para o view controller pai.

iOS Fundamentos 102


7

Ao executarmos o aplicativo, teremos o seguinte resultado:

Figura 7.6: Execução do aplicativo com voltar

7.3 Modal View Controller via código


Além da transição modal por Segues, é possível fazer a transição também por
código através de um IBAction, por exemplo. Para apresentar um modal view
controller por código, é necessário seguir os seguintes passos:

Criar o modal view controller.

Atribuir os valores para as propriedades do modal view controller, por


exemplo o tipo de transição.

Apresentar a modal view controller chamando o método


presentmodalViewController: animated: da view controller antiga e passando
a modal view controller como parâmetro.

Existem quatro tipos diferentes de transição para o modal view controller e


elas são definidas na enum UIModalTransitionStyle:
iOS Fundamentos 103
7

UIModalTransitionStyleCoverVertical: a modal view controller aparece de


baixo para cima (é o valor padrão para a transição).

UIModalTransitionStyleFlipHorizontal: a view controller antiga gira no eixo


vertical para exibir a modal view controller.

UIModalTransitionStyleCrossDissolve: a view controller antiga some aos


poucos enquanto a modal view controller aparece aos poucos.

UIModalTransitionStylePartialCurl: o canto da view controller antiga se enrola


para mostrar o conteúdo da modal view controller embaixo. Se exibida dessa
forma, a modal view controller não pode exibir outra view controller.

Também é possível alterar o tipo de transição utilizando Segues, para isso basta
alterar a propriedade “Transition” nos seus atributos:

Figura 7.7: Atributos da Segue

7.4 Exemplo de Modal View Controller


via código
Vamos alterar o projeto anterior e adicionar um IBAction no arquivo
iOS Fundamentos 104
7

ViewController.h:
#import <UIKit/UIKit.h>
@interface ViewController : UIViewController
-(IBAction)mudaViewController:(id)sender;
@end

Na Scene do ViewController do Storyboard, vamos adicionar um novo botão e


ligar o IBAction ao evento “Touch Up Inside” dele:

Figura 7.8: Scene do ViewController no Storyboard

Em seguida, ainda no arquivo de Storyboard, vamos alterar o atributo “Identifier”


do NovoViewController para “NovoViewController”. Esse valor pode ser
qualquer string, entretanto deve ser única no Storyboard, portanto colocando
o nome da classe do view controller é uma boa prática. Esse atributo não é
uma propriedade da classe UIViewController, é apenas um identificador para o
Storyboard localizar um view controller específico.
iOS Fundamentos 105
7

Figura 7.9: Atributos da Scene do NovoViewController no Storyboard

Vamos implementar o método mudaViewController: para apresentar uma


instância do NovoViewController de forma modal. Sempre que vamos
utilizar uma classe, antes precisamos importá-la. Portanto no arquivo
ViewController.m, vamos adicionar a linha abaixo junto dos outros imports e
acima da linha @implementation ViewController:

#import “NovoViewController.h”

Em seguida vamos implementar o método mudaViewController:

#pragma mark - IBActions

-(void)mudaViewController:(id)sender {
UIStoryboard *mainStoryboard = [UIStoryboard storyboardWithName:@”Main
Storyboard” bundle:nil];

iOS Fundamentos 106


7

NovoViewController *novoViewController = [mainStoryboard instantiateVie


wControllerWithIdentifier:@”NovoViewController”];
novoViewController.modalTransitionStyle =
UIModalTransitionStyleFlipHorizontal;
[self presentModalViewController:novoViewController animated:YES];
}

A classe UIStoryboard possui as informações dos view controllers


definidas no arquivo de Storyboard, e para carregá-las utilizamos o método
storyboardWithName: bundle: que recebe como parâmetros o nome do
arquivo e o bundle em que ele está, passando nil como parâmetro e ele
automaticamente irá procurar no bundle principal, que é o único nos aplicativo
iOS. Com o storyboard carregado, podemos instanciar o novo view controller
através do método instanciateViewControllerWithIdentifier: passando
como parâmetro a string que atribuímos ao view controller na Scene do
Storyboard. Emseguida alteramos a propriedade modalTransitionStyle para
um dos valores da enum UIModalTransitionStyle e chamamos o método
presentModalViewController: animated: para apresentar de forma modal o view
controller passado como parâmetro.
Ao executarmos o aplicativo, teremos o seguinte resultado:

iOS Fundamentos 107


7

Figura 7.10: Execução do aplicativo com apresentação modal via código

7.5 @property e @synthesize


No Objective-C é bastante comum atribuirmos valores para propriedades de um
objeto para customizá-lo, como por exemplo a propriedade frame de uma view
ou text de um label. Para declararmos uma propriedade utilizamos a diretiva @
property, junto de seus atributos, tipo e nome, como abaixo:

@property (atributos) tipo nomeDaPropriedade;

Os atributos de uma propriedade são muito importantes para definir a forma


como essa propriedade será acessada. Os diferentes tipos de atributos são:

strong e weak: define se a propriedade deve guardar uma cópia do objeto ou


não. O atributo strong faz com que a propriedade guarde uma cópia, enquanto

iOS Fundamentos 108


7

o atributo weak faz com que a propriedade guarde apenas uma referência para
o objeto. Importante notar que no segundo caso, o objeto pode ser removido
da memória por alguma outra classe e a propriedade pode apontar para
um bloco de memória errado. Ambos os atributos são válidos apenas para
propriedades do tipo NSObject ou suas subclasses.

readwrite e readonly: define se a propriedade pode ser alterada ou não. Para


uma propriedade somente de leitura é necessário colocar o atributo readonly,
caso contrário não é necessário colocar o readwrite, pois é o padrão para uma
nova propriedade.

nonatomic: define se a propriedade pode ser acessada por vários objetos ao


mesmo tempo, o que pode levar a uma melhoria de performance. O padrão
é “atomic”, entretanto não existe um atributo com esse nome, portanto ou é
coloca nonatomic ou não coloca nada.

getter=nomeDoMetodoGetter: define o nome do método getter da


propriedade.

setter=nomeDoMetodoSetter: define o nome do método setter da


propriedade. Caso a propriedade seja do tipo readonly o compilador vai exibir
um warning.

A declaração de uma propriedade deve ser feita no arquivo de definição da


interface da classe (.h) entre as diretivas @interface e @end.
Toda propriedade precisa ter seus métodos de acesso (getters e, para
propriedades readwrite, setters) implementados, e a diretiva @synthesize pode
ser usada para criar automaticamente esses métodos da forma padrão. Essa
diretiva deve ser colocada logo abaixo da diretiva @implementation no arquivo
de implementação da classe (.m). Entretanto, não é obrigatório o uso dessa
diretiva, pois podemos implementar os métodos de acesso da propriedade

iOS Fundamentos 109


7

diretamente no arquivo de implementação da classe, o que geralmente


acontece quando é necessário um comportamento diferente do normal para
eles. utilizamos a diretiva @synthesize da seguinte maneira:

@synthesize nomeDaPropriedade;

7.6 Exemplo de @property


Vamos alterar o projeto anterior para definirmos uma propriedade do tipo
NSString na classe NovoViewController e exibir seu conteúdo em um label.
Portanto no arquivo NovoViewController.h vamos adicionar uma propriedade e
um IBOutlet:

#import <UIKit/UIKit.h>
@interface NovoViewController : UIViewController {
IBOutlet UILabel *label;
}
@property (nonatomic, strong) NSString *textLabel;
-(IBAction)voltar:(id)sender;
@end
Em seguida vamos adicionar um label na Scene do NovoViewController no
Storyboard e ligar o IBOutlet:

iOS Fundamentos 110


7

Figura 7.11: Scene da classe NovoViewController no Storyboard

No arquivo NovoViewController.m, vamos alterar o método viewDidLoad e


atribuir o valor da propriedade textLabel à propriedade text do label:

#pragma mark - View lifecycle

- (void)viewDidLoad
{
[super viewDidLoad];
if (textLabel && ![textLabel isEqualToString:@””]) {
label.text = textLabel;
}
}

Antes de atribuirmos o valor do textLabel ao text do label, verificamos se o


conteúdo da propriedade é diferente de nil e diferente de @””, para evitar que o
iOS Fundamentos 111
7

texto do label seja vazio.


Agora precisamos alterar o valor da propriedade quando criamos o view
controller, antes de exibí-lo na tela. Portanto o método mudaViewController:
ficará assim:

#pragma mark - IBActions

-(void)mudaViewController:(id)sender {
UIStoryboard *mainStoryboard = [UIStoryboard storyboardWithName:@”Main
Storyboard” bundle:nil];
NovoViewController *novoViewController = [mainStoryboard instantiateVie
wControllerWithIdentifier:@”NovoViewController”];
novoViewController.textLabel = @”Propriedade alterada no IBAction!”;
novoViewController.modalTransitionStyle =
UIModalTransitionStyleFlipHorizontal;
[self presentModalViewController:novoViewController animated:YES];
}

Depois de inicializarmos o view controller, atribuímos o novo valor a sua


propriedade. Ao executarmos o código, teremos o seguinte resultado:

iOS Fundamentos 112


7

Figura 7.12: Execução do aplicativo

A dúvida agora é: como alterar o valor das propriedades quando a apresentação


da modal view controller é feita por Segue? A classe UIViewController possui
um método que é chamado quando o Segue está para ser executado, que é o
prepareForSegue: sender:. Esse método passa como parâmetros as informações
da Segue (que contém informações sobre o view controller que originou o
segue e o view controller que está sendo exibido) e o objeto que originou a
Segue. Portanto para alterarmos o valor de uma propriedade através de uma
Segue fazemos assim:

#pragma mark - Storyboard related methods

-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {


if ([segue.destinationViewController isKindOfClass:[NovoViewController
class]]) {

iOS Fundamentos 113


7

NovoViewController *novoViewController = (NovoViewController *)


segue.destinationViewController;
novoViewController.textLabel = @”Propriedade alterada no Segue.”;
}
}

Para identificarmos a classe destino que vai ser chamada pelo Segue, utilizamos
o método chamado isKindOfClass:, que recebe como parâmetro o objeto do
tipo Class e retorna verdadeiro quando o objeto for da mesma classe ou uma
subclasse dele. Como esse método é chamado para todos os Segues dessa
classe, é importante fazer esse tipo de verificação antes de fazer o cast e
acessar a propriedade da classe, caso contrário poderá ocorrer um erro de
execução e o aplicativo pode travar.
Ao executarmos o aplicativo, teremos o seguinte resultado:

Figura 7.13: Execução do aplicativo

iOS Fundamentos 114


iOS: Fundamentos

Capítulo 8
8

8.1 UINavigationController
A classe UINavigationController estende a classe UIViewController para
implementar um view controller especial para controlar uma navegação em que
diferentes subníveis são mostrados a medida que itens são selecionados. Cada
tela de cada nível da navegação é controlada por seu próprio view controller,
enquanto a navegação entre as telas é controlada pelo navigation controller.
Essa classe não precisa ser estendida e sua implementação fornece uma
interface pronta para todas as situações.
Um navigation controller controla as telas apresentadas em uma pilha
de navegação, ou seja, o item na parte de baixo da pilha é o primeiro view
controller exibido, conhecido como root view controller, enquanto o item mais
acima da pilha é o view controller que está sendo exibido no momento.
Para fazer o controle da navegação entre os view controllers exibidos, o
navigation controller possui dois métodos:

pushViewController: animated: adiciona o view controller passado como


parâmetro ao topo da pilha e o apresenta na tela.

popViewController: animated: remove o view controller do topo da pilha e


apresenta o próximo view controller da pilha.

Quando chamado esses métodos, o navigation controller atualiza


automaticamente sua barra de navegação para exibir corretamente as
informações referentes ao view controller exibido e ao controle de navegação.
iOS Fundamentos 116
8

O navigation cotroller possui uma propriedade chamada delegate,


que faz referência a um view controller que implemente o protocolo
UINavigationControllerDelegate. O protocolo possui apenas dois métodos e
ambos são opcionais:

navigationController: willShowViewController: animated: é chamado quando


o navigation controller está prestes a exibir um view controller na tela.

navigationController: didShowViewController: animated: é chamado quando o


navigation controller terminou de exibir o view controller na tela.

Como a classe UINavigationController estende a classe UIViewController, o


navigation controller possui a propriedade view que é a que deve ser exibida
primeiramente, enquanto a view dos view controllers que são controlados pelo
navigation controller serão exibidas dentro dessa view. A view do navigation
controller possui uma barra de navegação, o que faz com que o espaço
utilizado pela view do view controller seja menor que antes.

8.2 Exemplo de UINavigationController


Vamos criar um projeto do tipo “Empty Application” chamado
“NavigationController” utilizando ARC. Em seguida vamos criar um arquivo de
Storyboard chamado “MainStoryboard”. O próximo arquivo a ser criado é uma
subclasse do tipo UIViewController chamada “PrimeiraViewController”, e em
seguida vamos adicionar uma view controller no Storyboard e colocar sua

iOS Fundamentos 117


8

classe como PrimeiraViewController na aba de inspeção e alterar a na aba de


atributos vamos alterar o atributo Identifier para PrimeiraViewController e a
propriedade Title para Primeira. Essa propriedade vai ser lida pelo navigation
controller e exibida na barra de navegação. Precisamos também desmarcar
o atributo “It’s Inicial ViewController” pois o primeiro view controller do
aplicativo não está representado no Storyboard, que é o navigation controller.
Ainda na parte de atributos, o Interface Builder fornece uma simulação de
componentes. Como vamos utilizar um navigation controller, e ele tem uma
barra de navegação, precisamos simular no Top Bar um Navigation Bar.

Figura 8.1: Atributos da classe PrimeiraViewController no Storyboard

iOS Fundamentos 118


8

Figura 8.2: Representação da PrimeiraViewController no Storyboard

No arquivo AppDelegate.h vamos adicionar uma variável de classe do tipo


UINavigationController, que será o navigation controller durante toda a
execução do aplicativo:

#import <UIKit/UIKit.h>

@interface AppDelegate : UIResponder <UIApplicationDelegate> {


UINavigationController *navigationController;
}

@property (strong, nonatomic) UIWindow *window;

@end

iOS Fundamentos 119


8

No arquivo AppDelegate.m, vamos alterar o método application:


didFinishLaunchingWithOptions: para que o navigationController seja
inicializado com uma view controller inicial, que será uma instância da classe
PrimeiraViewController. Como estamos utilizando a classe que criamos dentro
do arquivo do AppDelegate, precisamos importar a classe adicionando a linha
#import “PrimeiraViewController.h” abaixo do import existente na classe. O
método ficará assim:

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(


NSDictionary *)launchOptions
{
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen]
bounds]];
// Override point for customization after application launch.
self.window.backgroundColor = [UIColor whiteColor];
PrimeiraViewController *primeiraViewController = [[UIStoryboard storyboard
WithName:@”MainStoryboard” bundle:nil] instantiateViewControllerWithIdentifi
er:@”PrimeiraViewController”];
navigationController = [[UINavigationController alloc] initWithRootViewCont
roller:primeiraViewController];
[self.window addSubview:navigationController.view];
[self.window makeKeyAndVisible];
return YES;
}

iOS Fundamentos 120


8

Primeiro criamos a instância da classe PrimeiraViewController utilizando a


interface definida no Storyboard e depois inicializamos o navigationController
utilizando o método initWithRootViewController:, que vai carregar as
informações do navigationController e adicionar o view controller como
primeiro elemento da pilha, ou o rootViewController. Por último adicionamos a
view do navigationController na tela. Ao executarmos o aplicativo teremos o
seguinte resultado:

Figura 8.3: Execução do aplicativo

Vamos criar uma nova classe que estende UIViewController chamado


SegundaViewController e adicionar um view controller no Storyboard para
representá-lo, e identificar sua classe como SegundaViewController na aba de
inspeção. Da mesma forma que fizemos com a classe anterior, vamos alterar os
atributos do SegundaViewController, que ficará assim:

iOS Fundamentos 121


8

Figura 8.4: Atributos da classe SegundaViewController

Na view da classe PrimeiraViewController vamos adicionar um botão


que vai adicionar uma instância da classe SegundaViewController à
pilha de view controllers do navigation controller. Portanto no arquivo
PrimeiraViewController vamos adicionar um IBAction:

#import <UIKit/UIKit.h>

@interface PrimeiraViewController : UIViewController

-(IBAction)mudarViaCodigo:(id)sender;

@end

No Storyboard vamos adicionar o botão e ligar o IBAction a ele:

iOS Fundamentos 122


8

Figura 8.5: PrimeiraViewController no Storyboard

Por último vamos importar a classe SegundaViewController no arquivo


PrimeiraViewController.m e implementar o método mudarViaCodigo:
#pragma mark - IBActions
-(IBAction)mudarViaCodigo:(id)sender {
SegundaViewController *segundaViewController = [[UIStoryboard storyboar
dWithName:@”MainStoryboard” bundle:nil] instantiateViewControllerWithIdentif
ier:@”SegundaViewController”];
[self.navigationController pushViewController:segundaViewController
animated:YES];
}
Primeiro criamos a instância da classe SegundaViewController utilizando
as informações do Storyboard e em seguida chamamos o método
pushViewController: animated: do navigation controller, que pode ser
recuperado pela propriedade navigationController do view controller.

iOS Fundamentos 123


8

Ao executarmos o aplicativo, teremos o seguinte resultado:

Figura 8.6: Execução do aplicativo

Repare que automaticamente, depois de adicionar um novo view controller à


pilha de view controllers do navigation controller, a barra de navegação passa
a possuir um botão de voltar para a view controller anterior, cujo texto é
baseado na propriedade title do view controller anterior.

8.3 Exemplo de UINavigationController


no Storyboard
Podemos obter o mesmo resultado do exemplo anterior adicionando
diretamente um navigation controller ao Storyboard. Portanto
vamos criar um novo projeto do tipo “Empty Application” chamado
“NavigationControllerStoryboard” utilizando ARC. Vamos criar um arquivo

iOS Fundamentos 124


8

de Storyboard chamado MainStoryboard e atribuí-lo ao Main Storyboard nas


preferências do target e uma nova classe do tipo UIViewController chamada
PrimeiraViewController.
Como o aplicativo vai ser carregado a partir de um Storyboard, precisamos
alterar o método application: didFinishLaunchingWithOptions: do arquivo
AppDelegate.m para o seguinte:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(
NSDictionary *)launchOptions
{
return YES;
}
Vamos adicionar ao Storyboard um componente do tipo UINavigationController,
que junto dele é adicionado um componente do tipo UIViewController já com
a Segue Relationship rootViewController já ligado a ele.

Figura 8.7: Visão geral do Storyboard

iOS Fundamentos 125


8

Em seguida vamos alterar o tipo do UIViewController para


PrimeiraViewController na aba de inspeção. Reparem que o view controller
possui um navigation item, que será carregado pela barra de navegação. Quando
o navigation item não é definido, é carregado as informações diretas do view
controller. Vamos alterar a propriedade Title do navigation item para “Primeira”,
e notem que é possível definir um valor para o botão de voltar (Back Button)
caso queira um valor diferente do título.
Vamos adicionar uma nova classe chamada SegundaViewController que
estende UIViewController e adicionar também um novo componente
do tipo UIViewController no Storyboard e alterá-lo para ser do tipo
SegundaViewController. Em seguida vamos adicionar um botão na view do
PrimeiraViewController e ligar seu Segue do tipo Push ao SegundaViewController,
que automaticamente vai receber um navigation item, cujo título vamos alterar
para Segunda. A visão geral do Storyboard deve ficar assim:

Figura 8.8: Visão geral do Storyboard

iOS Fundamentos 126


8

Ao executarmos o aplicativo, vamos obter o seguinte resultado:

Figura 8.9: Execução do aplicativo

8.4 UITableView e UITableViewCell


Uma instancia da classe UITableView, que é uma extensão da classe
UIScrollView com navegação vertical apenas, é utilizada para exibir e controlar
uma tabela de itens e, como ela foi projetada para aparelhos de tela pequena,
ela suporta apenas uma coluna, na qual os itens estão dispostos em seções.
Cada item da tabela é um objeto do tipo UITableViewCell, que estende UIView.
Ela possui principalmente as seguintes propriedades:

textlabel: o texto principal.

detailTextLabel: um texto secundário.

imageView: uma imagem posicionada no canto esquerdo da célula.

reuseIdentifier: identificador da célula.

iOS Fundamentos 127


8

A disposição existência dos elementos depende do estilo da célula utilizado


na sua inicialização, e seus diferentes valores estão definidos na enum
UITableViewCellStyle:

UITableViewCellStyleDefault: tipo default de célula, com um texto preto e


alinhado a esquerda com uma imagem opcional.

UITableViewCellStyleValue1: possui um texto preto principal alinhado a


esquerda e um texto secundário azul alinhado a direita.

UITableViewCellStyleValue2: possui um texto preto principal alinhado a


esquerda e um texto secundário também preto alinhado a direita.

UITableViewCellStyleSubtitle: possui um texto preto principal alinhado a


esquerda e um texto secundário também preto abaixo do texto principal.

A tabela também possui dois estilos diferentes definidos na enum


UITableViewStyle:

UITableViewStylePlain: estilo simples com as seções agrupadas como uma só


lista.

UITableViewStyleGrouped: estilo que agrupa os itens das seções e mantém um


espaço entre elas, fazendo com que cada seção seja facilmente identificada.

Os itens da tabela são acessados diretamente através de sua seção (section) e


linha (row), definidos em um objeto do tipo NSindexPath, alterado para facilitar
o acesso dos itens da tabela.
Um table view precisa necessariamente de um objeto que funcione como data

iOS Fundamentos 128


8

source (precisa implementar o protocolo UITableViewDataSource) e outro como


delegate (precisa implementar o protocolo UITableViewDelegate). Ambos os
protocolos pode estar definidos na mesma classe e o mesmo objeto pode ser
tanto o delegate quanto o data source.
Os métodos mais importantes do data source são:

tableView: cellForRowAtIndexPath: método obrigatório que retorna a célula


da tabela para o índice passado como parâmetro.

numberOfSectionsInTableView: retorna o número de seções da tabela.

tableView: numberOfRowsInSection: método obrigatório que retorna o


número de linhas para a seção passada como parâmetro.

Todos os métodos do delegate são opcionais, entretanto o método tableView:


didSelectRowAtIndexPath: é importante pois é chamado quando uma linha da
tabela é selecionada.

8.5 Exemplo de UITableView


Vamos alterar o projeto anterior e adcionar uma tabela na
classe PrimeiraViewController. Portanto vamos alterar o arquivo
PrimeiraViewController.h e adicionar uma variável de classe do tipo UITableView
e implementar os protocolos UITableViewDelegate e UITableViewDataSource:

#import <UIKit/UIKit.h>

iOS Fundamentos 129


8

@interface PrimeiraViewController : UIViewController <UITableViewDelegate,


UITableViewDataSource> {
IBOutlet UITableView *myTableView;
}
@end

Vamos agora alterar a Scene da classe PrimeiraViewController do Storyboard e


remover o botão existente, adicionar uma tabela e fazer a ligação do IBOutlet
e do delegate e do datasource:

Figura 8.10: Scene da classe PrimeiraViewController no Storyboard

iOS Fundamentos 130


8

Figura 8.11: Atributos da tabela

Na aba de atributos da tabela, podemos editar a propriedade Style da tabela,


mudando seu estilo de visualização. Após implementarmos os métodos necessários
da tabela, vamos executar o aplicativo duas vezes, uma para cada estilo.
Vamos adicionar os seguintes método no arquivo PrimeiraViewController.m:
#pragma mark - UITableViewDataSource methods
-(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 2;
}
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInt
eger)section {
if (section == 0) {
return 5;
} else {
return 3;
}
}
iOS Fundamentos 131
8

O primeiro vai informar a quantidade de seções da tabela, enquanto o segundo


vai informar quantas linhas a tabela possui para a seção passada como
parâmetro.
Ainda no arquivo PrimeiraViewController.m, vamos adicionar o outro método
obrigatório do datasource:

-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:


(NSIndexPath *)indexPath {
NSString *cellIdentifier = @”Cell”;

UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellId


entifier];
if (!cell) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:cellIdentifier];
}

cell.textLabel.text = [NSString stringWithFormat:@”Cell - section: %i row: %i”,


indexPath.section, indexPath.row];

return cell;
}

iOS Fundamentos 132


8

Precisamos implementar esse método para que ele retorne o objeto do tipo
UITableViewCell referente ao item da tabela com o indexPath passado como
parâmetro. Como uma tabela pode possuir muitos itens e seções, e para não
utilizar muita memória e melhorar sua performance, a classe UITableView é
implementada da seguinte forma: somente é carregado na memória os itens
que estão sendo exibidos na tela e a medida que é feita a navegação entre os
itens da tabela, os itens que somem são reutilizados para criar os próximos itens
que estão aparecendo. E o método dequeueReusableCellWithIdentifier: faz
exatamente isso utilizando o identificador para a célula, e retorna o objeto que
pode ser reutilizado ou então nil caso ainda não existam células que possam
ser reutilizadas. Caso a tabela não consiga reutilizar a célula, então ela é criada
através do método initWithStyle: reuseIdentifier:, que recebe um estilo de
célula e o identificador para reutilização. Depois de criada a célula, alteramos
seu texto para exibir a seção e a linha da tabela e então retornamos o objeto
referente à célula.
Ao executarmos teremos os seguinte resultado, primeiro com o estilo Plain e
depois com o estilo Grouped da tabela:

Figura 8.12: Execuções do aplicativo com os estilos diferentes da tabela

iOS Fundamentos 133


8

O datasource da tabela ainda fornece a possibilidade de adicionarmos um


cabeçalho e um rodapé para cada seção da tabela utilizando os métodos
tableView: titleForHeaderInSection: e tableView: titleForFooterInSection:
respectivamente. Portanto vamos adicionar as implementações abaixo no
arquivo PrimeiraViewController.m:

#pragma mark - UITableViewDataSource methods

-(NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInte


ger)section {
return [NSString stringWithFormat:@”Header section %i”, section];
}

-(NSString *)tableView:(UITableView *)tableView titleForFooterInSection:(NSInte


ger)section {
return [NSString stringWithFormat:@”Footer section %i”, section];
}

Ao executarmos o aplicativo novamente, teremos os seguintes resultados para


cada estilo de tabela (primeiro Plain e depois Grouped):

iOS Fundamentos 134


8

Figura 8.13: Execuções do aplicativo com cabeçalho e rodapé

No exemplo acima, se selecionamos uma linha, nada acontece pois não


implementamos o método que é chamado quando uma linha é selecionada.
Portanto no arquivo de Storyboard vamos alterar a propriedade Identifier
da classe SegundaViewController para seu próprio nome. No arquivo
PrimeiraViewController vamos importar o conteúdo da classe anterior e
implementar o seguinte método:

-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPa


th *)indexPath {
SegundaViewController *segundaViewController = [[UIStoryboard storyboar
dWithName:@”MainStoryboard” bundle:nil] instantiateViewControllerWithIdentif
ier:@”SegundaViewController”];
[self.navigationController pushViewController:segundaViewController
animated:YES];
[tableView deselectRowAtIndexPath:indexPath animated:YES];
}

iOS Fundamentos 135


8

Primeiro criamos uma instância da classe SegundaViewController e a


adicionamos na pilha de view controllers do navigation controller. Em seguida
chamamos o método deselectRowAtIndexPath: animated: para desmarcar a
linha selecionada.
Ao executarmos o aplicativo, teremos o seguinte resultado:

Figura 8.14: Execução do aplicativo

8.6 UITableViewController
Ao analisarmos a classe PrimeiraViewController, ela é do tipo
UIViewController e implementa os protocolos UITablewViewDelegate e o
UITableViewDataSource, além de possuir uma tabela na sua interface. Isso é
exatamente uma classe do tipo UITableViewController. Essa classe fornece
todos os componentes necessários para implementarmos uma tabela, e
assim como não utilizamos diretamente a classe UIViewController, o mesmo
acontece com a UITableViewController.
Uma das diferenças entre a implementação da classe PrimeiraViewController

iOS Fundamentos 136


8

e a de uma subclasse de UITableViewController é que a segunda não possui a


propriedade view, sua primeira view é uma tabela e é acessada pela propriedade
tableView. Outra diferença é a propriedade clearsSelectionOnViewWillAppear,
que define se a tabela deve desmarcar a linha selecionada quando
for reaparecer, o que torna desnecessário a chamada do método
deselectRowAtIndexPath: animated:.

8.7 Master Detail Application


O exemplo anterior contém os mesmos componentes que existem numa
aplicação do tipo Master Detail Application. Esse template nos fornece uma
classe do tipo UITableViewController chamada MasterViewController e uma
classe do tipo UIViewController chamada DetailViewController, controlados
por um navigation controller. A segunda classe ainda possui uma propriedade
chamada detailItem do tipo id, que pode receber qualquer tipo de objeto que
é subclasse de NSObject e um IBOutlet do tipo UILabel cujo texto é alterado
para o retorno do método description do objeto detailItem.

iOS Fundamentos 137


iOS: Fundamentos

Capítulo 9
9

9.1 NSDictionary
Um objeto do tipo NSDictionary (ou dicionário) contém associações de
pares chave-valor. Cada par desses possui um objeto que representa a chave
(geralmente do tipo NSString) e o outro objeto que é o valor recuperado por
essa chave. Toda chave do dicionário deve ser única, ou seja, não pode existir
dois pares que possuam a mesma chave para objetos diferentes.
Os principais métodos da classe NSDictionary são:

count: retorna a quantidade de itens no dicionário.

allKeys: retorna um array com todas as chaves do dicionário.

allValues: retorna um array com todos os valores do dicionário.

objectForKey: retorna o valor referente à chave passada como parâmetro.

9.2 Exemplo de NSDicitonary


Vamos criar um novo projeto do tipo “Master Detail Application” chamado
“TableView” utilizando ARC e Storyboard. Vamos também adicionar
umas imagens ao projeto e em seguida, vamos alterar a Scene da classe
MasterViewController do Storyboard para fazer com que a tabela seja de
valores dinâmicos e não estáticos como é criado no template. Na aba de
atributos da tabela alteramos o atributo Content de Static Cells para Dynamic
Prototypes.
iOS Fundamentos 139
9

Figura 9.1: Atributo Content da tabela

Agora vamos alterar o arquivo MasterViewController.h e adicionar uma variável


de classe do tipo NSArray:
#import <UIKit/UIKit.h>
@interface MasterViewController : UITableViewController {
NSArray *tableInfo;
}
@end

Agora vamos inicializar o conteúdo do array com dicionários de informações


sobre as fotos e também implementar os métodos necessários do datasource
da tabela. Primeiro vamos alterar o método viewDidLoad do arquivo
MasterViewController.m:

#pragma mark - View lifecycle


- (void)viewDidLoad
{
iOS Fundamentos 140
9

[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.

NSDictionary *eagle = [NSDictionary dictionaryWithObjectsAndKeys:@”Águia”,


@”nome”, @”Ave”, @”especie”, @”Eagle.tif”, @”imagem”, nil];
NSDictionary *owl = [NSDictionary dictionaryWithObjectsAndKeys:@”Coruja”,
@”nome”, @”Ave”, @”especie”, @”Owl.tif”, @”imagem”, nil];
NSDictionary *parrot = [NSDictionary dictionaryWithObjectsAndKeys:@”Ara
ra”, @”nome”, @”Ave”, @”especie”, @”Parrot.tif”, @”imagem”, nil];
NSDictionary *penguim = [NSDictionary dictionaryWithObjectsAndKeys:@”Ping
uim”, @”nome”, @”Ave”, @”especie”, @”Penguin.tif”, @”imagem”, nil];
NSDictionary *zebra = [NSDictionary dictionaryWithObjectsAndKeys:@”Zeb
ra”, @”nome”, @”Mamífero”, @”especie”, @”Zebra.tif”, @”imagem”, nil];

tableInfo = [NSArray arrayWithObjects:eagle, owl, parrot, penguim, zebra,


nil];
}

Para cada animal criamos um dicionário com suas informações e depois criamos
um array com esses dicionários.
Em seguida vamos implementar os método do datasource da tabela:

#pragma mark - UITableViewDataSource methods

iOS Fundamentos 141


9

-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInt


eger)section {
return [tableInfo count];
}
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:
(NSIndexPath *)indexPath {
NSString *cellIdentifier = @”Cell”;
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cel
lIdentifier];
if (!cell) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle
reuseIdentifier:cellIdentifier];
}
NSDictionary *animal = [tableInfo objectAtIndex:indexPath.row];
cell.textLabel.text = [animal objectForKey:@”nome”];
cell.detailTextLabel.text = [animal objectForKey:@”especie”];
return cell;
}
O número de linhas da tabela vai depender da quantidade de objetos do array
tableInfo. Em seguida, quando não for possível reutilizar, criamos uma célula do
tipo UITableViewCellStyleSubtitle e alteramos suas informações.
Ao executarmos o aplicativo teremos o seguinte resultado:

iOS Fundamentos 142


9

Figura 9.2: Execução do aplicativo

Agora precisamos implementar o método do delegate para que quando


selecionarmos uma linha da tabela, seja exibido uma tela com os detalhes do
animal que selecionamos. Portanto primeiro precisamos atribuir um Identifier
para o DetailViewController no Storyboard e colocar também para ele simular
o Top Bar como uma barra de navegação:

Figura 9.3: Atributos da Scene da classe DetailViewController no Storyboard

Vamos agora importar as informações da classe DetailViewController e


implementar, no arquivo MasterViewController.m, o método do delegate da

iOS Fundamentos 143


9

tabela para quando for selecionado um item dela:

#pragma mark - UITableViewDelegate methods

-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPa


th *)indexPath {
DetailViewController *detailViewController = [[UIStoryboard storyboardWit
hName:@”MainStoryboard” bundle:nil] instantiateViewControllerWithIdentifier:@”
DetailViewController”];
detailViewController.detailItem = [tableInfo objectAtIndex:indexPath.row];
[self.navigationController pushViewController:detailViewController
animated:YES];
}

O que fazemos no método acima é criar uma instância da classe


DetailViewController utilizando a interface definida no Storyboard, atribuímos
o dicionário referente à linha selecionada para a propriedade detailItem e o
adicionamos à pilha de view controllers do navigation controller.
Em seguida vamos alterar o arquivo DetailViewController.h para adicionar
novos IBOutlets para as informações temos no dicionário (não vamos criar um
IBOutlet para o nome do animal pois vamos coloca-lo no atributo title do view
controller):

#import <UIKit/UIKit.h>

iOS Fundamentos 144


9

@interface DetailViewController : UIViewController {


IBOutlet UIImageView *imagem;
IBOutlet UILabel *especie;
}

@property (strong, nonatomic) id detailItem;

@end

Vamos mudar agora a Scene da classe DetailViewController e adicionar os


novos componentes na tela e fazer a ligação de seus IBOutlets (remover o
label que já está na tela e adicionar os novos):

Figura 9.4: Scene da classe DetailViewController no Storyboard

iOS Fundamentos 145


9

Por último vamos remover todas as referências ao componente


detailDescriptionLabel no arquivo DetailViewController.m, pois ele não existe
mais no arquivo .h e sua utilização vai gerar erros na compilação. Em seguida
vamo alterar o método viewDidLoad para o seguinte:
#pragma mark - View lifecycle

- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
self.title = [self.detailItem objectForKey:@”nome”];
especie.text = [self.detailItem objectForKey:@”especie”];
imagem.image = [UIImage imageNamed:[self.detailItem
objectForKey:@”imagem”]];
}
Ao rodarmos o aplicativo, teremos o seguinte resultado:

Figura 9.5: Execução do aplicativo

iOS Fundamentos 146


9

9.3 Arquivos de Property List


Arquivos de property List (ou somente plist) são arquivos XML com um conjunto
de tags pré-definidas. As tags mais comuns são:

<plist>: indica o começo do arquivo plist.

<string>: guarda um objeto do tipo string.

<array>: guarda um array de objetos e seu conteúdo pode conter qualquer tipo
de tag.

<dict>: guarda um dicionário e seu conteúdo deve conter a tag <key>, cujo
conteúdo será a chave do par, e o objeto seguinte será o valor que essa chave
representa.

O Xcode possui um editor de plists bem completo e nem precisaríamos nos


preocupar com o formato do arquivo, que fornece tanto a plist no formato
mais simples de visualizar ou no formato de XML, ambos possíveis de fazer
edição no arquivo. Para utilizar os diferentes editores, apertamos com o botão
direito do mouse para exibir a lista de opções e no menu “Open As” existem as
duas opções: ou “Property List” ou “Source Code”.

9.4 Exemplo de plist


Vamos adicionar no projeto anterior um arquivo de plist. Para isso no menu de

iOS Fundamentos 147


9

criar um novo arquivo, escolhemos a seção Resource do iOS e escolhemos um


arquivo do tipo Property List, como na imagem abaixo:

Figura 9.6: Criação de um arquivo plist

Vamos adicionar os itens que utilizamos na criação dos dicionários e montar


o arquivo para que seja um array de dicionários. Portanto o arquivo plist ficará
assim:

<?xml version=”1.0” encoding=”UTF-8”?>

<!DOCTYPE plist PUBLIC “-//Apple//DTD PLIST 1.0//EN” “http://www.apple.com/DTDs/PropertyList-1.0.dtd”>

<plist version=”1.0”>

<array>

<dict>

<key>nome</key>

<string>Águia</string>

<key>especie</key>

iOS Fundamentos 148


9

<string>Ave</string>

<key>imagem</key>

<string>Eagle.tif</string>

</dict>

<dict>
<key>nome</key>
<string>Coruja</string>
<key>especie</key>
<string>Ave</string>
<key>imagem</key>
<string>Owl.tif</string>
</dict>
<dict>
<key>nome</key>
<string>Arara</string>
<key>especie</key>
<string>Ave</string>
<key>imagem</key>
<string>Parrot.tif</string>
</dict>
<dict>
<key>nome</key>
<string>Pinguim</string>
<key>especie</key>

iOS Fundamentos 149


9

<string>Ave</string>
<key>imagem</key>
<string>Penguin.tif</string>
</dict>
<dict>
<key>nome</key>
<string>Zebra</string>
<key>especie</key>
<string>Mamífero</string>
<key>imagem</key>
<string>Zebra.tif</string>
</dict>
</array>
</plist>

Reparem que a primeira tag após a tag <plist> é <array> e não um <dict> como
fica no arquivo recém criado. Visualmente a plist ficará assim:

iOS Fundamentos 150


9

Figura 9.7: Visulização da plist

Agora vamos alterar o método viewDidLoad do arquivo MasterViewController


para criarmos o array utilizando o conteúdo da plist criada:

#pragma mark - View lifecycle

- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.

NSString *path = [[NSBundle mainBundle] pathForResource:@”animais”


ofType:@”plist”];
tableInfo = [NSArray arrayWithContentsOfFile:path];
}

iOS Fundamentos 151


9

A classe NSBundle guarda a localização de um pacote de arquivos no sistema


de arquivos do aparelho, e o método mainBundle retorna o pacote atual do
aplicativo, que é o único que pode ser acessado de dentro do aplicativo. Com
o objeto retornado por esse método, chamamos o método pathForResource:
ofType: passando como parâmetros o nome e o tipo (extensão) do arquivo
para recuperar o caminho do arquivo no aparelho. Depois utilizamos o método
arrayWithContentsOfFile: passando o caminho do arquivo para criar o array a
partir do conteúdo desse arquivo.
Ao executarmos o programa, teremos o seguinte resultado:

Figura 9.8: Execução do aplicativo com plist

9.5 Objetos mutáveis


Foi mostrado anteriormente que os objetos NSString, NSArray e NSDictionary
são imutáveis, ou seja, seu conteúdo não pode ser alterado. Para casos em que
seja necessário a alteração, o Objective-C possui classes mutáveis para cada
uma delas: NSMutableString, NSMutableArray e NSMutableDictionary. Essas

iOS Fundamentos 152


9

classes são extensões das classes imutáveis com a adição de métodos para o
controle de seus conteúdos.
A classe NSMutableString possui praticamente os mesmos métodos da
classe NSString, com a diferença que a segunda cria um novo objeto como
retorno do método, enquanto a primeira vai alterar o objeto que está
recebendo a mensagem. Por exemplo, na classe NSString existe o método
stringByAppendingFormat: que retorna uma nova string resultado da
concatenação das duas strings, enquanto a classe NSMutableString possui
o método appendFormat: que altera o objeto que recebe a mensagem,
concatenando a string passada como parâmetro.
A classe NSMutableArray possui os seguintes métodos:

insertObject: atIndex: insere o objeto na determinada posição.

removeObjectAtIndex: remove o objeto da posição passada como parâmetro.

addObject: adiciona um objeto no final do array.

replaceObjectAtIndex: withObject: substitui o objeto da posição determinada


pelo novo objeto.

A classe NSMutableDictionary possui os seguintes métodos:

setObject: forKey: troca o objeto da chave passada como parâmetro pelo


novo objeto.

removeObjectForKey: remove o objeto associado à chave passada como


parâmetro.

removeAllObjects: remove todos os objetos do dicionário.

iOS Fundamentos 153


9

9.6 Edição de tabelas


O protocolo UITableViewDataSource possui métodos que fornecem a habilidade
de edição do conteúdo da tabela. Existem dois tipos diferentes de edição:
podemos adicionar/remover um item e podemos mover um item para uma nova
posição.
Para adicionar/remover um item, precisamos implementar os seguintes métodos:

tableView: canEditRowAtIndexPath: informa se o item no indexPath passado


como parâmetro pode ser editado.

tableView: commitEditingStyle: forRowAtIndexPath: pede para o datasource


efetuar a inserção ou deleção do conteúdo, atualizando tanto a tabela como
também sua fonte de dados.

Para mover um item, precisamos implementar os seguintes métodos:

tableView: canMoveRowAtIndexPath: informa se o item do indexPath passado


como parâmetro pode ser movido.

tableView: moveRowAtIndexPath: toIndexPath: pede para o datasource efetuar


a movimentação do item da tabela, atualizando sua fonte de dados.

tableView: targetIndexPathForMoveFromRowAtIndexPath:
toProposedIndexPath: retorna o indexPath final do item a ser movimentado,
recebendo como parâmetro o indexPath inicial e o final. Esse método não
é necessário, pois sua implementação básica sempre retorna o indexPath
final, mas é importante nos casos que não queremos que um item seja
movimentado para determinada posição, retornando então o indexPath inicial.

iOS Fundamentos 154


9

Para iniciarmos a edição da tabela, geralmente colocamos o botão de edição


na barra de navegação, pois assim ele estará sempre acessível. A classe
UIViewController fornece um botão de edição para a barra de navegação,
acessível por sua propriedade editButtonItem. Ele vai alternar o botão entre
o tipo UIBarButtonSystemItemEdit e o UIBarButtonSystemItemDone para
início e término da edição. Ambos os tipos de botões estão definidos na enum
UIBarButtonSystemItem da classe UIBarButtonItem.

9.7 Exemplo de edição de tabela


Vamos alterar o projeto anterior para permitir a edição do conteúdo da tabela.
Para isso, primeiro vamos alterar o array com a informação da tabela de um
NSArray para um NSMutableArray no arquivo MasterViewController.h:

#import <UIKit/UIKit.h>
@interface MasterViewController : UITableViewController {
NSMutableArray *tableInfo;
}
@end

Em seguida, vamos alterar o método viewDidLoad do arquivo


MasterViewController.m para que ele crie um objeto do tipo NSMutableArray
e adicione o botão de editar a tabela no lado direito da barra de navegação:

iOS Fundamentos 155


9

- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
self.navigationItem.rightBarButtonItem = self.editButtonItem;
NSString *path = [[NSBundle mainBundle] pathForResource:@”animais”
ofType:@”plist”];
tableInfo = [NSMutableArray arrayWithContentsOfFile:path];
}

Vamos agora implementar os métodos para adicionar/remover um item na


tabela. Para isso vamos adicioná-los no arquivo MasterViewController.m:
#pragma mark - UITableViewDataSource methods
- (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPa
th *)indexPath
{
if (indexPath.row == 1) {
return NO;
}
return YES;
}
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCell
EditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{

iOS Fundamentos 156


9

if (editingStyle == UITableViewCellEditingStyleDelete) {
[tableInfo removeObjectAtIndex:indexPath.row];
[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath]
withRowAnimation:UITableViewRowAnimationFade];
} else if (editingStyle == UITableViewCellEditingStyleInsert) {
// código para adicionar item
}
}

No primeiro método apenas informamos que todos os itens são editáveis,


com exceção do item da linha com índice 1. No método seguinte estamos
implementando a edição de acordo com seu tipo (adição ou remoção). Para
removermos um item primeiro precisamos removê-lo da fonte de dados, que
no caso é o array, e em seguida removê-lo da tabela através do método
deleteRowAtIndexPaths: withRowAnimation:. Esse método vai remover todas os
itens do array de indexPaths passado como parâmetro utilizando uma animação,
que pode ser de vários tipos, definidos na enum UITableViewRowAnimation da
classe UITableView.
Ao executarmos o aplicativo, teremos o seguinte resultado:

iOS Fundamentos 157


9

Figura 9.9: Execução do aplicativo para remover item

Para fazer a remoção de um item, é possível passar o dedo horizontalmente


sobre a linha que aparecerá o botão de remover somente nessa linha, como
mostra a execução abaixo:

Figura 9.10: Execução do aplicativo para remover apenas um item

Vamos agora alterar o arquivo MasterViewController.m para adicionarmos a


funcionalidade de alterar a ordem dos itens da tabela:

#pragma mark - UITableViewDataSource methods


- (BOOL)tableView:(UITableView *)tableView canMoveRowAtIndexPath:(NSIndex

iOS Fundamentos 158


9

Path *)indexPath
{
return YES;
}
- (void)tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath
*)fromIndexPath toIndexPath:(NSIndexPath *)toIndexPath
{
[tableInfo exchangeObjectAtIndex:fromIndexPath.row
withObjectAtIndex:toIndexPath.row];
}

No primeiro método apenas informamos que todos os itens podem ser movidos,
mas como no método tableView: canEditRowAtIndexPath: informamos que o
item com índice 1 não poderia ser editado, isso vale para esse caso também. No
método seguinte fazemos apenas as alterações necessárias no array, trocando a
posição de seus itens.
Ao executarmos o aplicativo, teremos o seguinte resultado:

Figura 9.11: Execução do aplicativo para mover os itens

iOS Fundamentos 159


9

9.8 Persistência de dados


Assim como é possível inicializar um objeto com o conteúdo de um arquivo,
é possível também escrever um arquivo com o conteúdo do objeto. Para isso
chamamos o método writeToFile: atomically:, presente tanto na classe NSArray
quanto na NSDictionary. Para outros tipos de objetos, esse mesmo método
pode aparecer com alguma variação, como por exemplo a classe NSString, que
possui o método writeToFile: atomically: encoding: error:.
O iOS é um sistema fechado, e não é toda pasta que é possível de ser escrita.
Dentro de um aplicativo, só é possível acessar o conteúdo de dentro do
pacote desse aplicativo, e a função NSHomeDirectory() retorna uma string com
o caminho para ele. Dentro desse pacote, temor permissão de escrever em
apenas duas pastas: tmp, que é uma pasta temporária cujo conteúdo é apagado
a cada três dias, e a pasta Documents, que é uma pasta para salvar documentos
que são acessados por toda execução do aplicativo.

9.9 Exemplo de persistência de dados


Vamos alterar o exemplo anterior para que sempre que seja efetuada alguma
alteração na tabela nós vamos salvar o conteúdo do array em um arquivo da
pasta Documents. Também vamos utilizar esse arquivo, quando ele existir, na
inicialização do aplicativo, carregando suas informações para o array.
Portanto vamos alterar o método viewDidLoad do arquivo

iOS Fundamentos 160


9

MasterViewController.m para ele ler do arquivo quando ele existir:

#pragma mark - View lifecycle

- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.

self.navigationItem.rightBarButtonItem = self.editButtonItem;

NSString *filePath = [NSHomeDirectory() stringByAppendingString:@”/


Documents/animais.plist”];

if ([[NSFileManager defaultManager] fileExistsAtPath:filePath]) {


tableInfo = [NSMutableArray arrayWithContentsOfFile:filePath];
} else {
NSString *path = [[NSBundle mainBundle] pathForResource:@”animais”
ofType:@”plist”];
tableInfo = [NSMutableArray arrayWithContentsOfFile:path];
}
}

iOS Fundamentos 161


9

Para criarmos o caminho para o novo arquivo, concatenamos o caminho do


pacote do aplicativo com o local onde vamos escrevê-lo, no caso dentro da
pasta Documents, no arquivo animais.plist. Reparem que esse arquivo é diferente
do arquivo animais.plist que está dentro do projeto, pois esse arquivo só pode
ser lido via código. Em seguida utilizamos o objeto da classe NSFileManager
(utilizada para executar operações relacionadas com o sistema de arquivos)
para verificar se existe um arquivo no caminho do novo arquivo. Caso exista,
carregamos o conteúdo do novo arquivo, caso contrário carregamos o conteúdo
do arquivo que está dentro do pacote do aplicativo.
Agora precisamos adicionar o código de escrita do conteúdo do array no arquivo
para todos os casos de edição da tabela (adicionar/remover e mover). Para isso
vamos alterar os métodos como abaixo:

#pragma mark - UITableViewDataSource methods

- (void)tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath


*)fromIndexPath toIndexPath:(NSIndexPath *)toIndexPath
{
[tableInfo exchangeObjectAtIndex:fromIndexPath.row
withObjectAtIndex:toIndexPath.row];
NSString *filePath = [NSHomeDirectory() stringByAppendingString:@”/
Documents/animais.plist”];
[tableInfo writeToFile:filePath atomically:YES];
}

iOS Fundamentos 162


9

- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCell


EditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{
if (editingStyle == UITableViewCellEditingStyleDelete) {
[tableInfo removeObjectAtIndex:indexPath.row];
[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath]
withRowAnimation:UITableViewRowAnimationFade];
} else if (editingStyle == UITableViewCellEditingStyleInsert) {
// código para adicionar item
}

NSString *filePath = [NSHomeDirectory() stringByAppendingString:@”/


Documents/animais.plist”];
[tableInfo writeToFile:filePath atomically:YES];
}

O que fizemos de diferente foi, no final dos métodos, recuperar a localização


do novo arquivo e então escrever o conteúdo do array nele.
Ao executarmos o aplicativo depois de termos alterado alguns itens, teremos o
seguinte resultado:

iOS Fundamentos 163


9

Figura 9.12: Execução do aplicativo alterando itens

Figura 9.13: Execução do aplicativo após ter alterado alguns itens

iOS Fundamentos 164


iOS: Fundamentos

Capítulo 10
10

10.1 UITabBarController
A classe UITabBarController é uma extensão da classe UIViewController,
customizada para exibir uma navegação por abas de diferentes view controllers
e, assim como a UINavigationController, essa classe não precisa ser estendida.
Ela possui uma barra que contém as abas, e cada uma das abas é associada
a uma view controller, Essa barra de abas, ou tabbar, deve ser levada em
consideração quando é feita a interface do view controller.
Quando o usuário seleciona uma aba, o tabbar controller carrega e exibe a
view do view controller correspondente. Se a view já tinha sido carregada
ela é exibida na mesma posição em que ela estava anteriormente (imagine um
navigation controller com três view controllers na sua pilha, quando voltar para
sua aba correspondente, ele exibirá a mesma view controller de antes).
Cada aba da tabbar, definida pela classe UITabBarItem, possui um título e uma
imagem que será exibida junto do título e o próprio tabbar controller faz a
coloração azul quando uma aba é selecionada. Para isso, essa imagem deve
ser definida de acordo com sua propriedade alpha, em que ela é delimitada
pelos pixels que não são transparentes, ou seja, não importa a cor da imagem,
ela será sempre cinza na tabbar (seu tamanho deve ser de 30x30 pixels). Por
exemplo, a imagem abaixo mostra uma imagem original ao lado da imagem da
aba selecionada a aba não selecionada dessa imagem:

iOS Fundamentos 166


10

Figura 10.1: Imagem da tabbar

Caso a view controller não possua nenhum UITabBarItem especificando o


conteúdo de sua aba, uma aba padrão é definida para ele (sem imagem e
utilizando a propriedade title do view controller).
O tabbar controller possui uma propriedade de delegate, que faz referência a
um view controller que implemente o protocolo UITabBarControllerDelegate,
cuja principal função é controlar o comportamento da tabbar. Por exemplo,
você pode usar o delegata do tabbar controller para prevenir que uma
view seja exibida para uma determinada situação. Os principais métodos do
UITabBarControllerDelegate são:

tabBarController: shouldSelectViewController: pergunta ao delegate se a aba


referente ao view controller deve ser selecionada.

tabBarController: didSelectViewController: notifica o delegate que a aba


referente ao view controller foi selecionada.

Como o espaço da tela é limitado nos dispositivos iOS (principalmente no


iPhone e no iPod Touch), quando o número de abas é mais que seis, o tabbar
automaticamente mostra uma aba “More” na sexta posição e, quando
selecionada, um menu é aberto com as outras opções de abas.
Toda subclasse de UIViewController possui uma propriedade chamado
tabBarController que aponta para o tabbar controller dessa view controller,
que possui as seguintes propriedades:
iOS Fundamentos 167
10

viewControllers: array de view controllers.

selectedViewController: o view controller associada ao item da tabbar que


está selecionado.

selectedIndex: o índice do item da tabbar que está selecionado.

10.2 Exemplo de UITabBarController


Vamos criar um novo projeto do tipo “Empty Application” chamado “TabBar”
utilizando ARC. Em seguida vamos adicionar um arquivo de Storyboard ao
projeto chamado MainStoryboard e alterar as configurações do target do
projeto para utilizar o recém criado Storyboard como Main Storybard. Vamos
também alterar o método do AppDelegate.m para o seguinte:

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(


NSDictionary *)launchOptions
{
return YES;
}

Precisamos criar também duas novas classes de UIViewController, uma


chamada MusicaViewController e outra chamada FilmeViewController, além
de adicionar também duas imagens no projeto para representarem os view
controllers no tabbar.
No arquivo de Storyboard, vamos adicionar um componente do tipo
iOS Fundamentos 168
10

UITabBarController. Junto dele são adicionados dois componentes do tipo


UIViewController e que já possuem uma associação com o tabbar controller.
Em cada um dos view controllers, vamos associar seu tipo de classe
customizada, adicionar um label e alterar o componente UITabBarItem para
adicionar um título e uma imagem a eles. Numa visão geral, o Storyboard deve
ficar assim:

Figura 10.2: Visão geral do Storyboard

Ao executarmos o aplicativo, teremos o seguinte resultado:

Figura 10.3: Execução do aplicativo

iOS Fundamentos 169


10

Um tabbar controller permite adicionar qualquer subclasse da


UIViewController em suas abas, portanto é possível também adicionarmos um
navigation controller dentro de uma tabbar controller, assim como também
é possível adicionar um tabbar controller à pilha de view controllers de um
navigation controller.
Vamos adicionar no exemplo uma nova aba que vai apontar para um navigation
controller. Portanto no Storyboard vamos adicionar um componente do tipo
UINavigationController. Em seguida, precisamos fazer uma ligação entre o
tabbar controller e o navigation controller do tipo “Relationship”. Para isso,
na aba de conexões do tabbar controller, vamos ligar um Segue do tipo
“Relationship” ao navigation controller que adicionamos no Storyboard:

Figura 10.4: Adicionando um navigation controller ao tabbar controller

iOS Fundamentos 170


10

As conexões do tabbar controller devem ficar assim:

Figura 10.5: Conexões do tabbar controller

Por último vamos adicionar um novo view controller e fazer um botão para
navegarmos dentro do navigation controller:

Figura 10.6: Visão geral do navigation controller

iOS Fundamentos 171


10

Percebam que dentro no navigation controller, temos também um componente


do tipo UITabBarItem, que vai definir a aparência da aba que está ligada ao
navigation controller. Existem alguns tipos pré-definidos de abas para o tabbar
controller e podemos selecioná-los através do menu “Identifier” da aba de
atributos do tabbar item:

Figura 10.7: Tipos pré-definidos de tabbar item

Vamos atribuir um desses valores, por exemplo “Favorites”. A visão geral do


Storyboard ficará assim:

Figura 10.8: Visão geral do Storyboard

iOS Fundamentos 172


10

Ao executarmos o aplicativo, teremos o seguinte resultado:

Figura 10.9: Execução do aplicativo com navigation controller

10.3 Tabbed Application


O Xcode nos fornece um template chamado “Tabbed Application”, que vai gerar
um aplicativo com um tabbar controller e dois view controllers associados
a ele, da mesma forma que é criado o componente UITabBarController
quando o adicionamos no Storyboard. Entretanto ele também traz algumas
customizações para as abas, como duas imagens para elas.

10.4 Imagens padrões e Retina Display


Os aplicativos de iOS possuem duas imagens padrões: uma para o ícone do
aplicativo e outra para ser exibida enquanto o aplicativo é carregado (splash
screen). Ambas as imagens podem ser atribuídas ao projeto pela sua tela de
propriedades.

iOS Fundamentos 173


10

Figura 10.10: Imagens do aplicativo

As imagens são separadas em duas parates: ícone do aplicativo e imagem do


splash screen, ambas dividias entre imagem normal e Retina Display. As imagens
de ícones devem ser quadradas e não precisam ter os efeitos padrões dos
aplicativos de iOS (arredondamento nas bordas e brilho) pois o próprio iOS faz
isso. O tamanho das imagens devem seguir o seguinte tamanho em pixels:

Tela Normal Tela de retina iPad Portrait iPad Landscape

App Icon 57x57 114x114 72x72

Launch Image 320x480 640x960 768x1024 1024x768

Tabela 10.1: Tamanho das imagens padrão

Depois do lançamento do iPhone 4, o SDK da Apple criou uma para facilitar


o trabalho com imagens para a tela de retina. Não é necessário alterar nada
iOS Fundamentos 174
10

no código, apenas adicionar imagens com o dobro do tamanho das imagens


para a tela normal e adicionar a diretiva @2x nos seus nomes. Por exemplo,
suponha que possua uma imagem chamada Bota.png no projeto e que ela seja
do tamanho apropriado para a tela normal dos iPhones antigos. Para utilizarmos
uma imagem apropriada para a tela de retina basta adicionarmos uma imagem
chamada Bota@2x.png com o dobro do tamanho que anterior no projeto e
automaticamente o iOS utilizará essa imagem nos aparelhos com tela de retina.
Por padrão, as imagens para o ícone do aplicativo são chamadas de Icon.png e
as imagens para o splash screen são chamadas Default.png. Portanto para a tela
de retina, as imagens padrão são chamadas Icon@2x.png e Default@2x.png.

10.5 Exemplo
Vamos criar um exemplo do tipo “Single View Application” chamado “Icones”
utilizando Storyboard e ARC. Vamos adicionar imagens para o ícone do
aplicativo e para o splash screen. Para isso basta arrastarmos as imagens
desejadas para o espaço correto nas preferencias do target do projeto:

iOS Fundamentos 175


10

Figura 10.11: Imagens do projeto

Ao executarmos o aplicativo teremos o seguinte resultado:

Figura 10.12: Execução do aplicativo

Vamos agora adicionar a mesma imagem três vezes ao projeto de acordo com a
tabela abaixo:
Nome da Imagem Tamanho (pixels)
Eagle.png 200x200
EagleRetina.png 200x200
EagleRetina@2x.png 400x400

Tabela 10.2: Imagens do projeto

iOS Fundamentos 176


10

Notem que a imagem Eagle.png não possui uma versão para tela de retina,
enquanto a EagleRetina.png possui sua correspondente. Agora no arquivo
de Storboard, na Scene referente ao ViewController, vamos adicionar duas
imagens, e associar a uma dela a imagem Eagle.png e a outra a imagem
EagleRetina.png. Vamos adicionar também um label para identificar as imagens.
Ao rodarmos o aplicativo primeiro em um iPhone com tela normal e depois em
um iPhone com tela de retina, teremos o seguinte resultado:

Figura 10.13: Execução do aplicativo nos diferentes modelos de iPhone

Dessa forma não dá para notar muito a diferença, portanto vamos aproximar
a imagem na execução do iPhone com tela de retina para percebermos a
diferença:

Figura 10.14: Zoom das imagens (Sem @2x e Com @2x)

iOS Fundamentos 177


10

10.6 Arquivo Info.plist


Todo aplicativo para iOS possui dentro do seu pacote um arquivo do tipo Info.
plist, que contém diversas informações sobre o aplicativo, algumas delas
influenciam na interface do aplicativo e algumas são utilizadas internamente
pelo sistema. Quando criamos um novo projeto no Xcode, ele automaticamente
cria esse arquivo e seu nome é baseado no nome do projeto, por exemplo, no
projeto anterior o arquivo é chamado Icones-Info.plist.

Figura 10.15: Arquvo Icones-Info.plist

Os campos mais importantes desse arquivo são:

Bundle display name: nome que aparece abaixo do ícone na tela do aparelho
(valor default é o nome do projeto, que é guardado na variável PRODUCT_
NAME e é utilizado ${PRODUCT_ NAME} para recuperar seu conteúdo).

Icon files (iOS 5.0): ícone dos aplicativos para aparelhos rodando iOS 5.0 (para
versões anteriores do iOS, utilizar o campo “Icon files”). O item “Icon already
includes gloss effects” informa se o ícone utilizado já possui efeito de brilho ou não.
iOS Fundamentos 178
10

Bundle identifier: é o identificador único do aplicativo, geralmente é utilizado


o nome inverso ao domínio da empresa mais o nome do aplicativo, por
exemplo br.art.iai.${PRODUCT_ NAME}.

Bundle name: nome do pacote do aplicativo no sistema de arquivos do iOS.

Bundle version: versão do aplicativo.

Main storyboard file base name: nome do storyboard onde está definido o
início do aplicativo.

Além desses itens, existem outros que podem customizar a aparência do


aplicativo:

Application supports iTunes file sharing: permite a troca de arquivos do


aplicativo pelo iTunes. Os arquivos ficam situados na pasta Documents do
aplicativo.

Status bar is initially hidden: informa se a barra de status do dispositivo deve


ser escondida ou não durante a execução do aplicativo.

Status bar style: tipo da barra de status do dispositivo (cinza, preta opaca o
preta translúcida).

Figura 10.16: Ícone com e sem o efeito de brilho

iOS Fundamentos 179

Você também pode gostar