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

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.

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.

Correspondendo aos números verdes que eu adicionei à figura:
- Este botão esconde o Assistant Editor para que somente o editor principal fique visível.
- 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.
- 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.
- 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.

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.
import 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.

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.
import UIKit import XCPlayground import GameplayKit // (1) public extension UIImage { // (2) public convenience init?(color: UIColor, size: CGSize = CGSize(width: 1, height: 1)) { let rect = CGRect(origin: .zero, size: size) UIGraphicsBeginImageContextWithOptions(rect.size, false, 0.0) color.setFill() UIRectFill(rect) let image = UIGraphicsGetImageFromCurrentImageContext() UIGraphicsEndImageContext() guard let cgImage = image.CGImage else { return nil } self.init(CGImage: cgImage) } } let cardWidth = CGFloat(120) // (3) let cardHeight = CGFloat(141) public class Card: UIImageView { // (4) public let x: Int public let y: Int public init(image: UIImage?, x: Int, y: Int) { self.x = x self.y = y super.init(image: image) self.backgroundColor = .grayColor() self.layer.cornerRadius = 10.0 self.userInteractionEnabled = true } required public init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } }
Eu adicionei alguns poucos comentários numerados para explicar algumas seções da implementação.
- Em adição ao
UIKit
eXCPlayground
, também estaremos importando oGamePlayKit
. Este framework inclui um método conveniente que nos ajudará a implementar um método para embaralhar randomicamente um array. - Essa extensão de
UIImage
nos permite, com a ajuda dos métodos doUIKit
, 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. - As constantes
cardHeight
ecardWidth
representam o tamanho das imagens das cartas do qual nós iremos calcular outros tamanhos.
- A classe
Card
, herdando deUIImageView
, representa uma carta. Mesmo configurando algumas poucas propriedade da classeCard
, 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 propriedadesx
ey
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.
public class GameController: UIViewController { // (1): public variables so we can manipulate them in the playground public var padding = CGFloat(20)/* { didSet { resetGrid() } } */ public var backImage: UIImage = UIImage( color: .redColor(), size: CGSize(width: cardWidth, height: cardHeight))! // (2): computed properties var viewWidth: CGFloat { get { return 4 * cardWidth + 5 * padding } } var viewHeight: CGFloat { get { return 4 * cardHeight + 5 * padding } } var shuffledNumbers = [Int]() // stores shuffled card numbers // var firstCard: Card? // uncomment later public init() { super.init(nibName: nil, bundle: nil) preferredContentSize = CGSize(width: viewWidth, height: viewHeight) shuffle() setupGrid() // uncomment later: // let tap = UITapGestureRecognizer(target: self, action: #selector(GameController.handleTap(_:))) // view.addGestureRecognizer(tap) } required public init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } public override func loadView() { view = UIView() view.backgroundColor = .blueColor() view.frame = CGRect(x: 0, y: 0, width: viewWidth, height: viewHeight) } // (3): Using GameplayKit API to generate a shuffling of the array [1, 1, 2, 2, ..., 8, 8] func shuffle() { let numbers = (1...8).flatMap{[$0, $0]} shuffledNumbers = GKRandomSource.sharedRandom().arrayByShufflingObjectsInArray(numbers) as! [Int] } // (4): Convert from card position on grid to index in the shuffled card numbers array func cardNumberAt(x: Int, _ y: Int) -> Int { assert(0 <= x && x < 4 && 0 <= y && y < 4) return shuffledNumbers[4 * x + y] } // (5): Position of card's center in superview func centerOfCardAt(x: Int, _ y: Int) -> CGPoint { assert(0 <= x && x < 4 && 0 <= y && y < 4) let (w, h) = (cardWidth + padding, cardHeight + padding) return CGPoint( x: CGFloat(x) * w + w/2 + padding/2, y: CGFloat(y) * h + h/2 + padding/2) } // (6): setup the subviews func setupGrid() { for i in 0..<4 { for j in 0..<4 { let n = cardNumberAt(i, j) let card = Card(image: UIImage(named: String(n)), x: i, y: j) card.tag = n card.center = centerOfCardAt(i, j) view.addSubview(card) } } } // (7): reset grid /* func resetGrid() { view.frame = CGRect(x: 0, y: 0, width: viewWidth, height: viewHeight) for v in view.subviews { if let card = v as? Card { card.center = centerOfCardAt(card.x, card.y) } } } */ override public func viewDidAppear(animated: Bool) { for v in view.subviews { if let card = v as? Card { // (8): failable casting UIView.transitionWithView( card, duration: 1.0, options: .TransitionFlipFromLeft, animations: { card.image = self.backImage }, completion: nil) } } } }
- As duas propriedades,
padding
ebackImage
, são declaradaspublic
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.
- 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 daviewHeight
. - 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çofor
para gerar o array Utilizando os métodos do frameworkGamePlayKit
, 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 valor1
nosuffledArray
corresponde ao arquivo 1.png). - 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 fator4
no calculo aritmético reflete o fato de termos quatro cartas por linha. - 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.
- O método
setupGrid()
é chamado durante a inicialização do view controller. Ele estabelece a grade 4x4 deCard
. Ele também atribui a identidade de cada carta baseado no arrayshuffledNumbers
e a guarda na propriedadetag
herdada da classe base das catas,UIView
. Na lógica do jogo, nós comparamos os valores da propriedadetag
para descobrir se duas cartas combinam ou não. Este esquema de modelagem bastante rudimentar é suficiente para as nossas necessidades. - 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. - 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 classeCard
, (checada através do operadora de downcasting falívelas?
) o corpo da declaraçãoif
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 doUIView
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:
import XCPlayground import UIKit let gc = GameController() XCPlaygroundPage.currentPage.liveView = gc
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.

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.
gc.backImage = UIImage(named: "b")!
Depois de um segundo ou dois, você verá que o verso das cartas mudou de vermelho para o desenho de uma mão.

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:
gc.padding = 75
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:
// (7): reset grid func resetGrid() { view.frame = CGRect(x: 0, y: 0, width: viewWidth, height: viewHeight) for v in view.subviews { if let card = v as? Card { card.center = centerOfCardAt(card.x, card.y) } } }
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
:
// (1): public variables so we can manipulate them in the playground public var padding = CGFloat(20) { didSet { resetGrid() } }
O método resetGrid()
entra em ação e a view é atualizada para refletir o novo espaçamento. Você pode verificar isso no playground.

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
:
var firstCard: Card?
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:
func handleTap(gr: UITapGestureRecognizer) { let v = view.hitTest(gr.locationInView(view), withEvent: nil)! if let card = v as? Card { UIView.transitionWithView( card, duration: 0.5, options: .TransitionFlipFromLeft, animations: {card.image = UIImage(named: String(card.tag))}) { // trailing completion handler: _ in card.userInteractionEnabled = false if let pCard = self.firstCard { if pCard.tag == card.tag { UIView.animateWithDuration( 0.5, animations: {card.alpha = 0.0}, completion: {_ in card.removeFromSuperview()}) UIView.animateWithDuration( 0.5, animations: {pCard.alpha = 0.0}, completion: {_ in pCard.removeFromSuperview()}) } else { UIView.transitionWithView( card, duration: 0.5, options: .TransitionFlipFromLeft, animations: {card.image = self.backImage}) { _ in card.userInteractionEnabled = true } UIView.transitionWithView( pCard, duration: 0.5, options: .TransitionFlipFromLeft, animations: {pCard.image = self.backImage}) { _ in pCard.userInteractionEnabled = true } } self.firstCard = nil } else { self.firstCard = card } } } }
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 mesmoas?
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 propriedadeuserInteractionEnabled
. 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âmetroBool
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 Swiftif 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 valorestag
) para verificar se nós temos uma combinação ou não. Se tivermos, nós animamos o desaparecimento das cartas (configurando oalpha
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 configuramosuserInteractionEnabled
comotrue
para que o usuário possa seleciona-las novamente.
- Baseado no valor atual de
firstCard
, nós a configuramos paranil
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:
let tap = UITapGestureRecognizer(target: self, action: #selector(GameController.handleTap(_:))) view.addGestureRecognizer(tap)
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(_:)
:
public func quickPeek() { for v in view.subviews { if let card = v as? Card { card.userInteractionEnabled = false UIView.transitionWithView(card, duration: 1.0, options: .TransitionFlipFromLeft, animations: {card.image = UIImage(named: String(card.tag))}) { _ in UIView.transitionWithView(card, duration: 1.0, options: .TransitionFlipFromLeft, animations: {card.image = self.backImage}) { _ in card.userInteractionEnabled = true } } } } }
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:
class LPGR { static var counter = 0 @objc static func longPressed(lp: UILongPressGestureRecognizer) { if lp.state == .Began { gc.quickPeek() counter += 1 print("You peeked \(counter) time(s).") } } } let longPress = UILongPressGestureRecognizer(target: LPGR.self, action: #selector(LPGR.longPressed)) longPress.minimumPressDuration = 2.0 gc.view.addGestureRecognizer(longPress)
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.

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.
Subscribe below and we’ll send you a weekly email summary of all new Code tutorials. Never miss out on learning about the next big thing.
Update me weeklyEnvato Tuts+ tutorials are translated into other languages by our community members—you can be involved too!
Translate this post