Criando uma aplicação para o iPhone – primeiros passos

Numa visão geral, o processo de criação de uma aplicação para iPhone é similar a criação de uma aplicação MacOSX. Ambas usam as mesmas ferramentas e muitas dessas aplicações usam as mesmas bibliotecas básicas. Apesar das similaridades, existem também diferenças significativas. Um iPhone não é um computador de mesa; tem um propósito diferente e requer uma abordagem muito diferente de design. Essa abordagem precisa tomar vantagem dos recursos do iOS e renunciar características que podem ser irrelevantes ou impraticáveis em um ambiente móvel. O tamanho menor da tela do iPhone e do iPod touch também significa que a interação com o usuário de sua aplicação  deve ser bem organizada e sempre focar na informação que o usuário mais precisa.

O iOS deixa os usuários interagirem com o iPhone e com o iPod touch de formas que você não poderia interagir com aplicações para computadores de mesa. A interface multi-toque reporta cada dedo que toca na tela separadamente e torna possível manipular gestos multi-dedo e outras entradas complexas facilmente. Além disso, características embutidas do hardware como acelerômetro, apesar de presente em alguns sistemas de mesa, são usados mais extensivamente no iOS para rastrear a orientação atual da tela e ajustar o seu conteúdo de acordo. O entendimento de como você pode usar essas características ajudarão você a focar em um design que seja adequado aos seus usuários.

A melhor maneira de entender o desenho de uma aplicação para iPhone é partir para um exemplo. Esse artigo leva você a uma viagem para criar uma aplicação exemplo chamada MoveMe. Esse exemplo demonstra muitos dos comportamentos típicos de uma aplicação para iPhone, incluindo:

  • Inicialização da aplicação
  • Exibição de uma janela
  • Desenho de um conteúdo personalizado
  • manipulação de eventos relacionados ao toque
  • Execução de animações

A figure 1 mostra a interface de nossa aplicação. Um toque no botão Welcome dispara uma animação que faz com que o botão pulse e centralize-se debaixo de seu dedo. A medida que você arrasta o seu dedo pela tela, o botão segue o seu dedo. Levantar o seu dedo da tela e, usando outra animação, o botão volta ao seu ponto de origem. Um batida dupla em algum ponto do botão muda o idioma do texto do botão.

Figure 1  The MoveMe application window
Figure 1 The MoveMe application window

Antes de ler as outras seções desse artigo, você deveria baixar o exemplo (MoveMe) para que assim você possa seguir adiante diretamente do código fonte. Vocẽ deveria também ler as seguintes páginas do iOS Dev Center para obter um entendimento básixo do iOS e das ferramentas e linguagens que usará para o desenvolvimento:

Se você não estiver familiarizado com a libguagem Objective-C, deve ler o artigo Introdução ao Objective-C, para se familiarizar com os conceitos básicos da sintaxe de Objective-C

Examinando o projeto exemplo MoveMe

Baixando  o exemplo MoveMe lhe será fornecido o código fonte e arquivos de suporte necessários para compilar e executar a aplicação. Você gerencia os projetos do iOS usando a aplicação Xcode (localizada em /Developer/Application por padrão). Cada janela de um projeto Xcode combina uma área de trabalho manipular o código fonte e arquivos de recursos, regras de compilação para o seu código e montagem de sua aplicação, além de ferramentas para edição e depuração do código.

A figure 2 mostra a janela de projeto Xcode para a aplicação MoveMe. Para abrir esse projeto, copie ele para seu disco e dê um clique duplo no arquivo MoveMe.xcodeproj para abri-lo. (Você pode também abrir o projeto a partir do menu File > Open do Xcode  e selecionar esse arquivo na caixa de diálogo). O projeto inclui muitos arquivos de código fonte Objective-C (denotamos pela extensão .m), algumas imagens e outros recursos, e um alvo pre-definido (MoveMe) para compilar a aplicação.

 Figure 2  The MoveMe project window
Figure 2 The MoveMe project window

No iOS, o alvo final de seu projeto do Xcode é um empacotador para a aplicação, que é um tipo especial de diretório que armazena todo os binários e arquivos de recurso. Esses pacotes no iOS tem uma estrutura relativamente rasa, com muitos arquivos residindo no diretório do nível mais alto do empacotador. Por[em, um empacotador pode conter sub-diretórios para armazenar versões localizadas das strings e outros arquivos de recursos específicos de idioma. Você não precisa saber a estrutura exata de empacotamento da aplicação para os propósitos desse artigo, mas pode achar mais informações no artigo “Build-Time Configuration Details” em iOS Application Programming Guide se estiver interessado.

Compilando a aplicação MoveMe

Para compilar a aplicação MoveMe e executa-la no simulador, siga os passos:

  1. Abra o arquivo MoveMe.xcodeproj no Xcode.
  2. Na barra de ferramentas do projeto, certifique-se de que a opção simulador esteja selecionada no menu Active SDK (Se esse menu não estiver aparecendo, escolha Project > Set Active SDK > Simulator).
  3. Selecione Build > Build and Go (Run) a partir do menu, ou simplesmente clique no botão Build and Go da barra de ferramentas.

Quando a aplicação termina de ser compilada, o Xcode carrega ela no simulador do iOS e executa. Usando o seu mouse, você pode clicar no botão Welcome e arrasta-lo pela tela para ver o comportamento da aplicação. Se você possuir um dispositivo configurado para desenvolvimento, pode compilar a sua aplicação executa-la em seu dispositivo.

Algumas palavras sobre gerenciamento de memória

O iOS é primariamente um sistema orientado a objeto, de modo que grande parte da memória que você aloca é na forma de objetos do Objective-C. O iOS usa um esquema de contagem de referência para saber quando é seguro liberar a memória ocupada por um objeto. Quando você criar inicialmente um objeto, ele começa com uma contagem de referência de 1. Os clientes que recebem esse objeto pode optar por retê-lo, incrementando assim a contagem em 1 unidade. Se um cliente retêm um objeto, esse cliente precisa também libera-lo quando não for mais necessário. A liberação do objeto diminui a contagem das referências em 1 unidade. Quando a contagem de referência de um objeto chega a 0, o sistema automaticamente libera a memória ocupada pelo objeto.

Nota: O iOS não suporta gerenciamento de memória usando o recurso de coleta de lixo que existe no Mac OS X 10.5 e mais recentes.

Se quiser alocar blocos genéricos de memória – isso é, memória não associada a um objeto – você pode usar a biblioteca padrão malloc. Nesse caso, qualquer quantidade de memória que você alocar usando malloc, você é o responsável pela liberação dessa memória quando não precisar mais dela usando a função free. O sistema não libera blocos baseados em malloc para você.

Independentemente de como você aloca a memória, o gerenciar seu uso dela é importante. Embora o iOS tenha um sistema de memória virtual, esse sistema não faz uso de um arquivo trocas. Isso significa que as páginas de código pode ser apagadas quando necessário, mas os dados de sua aplicação devem caber na memória ao mesmo tempo. O sistema monitora a quantidade de memória livre e faz o que pode para fornecer a sua aplicação a memória que ela precisa. Se o uso da memória tornar-se crítico, o sistema pode encerrar a sua aplicação. Porém, esse opção é usada apenas em último caso, para garantir que o sistema tenha memória suficiente para executar operações fundamentais como receber uma chamada telefônica.

Iniciando a aplicação MoveMe

Como acontece em todas as aplicações baseadas em C, o ponto inicial de qualquer aplicação para iPhone é uma função chamada main. As boas notícias são que, quando você cria um novo projeto usando os modelos do iPhone no Xcode, você não tem que escrever essa função.  O modelo do projeto inclui uma versão dessa função com todo código necessário para iniciar a sua aplicação.

A listagem 1 mostra a função main da aplicação MoveMe. Essa função está localizada no arquivo main.m do projeto. Toda aplicação que você criar terá uma função main praticamente igual a essa. Esse função executa duas tarefas chave. Primeiro, cria o pool de auto-liberação da aplicação, usado pelo sistema de contagem de referência do gerenciamento de memória . Em segundo lugar, chama a função UIApplicationMain para criar os objetos chave da aplicação. inicializa esses objetos e inicia o loop de processamento de eventos. A aplicação não retorna a essa função até o fechamento desta.

Listing 1 Using the provided main function

int main(int argc, char *argv[])
{
    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
    int retVal = UIApplicationMain(argc, argv, nil, nil);
    [pool release];
    return retVal;
}

Definindo a Delegação da aplicação

Um dos detalhes da arquitetura mais importante de seu projeto é a definição do objeto de delegação da aplicação, que é instanciado a partir de uma classe que você fornece em seu projeto. A classe de delegação da aplicação no projeto MoveMe declara sua interface em MoveMeAppDelegate.h e define sua implementação em MoveMeAppDelegate.m. Uma vez que você tenha adicionado esses arquivos ao seu projeto, pode usar a ferramenta Interface Builder para designar uma instancia da classe como delegação de sua aplicação. A Interface Builder é uma ferramenta visual onde você cria e organiza as visões em uma janela, configura a hierarquia das visões, configura as opções de cada visão e estabelece relacionamentos entre as visões e os outros objetos de sua aplicação. Por ser uma ferramenta visual, você executar todas essas tarefas arrastando componentes pela janela. O resultado é uma versão interativa de sua interface que você pode ver e alterar imediatamente. A Interface Builder salva a sua interface em um arquivo conhecido com arquivo nib, que é um arquivo do gráfico de objetos de sua aplicação.

Para carregar a Interface Builder e ver como a delegação da aplicação é definida, dê um clique duplo no arquivo MainWindow.xib (em MoveMe > Recourses) no painel Groups & Files da janela de projeto Xcode. MainWindow.sib é o arquivo nib que contém a janela de sua aplicação e define os relacionamentos entre diversos objetos importantes de sua aplicação, incluindo a delegação da aplicação. Para ver como o relacionamento da delegação da aplicação é estabelecido, clique na ava Application Connection da janela. Como mostrado na figura 3, o Inspector mostra que os objetos do dono do arquivo (que representa da aplicação no arquivo nib) tem canal com a delegação conectada ao objeto MoveMeAppDelegate.

Figure 3 The application delegate
Figure 3 The application delegate

O objeto de delegação da aplicação trabalha em conjunto com o objeto UIApplication padrão para responder às mudanças de condição da aplicação. O objeto da aplicação faz muito do trabalho pesado, mas a delegação é responsável por muitos comportamentos importantes, que incluem:

  • Configurar a janela da aplicação e a interface inicial com o usuário
  • Executar algumas tarefas de inicialização iniciais necessárias para seu mecanismo de dados personalizado
  • Abrir conteúdos associados ao esquema de URL personalizada da aplicação
  • Responder as mudanças na orientação dispositivo
  • manipular aviso de memória baixa
  • Manipular pedidos do sistema para encerar a aplicação

No momento do carregamento, a preocupação mais imediata do objeto da delegação é configurar e apresentar a janela da aplicação ao usuário, a qual édescrita na próxima seção. A delegação deve tambpem executar algumas tarefas para preparar a sua aplicação para o uso, como restauração a um estado anterior ou criação dos objetos necessários. Quando a aplicação é encerrada, a delegação precisa executar um encerramento ordenado da aplicação e salvar qualquer informação sobre o estado que possa ser necessário para o próximo ciclo de abertura.

Criando a janela da aplicação

Cada aplicação é responsável pela criação de uma janela que ocupa toda a tela e preencher essa janela com conteúdo. Aplicações gráficas que rodam sobre o iOS não rodam lado a lado com outras aplicações. De fato, além do kernel e alguns poucos daemons de baixo nível, sua aplicação é a única coisa que vai estar sendo executada após o carregamento. Além disso, sua aplicação nunca deve precisar de mais de uma janela – uma instancia da classe UIWindow. Em situações onde é necessaŕio alterar a sua interface, você pode alterar as visões exibidas pela janela.

As janelas fornecem o desenho da superfície de sua interface com o usuário, mas o objetos das visões fornecem o conteúdo. Um objeto de uma visão é uma instancia da classe UIView que desenha algum item e responde a interação com esse item. O iOS define visões padrões para representar coisas como tabelas, botões, campos de tezto e outros tipos de controles interativos. Você pode adicionar qualquer um desses itens a sua janela, ou pode definir visões personalizadas pela criação de sub-classes de UIView e a implementação de desenhos personalizados e código de manipulação de eventos. A aplicação MoveMe define duas visões – representadas pelas classes MoveMeView e PlacarView – para exibir a interface da aplicação e manipular a interação do usuário.

Durante o carregamento, o objetivo é criar janela da aplicação e exibir o conteúdo inicial tão rápido quanto possível. A janela é desarquivada do arquivo nib MainWindow.xib. Quando a aplicação alcança um estado onde está pronta para iniciar os eventos de processamento, o objeto UIApplication envia à delegação uma mensagem applicationDidFinishLaunching. Essa mensagem é a dica para que a delegação coloque o conteúdo na janela e execute outras tarefas de inicialização que podem ser necessárias.

Na aplicação MoveMe, o método applicationDidFinishLaunching: da delegação faz o seguinte:

  1. Cria um objeto controlador das visões cujo trabalho é gerenciar o conteúdo da janela
  2. Inicializa o controlador das visões com uma instancia da classe MoveMeView, que é armazenada no arquivo MoveMeView.xib, para atuar na retaguarda e preenche a janela
  3. Adiciona o controlador como uma sub-visão da janela
  4. Exibe a janela

A listagem 2 mostra o método applicationDidFinishLaunching: da aplicação MoveMe, que é definido no arquivo de delegação da aplicação, MoveMeAppDelegate.m. Esse método cria o conteúdo principal da janela e torna ela visível. A exibição da janela deixa o sistema saber que sua aplicação está pronta para começar a manipular eventos.

Listing 2 Creating the content view

- (void)applicationDidFinishLaunching:(UIApplication *)application
{
    // Set up the view controller
    UIViewController *aViewController = [[UIViewController alloc]
               initWithNibName:@"MoveMeView" bundle:[NSBundle mainBundle]];
    self.viewController = aViewController;
    [aViewController release];
    // Add the view controller's view as a subview of the window
    UIView *controllersView = [viewController view];
    [window addSubview:controllersView];
    [window makeKeyAndVisible];
}
Nota: Você pode usar o método applicationDidFinishLaunching: para executar outras tarefas além de configurar a interface da aplicação. Muitas aplicações usam esse método para inicializar estruturas de dados necessárias, ler alguma preferência do usuário, ou retorna ao estado anterior da aplicação.

Embora o código anterior crie o fundo da janela e depois exiba a janela, o que você não vê nesse código é a criação da classe PlacardView que exibe o botão Welcome. Esse comportamento é manipulado pelo método setUpPlacardView da classe MoveMeView, que é chamado pelo método initWithCoder: quando o objeto MoveMeView é desarquivado do arquivo nib. Esse método é mostrado na listagem 3. Parte da inicialização dessa visão inclui a criação de um objeto PlacardView. Como a classe MoveMeBiew fornece o fundo da aplicação inteira, o método adiciona o objeto PlacardView como uma sub-visão. O relacionamento entre essas duas visões não apenas exibe o botão Welcome sobre o fundo da aplicação, como também permite que a classe MoveMeView possa manipular os eventos que tem como alvo esse botão.

Listing 3 Creating the placard view

- (void)setUpPlacardView
{
    // Create the placard view -- it calculates its own frame based on its image.
    PlacardView *aPlacardView = [[PlacardView alloc] init];
    self.placardView = aPlacardView;
    [aPlacardView release];
    placardView.center = self.center;
    [self addSubview:placardView];
}

Desenhando o botão Welcome

Você pode usar as visões padrão fornecidas pelo UIKit sem modificações para desenhar muitos tipos de conteúdo simples. Por exemplo, pode usar a classe UIImageView para exibir imagens e UILabel para exibir strings e texto. A classe MoveMeView da aplicação MoveMe também tira vantagem de um propriedade básica de todos os objetos UIView – esepcificamente, a propriedade backgroundColor – para preencher a visão com um cor sólida. Essa propriedade pode ser ajustada no código no método de inicialização do objeto. Nesse caso, a propriedade é ajustada quando MoveMeView é criado no arquivo MoveMeView.xib, usando uma cor da aba Attributes da janela Inspector da Interface Builder. Quando precisar desenhar o conteúdo dinamicamente, porém, você precisa usar alguns recursos mais avançados de desenho do UIKit ou pode usar o Quartz ou OpenGL ES.

A classe PlacardView da aplicação MoveMe desenha o botão Welcome e gerencia sua posição na tela. Embora a classe PlacardView possa desenhar seu conteúdo usando um objeto UIImageView e UILabel, desenharemos o conteúdo explicitamente ao invés disso, para demonstrar o processo. Como resultado, esse classe implementa o método drawRect:, que é onde todo o desenho personalizado de uma visão é feito.

No momento que o método drawRect da visão é chamado, o ambiente de desenho é configurado para fazer seu trabalho. Tudo que você tem que especificar são os comandos para desenhar o conteúdo personalizado. Na classe PlcardView, o conteúdo consiste de uma imagem de fundo (armazenada no arquivo de recurso Placard,png) e uma string de texto que podemos alterar dinamicamente. Para desenhar esse conteúdo, a classe segue os seguintes passos:

  1. Desenha a imagem de fundo na origem atual da visão (Pela visão já se encaixar a imagem, esse passo lhe dá o fundo inteiro do botão)
  2. Computa a posição da string de modo que fique centralizada na botão (Por que o tamanho da string poder ser alterado,  a posição precisa ser computada cada vez baseada no novo tamanho)
  3. Ajusta a cor do desenho para preto
  4. Desenha a string na cor preto, e ligeiramente deslocada
  5. Ajusta a cor do desenho para branco
  6. Desenha a string novamente em branco no local pretendido

A listagem 4 mostra o método drawRect para a classe PlacardView. A variável placardImage contém um objeto UIImage que contém a imagem de fundo do botão e a variável currentDisplayString é um objeto NSString que contém o texto do botão. Depois de desenhar a imagem, esse método calcula a posição da string dentro da visão. O tamanho da string já é conhecido, tendo sido calculado quando a string foi carregada e armazenada na variável textSize. A string é então desenhada duas vezes – uma em preto e outra em branco – usando o método drawAtPoint:forWidth:withFont:fontSize:lineBreakMode:baselineAdjustment: de NSString.

Listing 4 Drawing the Welcome button

- (void)drawRect:(CGRect)rect
{
    // Draw the placard at 0, 0
    [placardImage drawAtPoint:(CGPointMake(0.0, 0.0))];
    /*
     Draw the current display string.
     This could be done using a UILabel, but this serves to illustrate
     the UIKit extensions to NSString. The text is drawn center of the
     view twice - first slightly offset in black, then in white -- to give
     an embossed appearance. The size of the font and text are calculated
     in setupNextDisplayString.
     */
    // Find point at which to draw the string so it will be in the center of the view
    CGFloat x = self.bounds.size.width/2 - textSize.width/2;
    CGFloat y = self.bounds.size.height/2 - textSize.height/2;
    CGPoint point;
    // Get the font of the appropriate size
    UIFont *font = [UIFont systemFontOfSize:fontSize];
    [[UIColor blackColor] set];
    point = CGPointMake(x, y + 0.5);
    [currentDisplayString drawAtPoint:point
                forWidth:(self.bounds.size.width-STRING_INDENT)
                withFont:font
                fontSize:fontSize
                lineBreakMode:UILineBreakModeMiddleTruncation
                baselineAdjustment:UIBaselineAdjustmentAlignBaselines];
    [[UIColor whiteColor] set];
    point = CGPointMake(x, y);
    [currentDisplayString drawAtPoint:point
                forWidth:(self.bounds.size.width-STRING_INDENT)
                withFont:font
                fontSize:fontSize
                lineBreakMode:UILineBreakModeMiddleTruncation
                baselineAdjustment:UIBaselineAdjustmentAlignBaselines];
}

Quando você precisa desenhar um conteúdo que seja mais complexo que imagens e strings, pode usar o Quartz ou o OpenGL ES. O Quartz trabalha com o UIKit para manipular o desenho de qualquer conteúdo vetorial que você quer criar dinamicamente. Por Quartz e UIKit serem baseadas no mesmo ambiente de desenho, você pode chamar as funções do Quartz diretamente do método drawRect de sua visão e até mesmo misturar chamadas Quartz com as classes UIKit.

O OpenGl Es é uma alternativa ao Quartz e UIKit que permite a renderização de conteúdo 2D e 3D usando um conjunto de funções que lembram (mas não são exatamente iguais) aquelas encontradas no OpenGL para MacOSX. Ao contrário do Quartz e UIKit, você não usa o método drawRect para os desenhos. Ainda usará uma visão, mas usará essa visão primariamente para fornecer a superfície de desenho para seu código OpenGL ES. Quão frequentemente você atualizará sua superfície, e quais objetos usará para isso, cabe somente a você.

Manipulando eventos de toque

A interface multi-toque do iOS torna possível a sua aplicação reconhecer e responder a eventos distinto gerados por múltiplos dedos que tocam o dispositivo. A habilidade de responder a múltiplos dedos oferece um considerável poder mas representa um desvio considerável do jeito tradicional, baseado na manipulação das ações do mouse. Quando cada dedo toca na superfície do dispositivo, o sensor de toque gera um novo evento. Quando o dedo se move, evento adicionais são gerados para indicar a nova posição do dedo. Quando um dedo perde contato com a superfície, o sistema entrega ainda um outra evento para indicar esse fato.

Por poderem haver múltiplos dedos tocando o dispositivo de uma vez só, é possível usar esses evento para identificar gestos complexos do usuário. O sistema lhe dá alguma ajuda na detecção de gestos comuns como batidas, mas você é o responsável pela detecção de gestos complexos. Quando o sistema de eventos gera um novo evento de toque, inclui informações sobre o estado atual de cada dedo que estiver tocou ou foi tirado da superfície do dispositivo. Por cada evento conter informações sobre todos os toques ativos, você pode monitorar as ações de cada dedo com a chegada de cada novo evento. Você pode assim rastrear os movimentos de cada dedo de evento a evento para detectar gestos, que você pode aplicar ao conteúdo de sua aplicação. Por exemplo, se o vento indica que o usuário está executando um gesto de pinça (como mostrado na figura 4) e a visão sob os dedos tiverem suporte a zoom, você pode usar esses eventos para alterar o nível atual de zoom da visão.

Figure 4 Using touch events to detect gestures
Figure 4 Using touch events to detect gestures

O sistema entrega eventos a objetos especiais da aplicação, que são instancias da classe UIResponder. Em uma aplicação para iPhone, suas visões formam a massa desses objetos especiais personalizados. A aplicação MoveMe implementa duas dessas classes, mas apenas a classe MoveMeView responde a mensagens de eventos. Essa classe detecta batidas tanto fora quanto dentro dos limites do botão Welcome pela sobrecarga dos métodos seguintes de UIResponder:

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event;
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event;
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event;
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event;

Para simplificar seu próprio comportamento de manipulação de eventos, a aplicação MoveMe rastreia somente o primeiro dedo que toca a superfície. Ela faz isso com o suporte da classe UIView, que desabilita eventos multi-toque por padrão. Para aplicações que não precisam rastrear múltiplos dedos, essa característica é bem conveniente. Quando os eventos multi-toque são desabilitados, o sistema entrega apenas evento relacionados ao primeiro dedo que toca o dispositivo. Os eventos relacionados aos toques adicionais em uma sequência nunca serão entregues à visão. Se quiser a informação desses toques adicionais, você reativar o suporte a multi-toque usando o método setMultipleTouchEnabled da classe UIView.

Como parte do seu comportamento de manipulação de eventos, a classe MoveMeView executa os seguintes passos:

  1. Quando um toque é detectado, checa para ver onde o evento ocorreu.
    • Uma batida dupla fora do botão atualiza a string exibida nele.
    • Uma batida única dentro do botão centraliza o botão embaixo do dedo e dispara uma animação inicial para aumentar o botão.
    • Todos os outros toques são ignorados.
  2. Se o dedo se move e está dentro do botão, a posição do botão é atualizada para bater com a nova posição do dedo.
  3. Se o dedo estiver dentro do botão e deixar a superfície, uma animação move o botão para sua posição original.

A listagem 5 mostra o método touchesBegan:withEvent da classe MoveMeView. O sistema chama esse método quando um dedo toca o dispositivo. Esse método pega o conjunto de todos os toques e extrai somente o toque sobre o objeto. A informação do objeto UITouch é usada para identificar em qual visão o toque ocorreu (o objeto MoveMeView ou o PlacardView) e o número de batidas associadas ao toque. Se o toque representa uma batida dupla fora do botão, o método touchesBegan:withEvent chama o método setupNextDisplayString para alterar a string do botão, Se o evento ocorreu dentro do botão, usa o método animateFirstTouchAtpoint para aumentar o botão e rastreá-lo até posição do toque. Todos os outros toques são ignorados.

Listing 5 Handling an initial touch event

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
    // We only support single touches, so anyObject
    // retrieves just that touch from touches
    UITouch *touch = [touches anyObject];
    // Only move the placard view if the touch was in the placard view
    if ([touch view] != placardView)
    {
        // In case of a double tap outside the placard view,
        // update the placard's display string
        if ([touch tapCount] == 2)
        {
            [placardView setupNextDisplayString];
        }
        return;
    }
    // Animate the first touch
    CGPoint touchPoint = [touch locationInView:self];
    [self animateFirstTouchAtPoint:touchPoint];
}

A listagem 6 mostra o método touchesMoved:withEvent da classe MoveMeView. O sistema chama esse método após o dedo ter tocado o dispositivo em resposta ao movimento do dedo de sua posição original. A aplicação MoveMe rastreia apenas o movimento que ocorre dentro do botão Welcome. Em resultadom esse método checa a posição do evento e usa essa informação para ajustar o ponto central do objeto PlacardView. O movimento da visão causa o redesenho desta para a nova posição automaticamente.

Listing 6 Responding to movement from a touch

- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
    UITouch *touch = [touches anyObject];
    // If the touch was in the placardView, move the placardView
    // to its location
    if ([touch view] == placardView)
    {
        CGPoint location = [touch locationInView:self];
        placardView.center = location;
        return;
    }
}

Quando o dedo do usuário finalmente deixa a tela, a aplicação MoveMe responde esse evento pelo disparo de uma animação que move o botão de volta a sua posição inicial  no centro da janela da aplicação. A listagem 7 mostra o método toucherEnded:withEvent que inicia a animação.

Listing 7 Releasing the Welcome button

- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
    UITouch *touch = [touches anyObject];
    // If the touch was in the placardView, bounce it back to the center
    if ([touch view] == placardView)
    {
        // Disable user interaction so subsequent touches
        // don't interfere with animation
        self.userInteractionEnabled = NO;
        [self animatePlacardViewToCenter];
        return;
    }
}

Para simplificar o processo de manipulação de eventos da applicação, o método toucherEnded:withEvent desativa eventos de toque para a visão temporariamente enquanto o botão executa a animação de volta a sua posição original. Se não fizesse isso, cada método de manipulação de evento poderia precisar incluir lógica para determinar se o botão estava no meio de uma animação e, nesse caso, cancelar a animação.Desativar interações do usuário durante esse tempo que leva para o botão voltar a sua posição original simplifica o código de manipulação de eventos e elimina a necessidade de lógica extra. Quando alcança a posição original, o método animationDidStop:finished da classe MoveMeView reativa interações do usuário e assim o ciclo de eventos começa outra vez.

Se a aplicação for interrompida por alguma razão – por exemplo, uma chamada telefônica – a visão envia uma mensagem toucherCancelled:withEvent. Nessa situação, a aplicação deve tentar reduzir sua atividade ao mínimo possível para evitar competir por recursos dos dispositivos. Na implementação exemplo, o letreiro centraliza e a transformação é simplesmente ajustada aos seus valores originais.

- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event {
    placardView.center = self.center;
    placardView.transform = CGAffineTransformIdentity;
}

Animando o movimento do botão

Em aplicações para iPhone, as animações desempenham um papel muito importante. As animações são usadas extensivamente para prover ao usuário informações contextuais e feedback imediato. Por exemplo, quando o usuário navega por dados hierárquicos em uma aplicativo de produtividade, ao invés de somente trocar uma tela por outra, o aplicativo anima o movimento de cada nova tela que é aberta. A direção do movimento indica se o usuário está se movendo para cima ou para baixo na hierarquia e também fornece uma dica visual que existe novas informações para serem vistas.

Por causa de sua importância, o suprote a animações é embutido no conjunto de classes de UIKit. A aplicação MoveMe tira vantagem desse suporte pelo uso de animações para os diferentes aspectos do botão Welcome. Quando o usuário toca o botão inicialmente, a aplicação aplica uma animação que aumenta o tamanho do botão brevemente. Quando o usuário solta o botão, outra animação traz o botão de volta a sua posição original. Os passos básicos para criar essas animações são essencialmente os mesmos:

  1. Chamar o método beginAnimations:context: da visão que você quer animar
  2. Configurar as propriedades da animação
  3. Chamar o método commitAnimations da visão para iniciar a animação

A listagem 8 mostra o código para a animação usado para pulsar o botão Welcome quando ele tocado pela primeira vez. Esse método ajusta a duração da animação e depois aplica uma transformação ao botão que altera o seu tamanho. Quando a animação é completada, a infraestrutura da animação chama o método growAnimationDidStop:finished:context: da delegação da animação, que completa a animação com o encolhimento do botão e movendo o letreiro sob o toque.

Listing 8 Animating the Welcome button

- (void)animateFirstTouchAtPoint:(CGPoint)touchPoint
{
#define GROW_ANIMATION_DURATION_SECONDS 0.15
    NSValue *touchPointValue = [[NSValue valueWithCGPoint:touchPoint] retain];
    [UIView beginAnimations:nil context:touchPointValue];
    [UIView setAnimationDuration:GROW_ANIMATION_DURATION_SECONDS];
    [UIView setAnimationDelegate:self];
    [UIView setAnimationDidStopSelector: @selector(growAnimationDidStop:finished:context:)];
    CGAffineTransform transform = CGAffineTransformMakeScale(1.2, 1.2);
    placardView.transform = transform;
    [UIView commitAnimations];
}
- (void)growAnimationDidStop:(NSString *)animationID finished:(NSNumber *)finished context:(void *)context
{
    #define MOVE_ANIMATION_DURATION_SECONDS 0.15
    [UIView beginAnimations:nil context:NULL];
    [UIView setAnimationDuration:MOVE_ANIMATION_DURATION_SECONDS];
    placardView.transform = CGAffineTransformMakeScale(1.1, 1.1);
    // Move the placard view under the touch
    NSValue *touchPointValue = (NSValue *)context;
    placardView.center = [touchPointValue CGPointValue];
    [touchPointValue release];
    [UIView commitAnimations];
}

Finalizando a aplicação

Nas seções anteriores, você viu como a aplicação MoveMe é inicializada, foi apresentada a sua interface com o usuário, e como ela responde a eventos. Além desses aspectos da criação da aplicação, existem alguns detalhes menores que precisam ser considerados antes de construir uma aplicação e carrega-la no dispositivo. Uma das peças finais na criação da aplicação é o arquivo de propriedades (Info.plist). Se trata de um arquivo XML que contém informações básicas sobre a sua aplicação ao sistema. O Xcode cria uma versão padrão desse arquivo para você e insere as informações iniciais de sua aplicação nele. Você pode estender essa informação, porém, para prover detalhes adicionais sobre sua aplicação que o sistema deve saber. Por exemplo, você poderia usar esse arquivo para prestar informações sobre a versão do aplicativo, algum esquema de URL personalizado que a aplicação suporta, imagem de carregamento, e status de visibilidade padrão além do estilo das barras de status do sistema.

A listagem 9 mostra o conteúdo da arquivo Info.plist para a aplicação MoveMe. O arquivo identifica o nome do executável, o arquivo de imagem a ser exibido na tela Home do usuário, e a string que identifica a aplicação individualmente no sistema. Por causa da aplicação MoveMe ser executada em tela cheia – em outras palavras, não exibe a barra de status – também inclui a chave UIStatusBarHidden e associa a ela o valor true. O ajuste dessa chave para true permite que o sistema saiba que não deve mostrar a barra de status durante o carregamento ou execução da aplicação. Embora a aplicação MoveMe pudesse configurar esse caracteristica através do código, isso levaria o efeito a não ter efeito até que a aplicação fosse carregada, o que pode parecer estranho.

Listing 9 The contents of the Info.plist file

<?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">
<dict>
    <key>CFBundleDevelopmentRegion</key>
    <string>en</string>
    <key>CFBundleDisplayName</key>
    <string>${PRODUCT_NAME}</string>
    <key>CFBundleExecutable</key>
    <string>${EXECUTABLE_NAME}</string>
    <key>CFBundleIconFile</key>
    <string>Icon.png</string>
    <key>CFBundleIdentifier</key>
    <string>com.yourcompany.${PRODUCT_NAME:identifier}</string>
    <key>CFBundleInfoDictionaryVersion</key>
    <string>6.0</string>
    <key>CFBundleName</key>
    <string>${PRODUCT_NAME}</string>
    <key>CFBundlePackageType</key>
    <string>APPL</string>
    <key>CFBundleSignature</key>
    <string>????</string>
    <key>CFBundleVersion</key>
    <string>1.0</string>
    <key>UIStatusBarHidden</key>
    <true/>
    <key>NSMainNibFile</key>
    <string>MainWindow</string>
</dict>
</plist>

Nota: Você pode editar o conteúdo do arquivo Info.plist de sua aplicação usando o TextEdit, que exibe o conteúdo XML do arquivo como mostrado na listagem 9, oy no Editor de propriedade, que exibe as chaves e valores do arquivo como uma tabla. O Xcode também provê acesso a alguns deses atributos na janela de informações do alvo de sua aplicação. Para ver essa janela, selecione o alvo de sua aplicação (no grupo targets) e escolha File > Get Info. A aba Properties contém algumas (mas não todas) das propriedades do arquivo Info.plist.

Com essa parte final tratada, agora temos todas as informações básicas necessárias para criar nossa própria aplicação para iPhone. O próximo passo é expandir as informações aprendidas aqui pela leitura de mais recursos do iOS. As aplicações que você criar devem tirar vantagem dos recursos nativos do iOS para criar uma experiência com o usuário agradável e intuitiva. Alguns desses recursos são descritos na próxima seção.

Recursos adicionais para sua aplicação

Existem muitos recursos associados ao iPhone e iPod touch que os usuários esperam encontrar em uma aplicação. Alguns desses recursos são relacionados ao hardware, como o ajuste automático das visões devido mudanças na orientação do dispositivo. Outras são relacionadas ao software, como o fato das aplicações para iPhone todas compartilharem um única lista de contatos. Como muitas dos recursos descritos a seguir são obrigatórias para uma experiencia com o usuário básica, você deve considera-las durante o desenho inicial de sua aplicação para ver como elas podem ser inseridas ao aplicativo.

Rastreando a orientação e movimentos usando o Acelerômetro

O acelerômetro no iPhone e iPod touch fornece entrada valiosa para o sistema e para suas aplicações. Um acelerômetro mede as alterações da velocidade ao longo de um único eixo linear. Tanto o iPhone quanto o iPod Touch tem três acelerômetros para medir essas alterações, um para cada eixo primário de um espaço tri-dimensional, permitindo que você detecte movimentos em qualquer direção.

Figure 5 Accelerometer axes
Figure 5 Accelerometer axes

Embora você possa não utilizar esse recursos, a medição de alterações da aceleração pode ser muito útil; de fato existe um bocado de coisas que podem ser feitas com essa informação. A força da gravidade está constantemente tentando puxar os objetos para o chão. Essa força resulta em uma quantidade mensurável de aceleração em direção ao chão quando o dispositivo está parado. Pelo rastreamento do que os acelerômetros estão registrando dessa aceleração, e a extensão dela, você pode detectar a orientação física de um dispositivo no espaço 3D com um nível bem justo de precisão. Você pode então aplicar essa orientação como um dado de entrada na sua aplicação.

O sistema usa o acelerômetro para monitorar a orientação atual dos dispositivo e notificar sua aplicação quando a orientação muda. Se a interface de sua aplicação puder ser exibida tanto no modo paisagem quanto no modo retrato, você deve incorporar controladores de visão ao seu design básico. A classe UIViewController fornece a infraestrutura necessária para rotacionar sua interface e ajustar a posição das visões automaticamente quando a orientação mudar.

Se quiser acessar os dados brutos do acelerômetro diretamente, pode fazer isso usando o objeto compartilhado UIAccelerometer do UIKit. Esse objeto reporta os valores atuais do acelerômetro em um intervalo configurável. Você também pode usar esses dados para detectar a orientação do dispositivo ou detectar outros tipos de movimento instantâneo, como quando o usuário balança o dispositivo. Você pode depois usar essas informação como dados de entrada para um jogo ou outro tipo de aplicação.

Acessando os Contatos

A lista de contatos é um recursos importante que todas as aplicações do sistema compartilham. O telefone, e-mail e mensagens de texto usam essa lista para identificar as pessoas que usuário precisa contactar para facilitar as interações básicas como iniciar uma chamada telefônica, mandar um e-mail ou enviar uma mensagem de texto. Seu aplicativo pode acessar essa lista para propósitos similares ou obter outras informações relevantes para sua aplicação.

Figure 6 Accessing the user’s contacts
Figure 6 Accessing the user’s contacts

O iOS fornece tanto acesso direto a lista de contatos quanto acesso indireto através de um conjunto de interfaces coletoras padrão. Usando o acesso direto, vocẽ pode obter as informações do contato diretamente do banco de dados de contatos. Você poderia usar essa informação em caso onde você queira apresenta-la de formas diferentes ou filtra-la baseando-se em um critério específico. Em caso onde você não precisa de uma interface personalizada, porém, o iOS fornece um conjunto de interfaces para coletar e criar contatos. A incorporação dessas interfaces em sua aplicação requer um pequeno esforço mas torna o comportamento de sua aplicação parecido com odo sistema.

Você acessa as informações dos contatos usando os frameworks AddressBook e AddressBookUI. Para obter mais informações sobre esses frameworks, veja Address Book Framework Reference for iOS e Address Book UI Framework Reference for iOS.

Obtendo a localização atual do usuário

Os dispositivos que rodam o iOS são designados para usuários em movimento. Portanto o software que você criar para esses dispositivos deve levar esse fato em consideração. E por que a Internet torna possível fazer negócios em qualquer lugar, ser capaz de usar informações sobre a localização do usuário pode criar uma experiência com o usuário convincente. Afinal de contas, por que listar cafés em Nova York para alguém que está em Los Angeles? Esse é um exemplo de como o framework de Localização pode ser útil.

O framework de Localização monitora sinais vindos das torres de telefonia celular e hotspot Wi-Fi e usa-os para triangular a posição atual do usuário. Você pode usar esse framework para capturar uma localização inicial apenas, ou pode ser notificado toda vez que a localização do usuário mudar. Com essa informação, pode filtrar os dados que sua aplicação fornece ou usa-las de outras formas.

Para obter um exemplo de como obter dados de localização em sua aplicação, veja “Getting the User’s Current Location” no iOS Application Programming Guide.

Tocando áudio e vídeo

O iOS suporta recursos de áudio em sua aplicação através dos frameworks Core Audio e OpenAL, e fornece suporte a execução de vídeo usando o framework Media Player. O Core Audio fornece uma interfacr avançada para tocar, gravar e manipular sons e fazer análise de áudio. Você também pode usa-lo para executar efeitos de sons simples ou áudio multi-canal, misturar sons e posiciona-los em um campo de áudio, e até disparar o recurso de vibração do iPhone. Se você for um desenvolvedor de jogos e já tem algum código que usa o OpenAL, pode usar seu código no iOS para posicionar e tocar áudio em seus jogos.

O framework Media Player é usado para executar vídeos em tela cheia. Esse framework suporta a execução de muitos formatos de vídeo e lhe dá controle total sobre o ambiente de execução, incluindo se exibe os controles para usuário e como configurar o aspecto do vídeo. Desenvolvedores de jogos podem usar esse framework para tocar cortes de cenas ou outro conteúdo pré-renderizado, enquanto aplicações de mídia pode também usar esse framework para executar arquivos de filmes.

Figure 7 Playing back custom video
Figure 7 Playing back custom video

Para obter mais informações sobre tecnologias relacionadas a mídia, veja Multimedia Programming Guide.

Tirando fotos com a cãmera embutida

A aplicação Câmera no iPhone permite que os usuários tirem fotos e armazene-as em uma biblioteca de fotos centralizada, junto com outras fotos que eles copiaram de seus computadores. O iOS provê acesso a esse recurso através da classe UIImagePickerController do framework UIKit.

Figure 8 The iPhone camera
Figure 8 The iPhone camera

A classe UIImagePickerController fornece a implementação tanto para a interface da câmera quanto para a da biblioteca de fotos para sua aplicação. Estas são as interfaces dos sistema padrão usadas por outras aplicações, incluindo Camera e Photos. Quando você exibe a interface de seleção, o controlador cuida de todas as interações necessárias e retorna a imagem resultante para sua aplicação.

Para informações de como usar as interfaces de seleção de fotos, veja “Taking Pictures with the Camera” no iOS Application Programming Guide e “Picking a Photo from the Photo Library” no iOS Application Programming Guide.
Traduzido de developer.apple.com