Prototipazione rapida e interattiva con Xcode Playgrounds

Cosa starai creando

introduzione

Dalla loro introduzione in Xcode 6 insieme a Swift, fino alla loro attuale iterazione in Xcode 7.3.1, i parchi giochi hanno fatto molta strada. Con nuove funzionalità e una migliore stabilità, si stanno evolvendo in uno strumento fattibile per la prototipazione rapida o l'hacking rapido di una dimostrazione del concetto.

Come sviluppatore, a volte hai un lampo di ispirazione sotto forma di un'idea interessante per un'app e vuoi codificare rapidamente un prototipo che rappresenta la nuda essenza della tua idea. O vuoi solo verificare la tua comprensione di come si comporterà una parte del codice UIKit. Se sei come me, preferiresti evitare il fastidio e il sovraccarico mentale della creazione di un progetto Xcode e di dover gestire una miriade di fattori, come i tipi di dispositivi e le risoluzioni e le impostazioni di costruzione. Queste decisioni possono essere rimandate fino a dopo che hai deciso che l'idea principale è degna di essere perseguita.

In questo tutorial, creiamo un gioco di memoria basato su schede tutto all'interno dei confini di un parco giochi. È un gioco comune e ben noto, quindi non c'è credito per l'originalità lì. Il gioco consiste di otto coppie di carte identiche (quindi un totale di 16 carte) messe a faccia in giù in una griglia 4x4.

Il giocatore ha bisogno di capovolgere due carte i cui volti sono rivelati brevemente e poi rapidamente tornare indietro. L'obiettivo del gioco è che il giocatore tenti di ricordare le posizioni delle carte e scoprire coppie identiche, che vengono rimosse dal gioco. Il gioco termina quando viene cancellata la griglia.

Il gioco è basato sul tocco e incorpora semplici animazioni di visualizzazione. Scopri come apportare modifiche alla tua app e vedere i risultati delle tue modifiche dal vivo.

1. Per iniziare

Accendi Xcode e seleziona Nuovo> Parco giochi ... da Xcode's File menu. Dai al parco giochi un nome, ad esempio MemoryGameXCPTut, impostato piattaforma a iOS, e salvare il parco giochi. Sto usando Xcode 7.3.1 per questo tutorial.

Trovare la strada intorno al parco giochi

Passiamo un po 'di tempo a familiarizzare con l'interfaccia del parco giochi. Sentiti libero di sfogliare questa sezione se hai già familiarità con i campi da gioco.

Un parco giochi può avere più pagine, ciascuna associata alla propria visualizzazione live e alle proprie cartelle di risorse / risorse. Non useremo più pagine in questo tutorial. I parchi giochi supportano la formattazione del markup che ti consente di aggiungere un testo avanzato a un parco giochi e un collegamento tra le pagine del parco giochi.

La prima cosa che vedi dopo aver creato un parco giochi è l'editor di sorgenti del parco giochi. Qui è dove scrivi il codice, che ha un effetto immediato sulla visualizzazione live. Uno dei modi per attivare l'aspetto (dis) di Project Navigator sta usando la scorciatoia Comando-0. Nel Project Navigator, puoi vedere due cartelle, fonti e risorse.

fonti

Nel fonti cartella, è possibile aggiungere un codice ausiliario in uno o più file Swift, come classi personalizzate, visualizzare i controller e le viste. Anche se la maggior parte del codice che definisce la logica del tuo prototipo va lì, è ausiliaria nel senso che è nascosta in background quando stai visualizzando la tua app in diretta.

Il vantaggio di inserire il codice ausiliario nel file fonti la cartella è che viene compilata automaticamente ogni volta che si modifica e si salva il file. In questo modo, si ottiene un feedback più veloce nella visualizzazione live dalle modifiche apportate nel campo da gioco. Torna nel parco giochi, sei in grado di accedere pubblico proprietà e metodi esposti nel codice ausiliario che influiscono sul comportamento della tua app.

risorse

È possibile aggiungere risorse esterne, come immagini, in risorse cartella.

In questo tutorial, è spesso necessario saltare tra un file Swift che creiamo in fonti cartella e il file del parco giochi (tecnicamente anche un file Swift, tranne che non ti riferirai con il nome del file). Usiamo anche il Assistente editore nel tutorial, avendo visualizzato il Sequenza temporale, per visualizzare l'output dal vivo parallelamente al codice del parco giochi. Qualsiasi modifica apportata al campo da gioco si riflette istantaneamente (beh, in pochi secondi) nell'output live. Puoi anche interagire con la vista live e i suoi elementi dell'interfaccia utente. Per assicurarti di poter fare tutto questo, dai un'occhiata alla figura sottostante.

Corrispondente ai numeri verdi che ho aggiunto alla figura:

  1. Questo pulsante nasconde il Assistente editore in modo che solo l'editor principale sia visibile.
  2. Questo pulsante rivela il Assistente editore. Il Assistente editore è visibile a destra dell'editor principale. Questo editor può aiutare mostrandoci file rilevanti, come la controparte del file nell'editor principale.
  3. Da sinistra a destra, questi due pulsanti sono utilizzati rispettivamente per alternare l'aspetto del Project Navigator e la console di debug. Nella console, possiamo controllare l'output delle dichiarazioni di stampa, tra le altre cose.
  4. Il salta la barra nella parte superiore dell'editor principale può essere utilizzato anche per navigare in un determinato file. Cliccando due volte sul nome del progetto si ritorna al parco giochi. In alternativa, puoi anche usare il Project Navigator.

A volte, quando si guarda il parco giochi, è necessario assicurarsi che il Assistente editore sta visualizzando il Sequenza temporale invece di qualche altro file. La seguente figura mostra come fare questo. Nel Assistente editore, selezionare Sequenza temporale, la controparte del parco giochi, invece di Manuale, che ti permette di mostrare qualsiasi file nel file Assistente editore.

Quando si modifica un file sorgente dal fonti cartella, come sua controparte, il Assistente editore mostra l'interfaccia del tuo codice, cioè dichiarazioni e prototipi di funzioni senza le loro implementazioni. Preferisco nascondere il Assistente editore quando sto lavorando su un file in fonti cartella e esporre solo il Assistente editore nel parco giochi per vedere la vista dal vivo.

Per accedere alle abilità speciali dei campi da gioco, è necessario importare il modulo XCPlayground.

importa XCPlayground

Hai impostato il dal vivo proprietà del pagina corrente del XCPlaygroundPage oggetto a un oggetto conforme al  XCPlaygroundLiveViewable protocollo. Questo può essere un corso personalizzato o può essere un UIView o UIViewController esempio.

Aggiunta di file alla cartella Sources / Resources

Ho aggiunto alcune immagini con le quali possiamo lavorare in questo tutorial. Scarica le immagini, estrai l'archivio e aggiungi le immagini nel immagini cartella al risorse cartella del parco giochi nel Project Navigator.

Assicurati di trascinare solo le immagini in modo che ciascun file immagine risieda nel risorse cartella, non in Risorse / Immagini.

Elimina il codice nel parco giochi. Fare clic con il tasto destro del mouse su fonti cartella e selezionare Nuovo file dal menu. Imposta il nome del file su Game.swift.

2. Scrivere classi e metodi di supporto

Aggiungere il seguente codice a Game.swift. Assicurati di salvare il file dopo ogni aggiunta di codice.

import UIKit import XCPlayground import GameplayKit // (1) estensione pubblica UIImage // (2) public convenience init? (color: UIColor, dimensione: CGSize = CGSize (larghezza: 1, altezza: 1)) let rect = CGRect ( origine: .zero, dimensione: dimensione) UIGraphicsBeginImageContextWithOptions (rect.size, false, 0.0) color.setFill () UIRectFill (rect) let image = UIGraphicsGetImageFromCurrentImageContext () UIGraphicsEndImageContext () guardia lascia 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 lascia x: Int pubblico let y: Int pubblico init (image: UIImage ?, x: Int, y: Int) self.x = x self.y = y super.init (immagine: immagine) self.backgroundColor = .grayColor () self.layer.cornerRadius = 10.0 self .userInteractionEnabled = true richiesto public init? (coder aDecoder: NSCoder) fatalError ("init (coder :) non è stato implementato")

Ho aggiunto alcuni commenti numerati per spiegare alcune sezioni dell'implementazione:

  1. Inoltre UIKit e XCPlayground, stiamo anche importando GamePlayKit. Questo framework include un metodo conveniente che ci aiuterà a implementare un metodo per mescolare casualmente un array.
  2. Questa estensione su UIImage ci consente, con l'aiuto di UIKit metodi, per rendere le immagini con un colore solido di qualsiasi dimensione che vogliamo. Lo useremo per impostare l'immagine di sfondo iniziale delle carte da gioco.
  3. Il cardHeight e cardWidth le costanti rappresentano le dimensioni delle immagini delle carte in base alle quali calcoleremo altre dimensioni.
  4. Il Carta classe, ereditando da UIImageView, rappresenta una carta. Anche se impostiamo alcune proprietà nel Carta classe, lo scopo principale della creazione di questa classe è quello di aiutarci a identificare e scorrere le sottoview che corrispondono alle carte da gioco nel gioco. Le carte hanno anche proprietà X e y per ricordare la loro posizione nella griglia.

3. Visualizza controller

Aggiungere il seguente codice a Game.swift, subito dopo il codice precedente:

public class GameController: UIViewController // (1): variabili pubbliche in modo da poterle manipolare nel playground public var padding = CGFloat (20) / * didSet resetGrid () * / public var backImage: UIImage = UIImage ( color: .redColor (), size: CGSize (width: cardWidth, height: cardHeight))! // (2): proprietà calcolate var viewWidth: CGFloat get return 4 * cardWidth + 5 * padding var viewHeight: CGFloat get return 4 * cardHeight + 5 * padding var shuffledNumbers = [Int] () // memorizza i numeri delle carte mescolate // var firstCard: Card? // uncomment tardi public init () super.init (nibName: nil, bundle: nil) preferredContentSize = CGSize (width: viewWidth, height: viewHeight) shuffle () setupGrid () // commento successivo: // let tap = UITapGestureRecognizer (target: self, action: #selector (GameController.handleTap (_ :))) // view.addGestureRecognizer (tap) required public init? (coder aDecoder: NSCoder) fatalError ("init (coder :) non è stato implementato ") override pubblico func loadView () view = UIView () view.backgroundColor = .blueColor () view.frame = CGRect (x: 0, y: 0, larghezza: viewWidth, altezza: viewHeight) // ( 3): Utilizzo dell'API GameplayKit per generare uno shuffling dell'array [1, 1, 2, 2, ..., 8, 8] func shuffle () let numbers = (1 ... 8) .flatMap [$ 0, $ 0] shuffledNumbers = GKRandomSource.sharedRandom (). arrayByShufflingObjectsInArray (numeri) come! [Int] // (4): Converti dalla posizione della carta sulla griglia all'indice nei numeri della carta mescolata. Matrice 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 asserire (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)    
  1. Le due proprietà, imbottitura e backImage, sono dichiarati pubblico in modo che possiamo accedervi nel parco giochi più tardi. Rappresentano lo spazio vuoto che circonda le carte sulla griglia e l'immagine visualizzata sul retro di ciascuna carta, rispettivamente. Si noti che a entrambe le proprietà sono stati dati valori iniziali, che rappresentano una spaziatura di 20 e un colore rosso fisso per l'immagine non frontale della carta. Puoi ignorare il codice commentato per ora.
  2. Stiamo calcolando la larghezza e l'altezza desiderate delle viste per mezzo di proprietà calcolate. Per capire il viewWidth calcolo, ricorda che ci sono quattro carte in ogni riga e dobbiamo anche prendere in considerazione il padding di ogni carta. La stessa idea si applica al viewHeight calcolo.
  3. Il codice (1 ... 8). FlatMap [$ 0, $ 0] è un modo conciso per produrre l'array [1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8]. Se non hai familiarità con la programmazione funzionale, potresti anche scrivere a per-loop per generare l'array. Usando metodi dal GamePlayKit framework, mescoliamo i numeri nell'array. I numeri corrispondono alle otto coppie di carte. Ogni numero rappresenta l'immagine della carta con lo stesso nome (ad esempio, un valore di 1 nel shuffledArray corrisponde a 1.png).
  4. Abbiamo scritto un metodo che mappa la posizione di una carta sulla griglia 4x4 alla sua posizione nel shuffledNumbers array di lunghezza 16. Il fattore 4 nel calcolo aritmetico riflette il fatto che abbiamo quattro carte per riga.
  5. Abbiamo anche un metodo che individua la posizione di una carta (la sua centro proprietà) nella griglia in base alle dimensioni e al riempimento della carta.
  6. Il setupGrid () il metodo viene chiamato durante l'inizializzazione del controller della vista. Disegna il 4x4 Carta griglia. Assegna inoltre l'identità di ciascuna carta in base al shuffledNumbers array e lo memorizza nel etichetta proprietà ereditata dalla classe base della carta, UIView. Nella logica del gioco, confrontiamo il etichetta valori per capire se due carte corrispondono o meno. Questo schema di modellizzazione piuttosto rudimentale serve abbastanza bene per i nostri bisogni attuali.
  7. Questo pezzo di codice attualmente inutilizzato ci aiuterà a riposizionare le carte nel caso in cui il padding cambi. Ricorda che abbiamo dichiarato il imbottitura proprietà come proprietà pubblica in modo che possiamo accedervi nel parco giochi.
  8. Il codice in viewDidAppear (_ :) viene eseguito immediatamente dopo che la vista del controller della vista diventa visibile. Effettuiamo l'iterazione delle sottoview della vista e, se la sottoview è un'istanza di Carta classe, (controllato attraverso il come? operatore di downcasting disponibile) il corpo del Se-la dichiarazione definisce la transizione da eseguire. Qui è dove cambieremo l'immagine visualizzata sulle carte, dall'immagine del fumetto che definisce la faccia di ogni carta al (comune) backImage di tutte le carte. Questa transizione è accompagnata da un'animazione di vibrazione da sinistra a destra che dà l'impressione che le carte vengano fisicamente girate. Se non hai familiarità con come UIView le animazioni funzionano, questo può sembrare un po 'strano. Anche se abbiamo aggiunto l'animazione di ogni carta in sequenza in un ciclo, le animazioni sono raggruppate in una singola transazione di animazione ed eseguite simultaneamente, cioè le carte ruotano insieme.

Rivedi il campo giochi e sostituisci qualsiasi testo nell'editor con il seguente:

import XCPlayground import UIKit let gc = GameController () XCPlaygroundPage.currentPage.liveView = gc

Assicurati che la timeline sia visibile. La visuale del controller della vista dovrebbe prendere vita e mostrarci una griglia di carte 4x4 con simpatici cartoni animati che girano per mostrarci il retro delle carte. In questo momento, non possiamo fare molto con questa visione perché non abbiamo ancora programmato alcuna interazione al suo interno. Ma è sicuramente un inizio.

4. Modifica delle variabili nel parco giochi

Passiamo ora alle facce posteriori delle carte dal rosso fisso a un'immagine, in particolare b.png nel risorse cartella. Aggiungi la seguente riga alla fine del campo da giuoco.

gc.backImage = UIImage (named: "b")!

Dopo un secondo o due, vedrai che i lati posteriori delle carte sono passati da una semplice mano rossa a una mano da cartone animato.

Proviamo ora a modificare il imbottitura proprietà, a cui è stato assegnato un valore predefinito di 20 in Game.swift. Di conseguenza, lo spazio tra le carte dovrebbe aumentare. Aggiungi la seguente riga alla fine del campo giochi:

gc.padding = 75

Attendi che la vista live si aggiorni e vedi che ... nulla è cambiato.

5. Una breve deviazione

Per capire cosa sta succedendo, devi tenere presente che le entità, come i controller di visualizzazione e le loro viste associate, hanno un ciclo di vita complesso. Ci concentreremo su questi ultimi, cioè sulle visualizzazioni. La creazione e l'aggiornamento della vista di un controller di visualizzazione è un processo a più fasi. In punti specifici del ciclo di vita della vista, le notifiche vengono inviate al UIViewController, informandolo di cosa sta succedendo. Ancora più importante, il programmatore può collegarsi a queste notifiche inserendo il codice per indirizzare e personalizzare questo processo.

Il loadview () e viewDidAppear (_ :) i metodi sono due metodi che abbiamo usato per agganciare il ciclo di vita della vista. Questo argomento è in qualche modo coinvolto e va oltre lo scopo di questa discussione, ma ciò che conta per noi è che il codice nel parco giochi, dopo l'assegnazione del controller di visualizzazione come il campo da gioco dal vivo, viene eseguito un po 'di tempo tra la chiamata a viewWillAppear (_ :) e la chiamata a viewDidAppear (_ :). È possibile verificare ciò modificando alcune proprietà nel parco giochi e aggiungere istruzioni di stampa a questi due metodi per visualizzare il valore di questa proprietà.

Il problema con il valore di imbottitura non avendo l'effetto visivo atteso è che, a quel punto, la vista e le sue sottoview sono già state esposte. Tieni presente che, ogni volta che apporti una modifica al codice, il campo di gioco viene ripetuto dall'inizio. In questo senso, questo problema non è specifico per i campi da gioco. Anche se stavi sviluppando codice da eseguire sul simulatore o su un dispositivo fisico, spesso dovresti scrivere del codice aggiuntivo per assicurarti che la modifica del valore di una proprietà abbia l'effetto desiderato sull'aspetto o sul contenuto della vista.

Potresti chiedere perché siamo stati in grado di modificare il valore di backImage proprietà e vedere il risultato senza fare nulla di speciale. Osservare che il backImage la proprietà viene effettivamente utilizzata per la prima volta in viewDidAppear (_ :), da quel momento ha già preso il suo nuovo valore.

6. Osservare le proprietà e agire

Il nostro modo di affrontare questa situazione sarà monitorare i cambiamenti nel valore di imbottitura e ridimensiona / riposiziona la vista e le sottoview. Fortunatamente, questo è facile da fare con Swift a portata di mano osservazione delle proprietà caratteristica. Inizia decommentando il codice per resetGrid () metodo in Game.swift:

// (7): reset grid func resetGrid () view.frame = CGRect (x: 0, y: 0, larghezza: viewWidth, height: viewHeight) per v in view.subviews if let card = v as? Card card.center = centerOfCardAt (card.x, card.y)

Questo metodo ricalcola la posizione della cornice della vista e quella di ciascuna Carta oggetto basato sui nuovi valori di viewWidth e viewHeight. Ricorda che queste proprietà sono calcolate in base al valore di imbottitura, che è stato appena modificato.

Inoltre, modificare il codice per imbottitura usare il didSet osservatore il cui corpo, come indica il nome, viene eseguito ogni volta che viene impostato il valore di imbottitura:

// (1): variabili pubbliche in modo che possiamo manipolarle nel playground public var padding = CGFloat (20) didSet resetGrid ()

Il resetGrid () il metodo inizia e la vista viene aggiornata per riflettere la nuova spaziatura. Puoi verificarlo nel parco giochi.

Sembra che siamo stati in grado di sistemare le cose abbastanza facilmente. In realtà, quando ho deciso per la prima volta, volevo essere in grado di interagire con il imbottitura proprietà, ho dovuto tornare indietro e apportare modifiche al codice in Game.swift. Per esempio, ho dovuto astrarre il Carta calcolo del centro in una funzione separata (centerOfCardAt (_: _ :)) per calcolare in modo pulito e indipendente (ri) le posizioni delle carte ogni volta che dovevano essere disposte.

Creare proprietà calcolate per viewWidth e viewHeight anche aiutato. Mentre questo tipo di riscrittura è qualcosa che dovresti essere preparato per un compromesso di non fare molto design in anticipo, può essere ridotto con un po 'di lungimiranza ed esperienza.

7. Logica di gioco e interazione tattile

È giunto il momento di implementare la logica del gioco e permetterci di interagire con esso attraverso il tatto. Inizia decifrando il firstCard dichiarazione di proprietà nel Gamecontroller classe:

var firstCard: card?

Ricorda che la logica del gioco implica rivelare due carte, una dopo l'altra. Questa variabile tiene traccia di se un capovolgimento della carta eseguito dal giocatore è il primo dei due o meno.

Aggiungi il seguente metodo alla fine del Gamecontroller classe, prima della parentesi graffa che termina:

func handleTap (gr: UITapGestureRecognizer) let v = view.hitTest (gr.locationInView (view), withEvent: nil)! se let card = v come? Card UIView.transitionWithView (scheda, durata: 0,5, opzioni: .TransitionFlipFromLeft, animazioni: card.image = UIImage (named: String (card.tag))) // finale del gestore di completamento: _ in card.userInteractionEnabled = false se let pCard = self.firstCard if pCard.tag == card.tag UIView.animateWithDuration (0.5, animations: card.alpha = 0.0, completamento: _ in card.removeFromSuperview ()) UIView.animateWithDuration (0.5, animazioni: pCard.alpha = 0.0, completamento: _ in pCard.removeFromSuperview ()) else UIView.transitionWithView (scheda, durata: 0.5, opzioni: .TransitionFlipFromLeft, animazioni: card.image = self.backImage) _ in card.userInteractionEnabled = true UIView.transitionWithView (pCard, durata: 0.5, opzioni: .TransitionFlipFromLeft, animazioni: pCard.image = self.backImage) _ in pCard.userInteractionEnabled = true  self.firstCard = nil else self.firstCard = card

Questo è un metodo lungo. Questo perché racchiude in un unico metodo tutta la gestione del tocco, la logica di gioco e le animazioni associate richieste. Vediamo come questo metodo fa il suo lavoro:

  • Innanzitutto, è presente un controllo per garantire che l'utente abbia effettivamente toccato a Carta esempio. Questo è lo stesso come? costrutto che abbiamo usato in precedenza.
  • Se l'utente ha toccato a Carta Per esempio, lo capovolgiamo usando un'animazione simile a quella che abbiamo implementato in precedenza. L'unico nuovo aspetto è che utilizziamo il gestore di completamento, che viene eseguito al termine dell'animazione, per disabilitare temporaneamente le interazioni tattili per quella particolare scheda impostando il userInteractionEnabled proprietà della carta. Questo impedisce al giocatore di capovolgere la stessa carta. Notare la _ nel costrutto che è usato più volte in questo metodo. Questo è solo per dire che vogliamo ignorare il Bool parametro che assume il gestore di completamento.
  • Eseguiamo codice in base al fatto che il firstCard è stato assegnato un valore non nullo utilizzando l'associazione opzionale, familiare a Swift se lasciato costruire.
  • Se firstCard è non-zero, quindi questa era la seconda carta della sequenza che il giocatore ha girato. Ora dobbiamo confrontare la faccia di questa carta con quella precedente (confrontando il etichetta valori) per vedere se abbiamo una corrispondenza o meno. Se lo facessimo, animiamo le carte in dissolvenza (impostando le loro alfa a 0). Rimuoviamo anche queste carte dalla vista. Se i tag non sono uguali, il che significa che le carte non corrispondono, semplicemente li capovolgere di nuovo verso il basso e impostare userInteractionEnabled a vero in modo che l'utente possa selezionarli di nuovo.
  • Basato sul valore corrente di firstCard, abbiamo impostato su entrambi zero o alla carta attuale. Questo è il modo in cui cambiamo il comportamento del codice tra due tocchi successivi.

Infine, decommentare le seguenti due dichiarazioni nel Gamecontrollerl'inizializzatore che aggiunge un tocco di riconoscimento del gesto alla vista. Quando il rilevatore del gesto di tocco rileva un tocco, il handleTap () il metodo è invocato:

let tap = UITapGestureRecognizer (target: self, action: #selector (GameController.handleTap (_ :))) view.addGestureRecognizer (tap)

Torna alla timeline del parco giochi e gioca al gioco di memoria. Sentiti libero di diminuire il grande imbottitura abbiamo assegnato un po 'prima.

Il codice in handleTap (_ :) è praticamente la versione non abbellita di ciò che ho scritto la prima volta. Si potrebbe sollevare l'obiezione che, come metodo unico, fa troppo. O che il codice non è abbastanza orientato agli oggetti e che la logica che gira la logica e le animazioni dovrebbe essere ordinatamente estratta in metodi del Carta classe. Mentre queste obiezioni non sono invalide di per sé, ricorda che la prototipazione rapida è al centro di questo tutorial e dal momento che non prevedevamo alcuna necessità di interagire con questa parte del codice nel parco giochi, potremmo permetterci di essere un po 'più "hack-ish".

Una volta che avremo qualcosa di funzionante e decidiamo di perseguire ulteriormente l'idea, dovremmo certamente prendere in considerazione il refactoring del codice. In altre parole, prima fallo funzionare, quindi rendilo veloce / elegante / bello / ...

8. Toccare Gestione nel parco giochi

Mentre la parte principale del tutorial è finita, come interessante a parte, voglio mostrarti come possiamo scrivere il codice di manipolazione del tocco direttamente nel parco giochi. In primo luogo aggiungeremo un metodo al Gamecontroller classe che ci permette di sbirciare sui volti delle carte. Aggiungi il seguente codice al Gamecontroller classe, immediatamente dopo il handleTap (_ :) metodo:

public func quickPeek () per 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 , durata: 1.0, opzioni: .TransitionFlipFromLeft, animazioni: card.image = self.backImage) _ in card.userInteractionEnabled = true

Supponiamo di voler attivare o disattivare questa funzione "quick peek" dall'interno del parco giochi. Un modo per farlo sarebbe creare un pubblico Bool proprietà nel Gamecontroller classe che potremmo impostare nel parco giochi. E, naturalmente, dovremmo scrivere un gestore di gesti nel Gamecontroller classe, attivata da un diverso gesto, che invocerebbe quickPeek ().

Un altro modo sarebbe quello di scrivere il codice di gestione dei gesti direttamente nel parco giochi. Un vantaggio di farlo in questo modo è che potremmo incorporare qualche codice personalizzato oltre a chiamare quickPeek (). Questo è ciò che faremo dopo. Aggiungi il seguente codice alla fine del campo da giuoco:

class LPGR static var counter = 0 @objc static func longPressed (lp: UILongPressGestureRecognizer) if lp.state == .Began gc.quickPeek () counter + = 1 print ("Hai sbirciato \ (contatore) tempo (s) . ") let longPress = UILongPressGestureRecognizer (target: LPGR.self, action: #selector (LPGR.longPressed)) longPress.minimumPressDuration = 2.0 gc.view.addGestureRecognizer (longPress)

Per attivare la funzione di quick peek, useremo un gesto a pressione lunga, cioè il giocatore tiene il dito sullo schermo per un certo periodo di tempo. Usiamo due secondi come soglia.

Per gestire il gesto, creiamo una classe, LPGR (premere a lungo il riconoscimento del gesto abbreviato), con a statico proprietà variabile, contatore, per tenere traccia di quante volte abbiamo sbirciato, e a statico metodo longPressed (_ :) per gestire il gesto.

Usando il statico qualificatore, possiamo evitare di dover creare un LPGR istanza perché le entità dichiarate statiche sono associate al LPGR scrivi (classe) piuttosto che con una particolare istanza.

A parte questo, non vi è alcun vantaggio particolare in questo approccio. Per motivi complicati, dobbiamo contrassegnare il metodo come @objc per mantenere il compilatore felice. Notare l'uso di LPGR.self per riferirsi al tipo di oggetto. Si noti inoltre che nel gestore del gesto, controlliamo se il stato del gesto è .Ha inizia