Snelle, interactieve prototypen met Xcode Playgrounds

Wat je gaat creëren

Invoering

Sinds hun introductie in Xcode 6 naast Swift, naar hun huidige iteratie in Xcode 7.3.1, hebben speelterreinen een lange weg afgelegd. Met nieuwe functies en betere stabiliteit evolueren ze naar een levensvatbaar hulpmiddel voor snelle prototyping of snel samen een proof of concept hacken.

Als ontwikkelaar heb je soms een vleugje inspiratie in de vorm van een interessant idee voor een app en wil je snel een prototype coderen dat de essentie van je idee vertegenwoordigt. Of je wilt gewoon verifiëren hoe je begrijpt hoe een stuk van de UIKit-code zich zal gedragen. Als je net als ik bent, vermijd je liever de rompslomp en mentale overhead van het maken van een Xcode-project en te maken krijgen met een groot aantal factoren, zoals apparaattypen en resoluties, en bouwinstellingen. Deze beslissingen kunnen worden uitgesteld tot nadat je hebt besloten dat het kernidee de moeite waard is om na te streven.

In deze tutorial maken we een op kaarten gebaseerd geheugenspel, allemaal binnen de grenzen van een speeltuin. Het is een bekende, bekende game, dus geen eer voor originaliteit daar. Het spel bestaat uit acht paar identieke kaarten (dus in totaal 16 kaarten) die met de afbeelding naar beneden in een 4x4-raster worden geplaatst.

De speler moet twee kaarten omdraaien waarvan de gezichten kort worden onthuld en vervolgens snel weer worden omgedraaid. Het doel van het spel is dat de speler probeert de posities van de kaarten te onthouden en identieke paren te onthullen, die vervolgens uit het spel worden verwijderd. Het spel is afgelopen als het raster is gewist.

De game is gebaseerd op aanraking en bevat eenvoudige weergave-animaties. U leert hoe u wijzigingen in uw app kunt aanbrengen en het resultaat van uw wijzigingen live kunt bekijken.

1. Aan de slag

Start Xcode op en selecteer Nieuw> Speeltuin ... van Xcode's het dossier menu. Geef de speelplaats een naam, zoals MemoryGameXCPTut, reeks Platform naar iOS, en red de speeltuin. Ik gebruik Xcode 7.3.1 voor deze zelfstudie.

Je weg vinden rond de speelplaats

Laten we ons enige tijd verdiepen in de interface met de speeltuin. Voel je vrij om dit gedeelte te bekommen als je al bekend bent met speeltuinen.

Een speeltuin kan meerdere pagina's bevatten, elk gekoppeld aan zijn eigen liveweergave en zijn eigen bronnen / bronnenmappen. In deze zelfstudie gebruiken we geen meerdere pagina's. Speelplekken ondersteunen opmaakopmaak waarmee u rich-text aan een speelplaats kunt toevoegen en een koppeling kunt maken tussen speelveldpagina's.

Het eerste dat je ziet na het maken van een speeltuin, is de editor voor de bron van de speeltuin. Hier schrijf je code, wat een onmiddellijk effect heeft op de liveweergave. Een van de manieren om de (dis) verschijning van de om te schakelen Project Navigator gebruikt de snelkoppeling Command-0. In de Project Navigator, je kunt twee mappen zien, bronnen en Middelen.

bronnen

In de bronnen map, kunt u hulpcode toevoegen in een of meer Swift-bestanden, zoals aangepaste klassen, controllers bekijken en weergaven. Hoewel het grootste deel van de code die de logica van je prototype bepaalt daar naartoe gaat, is het een hulpmiddel in die zin dat het zich op de achtergrond bevindt wanneer je je app live bekijkt.

Het voordeel van het plaatsen van de hulpcode in de bronnen map is dat deze automatisch wordt samengesteld telkens wanneer u het bestand wijzigt en opslaat. Op deze manier krijg je snellere feedback in de liveweergave van wijzigingen die in de speeltuin zijn aangebracht. Terug in de speeltuin heb je toegang openbaar eigenschappen en methoden die u blootstelt in de hulpcode die van invloed is op hoe uw app zich gedraagt.

Middelen

U kunt externe bronnen, zoals afbeeldingen, toevoegen in de Middelen map.

In deze zelfstudie moet u vaak tussen een Swift-bestand springen dat we in de bronnen map en het speeltuinbestand (technisch gezien ook een Swift-bestand, behalve dat u het niet naar de bestandsnaam verwijst). We maken ook gebruik van de Assistent redacteur in de tutorial, laat het de Tijdlijn, om de live uitvoer naast de speelplaatscode te bekijken. Alle wijzigingen die u in de speeltuin aanbrengt, worden onmiddellijk (binnen een paar seconden) in de live uitvoer weergegeven. Je kunt ook aanraken, communiceren met de liveweergave en de elementen van de gebruikersinterface. Om er zeker van te zijn dat u dit allemaal kunt doen, bekijkt u snel de onderstaande figuur.

Dit komt overeen met de groene cijfers die ik aan de figuur heb toegevoegd:

  1. Deze knop verbergt de Assistent redacteur zodat alleen de hoofdeditor zichtbaar is.
  2. Deze knop onthult de Assistent redacteur. De Assistent redacteur is zichtbaar aan de rechterkant van de hoofdeditor. Deze editor kan helpen door ons relevante bestanden te laten zien, zoals de tegenhanger van het bestand in de hoofdeditor.
  3. Van links naar rechts worden deze twee knoppen respectievelijk gebruikt om het uiterlijk van de om te schakelen Project Navigator en de debug-console. In de console kunnen we de uitvoer van printopdrachten onder andere inspecteren.
  4. De springbalk bovenaan de hoofdeditor kan ook worden gebruikt om naar een bepaald bestand te navigeren. Door tweemaal op de projectnaam te klikken, keert u terug naar de speelplaats. Als alternatief kunt u ook de Project Navigator.

Soms moet je er bij het bekijken van de speeltuin voor zorgen dat het Assistent redacteur geeft de Tijdlijn in plaats van een ander bestand. De onderstaande afbeelding laat zien hoe dit moet. In de Assistent redacteur, kiezen Tijdlijn, de tegenhanger van de speeltuin, in plaats van Met de hand, waarmee je elk bestand in de Assistent redacteur.

Wanneer u een bronbestand bewerkt vanuit de bronnen map, als zijn tegenhanger, de Assistent redacteur toont de interface van uw code, dat wil zeggen, verklaringen en functie-prototypen zonder hun implementaties. Ik versta liever de Assistent redacteur als ik aan een bestand aan het werken ben in de bronnen map en stel alleen de Assistent redacteur in de speeltuin om het livebeeld te zien.

Om toegang te krijgen tot de speciale vaardigheden van speelplaatsen, moet u de XCPlayground-module importeren.

importeer XCPlayground

U stelt de rechtstreekse beelden eigendom van de huidige pagina van de XCPlaygroundPage bezwaar maken tegen een object dat voldoet aan de  XCPlaygroundLiveViewable protocol. Dit kan een aangepaste klasse zijn of het kan een UIView of UIViewController aanleg.

Bestanden toevoegen aan de map Bronnen / Bronnen

Ik heb een paar afbeeldingen toegevoegd waarmee we kunnen werken in deze tutorial. Download de afbeeldingen, pak het archief uit en voeg de afbeeldingen toe in de Afbeeldingen map naar de Middelen map van de speeltuin in de Project Navigator.

Zorg ervoor dat u alleen de afbeeldingen sleept, zodat elk afbeeldingsbestand zich in de afbeelding bevindt Middelen map, niet in Resources / Images.

Verwijder de code in de speeltuin. Klik met de rechtermuisknop op de bronnen map en selecteer Nieuw bestand van het menu. Stel de naam van het bestand in Game.swift.

2. Writing Helper Classes & Methods

Voeg de volgende code toe aan Game.swift. Zorg ervoor dat u het bestand na elke codetoevoeging opslaat.

import UIKit import XCPlayground import GameplayKit // (1) openbare extensie UIImage // (2) public convenience init? (kleur: UIColor, maat: CGSize = CGSize (width: 1, height: 1)) let rect = CGRect ( origin: .zero, size: size) UIGraphicsBeginImageContextWithOptions (rect.size, false, 0.0) color.setFill () UIRectFill (rect) laat image = UIGraphicsGetImageFromCurrentImageContext () UIGraphicsEndImageContext () guard laat cgImage = image.CGImage else return nil self .init (CGImage: cgImage) let cardWidth = CGFloat (120) // (3) let cardHeight = CGFloat (141) public class Kaart: UIImageView // (4) openbare let x: Int public let y: Int public init (image: UIImage ?, x: Int, y: Int) self.x = x self.y = y super.init (afbeelding: afbeelding) self.backgroundColor = .grayColor () self.layer.cornerRadius = 10.0 self .userInteractionEnabled = true vereist public init? (coder aDecoder: NSCoder) fatalError ("init (codeerder :) is niet geïmplementeerd")

Ik heb een paar genummerde opmerkingen toegevoegd om enkele gedeelten van de implementatie toe te lichten:

  1. In aanvulling op UIKit en XCPlayground, we importeren ook GamePlayKit. Dit raamwerk bevat een handige methode waarmee we een methode kunnen implementeren om willekeurig een array te shufflen.
  2. Deze extensie is ingeschakeld UIImage staat ons toe, met de hulp van UIKit methoden om afbeeldingen te maken met een effen kleur van elke gewenste grootte. We zullen dit gebruiken om de eerste achtergrondafbeelding van de speelkaarten in te stellen.
  3. De cardHeight en cardWidth constanten vertegenwoordigen de kaartafbeeldingsformaten op basis waarvan we andere grootten zullen berekenen.
  4. De Kaart klasse, overerfd van UIImageView, staat voor een kaart. Hoewel we een paar eigenschappen in de Kaart klasse, het hoofddoel van het maken van deze klasse is om ons te helpen bij het identificeren en herhalen van subvisies die overeenkomen met het spelen van kaarten in het spel. De kaarten hebben ook eigenschappen X en Y om hun positie in het raster te onthouden.

3. Bekijk Controller

Voeg de volgende code toe aan Game.swift, onmiddellijk na de vorige code:

public class GameController: UIViewController // (1): openbare variabelen zodat we ze kunnen manipuleren in de speeltuin public var padding = CGFloat (20) / * didSet resetGrid () * / public var backImage: UIImage = UIImage ( color: .redColor (), size: CGSize (width: cardWidth, height: cardHeight))! // (2): berekende eigenschappen var viewWidth: CGFloat krijg return 4 * cardWidth + 5 * padding var viewHeight: CGFloat krijg return 4 * cardHeight + 5 * padding var shuffledNumbers = [Int] () // slaat geschud kaartnummer op // var firstCard: Card? // uncomment later public init () super.init (nibName: nihil, bundel: nihil) preferredContentSize = CGSize (width: viewWidth, height: viewHeight) shuffle () setupGrid () // uncomment later: // let op = UITapGestureRecognizer (doel: zelf, actie: #selector (GameController.handleTap (_ :))) // view.addGestureRecognizer (tik) vereist public init? (coder aDecoder: NSCoder) fatalError ("init (coder :) is niet geweest installed ") public override func loadView () view = UIView () view.backgroundColor = .blueColor () view.frame = CGRect (x: 0, y: 0, width: viewWidth, height: viewHeight) // ( 3): De GameplayKit API gebruiken om een ​​herverdeling van de array te genereren [1, 1, 2, 2, ..., 8, 8] func shuffle () let numbers = (1 ... 8) .flatMap [$ 0, $ 0] shuffledNumbers = GKRandomSource.sharedRandom (). arrayByShufflingObjectsInArray (numbers) as! [Int] // (4): Converteren van kaartpositie op raster naar index in de geschud kaartnummers 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)    
  1. De twee eigenschappen, vulling en backImage, worden verklaard openbaar zodat we ze later in de speeltuin kunnen gebruiken. Ze vertegenwoordigen de lege ruimte rond de kaarten op het raster en het beeld dat respectievelijk op de achterkant van elke kaart wordt weergegeven. Merk op dat beide eigenschappen beginwaarden hebben gekregen, wat neerkomt op een opvulling van 20 en een effen rode kleur voor de niet-gezichtzijde afbeelding van de kaart. U kunt de code met commentaar nu negeren.
  2. We berekenen de gewenste breedte en hoogte van de aanzichten door middel van berekende eigenschappen. Om het te begrijpen viewWidth berekening, onthoud dat er vier kaarten in elke rij staan ​​en dat we ook rekening moeten houden met de opvulling van elke kaart. Hetzelfde idee is van toepassing op de viewHeight berekening.
  3. De code (1 ... 8) .flatMap [$ 0, $ 0] is een beknopte manier om de array te produceren [1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8]. Als u niet bekend bent met functioneel programmeren, kunt u ook een voor-lus om de array te genereren. Methoden van de GamePlayKit raamwerk, schudden we de getallen in de array. De nummers komen overeen met de acht paar kaarten. Elk nummer staat voor de kaartafbeelding met dezelfde naam (bijvoorbeeld een waarde van 1 in shuffledArray komt overeen met 1.png).
  4. We schreven een methode die de locatie van een kaart op het 4x4-raster in kaart brengt op de locatie in de shuffledNumbers reeks van lengte 16. De factor 4 in de rekenkundige berekening geeft het feit weer dat we vier kaarten per rij hebben.
  5. We hebben ook een methode die de positie van een kaart bepaalt (zijn centrum eigenschap) in het raster op basis van kaartafmetingen en opvulling.
  6. De setupGrid () methode wordt aangeroepen tijdens de initialisatie van de view controller. Het legt de 4x4 uit Kaart raster. Het wijst ook de identiteit van elke kaart toe op basis van de shuffledNumbers array en slaat het op in de label eigenschap geërfd van de basisklasse van de kaart, UIView. In de spellogica vergelijken we het label waarden om uit te zoeken of twee kaarten overeenkomen of niet. Dit nogal rudimentaire modelschema dient goed genoeg voor onze huidige behoeften.
  7. Dit momenteel ongebruikte codefragment helpt ons de kaarten te verplaatsen in het geval dat de opvulling verandert. Vergeet niet dat we de vulling eigendom als een openbaar bezit, zodat we er toegang toe hebben in de speeltuin.
  8. De code in viewDidAppear (_ :) wordt uitgevoerd onmiddellijk nadat het aanzicht van de weergavecontroller zichtbaar wordt. We herhalen de subviews van de view en, als de subview een instantie is van de Kaart klasse, (gecontroleerd via de zoals? fcasting downcasting-operator) het lichaam van de als-statement definieert de overgang om te presteren. Dit is waar we de afbeelding op de kaarten zullen veranderen, van de cartoonafbeelding die het gezicht van elke kaart naar de (gewone) afbeelding definieert backImage van alle kaarten. Deze overgang wordt vergezeld door een flip-over-animatie van links naar rechts, waardoor de kaarten fysiek worden omgedraaid. Als u niet bekend bent met hoe UIView animaties werken, dit ziet er misschien een beetje vreemd uit. Hoewel we de animatie van elke kaart sequentieel in een lus hebben toegevoegd, worden de animaties samengevoegd in een enkele animatatransactie en tegelijkertijd uitgevoerd, dat wil zeggen, de kaarten worden samengevouwen.

Bezoek opnieuw de speeltuin en vervang alle tekst in de editor met het volgende:

importeren XCPlayground importeren UIKit laten gc = GameController () XCPlaygroundPage.currentPage.liveView = gc

Zorg ervoor dat de tijdlijn zichtbaar is. De weergave van de view controller zou tot leven moeten komen en ons een 4x4 raster van kaarten met schattige cartoons moeten tonen die omdraaien om ons de achterkant van de kaarten te laten zien. Op dit moment kunnen we niet veel met deze visie doen, omdat we nog geen interactie hebben geprogrammeerd. Maar het is absoluut een begin.

4. Variabelen wijzigen in de speeltuin

Laten we nu de achterzijden van de kaarten veranderen van effen rood in een afbeelding, specifiek b.png in de Middelen map. Voeg de volgende regel toe aan de onderkant van de speelplaats.

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

Na een seconde of twee ziet u dat de achterkant van de kaarten is veranderd van gewoon rood in een cartoonhand.

Laten we nu proberen het vulling property, waaraan we een standaardwaarde van 20 inch hebben toegewezen Game.swift. De ruimte tussen de kaarten zou daardoor moeten toenemen. Voeg de volgende regel toe aan de onderkant van de speelplaats:

gc.padding = 75

Wacht tot de liveweergave is vernieuwd en zie dat ... er is niets veranderd.

5. Een korte omweg

Om te begrijpen wat er aan de hand is, moet u in gedachten houden dat entiteiten, zoals view controllers en de bijbehorende views, een complexe levenscyclus hebben. We gaan ons richten op de laatste, dat wil zeggen op meningen. Het maken en bijwerken van de weergave van een weergavecontroller is een proces in meerdere fasen. Op specifieke punten in de levenscyclus van de weergave worden meldingen verzonden naar de UIViewController, informeren over wat er gaande is. Wat nog belangrijker is, de programmeur kan inhaken op deze meldingen door code in te voegen om dit proces te sturen en aan te passen.

De loadView () en viewDidAppear (_ :) methoden zijn twee methoden die we gebruikten om in de levenscyclus van de weergave te komen. Dit onderwerp is enigszins betrokken en buiten het kader van deze discussie, maar wat belangrijk voor ons is dat de code in de speeltuin, na de toewijzing van de view controller als de speeltuin rechtstreekse beelden, wordt enige tijd uitgevoerd tussen de aanroep naar viewWillAppear (_ :) en de oproep aan viewDidAppear (_ :). U kunt dit verifiëren door een eigenschap in de speeltuin aan te passen en printinstructies toe te voegen aan deze twee methoden om de waarde van deze eigenschap weer te geven.

Het probleem met de waarde van vulling niet het verwachte visuele effect hebben, is dat tegen die tijd het zicht en de subvisies al zijn vastgelegd. Houd er rekening mee dat wanneer u de code wijzigt, de speelplaats vanaf het begin opnieuw wordt afgespeeld. In die zin is deze kwestie niet specifiek voor speelplaatsen. Zelfs als u code zou ontwikkelen om op de simulator of op een fysiek apparaat te gebruiken, moet u vaak extra code schrijven om ervoor te zorgen dat de wijziging in de waarde van een eigenschap het gewenste effect heeft op het uiterlijk of de inhoud van de weergave..

Je zou je kunnen afvragen waarom we de waarde van de backImage eigendom en zie het resultaat zonder iets speciaals te doen. Merk op dat de backImage onroerend goed wordt feitelijk voor het eerst gebruikt in viewDidAppear (_ :), tegen die tijd heeft het zijn nieuwe waarde al opgepikt.

6. Waarnemingen observeren en actie ondernemen

Onze manier om met deze situatie om te gaan, is het volgen van veranderingen in de waarde van vulling en formaat / verplaats de weergave en subweergaven. Gelukkig is dit eenvoudig te doen met Swift handig eigendom observeren voorzien zijn van. Begin met het declareren van de code voor de resetGrid () methode in Game.swift:

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

Met deze methode wordt de positie van het frame van het aanzicht en dat van elk opnieuw berekend Kaart object op basis van de nieuwe waarden van viewWidth en viewHeight. Bedenk dat deze eigenschappen worden berekend op basis van de waarde van vulling, die zojuist is gewijzigd.

Wijzig ook de code voor vulling om de te gebruiken didSet waarnemer wiens lichaam, zoals de naam aangeeft, wordt uitgevoerd wanneer we de waarde van instellen vulling:

// (1): openbare variabelen zodat we ze kunnen manipuleren in de speeltuin public var padding = CGFloat (20) didSet resetGrid ()

De resetGrid () methode wordt geactiveerd en de weergave wordt vernieuwd om de nieuwe spatiëring weer te geven. Je kunt dit in de speeltuin verifiëren.

Het lijkt erop dat we dingen vrij gemakkelijk konden repareren. In werkelijkheid, toen ik voor het eerst besloot dat ik wilde kunnen communiceren met de vulling eigendom, moest ik teruggaan en de code in Game.swift. Ik moest bijvoorbeeld het Kaart centrumberekening in een afzonderlijke functie (centerOfCardAt (_: _ :)) om de posities van de kaarten netjes en onafhankelijk te (her) berekenen wanneer ze moeten worden aangelegd.

Het maken van berekende eigenschappen voor viewWidth en viewHeight ook geholpen. Hoewel dit soort herschrijven iets is waar je op moet worden voorbereid als een afweging van het niet doen van veel upfront-ontwerp, kan het worden gereduceerd met wat voorbedachtheid en ervaring.

7. Game Logic & Touch Interaction

Het is nu tijd om de logica van het spel te implementeren en onszelf in staat te stellen ermee te communiceren via aanraking. Begin met het opheffen van de firstCard eigendomsverklaring in de gamecontroller klasse:

var firstCard: Card?

Bedenk dat de logica van het spel het onthullen van twee kaarten betreft, de een na de ander. Deze variabele houdt bij of een kaartflits die door de speler wordt uitgevoerd de eerste van de twee is of niet.

Voeg de volgende methode toe aan de onderkant van de gamecontroller klasse, voor de beëindigende accolade:

func handleTap (gr: UITapGestureRecognizer) let v = view.hitTest (gr.locationInView (view), withEvent: nil)! indien kaart = v zoals? Kaart UIView.transitionWithView (kaart, duur: 0,5, opties: .TransitionFlipFromLeft, animaties: card.image = UIImage (named: String (card.tag))) // trailing completion handler: _ in card.userInteractionEnabled = false als 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, animaties: pCard.alpha = 0.0, voltooiing: _ in pCard.removeFromSuperview ()) else UIView.transitionWithView (kaart, duur: 0.5, opties: .TransitionFlipFromLeft, animaties: card.image = self.backImage) _ in card.userInteractionEnabled = true UIView.transitionWithView (pCard, duur: 0,5, opties: .TransitionFlipFromLeft, animaties: pCard.image = self.backImage) _ in pCard.userInteractionEnabled = true  self.firstCard = nil else self.firstCard = kaart

Dat is een lange methode. Dat komt omdat het alle vereiste touch-bediening, spellogica en bijbehorende animaties in één methode verzamelt. Laten we eens kijken hoe deze methode zijn werk doet:

  • Eerst wordt er gecontroleerd of de gebruiker daadwerkelijk een a heeft aangeraakt Kaart aanleg. Dit is hetzelfde zoals? constructie die we eerder hebben gebruikt.
  • Als de gebruiker a heeft aangeraakt Kaart we draaien het bijvoorbeeld om met een animatie die lijkt op de animatie die we eerder hebben geïmplementeerd. Het enige nieuwe aspect is dat we de voltooiingshandler gebruiken, die wordt uitgevoerd nadat de animatie is voltooid, om touch-interacties voor die specifieke kaart tijdelijk uit te schakelen door de userInteractionEnabled eigendom van de kaart. Dit voorkomt dat de speler over dezelfde kaart flipt. Merk op _ in construct dat meerdere keren wordt gebruikt in deze methode. Dit wil alleen maar zeggen dat we het willen negeren Bool parameter die de afhandelingsafhandeling neemt.
  • We voeren code uit op basis van of de firstCard heeft een niet-nulwaarde toegekend met optionele binding, de bekendheid van Swift indien toegestaan construeren.
  • Als firstCard is niet nul, toen was dit de tweede kaart van de reeks die de speler omsloeg. We moeten nu het gezicht van deze kaart vergelijken met de vorige (door de vergelijking van de label waarden) om te zien of we een match hebben gekregen of niet. Als we dat deden, animeren we de kaarten die vervagen (door hun alpha tot 0). We verwijderen deze kaarten ook uit het zicht. Als de tags niet gelijk zijn, wat betekent dat de kaarten niet overeenkomen, dan zetten we ze gewoon naar beneden en naar beneden userInteractionEnabled naar waar zodat de gebruiker ze opnieuw kan selecteren.
  • Gebaseerd op de huidige waarde van firstCard, we hebben het op beide gezet nul of naar de huidige kaart. Dit is hoe we het gedrag van de code tussen twee opeenvolgende aanrakingen schakelen.

Tenslotte, uncomment de volgende twee verklaringen in de gamecontroller's initializer die een tikgebaarherkenning toevoegt aan de weergave. Wanneer de tikgebaarherkenner een tik herkent, de handleTap () methode wordt aangeroepen:

laat tikken = UITapGestureRecognizer (doel: zelf, actie: #selector (GameController.handleTap (_ :))) view.addGestureRecognizer (tik)

Ga terug naar de tijdlijn van de speeltuin en speel het geheugenspel. Voel je vrij om het grote te verkleinen vulling we hebben iets eerder toegewezen.

De code in handleTap (_ :) is vrijwel de niet-verfilmde versie van wat ik de eerste keer schreef. Men zou het bezwaar kunnen aanvoeren dat het, als een enkele methode, te veel doet. Of dat de code niet voldoende objectgeoriënteerd is en dat de kaartomschakellogica en -animaties netjes moeten worden geabstraheerd tot methoden van de Kaart klasse. Hoewel deze bezwaren niet ongeldig zijn per se, onthoud dat snelle prototyping de focus is van deze tutorial en omdat we niet hadden voorzien in de noodzaak om met dit deel van de code in de speeltuin te communiceren, konden we het ons veroorloven een beetje "hack-achtig" te zijn.

Zodra we iets hebben dat werkt en we besluiten dat we het idee verder willen nastreven, moeten we zeker aandacht schenken aan code refactoring. Met andere woorden, zorg eerst dat het werkt, maak het dan snel / elegant / mooi / ...

8. Touch Handling in de speeltuin

Hoewel het grootste deel van de zelfstudie nu voorbij is, als een interessante kanttekening, wil ik je laten zien hoe we de code voor de afhandeling van het aanraken rechtstreeks in de speeltuin kunnen schrijven. We zullen eerst een methode toevoegen aan de gamecontroller klasse waarmee we naar de gezichten van de kaarten kunnen kijken. Voeg de volgende code toe aan de gamecontroller klasse, direct na de handleTap (_ :) methode:

public func quickPeek () voor v in view.subviews if let card = v as? Kaart card.userInteractionEnabled = false UIView.transitionWithView (kaart, duur: 1.0, opties: .TransitionFlipFromLeft, animaties: card.image = UIImage (named: String (card.tag))) _ in UIView.transitionWithView (card , duur: 1.0, opties: .TransitionFlipFromLeft, animaties: card.image = self.backImage) _ in card.userInteractionEnabled = true

Stel dat we de mogelijkheid willen hebben om deze "quick peek" -functie vanuit de speeltuin te activeren of te deactiveren. Een manier om dit te doen zou zijn om een ​​publiek te creëren Bool eigendom in de gamecontroller klasse die we konden spelen in de speeltuin. En natuurlijk zouden we een handler voor gebaar moeten schrijven in de gamecontroller klasse, geactiveerd door een ander gebaar, dat zou aanroepen snelle blik().

Een andere manier is om de code voor het verwerken van gebaren direct in de speeltuin te schrijven. Een voordeel van het op deze manier doen, is dat we naast het bellen ook een aangepaste code kunnen opnemen snelle blik(). Dit is wat we hierna zullen doen. Voeg de volgende code toe aan de onderkant van de speelplaats:

class LPGR static var counter = 0 @objc static func longPressed (lp: UILongPressGestureRecognizer) if lp.state == .Began gc.quickPeek () counter + = 1 print ("Je hebt gekeken \ (teller) tijd (s) . ") laat longPress = UILongPressGestureRecognizer (doel: LPGR.self, actie: #selector (LPGR.longPressed)) longPress.minimumPressDuration = 2.0 gc.view.addGestureRecognizer (longPress)

Om de snelkiekfunctie te activeren, gebruiken we een lang drukgebaar, dat wil zeggen dat de speler een bepaalde tijd lang zijn vinger op het scherm houdt. We gebruiken twee seconden als drempel.

Voor het verwerken van de beweging maken we een klas, LPGR (druk lang op gebaarherkenning afgekort), met een statisch variabele eigenschap, teller, om bij te houden hoe vaak we hebben gekeken, en een statisch methode longPressed (_ :) om het gebaar te verwerken.

Door de statisch in aanmerking komen, kunnen we voorkomen dat we een moeten maken LPGR bijvoorbeeld omdat de statussen die statisch zijn verklaard, zijn gekoppeld aan de LPGR type (klasse) in plaats van met een bepaalde instantie.

Afgezien daarvan is er geen bijzonder voordeel aan deze benadering. Om gecompliceerde redenen moeten we de methode markeren als @objc om de compiler blij te houden. Let op het gebruik van LPGR.self om naar het type object te verwijzen. Merk ook op dat we in de handler voor het gebaren controleren of het staat van het gebaar is .begon. Dit komt omdat het lange-druk-gebaar continu is, dat wil zeggen dat de handler herhaaldelijk wordt uitgev