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

Essa é última parte da nossa série de 3 artigos que ensina a desenvolvedores iniciantes como criar uma aplicação simples para iPhone. Na primeira parte da série, criamos uma aplicação que continha uma lista de insetos dentro de um TableView. Na segunda parte da série, vimos como criar uma visão detalhada para cada inseto. Nesse artigo, veremos como adicionar novos insetos, adicionar um ícone e uma imagem padrão ao nosso projeto e como manipular operações de longa duração.

Adicionando e Deletando itens da tabela

Até agora, tudo está funcionando bem, mas nesse momento nossa aplicação não é muito amigável ao usuário. Provavelmente a primeira coisa que alguém irá querer fazer é adicionar seu próprio item a lista, e nesse momento a única forma de fazer isso é editando o código fonte.

Por sorte, como escrevemos nosso EditBugViewController e estamos usando um UITableViewController como RootViewController, a maior parte da infraestrutura já está pronta. Existem somente algumas poucas mudanças que temos que fazer no RootViewController.m, que serão explicadas aos poucos para facilitar o entendimento:

1) Configure os botões da barra de navegação

// Inside viewDidLoad
self.navigationItem.leftBarButtonItem = self.editButtonItem;
self.navigationItem.rightBarButtonItem = [[[UIBarButtonItem alloc]
    initWithBarButtonSystemItem:UIBarButtonSystemItemAdd
    target:self action:@selector(addTapped:)] autorelease];

Em primeiro lugar, no viewDidLoad, configuramos alguns botões na barra de navegação. Da mesma forma que “title” é uma propriedade especial dos controladores de views usadas pelo controlador de navegação, “navigationItem” é uma outra propriedade desse tipo. O que você configurar para leftBarButtonItem e rightBarButtonItem será mostrado na barra de navegação quando o controlador de navegação mostrar seu controlador de view.

Para o leftBarButtonItem, usamos um botão embutido especial chamado “editButtonItem”. Esse botão possui a inscrição “Edit” e alterna o UITableView entre o modo de edição (onde você pode deletar linhas por exemplo) e o modo normal.

Para o rightBarButtonItem, criamos um botão que o usuário pode utilizar para criar uma nova entrada. Acontece que já existe um item do sistema para adição (que se parece com o simbolo +), de modo que vamos usa-lo e registrar o método “addTapped:” para ser chamado quando o botão é clicado.

2) Implemente tableView:commitEditingStyle:forRowAtIndexPath

// Uncomment tableView:commitEditingStyle:forRowAtIndexPath and replace the contents with the following:
if (editingStyle == UITableViewCellEditingStyleDelete) {
    [_bugs removeObjectAtIndex:indexPath.row];
    [tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
}

Esse método é chamado quando o usuário escolhe modificar uma linha de algum modo. Temos que verificar se o usuário está tentando deletar a linha, e se for o caso, remove-la. Observe que temos que remover tanto o item de nosso modelo de dados (_bugs) E notificar a TableView que uma das linhas foi removida, através do deleteRowsAtIndexPaths.

3) Manipule a adição de um novo item

// Add new method
- (void)addTapped:(id)sender {
    ScaryBugDoc *newDoc = [[[ScaryBugDoc alloc] initWithTitle:@"New Bug" rating:0 thumbImage:nil fullImage:nil] autorelease];
    [_bugs addObject:newDoc];

    NSIndexPath *indexPath = [NSIndexPath indexPathForRow:_bugs.count-1 inSection:0];
    NSArray *indexPaths = [NSArray arrayWithObject:indexPath];
    [self.tableView insertRowsAtIndexPaths:indexPaths withRowAnimation:YES];

    [self.tableView selectRowAtIndexPath:indexPath animated:YES scrollPosition:UITableViewScrollPositionMiddle];
    [self tableView:self.tableView didSelectRowAtIndexPath:indexPath];
}

Quando o usuário clica no botão “+”, criamos um ScaryBugDic com alguns valores padrão, e adicionamos o item ao array. Observe que temos que atualizar a TableView de forma que ela tome conhecimento que existe uma nova linha.

Depois chamamos um código para fazer com que a TableView aja como se o usuário selecionou a nova linha, de maneira que nós imediatamente iremos para a tela de edição do novo item.

E é isso! Se você compilar e executar o código, você deve ser capaz agora de adicionar seus próprios itens, como esse:

Adicionar um ícone e uma imagem padrão

Ok, agora nossa aplicação parece completamente funcional, e aparenta estar pronta para ser liberada ao público. Exceto que seria bem vergonhoso se fizermos isso agora, pois não temos nem mesmo um ícone para a aplicação.

Por sorte, é bem fácil corrigir isso. Anteriormente, adicionamos um ícone ao nosso projeto (logo1.png), do arquivo ExtraStuffForScaryBugs.zip. Vamos configurar essa imagem como ícone de nosso projeto.

Para fazer isso, simplesmente abra ScaryBugs-Info.plist e modifique o “Icon file” para “logo1.png”:

A medida que você avance no desenvolvimento para iOS, estará voltando a esse arquivo algumas vezes para configurar seus projetos de várias maneiras.

Existe um outra coisa que podemos fazer também. Se você tentar rodar o ScaryBugs em seu iPhone, pode notar que depois de clicar no ícone, haverá uma pausa antes da tela principal aparecer onde uma tela preta será mostrada. Esse comportamento não é adequado – parece que a aplicação não é suficientemente responsiva.

De acordo com a documentação da Apple, a melhor coisa a se fazer é exibir uma tela que se pareça com sua aplicação, mas sem nenhum dado nela. Isso é bem fácil de se fazer. Apenas abra o RootViewController.m e faça a seguinte alteração:

// Replace tableView:numberOfRowsInSection's return statement to the following:
return 0; //return _bugs.count;

Em seguida execute o projeto em seu dispositivo, e você verá uma TableView vazia logo após o carregamento. No Xcode, vá para Window/Organizer, clique em seu dispositivo, vá para a aba screenshots, e clique em “Capture” para obter uma captura da tela.

Você pode encontrar essas telas no diretório /Users/yourUserName/Library/Application Support/Developer/Shared/Xcode/Screenshots. Encontre a que você capturou de sua aplicação, renomeie para “Default.png” e arraste para a pasta Resources para adiciona-la ao seu projeto.

Agora restaure o tableView:numberOfRowsInSection para a forma que estava, e execute  a aplicação em seu dispositivo novamente, e se tudo correr bem você deve ver a tela padrão assim que a aplicação for carregada ao invés de uma tela preta.

Bônus: Manipulando operações de longa duração

Se você executar sua aplicação no simulador, tudo provavelmente irá parecer bem,  mas se rodar a aplicação em seu iPhone e tentar alterar uma imagem, existirá uma longa espera para que UIImagePicker seja inicializado. Depois de escolher a imagem, existirá outra espera longa para que a imagem seja redimensionada (especialmente se for muito grande). Isso é uma coisa ruim, pois fará que sua aplicação pareca não responder adequadamente ao usuário.

A regra principal a ter em mente é que você nunca deve executar operações de longa duração no thread principal. Nós violamos essa regra em dois locais, o que é o motivo de nossa aplicação aparentar não estar respondendo adequadamente.

O que você deve fazer é executar essas operações de longa duração em segundo plano. Idealmente, a operação seria executada em segundo plano a medida que o usuário continua a fazer outras coisas. Mas se a operação precisa ser feita antes que o usuário possa continuas (como no caso do carregamento do selecionador de imagens), você deve ao mesmo mostrar na tela um indicador de algum tipo de forma que o usuário entenda que a aplicação está trabalhando e não está travada.

Então, é isso que iremos fazer aqui – executar o código de longa duração em segundo plano, e exibir um indicador de carregamento por cima do thread enquanto esperamos que a operação seja completada.

O desejo de exibir um indicador de carregamento é um problema comum para desenvolvedores, de forma que muitas pessoas criam bibliotecas de indicação de atividade que podemos usar para economizar nosso tempo. Após tentar algumas, usaremos para nosso projeto o DSActivityView de David Sinclair. Você pode baixar uma cópia da página do autor, ou apenas pegar uma cópia aqui.

Após baixar o DSActivityView, adicione os arquivos ao seu projeto, dentro do grupo “Views”. Depois faça as seguintes alterações no arquivo EditBugViewController.h:

// Before @interface
@class DSActivityView;

// Inside @interface
DSActivityView *_activityView;
NSOperationQueue *_queue;

// After @interface
@property (retain) DSActivityView *activityView;
@property (retain) NSOperationQueue *queue;

Aqui nós declaramos DSActivityView e algo que não tinhamos discutido ainda – um NSOperationQueue (mas sobre isso a seguir):

Em seguida, faça as seguintes alterações em EditBugViewController.m:

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

// After @implementation
@synthesize activityView = _activityView;
@synthesize queue = _queue;

// At end of viewDidLoad
self.queue = [[[NSOperationQueue alloc] init] autorelease];

// In viewDidUnload
self.queue = nil;

// In dealloc
[_queue release];
_queue = nil;

// Replace addPictureTapped with the following:
- (IBAction)addPictureTapped:(id)sender {
    if (_picker == nil) {
        [DSBezelActivityView newActivityViewForView:self.navigationController.navigationBar.superview withLabel:@"Loading Image Picker..." width:160];
        [_queue addOperationWithBlock: ^{
            self.picker = [[UIImagePickerController alloc] init];
            _picker.delegate = self;
            _picker.sourceType = UIImagePickerControllerSourceTypePhotoLibrary;
            _picker.allowsEditing = NO;
            [[NSOperationQueue mainQueue] addOperationWithBlock:^{
                [DSBezelActivityView removeViewAnimated:YES];
                [self.navigationController presentModalViewController:_picker animated:YES];
            }];
        }];
    } else {
        [self.navigationController presentModalViewController:_picker animated:YES];
    }
}

// Replace imagePickerController:didFinishPickingMediaWithInfo with the following:
- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info {    

    [self dismissModalViewControllerAnimated:YES];

    [DSBezelActivityView newActivityViewForView:self.navigationController.navigationBar.superview withLabel:@"Resizing Image..." width:160];
    [_queue addOperationWithBlock: ^{
        UIImage *fullImage = (UIImage *) [info objectForKey:UIImagePickerControllerOriginalImage];
        UIImage *thumbImage = [fullImage imageByScalingAndCroppingForSize:CGSizeMake(44, 44)];
        [[NSOperationQueue mainQueue] addOperationWithBlock:^{
            _bugDoc.fullImage = fullImage;
            _bugDoc.thumbImage = thumbImage;
            _imageView.image = fullImage;
            [DSBezelActivityView removeViewAnimated:YES];
        }];
    }];
}

A primeira coisa que fazemos aqui é criar uma NSOperationQueue em nosso viewDidLoad. Os detalhes sobre o NSOperationQueue poderia facilmente ser o tópico de um tutorial inteiro, de forma que por agora pense nele como um objeto  para o qual você pode submeter tarefas, e executa-las em segundo plano.

Em seguida, você verá que reescrevemos nosso método addPictureTapped um bocado. A primeira coisa que fazemos é usar o DSActivityView para mostrar uma tela de carregamento através da chamada ao método newActivityForView. Passamos o superview da barra de navegação de forma que o pop-up cubra toda a tela, incluindo a barra de navegação.

Agora chamamos um método da fila de operações para agendar alguns trabalhes a serem executados em segundo plano chamando addOperationWithBlock. Observe que essa é uma nova API disponível apenas no iPhone 4.0 ou superior. Existem outras maneiras de fazer a mesma coisa em sistemas operacionais mais antigos, mas essa API é uma maneira mais legal de fazer isso.

De qualquer forma, addOperationWithBlock usa um novo recurso do iOS chamado blocks. Se você ainda não leu nada sobre eles, recomendo verificar excelente guia prático para os blocos da Apple.

Assim, tudo que estiver dentro dos colchetes após addOperationWithBlock será executado em segundo plano de forma que a animação de carregamento possa continuar e o usuário possa ver que a aplicação está trabalhando de alguma forma. Aqui fazemos o trabalho de longa duração de inicializar o selecionador de imagem. Quando o processamento for concluído, precisamos interromper o indicador de atividade e mostrar o controlador de views – mas precisamos fazer isso na thread principal.

Por quê isso? Bem, a regra é que toda vez que você precisa modificar a interface, precisa fazer isso na thread principal. E podemos usar uma NSOperationQueue especial chamada “mainQueue” para agendar blocos que devem ser executados na thread principal até que o trabalho seja completado.

Devemos seguir a mesma idéia no imagePickerController:didFinishPickingMediaWithInfo.

E é isso! Execute a aplicação em seu dispositivo, e você verá a nova animação enquanto a operação de longa duração for acionada, o que torna a experiência do usuário mais agradável.

Para onde ir a partir daqui?

Guess what – this isn’t the end for these bugs! We extend this project even more in a follow-up article on how to save your application data that you might enjoy if you’ve gotten this far, so check it out!

Adivinhe: isso não é o fim do túnel para nossa aplicação. Existe um outro artigo que estende a aplicação para salvar os dados da aplicação que você pode apreciar se quiser ir mais longe. Dê uma olhada.

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