Unlimited Plugins, WordPress themes, videos & courses! Unlimited asset downloads! From $16.50/m
Advertisement
  1. Code
  2. iOS

Prototipação Rápida e Interativa Com Xcode Playgrounds

by
Difficulty:IntermediateLength:LongLanguages:

Portuguese (Português) translation by Hugo De Oliveira Cordeiro (you can also view the original English article)

Final product image
What You'll Be Creating

Introdução

Desde a sua introdução no Xcode 6 juntamente com o Swift, até a sua atual iteração no Xcode 7.3.1, playgrounds tiveram uma longa jornada. Com novas features e melhor estabilidade, eles estão evoluindo como uma ferramenta viável para prototipação rápida ou montagem rápida de uma prova de conceito.

Como um desenvolvedor, as vezes você tem um flash de inspiração na forma de uma ideia interessante para um aplicativo e você quer rapidamente desenvolver e prototipar o que representa a essência crua da sua idéia. Ou você pode querer só verificar seu entendimento de como algum código do UIKit se comportaria. Se você é como eu, você preferiria evitar todo o aborrecimento e sobrecarga de criar um projeto do Xcode e ter de lidar com inúmeros fatores, como tipos de aparelhos e resoluções, configurações de build. Essas decisões podem ser adiadas até depois de você decidir se vale a pena prosseguir com a sua idéia.

Neste tutorial, nós criamos um jogo de memória baseado em cartas com tudo que o playground pode oferecer. É jogo comum e bastante conhecido, nada de original. O jogo consiste em oito pares de cartas idênticas (um total de 16 cartas) colocadas com a face para baixo em uma grade 4x4.

O jogador precisa virar duas cartas cujas faces são brevemente reveladas e então rapidamente são colocadas com a face para baixo novamente. O objetivo do jogo é tentar lembrar as posições das cartas e descobrir os pares idênticos, que são então removidos da grade. O jogo termina quando a grade fica vazia.

O jogo é baseado no toque e incorpora animações simples. Você aprende como fazer modificações no seu aplicativo e vê o resultado das suas modificações ao vivo.

1. Primeiros Passos

Inicie o Xcode e selecione New > Playground... do menu File do Xcode. Escolha um nome para o playground, como MemoryGameXCPTut, configure a Platform para iOS, e salve o playground. Eu estou usando o Xcode 7.3.1 para esse tutorial.

Create A New Playground

Se acostumando com Playground

Vamos gastar um pouco mais de tempo nos familiarizando com a interface do playground. Sinta-se livre para pular esta seção se você já está familiarizado com playgrounds.

Um playground pode ter múltiplas páginas, cada uma associada com sua própria view e suas pastas de fontes/recursos. Nós não utilizaremos múltiplas páginas neste tutorial. Playgrounds suportam formatação markup que permite você adicionar texto rico em um playground e estabeleça links entre páginas do playground.

A primeira coisa que você vê após criar um playground é o editor de código do playground. É aqui que você escreve o código, onde tem efeito imediato na view ativa. Uma das maneiras de (des)habilitar a visualização do Project Navigator é usando o atalho Command-0. No Project Navigator, você pode ver duas pastas, Sources e Resources.

Sources

Na pasta Sources, você pode adicionar código auxiliar em um ou mais arquivos Swift, como classes próprias, controladores, e views. Mesmo que a maioria do código que define a lógica do seu protótipo fique lá, é auxiliar no sentido de estar em segundo plano quando você estiver vendo a sua aplicação.

A vantagem de colocar o código auxiliar na pasta Sources é que ela é automaticamente compilada toda vez que você modifica ou salva o arquivo. Dessa maneira, você tem um feedback mais rápido na visualização de mudanças realizadas no playground. De volta ao playground, você tem acesso as propriedades e métodos públicos que você declara no código auxiliar modificando como a aplicação se comporta.

Resources

Você adicionar recursos adicionais, como imagens, na pasta Resources.

Neste tutorial, você frequentemente precisa pular de um arquivo Swift que criamos na pasta Sources para o arquivo do playground (que é tecnicamente também um arquivo Swift, mas não iremos nos referir a ele pelo seu nome). Nós também fazemos uso do Assistant Editor no tutoria, exibindo a Timeline, para ver o resultado lado-a-lado com o código do playground. Qualquer mudará que você fizer no playground é refletida instantaneamente (bem, com alguns segundos de diferença) na visualização. Você também está apto a interagir com a visualização e com os elementos de interface através do toque. Para ter certeza que você pode fazer tudo isso, dê uma olhada na figura abaixo.

Sources and Resources Folders

Correspondendo aos números verdes que eu adicionei à figura:

  1. Este botão esconde o Assistant Editor para que somente o editor principal fique visível.
  2. Este botão exibe o Assistant Editor. O Assistant Editor é visto ao lado direito do editor principal. Este editor pode ajudar mostrando-nos arquivos relevantes, como a contraparte do arquivo exibido pelo editor principal.
  3. Da esquerda para a direita, estes dois botões são respectivamente usados para alternar a exibição do Project Navigator e do console de debug. No console, nós podemos inspecionar a saída de comando impressos dentre outras coisas.
  4. A jump bar no topo do editor principal também pode ser usada para navegar para um arquivo específico. Clicando no nome do projeto duas vezes traz você de volta para o playground. Alternativamente, você também pode usar o Project Navigator.

Algumas vezes, quando visualizando o playground, você precisa se certificar que Assistant Editor está exibindo a Timeline ao invés de outro arquivo. A figura abaixo mostra como fazer isto. No Assistant Editor, selecione Timeline, a contraparte do playground, no lugar de Manual, que permite que você exiba qualquer arquivo no Assistant Editor.

Accessing the Timeline

Quando você está editando um arquivo fonte da pasta Sources, como sua contraparte, o Assistant Editor mostra a interface do seu código, isto é, protótipos de declarações e funções sem as suas implementações. Eu prefiro esconder o Assistant Editor quando estou trabalhando em um arquivo da pasta Sources e somente exibir o Assistant Editor no playground para exibir a visualização.

Para acessar as habilidades especiais dos playgrounds, você precisa importar o módulo XCPlayground.

Você configura a propriedade liveView da currentPage do objeto XCPlaygroundPage para um objetivo que esteja em conformidade com o protocolo XCPlaygroundLiveViewable. Isso pode ser uma classe própria ou pode ser uma instância de UIView ou UIViewController.

Adicionando Arquivos nas Pastas Sources/Resources

Eu adicionei algumas poucas imagens que nós podemos utilizar nesse tutorial. Faça o download das imagens, extraia o arquivo, e adicione as imagens da pasta Images na pasta Resources do Playground no Project Navigator.

Certifique-se de arrastar somente as imagens de modo que cada arquivo de imagem resida na pasta Resource, não em Resources/Images.

Delete o código do playground. Clique com o botão direito na pasta Sources e selecione New File do menu. Configure o nome do arquivo para Game.swift.

Importing Images and Creating a New Source File

2. Escrevendo Classes & Métodos Auxiliares

Adicione o seguinte código ao arquivo Game.swift. Certifique-se de salvar o arquivo após cada adição de código.

Eu adicionei alguns poucos comentários numerados para explicar algumas seções da implementação.

  1. Em adição ao UIKit e XCPlayground, também estaremos importando o GamePlayKit. Este framework inclui um método conveniente que nos ajudará a implementar um método para embaralhar randomicamente um array.
  2. Essa extensão de UIImage nos permite, com a ajuda dos métodos do UIKit , criar imagens com uma cor sólida e de qualquer tamanho que quisermos. Nós utilizaremos isso para configurar a imagem de fundo inicial das cartas do jogo.
  3. As constantes cardHeight e cardWidth representam o tamanho das imagens das cartas do qual nós iremos calcular outros tamanhos.
  4. A classe Card, herdando de UIImageView, representa uma carta. Mesmo configurando algumas poucas propriedade da classe Card, o objetivo principal da criação dessa classe é nos ajudar a identificar e iterar sobre as subviews que correspondem as cartas do jogo. As cartas também tem propriedades x e y para lembrar sua posição na grade.

3. View Controller

Adicione o seguinte código ao arquivo Game.swift, imediatamente após o código anterior.

  1. As duas propriedades, padding e backImage, são declaradas public para que possamos acossá-las do playground mais tarde. Elas representam o espaço em branco ao redor das cartas na grande e a imagem exibida no verso de cada carta respectivamente. Note que foram dados valores iniciais para ambas propriedades, representando um espaço de 20 e uma cor sólida vermelha para a imagem do verso da carta. Você pode ignorar o código comentado por enquanto.
  2. Nós vamos calcular a largura e altura desejada para as views através de propriedades calculadas. Para entender o cálculo da viewWidth, lembre-se que existem quatro cartas em cada linha e nós também precisamos levar em consideração o espaço entre as cartas. A mesma idéia se aplica ao cálculo da viewHeight.
  3. O código (1...8).flatMap{[$0, $0]} é uma maneira concisa de produzir o array [1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8]. Se você não está familiarizado com programação funcional, você também poderia escrever um laço for para gerar o array Utilizando os métodos do framework GamePlayKit, nós embaralhamos os números do array. Os números correspondem aos oito pares de cartas. Cada número representa a imagem de uma carta com o mesmo nome (por exemplo, o valor 1 no suffledArray corresponde ao arquivo 1.png).
  4. Nós escrevemos um método que mapeia a localização da carta na grade 4x4 para sua localização no array de 16 posições shuffledNumbers. O fator 4 no calculo aritmético reflete o fato de termos quatro cartas por linha.
  5. Nós também temos um método que descobre a posição de uma carta (a sua propriedade center) na grade baseado nas dimensões da carta e na distância entre elas.
  6. O método setupGrid() é chamado durante a inicialização do view controller. Ele estabelece a grade 4x4 de Card. Ele também atribui a identidade de cada carta baseado no array shuffledNumbers e a guarda na propriedade tag herdada da classe base das catas, UIView. Na lógica do jogo, nós comparamos os valores da propriedade tag para descobrir se duas cartas combinam ou não. Este esquema de modelagem bastante rudimentar é suficiente para as nossas necessidades.
  7. Este trecho de código atualmente não utilizado irá nos ajudar a reposicionar as cartas no caso de mudanças no espaçamento. Lembre-se que nós declaramos a propriedade padding como uma propriedade pública para que possamos acessa-la do playground.
  8. O código em viewDidAppear(_:) executa imediatamente após o view controller se tornar visível. Nós iteramos nas subviews das views e, caso a subview é uma instância da classe Card, (checada através do operadora de downcasting falível as?) o corpo da declaração if define a transição a ser executada. Aqui é onde nós mudaremos a imagem exibida nas cartas, da imagem do desenho definindo a face de cada carta para a (comum) backImage de todas as cartas. Esta transição é acompanhada de um animação de giro da esquerda para a direita dando a impressão das cartas estarem sendo viradas fisicamente. Se você não está familiarizado com as animações do UIView funcionam, isto pode parecer um pouco estranho. Mesmo que tenhamos adicionado a animação de cada carta sequencialmente em um loco, as animações são agrupadas em uma única transação de animação e executadas concorrentemente, isto é, as cartas viram juntas. 

Revisite o playground e troque qualquer texto no editor pelo seguinte:

Certifique-se que a Timeline está visível. A visualização do view controller deve ganhar vida e nos mostrar a grade de cartas 4x4 com desenhos de animais focinhos que giram para nos mostrar os versos das cartas. Agora, nós não podemos fazer muito nessa visualização porque não programamos nenhuma interação ainda. Mas é definitivamente um começo.

First Output

4. Modificando Variáveis no Playground

Agora vamos mudar o verso das cartas de uma cor sólida para uma imagem, b.png especificamente na pasta Resources. Adicione a seguinte linha ao final do playground.

Depois de um segundo ou dois, você verá que o verso das cartas mudou de vermelho para o desenho de uma mão.

Changing the Back Image of the Cards From the Playground

Agora vamos tentar alterar a propriedade padding, a qual atribuímos o valor padrão de 20 no arquivo Game.swift. O espaço entre as cartas deve aumentar como resultado. Adicione a seguinte linha ao final do arquivo do playground:

Espere o recarregamento da visualização para ver que... nada mudou.

5. Um Breve Desvio

Para entender o que está acontecendo, você deve ter emente que entidades, como os view controllers e as views associadas, têm um ciclo de vida complicado. Nós vamos discutir isso posteriormente, isto é, nas views. A criação e atualização da view de um view controller é um processo multi estágio. Em pontos específicos do ciclo de vida da view, notificações são enviadas para o UIViewController, informando sobre o que está acontecendo. Mais importante, o programador pode alterar estas notificações adicionando código para direcionar e customizar este processo.

Os métodos loadView() e viewDidAppear(_:) são dois métodos que usamos para alterar o ciclo de vida da view. Este tópico está de alguma maneira envolvido e fora do escopo dessa discussão, mas o que nos importa é que o código no playground, depois da atribuição do view controller como liveView do playground, é executado em algum tempo entre a chamada dos métodos viewWillAppear(_:) e viewDidAppear(_:). Você pode verificar isso modificando alguma propriedade no playground e adicionando comandos de impressão a esses dois métodos para exibir o valor dessa propriedade.

O problema com valor padding não ter o efeito visual esperado é que, naquele momento, a view e suas subviews já tinham sido estabelecidos. Mantenha em mente que, sempre que você fizer uma mudança no código, o playground é re-executado do início. Neste sentindo, esse problema não é específico do playground. Mesmo que você estivesse desenvolvendo um código para rodar no simulador ou em um aparelho físico, geralmente você precisa escrever código adicional para que mudanças no valor de uma propriedade tenha o efeito desejado na aparência da view ou conteúdo.

Você pode perguntar porque nós fomos capazes de mudar os valores da propriedade backImage e ver o resultado sem fazer nada especial. Observe que a propriedade backImage é na verdade utilizada pela primeira vez em viewDidAppear(_:), quando ela já adquiriu seu novo valor. 

6. Observando Propriedades e Agindo

Nossa maneira para lidar com essa situação será monitorar mudanças no valor de padding e redimensionar/reposicionar a view e as subviews. Felizmente, isso é fácil de fazer com a funcionalidade muito útil do Swift property observing. Comece descomentando código para o método resetGrid() no arquivo Game.swfit:

Este método recalcula a posição das estruturas das views e de cada objeto Card baseado nos novos valores de viewWidth e viewHeight. Lembre-se que essas propriedades são calculadas baseadas no valor de padding, que acabou de ser modificado.

Também, modifique o código para a propriedade padding usar o observador didSet cujo corpo, como o nome indica, executa toda vez que configuramos o valor de padding:

O método resetGrid() entra em ação e a view é atualizada para refletir o novo espaçamento. Você pode verificar isso no playground.

Padding Successfully Updated

Parece que nós fomos capazes de consertar as coisas facilmente. Na realidade, quando eu decidi que queria interagir com a propriedade padding, eu tive que voltar e fazer mudanças no código do arquivo Game.swift. Por exemplo, eu tive que abstrair o cálculo do centro de Card em uma função separada (centerOfCardAt(_:_:)) para limpar e (re)calcular independentemente as posições das cartas sempre que elas precisem ser dispostas.

Utilizar propriedades calculadas para viewWidth e viewHeight também ajudaram. Embora esse tipo de reescrita seja de alguma ajuda você deve estar preparado para um trade-off de não adiantar muito o design, isso pode ser reduzido com alguma preparação e experiência.

7. Lógica do Jogo & Interação por Toque

Agora é hora de implementar a lógica do jogo e permitir a nossa interação com ele através do toque. Comece descomentando a declaração da propriedade firstCard na classe GameController:

Lembre-se que a lógica do jogo envolve revelar duas cartas, uma após a outra. Essa variável mantém a informação que indica se a carta virada pelo jogador é a primeira das duas ou não.

Adicione o seguinte método ao final da classe GameController, antes da chave final:


Esse é um método longo. Isto é porque ele embala todos os tratamentos de toque necessárias, lógica de jogo bem como as animações associadas em um método. Agora vamos ver como esse método trabalha:

  • Primeiro, existe uma verificação para garantir que o usuário realmente tocou uma instância de Card. Feito através do mesmo as? utilizado anteriormente.
  • Se o usuário tocou uma instância de Card, nós a viramos utilizando uma animação similar a que implementamos anteriormente. O único aspecto novo é que usamos o completion handler, que executa após a animação ser finalizada, para temporariamente desabilitar as interações através de toque para aquela carta em particular configurando a propriedade userInteractionEnabled. Isso previne o jogador de virar a mesma carta. Note que o _ in é utilizado várias vezes nesse método. Isso é só para informar que queremos ignorar o parâmetro Bool que o completion handler pede.
  • Nós executamos o código baseados no fato de um valor não nulo ter sido atribuído para firstCard utilizando encadeamento opcional, a construção familiar do Swift if let.
  • Se firstCard não for nula, então a carta virada pelo jogador é a segunda da sequencia. Agora nós precisamos comparar as faces dessa carta com a anterior (através dos valores tag) para verificar se nós temos uma combinação ou não. Se tivermos, nós animamos o desaparecimento das cartas (configurando o alpha delas para 0). Nós também removemos essas cartas da view. Se as tags não forem iguais, significando que as cartas não combinam, nós simplesmente as viramos novamente colocando as suas faces para baixo e configuramos userInteractionEnabled como true para que o usuário possa seleciona-las novamente.
  • Baseado no valor atual de firstCard, nós a configuramos para nil ou para a carta atual. É assim que nós trocamos o comportamento do código entre dois toques sucessivos.

Finalmente, descomente as seguintes linhas no inicializador do GameController que adicionam um reconhecedor de toques na view. Quando o reconhecedor de toques detecta um toque, o método handleTap() é invocado:

Volte para a timeline do playground e jogue o jogo da memória. Sinta-se livre para diminuir o alto valor de padding que atribuímos anteriormente.

O código em handleTap(_:) é praticamente a versão não embelezada do que eu escrevi da primeira vez. Alguém pode argumentar que, como um único método, ele faz coisas demais. Ou que o código não é orientado-a-objetos o suficiente e que a lógica de virar as cartas e animações deveriam ser abstraídas em métodos da classe Card. Apesar desses argumentos não serem inválidos per se, lembre-se que prototipação rápida é o foco deste tutorial e já que nós não previmos nenhuma necessidade de interagir com essa parte do código no playground, nós pudemos ser um pouco mais alternativos.

Uma vez que possuímos algo funcionando e decidimos levar a ideia adiante, nós certamente poderíamos levar em consideração a refatoração do código. Em outras palavras, primeiro faça funcionar, depois se preocupe com velocidade/elegância/beleza/...

8. Tratamento de Toques no Playground

Apesar da parte principal do tutorial estar finalizada, como um aparte interessante, eu gostaria de mostrar a você como nós podemos escrever código para a manipulação de toques diretamente no playground. Primeiramente nós vamos adicionar um método à classe GameController que nos permite espiar as faces das cartas. Adicione o seguinte código à classe GameController, imediatamente depois do método handleTap(_:):

Suponha que nós queremos a habilidade de ativar ou desativar essa função "espiadinha" diretamente do playground. Uma maneira de fazer isso seria criar uma propriedade Bool pública na classe GameController que poderíamos configurar de dentro do playground. E claro, nós teríamos que escrever um handler para o gesto na classe GameController, ativado por um gesto diferente, que invocaria o método quickPeek().

Outra maneira seria escrever o código de tratamento do gesto diretamente no playground. Uma vantagem de fazer isso dessa maneira é que nós podemos incorporar algum código próprio em adição à chamada quickPeek(). É isso que vamos fazer. Adicione o seguinte código ao final do playground:

Para ativar a função "espiadinha", nós iremos usar um gesto de toque longo, isto é, o jogador mantém seu dedo na tela por um certo tempo. Nós usamos dois segundos como limite.

Para tratar o gesto, nós criamos uma clase, LPGR (abreviação do inglês "Long Press Gesture Recognizer"), com uma propriedade variável static, counter, para o controle de quantas vezes nós espiamos, e um método static longPressed(_:) para tratar o gesto.

Utilizado o qualificador static, nós evitamos ter que criar uma instância de LPGR porque entidades declaradas estáticas são associadas com o tipo (classe) LPGR no lugar de uma instância em particular.

Fora isso, não há nenhuma vantagem particular para essa abordagem. Por razões complicadas, nós precisamos marcar o método como @objc para manter o compilador feliz. Note o uso de LPGR.self para se referir ao tipo do objeto. Note também que no handler do gesto, que nós checamos se o state do gesto é .Began. Fazemos isso porque gesto de toque longo é continuo, isto é, o handler executaria repetidamente enquanto o usuário mantivesse seu dedo na tela. Já que nós queremos que o código execute somente uma vez por interação, nós fazemos isso no momento que o gesto é reconhecido.

O contador incremental é o código customizado que nós introduzimos, que não depende de funcionalidade fornecida pela classe GameController. Você pode ver a saída da função print(_:) (depois de espiar algumas vezes) no console na parte inferior.

Gesture Recognizer With External Behavior

Conclusão

Espero que este tutorial tenha demonstrado um exemplo interessante de prototipação rápida e interativa com playgrounds do Xcode. Além das razões que mencionei anteriormente para usar playgrounds, você pode pensar em outros cenários onde eles podem ser úteis. Por exemplo:

  • demonstrar protótipos de funcionalidades para clientes e deixa-los escolher entre as opções e fazer customizações com feedback em tempo real e sem ter que se aprofundar nos detalhes práticos do código.
  • desenvolver simulações, como físicas, onde estudantes podem brincar com os valores de alguns parâmetros e observar como a simulação é afetada. De fato, a Apple lançou um playground impressivo que demonstra a interatividade e incorporação de aspectos físicos via API UIDynamics. Eu encorajo você a conferir.

Quando utilizar playgrounds para objetivos de demonstração/ensino como esses, você provavelmente irá querer fazer uso das capacidades de markup dos playgrounds para texto rico e navegação.

O time do Xcode parece estar compromissado em melhorar os playground à medida que novas versões da IDE são lançadas. As últimas notícias são que o Xcode 8, atualmente em beta, irá disponibilizar playgrounds para iPad. Mas, obviamente, playgrounds não foram feitos para substituir a IDE Xcode e nem a necessidade de testar em aparelhos físicos quando desenvolvendo um aplicativo completo, funcional. Por último, eles são somente uma ferramenta para ser utilizada quando fizer sentido, mas uma ferramenta muito útil.

Advertisement
Advertisement
Advertisement
Looking for something to help kick start your next project?
Envato Market has a range of items for sale to help get you started.