Como criar uma aplicação simples para iPhone – Parte 2 de 3

Esse artigo é a segunda parte de uma série de três onde criaremos uma aplicação simples para iPhone onde serão mostrados aos desenvolvedores iniciantes os principais tópicos no desenvolvimento de aplicações para o iOS. No primeiro artigo da série, criamos uma aplicação que continha uma lista de insetos em um TableView. Nessa segunda parte, iremos ver como criar uma visão detalhada de cada item da tabela de modo que possamos visualizar uma imagem maior dos insetos, votar neles e alterar a foto deles.

Na parte três da série, iremos ver como adicionar novos insetos (itens da tabela), adicionar um ícone e imagem padrão ao projeto, e manipular operações de longa duração.

Controladores das views

Agora que temos uma listra de insetos, seria bom sermos capazes de selecionar um deles e trazer a tona uma tela onde seja possível editar o nome do inseto ou a sua foto, e votar nele.

Na maior parte do tempo em aplicações para iPhone, para cada “tela” da aplicação você possui uma classe que é um “Controlador da view” para essa tela. Nesse momento temos um único controlador para nosso TableView (RootViewController), de forma que agora precisaremos de um segundo controlador para nossa tela de detalhamento.

Cada “Controlador de views” pode conter múltiplos views. No controlador do nosso TableView, tínhamos apenas um único view – a tabela. Porém, no controlador de nossa tela de detalhamento, iremos precisar de um conjunto de views – iremos precisar de um view para o nome do inseto, outro para a imagem, outro para o voto e vários outros.

Baixe algumas coisas!

Falando nisso, iremos precisar de um sistema de votação que utilize 5 estrelas na tela de detalhamente – mas o iPhone não possui um por padrão. Porém, você pode usar o view descrito no tutorial How To Make a Custom UIView: A 5-Star Rating View nesse projeto.

Não se preocupe em seguir esse outro tutorial agora (a menos que você queira) – ao invés disso pode apenas baixar o pacote Extra Stuff for Scary Bugs disponibilizado pelo autor do artigo no qual essa tradução foi baseada.

Vá em frente e baixe o arquivo, e em seguida:

  • Crie um novo grupo chamado “Views” no XCode, e arraste RateView.h/RateView.m para esse grupo, certificando-se que a opção “Copy items into destination group’s folder (if needed)” esteja selecionada. Esse é oc código para o sistema de votação usado nesse tutorial.
  • Repita esse procedimento para UIImageExtras, exceto que esse item deve ser arrastado para o grupo “Helpers”. Esse é um código assistente que precisaremos para redimensionar as imagens um pouco mais para frente.
  • Repita agora esse procedimento para as três imagens de faces, só que agora arraste-as para o grupo “Resources”. Essas são as imagens que usaremos para as “estrelas” no nosso sistema de votação.
  • Por fim, faça o mesmo para o arquivo logo1.png, também arrastado para o grupo “Resources”. Usaremos essa iamgem como ícone da aplicação mais tarde.

Criando nosso próprio Controlador de view com o Interface Builder

Ok – agora finalmente estamos prontos para continuar. Dê um clique com o botão direito no grupo “View Controllers” e clique em “Add\New File…”. Selecione a subclasse “Cocoa Touch Class\UIViewController” e marque a opção “With XIB for user interface” e nenhuma outra além dela:

Clique em Next, nomeie o arquivo como “EditBugViewController.m”, e clique em Finish.

Você verá que três arquivos serão adicionados ao seu projeto – EditBugViewController.h, EditBugViewController.m, and EditBugViewController.xib.

Do que esse arquivo XIB se trata? Dê um clique duplo sobre ele para descobrir:

Um arquivo XIB é um tipo de arquivo que você pode editar com o Interface Builder – uma maneira visual para construir sua interface com o usuário no XCode. Você irá arrastar e soltar elementos de interface em sua view, configurar suas propriedades da forma que você quiser e pode mesmo conectar os elementos a propriedades de suas classes do View Controller.

A maneira mais fácil de entender isso é testando. Mas antes de fazer isso, precisamos configurar algumas propriedades em nosso arquivo EditBugViewController.h. Substitua o conteúdo desse arquivo pelo seguinte:

#import <UIKit/UIKit.h>
#import "RateView.h"

@class ScaryBugDoc;

@interface EditBugViewController : UIViewController <UITextFieldDelegate, RateViewDelegate, UIImagePickerControllerDelegate, UINavigationControllerDelegate> {
    ScaryBugDoc *_bugDoc;
    UITextField *_titleField;
    UIImageView *_imageView;
    RateView *_rateView;
    UIImagePickerController *_picker;
}

@property (retain) ScaryBugDoc *bugDoc;
@property (retain) IBOutlet UITextField *titleField;
@property (retain) IBOutlet UIImageView *imageView;
@property (retain) IBOutlet RateView *rateView;
@property (retain) UIImagePickerController *picker;

- (IBAction)titleFieldValueChanged:(id)sender;
- (IBAction)addPictureTapped:(id)sender;

@end

Você pode observar alguns tipos estranhos aqui – como IBOutlet e IBAction. Essas são “palavras chave mágicas” que o Interface Builder procura, para que possamos associar os controles que nós adicionarmos no Interface Builder as propriedades de nossas classes, se colocarmos um IBOutlet ou um IBAction. próximo ao método ou propriedade, o Interface Builder detectará ela de modo que possamos fazer a ligação mais tarde. Essas palavras chaves são chamadas “outlets”, por falar nisso.

Agora que nossa classe e nossos outlets estão prontos, vamos configurar nosso XIB. Na janela onde se lê “EditBugViewController.xib”, dê um clique duplo no View para trazê-lo para a janela View se ele ainda não está nela. Depois vá para “Layout\Show Bounds Rectangles”, que tornará um pouco mais fácil alinhar os elementos.

Agora vamos começar a adicionar alguns controles a nossa janela.Ative a Biblioteca selecionando Tools\Library, e arraste um UITextField, UIImageView, e um UIView para a tela de texto e arrume-os de acordo com a iamgem abaixo (o campo de texto está no topo):

Depois selecione UITextField e clique em Tools\Attributes Inspector para que possamos alterar algumas propriedades.

Ajuste a fonte para Helvetica-Bold stamanho 18.0, o alinhamento do texto para center, o comportamento Clear Button para “Appears While Editing”, a capitalização para Words como na imagem abaixo:

Depois, mude para o Size Inspector clicando na terceira aba ou selecionando Tools\Size Inspector, e ajuste os atributos de auto-dimensionamento como na imagem a seguir:

Isso fará com que quando nosso view seja rotacionado para a orientação paisagem, o campo de texto se estique pela tela para torna-se mais largo.

Em seguida ajuste o UIImageView. Na primeira aba (Attributes Inspector) ajuste-o para o modo “Aspect Fit”, e na terceira aba (Size Inspector) ajuste o auto-dimensionamento de acordo com a imagem a seguir:

Para o UIView, vá para a quarta aba (Identity Inspector) e configure o atributo “Class Identity” para “Rate View” de modo que nosso sistema de votação possa ser exibido aqui. Depois, na terceira aba (Size Inspector) ajuste o auto-dimensionamento de acordo com a imagem a seguir:

Até agora, tudo bem. Em seguida precisamos adicionar alguns controles para a tela de forma que o usuário possa tocar a área do UIImageView para alterar a imagem.

Existem algumas maneiras de fazer isso, mas uma das mais fáceis é criar um botão invisível sobre o UIImageView, e configura-lo para que possamos obter uma função de callback quando o botão for pressionado. Podemos também adicionar um UILabel por baixo da imagem para dizer “Toque para alterar a imagem” se nenhuma imagem tiver sido configurada.

Assim, arraste um UIButton da biblioteca, e redimensione ele para ficar no tamanho e posição exatos de UIImageView. Para torna-lo invisível, na primeira aba (Attributes Inspector) mude o tipo para Custom. Depois, nma terceira aba (Size Inspector) ajuste os atributos de auto-dimensionamento de acordo com a imagem a seguir:

Finalmente, arraste um UILabel da biblioteca, coloque-a no meio de UIImageView, e dê um clique duplo sobre ela para editar o texto para “Toque para alterar a imagem”. Depois mude o alinhamento do texto para center. Então, arraste o UILabel algumas posições no XIB de modo que fique por trás do UIImageView (a lista vai de trás para frente).

Em seguida, na terceira aba (Size Inspector) ajuste os atributos de auto-dimensionamento de acordo com a imagem a seguir:

Antes que você siga adiante, pode checar novamente se você ajustou todos os atributos de auto-dimensionamento corretamente selecionando o View raiz, e alterando a orientação de Retrato para Paisagem.

Se algo não estiver certo, não se preocupe – apenas altere de volta para Retrato e verifique as configurações.

Pronto! Adicionamos todos os controles que precisamos, de modo que agora tudo que precisamos é conectar tudo aos seus respectivos outlets em nossa classe:

  • Mantenha a tecla Control pressionada e arraste “File’s Owner” para UITextField, RateView, e UIImageView, e conecte cada um aos seus respectivos outlets.
  • Mantenha a tecla Control pressionada e arraste UITextField de volta a “File’s Owner” para configurar EditBugViewController como delegate de UITextFiels.
  • Mantenha a tecla Control pressionada e arraste UIButton de volta para “Files’s Owner”, e conecte-o ao outlet “addPictureTapped”. Isso é um atalho para conectar o evento TouchUpInside a esse outlet.
  • Dê um clique com o botão direito em UITextField, arraste do ponto próximo a “Editing Changed” até “File’s Owner”, e conecte a ação ao outlet “titleFieldValueChanged”.

Quando você tiver feito tudo isso, pode verificar todo o seu trabalho clicando com o botão direito no File’s Owner para trazer a tona uma janela pop-up com todas as conexões que foram configuradas, que deve se parecer com a imagem a seguir:

Implementando nossa visão detalhada

Nesse momento, já temos toda a parte que cabia ao Interface Builder pronta – agora precisamos terminar de implementar EditBugViewController.m e conecta-lo ao resto do projeto.

Iremos fazer algumas alterações em EditBugViewController.m. Haverá bastante código aqui, então vamos por partes.

1) Importe os cabeçalhos e sintetize as propriedades

// At top of file
#import "ScaryBugDoc.h"
#import "ScaryBugData.h"
#import "UIImageExtras.h"

// After @implementation
@synthesize bugDoc = _bugDoc;
@synthesize titleField = _titleField;
@synthesize imageView = _imageView;
@synthesize rateView = _rateView;
@synthesize picker = _picker;

2) Configure o View de votação

// Uncomment viewDidLoad and add the following inside
_rateView.notSelectedImage = [UIImage imageNamed:@"shockedface2_empty.png"];
_rateView.halfSelectedImage = [UIImage imageNamed:@"shockedface2_half.png"];
_rateView.fullSelectedImage = [UIImage imageNamed:@"shockedface2_full.png"];
_rateView.editable = YES;
_rateView.maxRating = 5;
_rateView.delegate = self;

NO viewDidLoad, nós fazemos os ajustes das propriedades de nosso RateView. Para mais detalhes, veja o tutorial How To Make a Custom UIView: A 5-Star Rating View

3) Ative a auto-rotação

// Uncomment shouldAutorotateToInterfaceOrientation and replace the return value to:
return YES;

No shouldAutorotateToInterfaceOrientation, retornamos YES já que fizemos todo o trabalho de ajuste nos atributos de auto-dimensionamento no Interface Builder. Isso permitirá que o usuário rotacione esse view e nossos controles sejam re-arranjados de acordo com os atributos de dimensionamento usados.

4) Libere memória de forma adequada quando necessário

// Inside didReceiveMemoryWarning
self.picker = nil;

// Inside viewDidUnload
self.titleField = nil;
self.imageView = nil;
self.rateView = nil;

// Inside dealloc
[_bugDoc release];
_bugDoc = nil;
[_titleField release];
_titleField = nil;
[_imageView release];
_imageView = nil;
[_rateView release];
_rateView = nil;
[_picker release];
_picker = nil;

Em didReveiveMemoryWarning, nós devemos liberar qualquer coisa que possa ter um ponteiro que não seja absolutamente necessário por poder ser recriado depois. Nesse caso, o Selecionador de Imagens se encaixa nessa categoria porque nós escreveremos nosso código de forma que possamos re-cria-lo do zero.

viewDidUnload é chamado quando o  iOS está tentando recuperar memória – uma das coisas que ele faz é descarregar todos os views que não estejam mais sendo exibidos. Quando eles forem ser exibidos novamente, viewDidLoad é chamado. Assim, em viewDidUnload, é importante configurar qualquer item que criarmos em viewDidLoad para nulo, assim como qualquer item que foi conectado pelo Interface Builder. Então, aqui iremos ajustar nossos três controles criados no Interface Builder para nulo.

E, naturalmente, em dealloc, nós liberamos toda a memória usada pelo aplicativo.

5) Configure um estado inicial para a Interface

// Add new method
- (void)viewWillAppear:(BOOL)animated {
    _titleField.text = _bugDoc.data.title;
    _rateView.rating = _bugDoc.data.rating;
    _imageView.image = _bugDoc.fullImage;
    [super viewWillAppear:animated];
}

viewWillAppear é um bom local para inicializar todos os nossos controles para o que eles devem exibir, baseados em nosso modelo.

6) Manipule os views de texto e de votação

- (IBAction)titleFieldValueChanged:(id)sender {
    _bugDoc.data.title = _titleField.text;
}

#pragma mark UITextFieldDelegate

- (BOOL)textFieldShouldReturn:(UITextField *)textField {
    [textField resignFirstResponder];
    return YES;
}

#pragma mark RateViewDelegate

- (void)rateView:(RateView *)rateView ratingDidChange:(float)rating {
    _bugDoc.data.rating = rating;
}

Aqui nós configuramos titleFieldValueChanged para ser chamado sempre que o usuário altera o valor do campo de texto, de forma que possamos atualizar o modelo caso uma alteração ocorra.

textFieldShouldReturn é chamado quando o usuário aperta a tecla Enter (Return) do teclado. Chamamos resignFirstResponder para desaparecer com o teclado da tela quando isso acontece.

rateView:ratingIsChanged é chamado quando o usuário escolhe uma nova opção do sistema de votação, atualizando automaticamente o modelo, por termos nos configurado como delegate de RateView.

Caso esteja se perguntando, as marcações #pragma são apenas linhas especiais que o XCode pode ler para configurar separadores na lista de funções do editor para propósitos de organização:

7) Mostre o Selecionador de imagens e processe o resultado

- (IBAction)addPictureTapped:(id)sender {
    if (_picker == nil) {
        self.picker = [[[UIImagePickerController alloc] init] autorelease];
        _picker.delegate = self;
        _picker.sourceType = UIImagePickerControllerSourceTypePhotoLibrary;
        _picker.allowsEditing = NO;
    }
    [self.navigationController presentModalViewController:_picker animated:YES];
}

#pragma mark UIImagePickerControllerDelegate

- (void)imagePickerControllerDidCancel:(UIImagePickerController *)picker {
    [self dismissModalViewControllerAnimated:YES];
}

- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info {    

    [self dismissModalViewControllerAnimated:YES];

    UIImage *fullImage = (UIImage *) [info objectForKey:UIImagePickerControllerOriginalImage];
    UIImage *thumbImage = [fullImage imageByScalingAndCroppingForSize:CGSizeMake(44, 44)];
    _bugDoc.fullImage = fullImage;
    _bugDoc.thumbImage = thumbImage;
    _imageView.image = fullImage;
}

Nós configuramos addPictureTapped para ser chamado quando o usuário tocar no botão invisível sobre UIImage, assim aqui criamos o UIImagePicker (se ele ainda não existir), e configuramos a fonte de imagens para ser a biblioteca de fotos (você pode escolher outras fontes, como a câmera). Nós nos auto-configuramos como delegate de forma que possamos capturar os callbacks quando o usuário terminar de escolher a imagem. Finalmente, apresentamos o Selecionador de imagens como um “Modal View Controller”, o que significa que ele ocupa toda a tela.

Finalmente, implementamos as chamadas do Selecionador de imagens para quando os usuário escolhe uma imagem ou cancela a ação. Em qualquer caso, nós fechamos a janela do selecionador. Se o usuário escolheu uma imagem, obtemos a imagem e sua miniatura (que nós redimensionamos com a classe UIImageExtras que adicionamos anteriormente) e atualizamos tanto o modelo quanto o view.

Integrando nossa visão detalhada

Essa parte é bem rápida. Faça as seguintes alterações no arquivo RootViewController.h:

// Before @interface
@class EditBugViewController;

// Inside @interface
EditBugViewController *_editBugViewController;

// After @interface
@property (retain) EditBugViewController *editBugViewController;

Nós apenas declaramos uma variável de instância e propriedade para manter o registro do controlador do view para a edição do inseto.

Agora, faça as seguintes alterações no arquivo RootViewController.m:

// At top of file
#import "EditBugViewController.h"

// In synthesize section
@synthesize editBugViewController = _editBugViewController;

// Uncomment viewWillAppear and add the following inside:
[self.tableView reloadData];

// Add inside tableView:didSelectRowAtIndexPath
if (_editBugViewController == nil) {
    self.editBugViewController = [[[EditBugViewController alloc] initWithNibName:@"EditBugViewController" bundle:[NSBundle mainBundle]] autorelease];
}
ScaryBugDoc *doc = [_bugs objectAtIndex:indexPath.row];
_editBugViewController.bugDoc = doc;
[self.navigationController pushViewController:_editBugViewController animated:YES]; 

// Inside didReceiveMemoryWarning
self.editBugViewController = nil;

// Inside dealloc
[_editBugViewController release];
_editBugViewController = nil;

A maior parte do código é auto-explicativa, porém existem duas coisas que devem ser apontadas.

Em primeiro lugar, observe que em viewWillAppear, nós recarregamos os dados na tabela. Isso se deve porque quando o usuário está na visão detalhada, ele pode mudar o nome do inseto ou da imagem, e nós queremos atualizar o nome e a imagem mostradas quando voltarmos a tabela. Uma maneira fácil de fazer isso é recarregar a tabela inteira, que é o que nós fazermos aqui.

O código importante está no método tableView:didSelectRowAtIndexPath. Quando o usuário seleciona uma linha, nós verificamos se já tínhamos criado o editBugViewController, se não seguimos em frente e criamos ele. Depois obtemos o inseto que o usuário clicou através da indexação do array _bugs, ajustamos a propriedade bug no editBugViewController, e apresentamos ao controlador do view passando ele a pilha de navegação.

Finalmente, temos tudo pronto. Vá em frente e compile e rode o seu projeto, e se tudo estiver correto você deve ser capaz de ativar a visão detalhada do inseto, altera o nome dele e sua imagem, votar neles e até mesmo rotacionar a tela em qualquer direção.

Para onde ir a partir daqui?

Na terceira e última parte da śerie, iremos ver como adicionar e deletar insetos da lista, adicionar um ícone e imagem padrão ao projeto, e  manipular corretamente operações de longa duração.

Traduzido de http://www.raywenderlich.com/1845/how-to-create-a-simple-iphone-app-tutorial-part-2