Rask, interaktiv prototyping med Xcode Playgrounds

Hva du skal skape

Introduksjon

Siden deres introduksjon i Xcode 6 sammen med Swift, til deres nåværende iterasjon i Xcode 7.3.1, har lekeplasser kommet langt. Med nye funksjoner og bedre stabilitet utvikler de seg til et levedyktig verktøy for rask prototyping eller raskt hacking sammen et bevis på konseptet.

Som utvikler, noen ganger har du et glimt av inspirasjon i form av en interessant ide for en app, og du vil raskt koble opp en prototype som representerer den bare essensen av ideen din. Eller du vil bare verifisere din forståelse av hvordan et stykke UIKit-kode vil oppføre seg. Hvis du er som meg, vil du heller unngå stresset og det mentale overheadet til å skape et Xcode-prosjekt og måtte håndtere et mylder av faktorer, for eksempel enhetstyper og oppløsninger, og bygge innstillinger. Disse avgjørelsene kan bli utsatt til etter at du har gjort deg oppmerksom på at kjernen ideen er verdt å forfølge.

I denne opplæringen lager vi et kortbasert minne spill alt innenfor en lekeplass. Det er et vanlig, velkjent spill, så ingen kreditt for originalitet der. Spillet består av åtte par identiske kort (så totalt 16 kort) plassert med forsiden ned i et 4x4-rutenett.

Spilleren må flippe to kort hvis ansikter er kort avslørt og deretter raskt snudd om igjen. Målet med spillet er at spilleren prøver å huske posisjonene til kortene og avdekke identiske par, som deretter fjernes fra spillet. Spillet er over når rutenettet er ryddet.

Spillet er touch-basert og inkorporerer enkle visning animasjoner. Du lærer hvordan du kan gjøre endringer i appen din og se resultatet av endringene dine live.

1. Komme i gang

Brann opp Xcode og velg Ny> Lekeplass ... fra Xcode er Fil Meny. Gi lekeplassen et navn, for eksempel MemoryGameXCPTut, sett Plattform til iOS, og lagre lekeplassen. Jeg bruker Xcode 7.3.1 for denne opplæringen.

Finne veien rundt lekeplassen

La oss bruke litt tid på å gjøre oss kjent med lekeplassgrensesnittet. Følg med på å skumre denne delen hvis du allerede er kjent med lekeplasser.

En lekeplass kan ha flere sider, hver knyttet til sin egen livevisning og egne kilder / ressursmapper. Vi vil ikke bruke flere sider i denne opplæringen. Lekeplasser støtter markupformatering som lar deg legge til rik tekst på en lekeplass og lenke mellom lekeplassssider.

Det første du ser etter å ha opprettet en lekeplass, er lekeplassens kileditor. Det er her du skriver kode, som har umiddelbar effekt på livevisningen. En av måtene å bytte til (dis) utseendet på Prosjektnavigator bruker snarveien Command-0. I Prosjektnavigator, Du kan se to mapper, kilder og ressurser.

kilder

I kilder mappe, kan du legge til hjelpekode i en eller flere Swift-filer, for eksempel egendefinerte klasser, vise kontroller og visninger. Selv om hoveddelen av koden som definerer prototypens logikk, går det, er det hjelpemiddel i den forstand at det er gjemt i bakgrunnen når du ser på appen din live.

Fordelen med å sette hjelpekoden i kilder mappen er at den automatisk samles hver gang du endrer og lagrer filen. På denne måten får du raskere tilbakemeldinger i livevisningen fra endringer gjort på lekeplassen. Tilbake på lekeplassen, er du i stand til å få tilgang offentlig egenskaper og metoder du eksponerer i hjelpekoden, som påvirker hvordan appen din oppfører seg.

ressurser

Du kan legge til eksterne ressurser, for eksempel bilder, i ressurser mappe.

I denne opplæringen må du ofte hoppe mellom en Swift-fil vi lager i kilder mappe og lekeplassfilen (teknisk også en Swift-fil, bortsett fra at du ikke vil referere til den av filnavnet). Vi benytter oss også av Assistent Editor i opplæringen, med å vise den Tidslinje, for å se live-utgangen side ved side med lekeplassskoden. Eventuelle endringer du gjør på lekeplassen reflekteres umiddelbart (vel, innen få sekunder) i live-utgangen. Du kan også berøre samspillet med livevisningen og brukergrensesnittelementene. For å sikre at du kan gjøre alt dette, ta et raskt blikk på figuren nedenfor.

Tilsvarer de grønne tallene jeg har lagt til figuren:

  1. Denne knappen skjuler Assistent Editor slik at bare hovedredaktøren er synlig.
  2. Denne knappen avslører Assistent Editor. De Assistent Editor er synlig til høyre for hovedredaktøren. Denne redaktøren kan hjelpe ved å vise oss relevante filer, for eksempel motparten av filen i hovedredigeren.
  3. Fra venstre til høyre brukes disse to knappene for å skifte utseendet til Prosjektnavigator og feilsøkingskonsollen. I konsollen kan vi blant annet inspisere utskrift av utskriftserklæringer.
  4. De hoppe bar øverst på hovedredigeren kan også brukes til å navigere til en bestemt fil. Ved å klikke på prosjektnavnet to ganger, kommer du tilbake til lekeplassen. Alternativt kan du også bruke Prosjektnavigator.

Noen ganger, når du ser på lekeplassen, må du sørge for at Assistent Editor viser Tidslinje i stedet for en annen fil. Nedenfor figuren viser hvordan du gjør dette. I Assistent Editor, å velge Tidslinje, motstykket til lekeplassen, i stedet for Håndbok, som lar deg vise hvilken som helst fil i Assistent Editor.

Når du redigerer en kildefil fra kilder mappe, som sin motpart, den Assistent Editor viser grensesnittet til koden din, det vil si erklæringer og funksjonsprototyper uten deres implementeringer. Jeg foretrekker å skjule Assistent Editor når jeg jobber med en fil i kilder mappe og bare avsløre Assistent Editor på lekeplassen for å se live-visningen.

For å få tilgang til de spesielle evnene på lekeplasser, må du importere XCPlayground-modulen.

importere XCPlayground

Du setter sanntid eiendom av nåværende side av XCPlaygroundPage motsette seg et objekt som samsvarer med  XCPlaygroundLiveViewable protokoll. Dette kan være en egendefinert klasse, eller det kan være en UIView eller UIViewController forekomst.

Legge til filer i kilden / ressursmappen

Jeg har lagt til noen bilder vi kan jobbe med i denne opplæringen. Last ned bildene, hent ut arkivet, og legg til bildene i Bilder mappe til ressurser mappe av lekeplassen i Prosjektnavigator.

Pass på at du bare drar bildene slik at hver bildefil ligger i ressurser mappe, ikke inn Ressurser / Images.

Slett koden på lekeplassen. Høyreklikk på kilder mappe og velg Ny fil fra menyen. Angi navnet på filen til Game.swift.

2. Skrivehjelpsklasser og metoder

Legg til følgende kode til Game.swift. Pass på at du lagrer filen etter hvert kode tillegg.

import UIKit import XCPlayground import GameplayKit // (1) offentlig utvidelse UIImage // (2) offentlig bekvemmelighet init? (farge: UIColor, størrelse: CGSize = CGSize (bredde: 1, høyde: 1)) la rect = CGRect opprinnelse: .zero, størrelse: størrelse) UIGraphicsBeginImageContextWithOptions (rect.size, false, 0.0) color.setFill () UIRectFill (rect) la bildet = UIGraphicsGetImageFromCurrentImageContext () UIGraphicsEndImageContext () vakt la cgImage = image.CGImage else return nil selv .init (CGImage: cgImage) la cardWidth = CGFloat (120) // (3) la cardHeight = CGFloat (141) offentlig klasse Kort: UIImageView // (4) public let x: Int public let y: Int offentlig init (bilde: UIImage ?, x: Int, y: Int) self.x = x self.y = y super.init (bilde: bilde) self.backgroundColor = .grayColor () self.layer.cornerRadius = 10.0 selv .userInteractionEnabled = true kreves offentlig init? (koder aDecoder: NSCoder) fatalError ("init (coder :) har ikke blitt implementert")

Jeg har lagt til noen nummererte kommentarer for å forklare enkelte deler av implementeringen:

  1. I tillegg til UIKit og XCPlayground, Vi importerer også GamePlayKit. Dette rammeverket inneholder en praktisk metode som vil hjelpe oss med å implementere en metode for å tilfeldigvis blande en matrise.
  2. Denne utvidelsen på UIImage tillater oss, ved hjelp av UIKit metoder, for å lage bilder med en solid farge av enhver størrelse vi vil ha. Vi vil bruke dette til å sette det første bakgrunnsbildet av spillekortene.
  3. De cardHeight og cardWidth konstanter representerer kortbildestørrelsene basert på hvilke vi skal beregne andre størrelser.
  4. De Kort klasse, arve fra UIImageView, representerer et kort. Selv om vi satt noen få egenskaper i Kort klassen, er hovedformålet med å skape denne klassen å hjelpe oss med å identifisere og iterere over undervisninger som tilsvarer spillekort i spillet. Kortene har også egenskaper x og y å huske sin posisjon i rutenettet.

3. Se kontrolleren

Legg til følgende kode til Game.swift, umiddelbart etter forrige kode:

offentlig klasse GameController: UIViewController // (1): offentlige variabler slik at vi kan manipulere dem på lekeplassen offentlige var padding = CGFloat (20) / * didSet resetGrid () * / public var backImage: UIImage = UIImage farge: .redColor (), størrelse: CGSize (bredde: cardWidth, height: cardHeight))! // (2): Beregnede egenskaper var viewWidth: CGFloat get return 4 * cardWidth + 5 * polstring var viewHeight: CGFloat get return 4 * cardHeight + 5 * polstring var shuffledNumbers = [Int] // butikker shuffled kortnummer // var firstCard: Card? // uncomment senere offentlig init () super.init (nibName: nil, bundle: nil) preferredContentSize = CGSize (bredde: viewWidth, height: viewHeight) shuffle () setupGrid () // ukomment senere: // let tap = UITapGestureRecognizer (mål: selv, handling: #selector (GameController.handleTap (_ :))) // view.addGestureRecognizer (trykk) kreves offentlig init? (koder aDecoder: NSCoder) fatalError ("init (coder :) har ikke vært implementert ") offentlig overstyring func loadView () view = UIView () view.backgroundColor = .blueColor () view.frame = CGRect (x: 0, y: 0, bredde: viewWidth, height: viewHeight) // 3): Bruk GameplayKit API for å generere en shuffling av arrayet [1, 1, 2, 2, ..., 8, 8] func shuffle () la tall = (1 ... 8) .flatMap [$ 0, $ 0] shuffledNumbers = GKRandomSource.sharedRandom (). arrayByShufflingObjectsInArray (tall) som! [Int] // (4): Konverter fra kortposisjon på rutenett til indeks i de blandede kortnummerene array func cardNumberAt (x: Int, _y: Int) -> Int assert <= 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)    
  1. De to egenskapene, padding og backImage, er erklært offentlig slik at vi kan få tilgang til dem på lekeplassen senere. De representerer det tomme rommet rundt kortene på rutenettet og bildet som vises på baksiden av hvert kort. Vær oppmerksom på at begge egenskapene har fått innledende verdier, som representerer en polstring på 20 og en solid rød farge for kortets side uten sideflate. Du kan ignorere kommentert kode for nå.
  2. Vi beregner ønsket bredde og høyde på visningene ved hjelp av beregnede egenskaper. For å forstå viewWidth beregning, husk at det er fire kort i hver rad, og vi må også ta hensyn til polstring av hvert kort. Den samme ideen gjelder for viewHeight beregningen.
  3. Koden (1 ... 8) .flatMap [$ 0, $ 0] er en kort måte å produsere matrisen på [1, 1, 2, 2, 3, 4, 4, 5, 5, 6, 7, 7, 8, 8]. Hvis du ikke er kjent med funksjonell programmering, kan du også skrive en til-loop for å generere array. Bruke metoder fra GamePlayKit rammeverk, blander vi tallene i gruppen. Tallene samsvarer med de åtte par kortene. Hvert tall representerer kortbildet med samme navn (for eksempel en verdi på 1 i shuffledArray tilsvarer 1.png).
  4. Vi skrev en metode som kartlegger plasseringen av et kort på 4x4-nettet til sin plassering i shuffledNumbers rekkevidde av lengde 16. Faktoren 4 i aritmetisk beregning gjenspeiler det faktum at vi har fire kort per rad.
  5. Vi har også en metode som viser posisjonen til et kort (dets senter eiendom) i rutenett basert på kort dimensjoner og polstring.
  6. De setupGrid () Metoden kalles under visningskontrollørens initialisering. Det legger ut 4x4 Kort Nett. Den tildeler også hvert kort identitet basert på shuffledNumbers array og lagrer den i stikkord eiendom arvet fra kortets grunnklasse, UIView. I spilllogikken sammenligner vi stikkord verdier for å finne ut om to kort stemmer overens eller ikke. Denne ganske rudimentære modelleringsordningen fungerer godt nok for våre nåværende behov.
  7. Denne ubrukte koden vil hjelpe oss med å plassere kortene dersom polstringen endres. Husk at vi erklærte padding eiendom som en offentlig eiendom slik at vi kan få tilgang til den på lekeplassen.
  8. Koden i viewDidAppear (_ :) kjører umiddelbart etter at visningskontrollens syn blir synlig. Vi gjenta gjennom visnings underviews og, hvis undervisningen er en forekomst av Kort klasse, (sjekket gjennom som? failable downcasting operatør) kroppen av hvis-setningen definerer overgangen til å utføre. Dette er hvor vi vil endre bildet som vises på kortene, fra tegneseriebildet som definerer ansiktet til hvert kort til (felles) backImage av alle kortene. Denne overgangen ledsages av en venstre-til-høyre flip-animasjon som gir utseendet på at kortene blir fysisk vendt over. Hvis du ikke er kjent med hvordan UIView animasjoner fungerer, dette kan se litt rart ut. Selv om vi legger til hvert korts animasjon i en loop, blir animasjonene batchet til en enkelt animasjonstransaksjon og utført samtidig, det vil si at kortene flipper sammen.

Revisér lekeplassen og erstatt tekst i redaktøren med følgende:

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

Kontroller at tidslinjen er synlig. Utsiktskontrollørens syn skal komme til livs og vise oss et 4x4-kort av kort med søte tegneseriedyr som spretter over for å vise oss baksiden av kortene. Akkurat nå kan vi ikke gjøre mye med denne visningen fordi vi ikke har programmert noen interaksjon i det ennå. Men det er definitivt en start.

4. Endre variabler på lekeplassen

La oss nå endre baksiden av kortene fra solid rød til et bilde, spesielt b.pngressurser mappe. Legg til følgende linje til bunnen av lekeplassen.

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

Etter et sekund eller to ser du at baksiden av kortene har forandret seg fra ren rød til en tegneseriehånd.

La oss nå prøve å endre padding eiendom, som vi tilordnet en standardverdi på 20 in Game.swift. Plassen mellom kortene skal øke som et resultat. Legg til følgende linje til bunnen av lekeplassen:

gc.padding = 75

Vent til levende visning for å oppdatere og se at ... ingenting har endret seg.

5. En kort omvei

For å forstå hva som skjer, må du huske på at enheter, for eksempel visningskontrollere og tilhørende visninger, har en kompleks livssyklus. Vi skal fokusere på sistnevnte, det vil si synspunkter. Oppretting og oppdatering av en visningskontrollers visning er en flertrinns prosess. På spesifikke punkter i livssyklusen av visningen, blir meldinger utstedt til UIViewController, informere om hva som skjer. Enda viktigere, kan programmøren koble til disse varslene ved å sette inn kode for å rette og tilpasse denne prosessen.

De loadView () og viewDidAppear (_ :) metoder er to metoder vi pleide å koble til visningen livssyklus. Dette emnet er litt involvert og utover omfanget av denne diskusjonen, men det som betyr noe for oss er at koden på lekeplassen, etter oppdragets tilsynsfører som lekeplassens sanntid, utføres en stund mellom samtalen til viewWillAppear (_ :) og anropet til viewDidAppear (_ :). Du kan bekrefte dette ved å endre noen eiendommer på lekeplassen og legge ut utsagn til disse to metodene for å vise verdien av denne eiendommen.

Problemet med verdien av padding uten å ha den forventede visuelle effekten er det på den tiden allerede utsikten og dens undervisninger blitt lagt ut. Husk at når du endrer koden, er lekeplassen gjenopprettet fra begynnelsen. I den forstand er dette problemet ikke spesifikt for lekeplasser. Selv om du utviklet kode for å kjøre på simulatoren eller på en fysisk enhet, må du ofte skrive flere koden for å sikre at endringen i en egenskaps verdi har den ønskede effekten på visningens utseende eller innhold.

Du kan spørre hvorfor vi var i stand til å endre verdien av backImage eiendom og se resultatet uten å gjøre noe spesielt. Vær oppmerksom på at backImage Eiendommen er faktisk brukt for første gang i viewDidAppear (_ :), da den allerede har hentet sin nye verdi.

6. Observere egenskaper og ta i bruk

Vår måte å håndtere denne situasjonen vil være å overvåke endringer i verdien av padding og endre størrelse / omplassering av visningen og undervisningen. Heldigvis er dette lett å gjøre med Swift er praktisk eiendom observere trekk. Start med å ikke kommentere koden for resetGrid () metode i Game.swift:

// (7): reset grid func resetGrid () view.frame = CGRect (x: 0, y: 0, bredde: viewWidth, height: viewHeight) for v i view.subviews hvis la kortet = v som? Kort card.center = centerOfCardAt (card.x, card.y)

Denne metoden rekomputerer posisjonen til visningsrammen og den for hver Kort objekt basert på de nye verdiene for viewWidth og viewHeight. Husk at disse egenskapene beregnes ut fra verdien av padding, som nettopp har blitt endret.

Endre også koden for padding å bruke didSet observatør hvis kropp, som navnet indikerer, utfører når vi setter verdien av padding:

// (1): offentlige variabler slik at vi kan manipulere dem på lekeplassen public var padding = CGFloat (20) didSet resetGrid ()

De resetGrid () Metoden sparker inn og utsikten er oppdatert for å gjenspeile den nye avstanden. Du kan bekrefte dette på lekeplassen.

Det ser ut til at vi var i stand til å fikse ting ganske enkelt. I virkeligheten, da jeg først bestemte meg, ville jeg kunne samhandle med padding eiendom, jeg måtte gå tilbake og gjøre endringer i koden i Game.swift. For eksempel måtte jeg abstrahere Kort senterberegning i en egen funksjon (centerOfCardAt (_: _ :)) å rent og uavhengig (re) beregne posisjonene til kortene når de trengte å bli lagt ut.

Lag beregne egenskaper for viewWidth og viewHeight hjalp også. Mens denne typen omskriving er noe du bør være forberedt på som et kompromiss med å ikke gjøre mye på forhånd, kan det reduseres med litt forethought og erfaring.

7. Game Logic & Touch Interaction

Det er nå på tide å implementere spillets logikk og gjøre det mulig å samhandle med det gjennom berøring. Begynn med å ikke kommentere firstCard eiendomserklæring i Gamecontroller klasse:

var firstCard: Kort?

Husk at logikken i spillet innebærer å avsløre to kort, den ene etter den andre. Denne variabelen holder oversikt over om et kortspill utført av spilleren er den første av de to eller ikke.

Legg til følgende metode i bunnen av Gamecontroller klasse, før den avsluttende krøllebøylen:

func handleTap (gr: UITapGestureRecognizer) la v = view.hitTest (gr.locationInView (visning), medEvent: null)! hvis la kortet = v som? Kort UIView.transitionWithView (kort, varighet: 0.5, alternativer: .TransitionFlipFromLeft, animasjoner: card.image = UIImage (oppkalt: String (card.tag))) // // ferdigstillingshåndterer: _ i card.userInteractionEnabled = falsk hvis la pCard = self.firstCard hvis pCard.tag == card.tag UIView.animateWithDuration (0.5, animasjoner: card.alpha = 0.0, ferdigstillelse: _ i card.removeFromSuperview ()) UIView.animateWithDuration (0.5, animasjoner: pCard.alpha = 0.0, ferdigstillelse: _ i pCard.removeFromSuperview ()) ellers UIView.transitionWithView (kort, varighet: 0,5, alternativer: .TransitionFlipFromLeft, animasjoner: card.image = self.backImage) _ i card.userInteractionEnabled = true UIView.transitionWithView (pCard, varighet: 0.5, alternativer: .TransitionFlipFromLeft, animasjoner: pCard.image = self.backImage) _ i pCard.userInteractionEnabled = true  self.firstCard = nil else self.firstCard = card

Det er en langvarig metode. Det er fordi det pakker all nødvendig berøring, spilllogikk og tilhørende animasjoner i en metode. La oss se hvordan denne metoden gjør sitt arbeid:

  • Først er det en sjekk for å sikre at brukeren faktisk rørte en Kort forekomst. Dette er det samme som? konstruere som vi brukte tidligere.
  • Hvis brukeren rørte ved a Kort For eksempel, vi slår den over med en animasjon som ligner den vi implementerte tidligere. Det eneste nye aspektet er at vi bruker ferdigstillingsbehandleren, som kjører etter at animasjonen er fullført, for midlertidig å deaktivere berøringsinteraksjoner for det aktuelle kortet ved å sette inn userInteractionEnabled eiendom av kortet. Dette forhindrer spilleren i å bla over samme kort. Legg merke til _ inn konstruksjon som brukes flere ganger i denne metoden. Dette er bare å si at vi vil ignorere bool parameter som ferdigstillingsbehandleren tar.
  • Vi utfører kode basert på om firstCard har blitt tildelt en ikke-null verdi ved hjelp av valgfri binding, Swift er kjent hvis la konstruere.
  • Hvis firstCard er ikke-null, så var dette det andre kortet av sekvensen som spilleren vendte om. Vi må nå sammenligne ansiktet til dette kortet med den forrige (ved å sammenligne stikkord verdier) for å se om vi fikk en kamp eller ikke. Hvis vi gjorde det, animerer vi kortene som falmer ut (ved å sette deres alfa til 0). Vi fjerner også disse kortene fra visningen. Hvis merkene ikke er like, noe som betyr at kortene ikke stemmer overens, slipper vi dem tilbake og vender ned og sett userInteractionEnabled til ekte slik at brukeren kan velge dem igjen.
  • Basert på dagens verdi av firstCard, vi satte det til heller nil eller til nåværende kort. Slik skifter du kodeens oppførsel mellom to påfølgende berøringer.

Til slutt, kommenterer de følgende to uttalelsene i Gamecontrollers initialiserer som legger til en trykkbevegelsesgenkjenning til visningen. Når trykkbevegelsesgenkjenningen oppdager et trykk, trykker du på handleTap () Metoden er påkalt:

la tap = UITapGestureRecognizer (mål: selv, handling: #selector (GameController.handleTap (_ :))) view.addGestureRecognizer (trykk)

Gå tilbake til lekeplassens tidslinje og spill minnespillet. Du er velkommen til å redusere den store padding vi tildelte litt tidligere.

Koden i handleTap (_ :) er ganske mye den uemballerte versjonen av det jeg skrev første gang. Man kan hevde innvendingen om at det som en enkelt metode gjør det for mye. Eller at koden ikke er objektorientert nok, og at kortets flippende logikk og animasjoner skal være nøyaktig abstraheret i metoder for Kort klasse. Selv om disse innvendingene ikke er ugyldige i seg selv, husk at hurtig prototyping er fokuset på denne opplæringen, og siden vi ikke hadde forutsett behov for å samhandle med denne delen av koden på lekeplassen, kunne vi ha råd til å være litt mer "hack-ish".

Når vi har noe som fungerer og vi bestemmer oss for å forfølge ideen ytterligere, ville vi absolutt måtte vurdere å kode refaktoring. Med andre ord, gjør det først, gjør det raskt / elegant / pent / ...

8. Trykk på Håndtering på lekeplassen

Mens hoveddelen av opplæringen er over, som et interessant til side, vil jeg vise deg hvordan vi kan skrive berøringshåndteringskode direkte på lekeplassen. Vi legger først til en metode til Gamecontroller klassen som gjør at vi kan kikke på kortets ansikter. Legg til følgende kode i Gamecontroller klasse, umiddelbart etter handleTap (_ :) metode:

public func quickPeek () for v i view.subviews hvis la kortet = v som? Kort card.userInteractionEnabled = false UIView.transitionWithView (kort, varighet: 1.0, alternativer: .TransitionFlipFromLeft, animasjoner: card.image = UIImage (kalt: String (card.tag))) _ i UIView.transitionWithView , varighet: 1.0, alternativer: .TransitionFlipFromLeft, animasjoner: card.image = self.backImage) _ i card.userInteractionEnabled = true

Anta at vi vil ha muligheten til å aktivere eller deaktivere denne "raske titt" -funksjonen fra lekeplassen. En måte å gjøre dette på ville være å skape et publikum bool eiendom i Gamecontroller klasse som vi kunne sette på lekeplassen. Og selvfølgelig måtte vi skrive en gestbehandler i Gamecontroller klasse, aktivert av en annen gest, som ville påberope seg quickPeek ().

En annen måte ville være å skrive bevegelseskoden direkte på lekeplassen. En fordel ved å gjøre det på denne måten er at vi kunne innlemme noen egendefinert kode i tillegg til å ringe quickPeek (). Dette er hva vi skal gjøre neste gang. Legg til følgende kode på bunnen av lekeplassen:

klassen LPGR static var counter = 0 @objc static func longPressed (lp: UILongPressGestureRecognizer) hvis lp.state == .Began gc.quickPeek () counter + = 1 print ("Du kikket \ . la longPress = UILongPressGestureRecognizer (mål: LPGR.self, action: #selector (LPGR.longPressed)) longPress.minimumPressDuration = 2.0 gc.view.addGestureRecognizer (longPress)

For å aktivere Quick Look-funksjonen, bruker vi en lang trykkbevegelse, det vil si at spilleren holder fingeren på skjermen i en viss tid. Vi bruker to sekunder som terskelen.

For å håndtere gesten oppretter vi en klasse, LPGR (lang trykkbevegelsesgjenkjenning forkortet), med a statisk variabel eiendom, disk, å holde oversikt over hvor mange ganger vi kikket, og a statisk metode longPressed (_ :) å håndtere bevegelsen.

Ved å bruke statisk kvalifiserer, kan vi unngå å måtte opprette en LPGR eksempel fordi enhetene som er deklarert statisk er knyttet til LPGR type (klasse) i stedet for med en bestemt forekomst.

Bortsett fra det er det ingen særlig fordel for denne tilnærmingen. Av kompliserte grunner må vi markere metoden som @objc å holde kompilatoren glad. Legg merke til bruken av LPGR.self å referere til typen av objektet. Vær også oppmerksom på at i håndbehandleren, kontrollerer vi om stat av bevegelsen er .begynte. Dette skyldes at den lange trykkbevegelsen er kontinuerlig, det vil si at håndtereren ville utføre gjentatte ganger så lenge brukeren holdt fingeren på skjermen. Siden vi bare vil at koden skal utføres en gang per fingerpress, gjør vi det når gesten først blir anerkjent.

Den økende telleren er den egendefinerte koden som vi introduserte, som ikke stole på funksjonalitet fra Gamecontroller klasse. Du kan se utgangen av skrive ut(_:) funksjon (etter å ha sett et par ganger) i konsollen nederst.

Konklusjon

Forhåpentligvis har denne opplæringen vist et interessant eksempel på rask, interaktiv prototyping i Xcode lekeplasser. Bortsett fra grunnene til å bruke lekeplasser som jeg nevnte tidligere, kan du tenke på andre scenarier der de kunne være nyttige. For eksempel:

  • demonstrerer prototypefunksjonalitet til klienter og lar dem velge alternativer og foreta tilpasninger med live tilbakemelding og uten å måtte grave inn i krevende detaljer av koden.
  • utvikle simuleringer, som for fysikk, hvor elevene kan spille med noen parameterverdier og observere hvordan simuleringen påvirkes. Faktisk har Apple gitt ut en imponerende lekeplass som viser deres interaktivitet og inkorporering av fysikk via UIDynamics API. Jeg oppfordrer deg til å sjekke det ut.

Når du bruker lekeplasser for demonstrasjons- / undervisningsmessige formål som disse, vil du sannsynligvis vil gjøre liberal bruk av markupfunksjonene til lekeplasser for rik tekst og navigasjon.

Xcode-teamet virker forpliktet til å forbedre lekeplasser ettersom nye versjoner av IDE er rullet ut. Den nye nyheten er at Xcode 8, for tiden i beta, vil ha lekeplasser for iPad. Men selvfølgelig er lekeplasser ikke ment å erstatte den fullt blåste Xcode IDE og behovet for å teste på faktiske enheter når man utvikler en komplett, funksjonell app. Til slutt er de bare et verktøy som skal brukes når det er fornuftig, men en veldig nyttig.