Laten we een RubyMotion-app schrijven deel 1

Wat je gaat creëren

RubyMotion is een raamwerk waarmee je iOS-applicaties in Ruby kunt bouwen. Het geeft je alle voordelen van de Ruby-taal, maar omdat je code is gecompileerd naar machine-code, krijg je alle onbewerkte prestaties van ontwikkelen in Objective-C. Met RubyMotion kun je de iOS SDK direct gebruiken, wat betekent dat je toegang hebt tot alle nieuwste functies van het platform. U kunt de Objective-C-code opnemen in uw project en RubyMotion werkt zelfs met CocoaPods.

In deze zelfstudie maakt u een geheel nieuwe tekenapplicatie. Ik zal u laten zien hoe u Interface Builder kunt integreren in uw workflow en hoe u uw applicatie goed kunt testen. Als je geen eerdere iOS- of Ruby-ervaring hebt, raad ik je aan hier eerst meer over te leren. De Tuts + Ruby voor nieuwkomers en het leren ontwikkelen van iOS SDK van Scratch-gidsen is een geweldige plek om te beginnen.

1. Project Setup

Voordat u kunt beginnen met coderen, moet u RubyMotion hebben geïnstalleerd en ingesteld. Raadpleeg het gedeelte Vereisten van de handleiding Aan de slag RubyMotion voor meer informatie over hoe u dit kunt doen.

Zodra je dat hebt gedaan, open je je terminal en maak je een nieuw RubyMotion-project door te draaien:

beweging maakt verf cd-verf

Dit creëert een verf map en verschillende bestanden:

  • .gitignore: Dit bestand vertelt Git welke bestanden moeten worden genegeerd. Omdat RubyMotion build-bestanden genereert wanneer het wordt uitgevoerd, is dit bestand nuttig om ervoor te zorgen dat uw gegenereerde build-bestanden geen broncontrole hebben.
  • Gemfile: Dit bestand bevat de afhankelijkheden van uw toepassing.
  • Rakefile: RubyMotion gebruikt Rake om uw applicatie te bouwen en uit te voeren. De Rakefile configureert uw applicatie en laadt de afhankelijkheden ervan. U kunt alle taken zien die beschikbaar zijn voor uw toepassing door ze uit te voeren hark -T vanaf de opdrachtregel.
  • app / app_delegate.rb: De toepassingsdeelnemer is het toegangspunt tot uw toepassing. Wanneer iOS klaar is met het laden van uw toepassing in het geheugen, wordt de gedelegeerde van de toepassing op de hoogte gebracht.

RubyMotion genereert ook een spec / main_spec.rb het dossier. Ik zal je laten zien hoe je je aanvraag iets later kunt testen in deze tutorial. Voor nu kun je dit bestand verwijderen door het uit te voeren rm spec / main_spec.rb vanaf de opdrachtregel.

De afhankelijkheden van uw toepassing installeren door ze uit te voeren bundel installeren gevolgd door bundel exec rake om uw toepassing te starten.

Woohoo! Een zwart scherm. Je zult het binnen een minuut interessanter maken.

2. Eerste wijziging

Hoewel het leuk is om een ​​draaiende app te hebben, is een zwart scherm een ​​beetje saai. Laten we een beetje kleur toevoegen.

Net als de native iOS SDK dwingt RubyMotion je niet om je bestanden op een specifieke manier in te delen. Het is echter handig om een ​​paar mappen in de map te maken app map om uw project georganiseerd te houden. Voer de volgende opdrachten uit vanaf de opdrachtregel om een ​​map te maken voor uw modellen, weergaven en controllers.

mkdir app / models mkdir app / views mkdir app / controllers

Neem vervolgens een kijkje in de app / app_delegate.rb het dossier:

class AppDelegate def application (application, didFinishLaunchingWithOptions: launchOptions) true end end

Als u bekend bent met de ontwikkeling van iOS, merkt u dat deze methode behoort tot de UIApplicationDelegate protocol, dat verschillende haken biedt in de levenscyclus van de toepassing. Merk op dat de AppDelegate class verklaart niet dat het de UIApplicationDelegate protocol. Ruby vertrouwt op eendtypen omdat het geen protocollen ondersteunt. Dit betekent dat het niet uitmaakt of jouw klas zegt dat het een protocol implementeert, het maakt alleen maar uit of het de juiste methoden implementeert.

De definitie van de toepassing: didFinishLaunchingWithOptions: methode binnen de AppDelegate klas ziet er misschien een beetje vreemd uit. In Objective-C zou deze methode als volgt zijn geschreven:

- (BOOL) applicatie: (UIApplication *) applicatie didFinishLaunchingWithOptions: (NSDictionary *) launchOptions;

Omdat objectieve-C-methode-namen kunnen worden opgesplitst in verschillende delen, implementeert Ruby ze op een unieke manier. Het eerste deel van toepassing: didFinishLaunchingWithOptions: is wat de methode naam in MRI zou zijn. De rest van de methodehandtekening is geschreven als trefwoordargumenten. In RubyMotion, toepassing: didFinishLaunchingWithOptions: is zo geschreven:

def applicatie (application, didFinishLaunchingWithOptions: launchOptions) end 

Laten we deze methode implementeren.

class AppDelegate def applicatie (application, didFinishLaunchingWithOptions: launchOptions) @window = UIWindow.alloc.initWithFrame (UIScreen.mainScreen.bounds) @ ​​window.makeKeyAndVisible @ window.rootViewController = UIViewController.alloc.initWithNibName (nihil, bundel: nihil) true end end

De eerste twee regels van de toepassing: didFinishLaunchingWithOptions: methode maak een nieuw vensterobject en maakt dit het belangrijkste venster van de toepassing. Waarom is @venster een instantievariabele? RubyMotion zal het venster slordig verzamelen, tenzij we het opslaan. De laatste regel van de methode stelt de root view-controller van het venster in op een nieuwe, lege view-controller.

Start de applicatie om alles nog steeds te laten werken.

Hmm. De toepassing wordt uitgevoerd, maar het scherm is nog steeds zwart. Hoe weet je dat je code werkt? U kunt een snelle sanity-check uitvoeren door het volgende aan de onderkant toe te voegen toepassing: didFinishLaunchingWithOptions:, voor waar. Zorg ervoor dat u dit verwijdert voordat u verdergaat.

@ window.rootViewController.view.backgroundColor = UIColor.yellowColor

3. Testen

Geen enkele toepassing is compleet zonder een solide reeks tests. Met testen kunt u erop vertrouwen dat uw code werkt en kunt u wijzigingen aanbrengen zonder dat u zich zorgen hoeft te maken over het verbreken van bestaande code.

RubyMotion wordt geleverd met een poort van de Bacon-testbibliotheek. Als je bekend bent met Rspec, voelt Bacon heel vertrouwd aan.

Om te beginnen, spiegelt u het app mappenstructuur in de spec map door de volgende opdrachten uit te voeren vanaf de opdrachtregel.

mkdir spec / models mkdir spec / views mkdir spec / controllers

Maak vervolgens de AppDelegatespecificatiebestand op spec / app_delegate_spec.rb. Volgens afspraak zijn bronbestanden mirrors in de spec directory en hebben _spec toegevoegd aan het einde van hun bestandsnaam.

Begin deze klasse door a te definiëren beschrijven blok dat de lezer vertelt wat uw bestand aan het testen is.

beschrijf AppDelegate do end 

Voeg vervolgens een seconde toe beschrijven blok binnen de eerste om te laten zien dat je de wilt testen toepassing: didFinishLaunchingWithOptions: methode.

beschrijven AppDelegate do beschrijven "#application: didFinishLaunchingWithOptions:" do end end

Heb je de # aan het begin van de methodehandtekening? Volgens afspraak beginnen instantiemethoden met een hash en class-methoden beginnen met een punt.

Voeg vervolgens een specificatie toe met een het blok.

beschrijven AppDelegate do beschrijven "#application: didFinishLaunchingWithOptions:" do it "maakt het venster" do UIApplication.sharedApplication.windows.size.should == 1 end end end

Een van de beste dingen aan Bacon-en andere BDD-testraamwerken is dat de specificaties heel duidelijk zijn over wat ze testen. In dit geval zorgt u ervoor dat het toepassing: didFinishLaunchingWithOptions: methode maakt een venster.

Uw spec hoeft de. Niet te bellen toepassing: didFinishLaunchingWithOptions: methode direct. Het wordt automatisch gebeld wanneer Bacon uw toepassing start.

Voer de specificaties van uw toepassing uit door te draaien bundel exec rake spec vanaf de opdrachtregel. Je zou de uitvoer als volgt moeten zien:

1 specificaties (1 vereisten), 0 storingen, 0 fouten 

Dit geeft aan dat Bacon één test heeft uitgevoerd en geen fouten heeft gevonden. Als een van uw specs faalt, zult u zien 1 mislukking en Bacon zal een gedetailleerde beschrijving van het probleem afdrukken.

Het bovenstaande werkt, maar je zult gebruiken UIApplication.sharedApplication voor al uw specs. Zou het niet fijn zijn als je dit object een keer zou pakken en het zou gebruiken in alle specs? Je kunt met een voor blok.

beschrijven AppDelegate do beschrijven "#application: didFinishLaunchingWithOptions:" doe voordat doe @application = UIApplication.sharedApplication end it "maakt het venster" do @ application.windows.size.should == 1 end end end

Nu kunt u eenvoudig de specificaties van de rest van de toepassing toevoegen.

beschrijf AppDelegate do beschrijven "#application: didFinishLaunchingWithOptions:" doe voordat doe @application = UIApplication.sharedApplication end the "create the window" do @ application.windows.size.should == 1 end it "makes the window key" do @application .windows.first.isKeyWindow.should.be.true end it "stelt de root view controller in" do @ application.windows.first.rootViewController.should.be.instance_of UIViewController end end end

Voer deze uit om te controleren of alles werkt voordat u verdergaat.

4. De gebruikersinterface toevoegen

Er zijn verschillende manieren om de gebruikersinterface te maken met behulp van RubyMotion. Mijn persoonlijke favoriet is om te gebruiken Interface Builder met de IB-edelsteen. Open je Gemfile en voeg het IB-juweel toe.

bron 'https://rubygems.org' gem 'rake' gem 'ib'

Rennen bundel installeren vanaf de opdrachtregel om het juweel te installeren. Als je Git gebruikt, voeg je toe ib.xcodeproj aan jouw .gitignore het dossier.

Interface Builder is een onderdeel van Xcode. Start Interface Builder door te starten bundel exec rake ib: open. Hiermee wordt een Xcode-project gemaakt dat is afgestemd op uw toepassing. Maak een nieuwe gebruikersinterface-bestanden door te selecteren Nieuw> Bestand ... van Xcode's het dossier menu en selecteer storyboard van de Gebruikersomgeving categorie aan de linkerkant. Klik volgende tweemaal om deze stap te voltooien.

Sla het storyboard op in de middelen directory als main.storyboard. Open het storyboard in Xcode en sleep een nieuw Bekijk Controller erin van de Objectbibliotheek aan de rechterkant. Stel de Storyboard-ID veld van de controller naar PaintingController.

Sleep een label vanuit de weergave in de weergave van de view controller Objectbibliotheek aan de rechterkant en stel de tekst in Hallo.

Open vervolgens app / app_delegateen vervang de laatste regel van toepassing: didFinishLaunchingWithOptions: met het volgende:

storyboard = UIStoryboard.storyboardWithName ("main", bundle: nil) @ window.rootViewController = storyboard.instantiateInitialViewController

Voer vervolgens de tests van uw toepassing opnieuw uit met bundel exec rake spec om ervoor te zorgen dat ze nog steeds passen. Merk op hoe je geen van hen hoeft te veranderen? Goede specificaties testen het gedrag van de code, niet de implementatie. Dit betekent dat je zou moeten kunnen veranderen hoe uw code is geïmplementeerd en uw specificaties moeten nog steeds werken. Voer uw applicatie uit om uw nieuwe gebruikersinterface te testen.

5. Knoppen

Wat je tot nu toe hebt gebouwd is geweldig, maar zou het niet leuk zijn als je app daadwerkelijk iets heeft gedaan? In dit gedeelte voegt u de bedieningselementen toe om de kleur van de kwast te wijzigen. Maak twee nieuwe bestanden, een controller en zijn spec, door de volgende opdrachten uit te voeren.

touch app / controllers / painting_controller.rb aanraakspecificatie / controllers / painting_controller_spec.rb

Implementeer de PaintingControllerzijn skelet samen met zijn spec.

klasse PaintingController < UIViewController end
beschrijf PaintingController do tests PaintingController,: storyboard => 'main',: id => 'PaintingController' end

RubyMotion verwerkt controller-specificaties op een speciale manier. De test PaintingController,: storyboard => 'main',: id => 'PaintingController' regel van het spec-bestand vertelt RubyMotion om de controller te gebruiken met een storyboard-ID van PaintingController in de hoofd storyboard. U kunt de controleur variabele om het te testen.

Vervolgens moet u stopcontacten aan uw controller toevoegen. Hiermee kunt u objecten verbinden met uw controller in Interface Builder.

klasse PaintingController < UIViewController extend IB outlet :black_button outlet :purple_button outlet :green_button outlet :blue_button outlet :white_button def select_color(sender) end end

verlengen IB voegt verschillende methoden toe aan uw controller, inclusief stopcontact. Je hebt vijf verkooppunten toegevoegd, één voor elke knop.

De afbeeldingen voor de knoppen zijn opgenomen in de bronbestanden van deze zelfstudie. Download de afbeeldingen en kopieer ze naar de middelen directory. U moet uw Xcode-project opnieuw genereren om Interface Builder in staat te stellen de wijzigingen op te nemen die we hebben aangebracht. De gemakkelijkste manier om dit te doen is door Xcode te sluiten en te draaien bundel exec rake ib: open, waarmee het project opnieuw wordt geopend.

Selecteer de view controller en verander zijn class in PaintingController.

Open spec / app_delegate_spec.rb en wijzig de laatste spec om te controleren op de PaintingController klasse.

het "stelt de rootview-controller in" do @ application.windows.first.rootViewController.should.be.instance_of PaintingController end

Voeg vijf knoppen toe aan de weergave van de weergave-controller door te slepen Knop objecten op het uitzicht vanaf de Objectbibliotheek aan de rechterkant.

Deze knoppen zijn een beetje saai. Selecteer de eerste knop, verander het type in gewoonte in de Kenmerken Inspector aan de rechterkant en verwijder de titel. Zorg ervoor dat de Standaard staat is geselecteerd in de Staat Config vervolgkeuzemenu en stel de achtergrondafbeelding in op button_black.png. Stel de Tint eigendom van de knop om transparant te maken.

Stel de Staat Config vervolgkeuzemenu naar Gekozen en verander de achtergrondafbeelding in button_black_selected.png.

In de Size Inspector, verander de breedte en hoogte van de knop in 50.

Herhaal dit proces voor de andere knoppen.

De volgende stap is om de knoppen aan de verkooppunten van de view controller te hangen die we eerder hebben verklaard. Houd de toets ingedrukt Controle toets op uw toetsenbord en sleep van de weergavecontroller naar de eerste knop. Er verschijnt een menu wanneer u uw muis loslaat. kiezen black_button van het menu. Houd vervolgens de knop ingedrukt Controle toets en sleep van de knop naar de view controller en kies de selecteer kleur methode uit het menu dat verschijnt. Herhaal deze twee stappen voor de andere knoppen.

Selecteer ten slotte de eerste knop en klik op de Gekozen selectievakje onder Controle in de Kenmerken Inspector.

Dit is een goed moment om een ​​paar nuttige specificaties toe te voegen spec / painting_controller_spec.rb.

beschrijf PaintingController do tests PaintingController,: storyboard => 'main',: id => 'PaintingController' beschrijven "#black_button" doen "is verbonden in het storyboard" do controller.black_button.should.not.be.nil end end beschrijven "#purple_button" do it "is verbonden in het storyboard" do controller.purple_button.should.not.be.nil end end omschrijven "#green_button" doen "is verbonden in het storyboard" do controller.green_button.should.not. be.nil end-end beschrijven "#blue_button" doen "verbonden in het storyboard" doen controller.blue_button.hould.not.be.nee einde beschrijven "#white_button" doen "verbonden in de storyboard" doen controller. white_button.should.not.be.nil end end end

Deze specificaties zorgen ervoor dat de stopcontacten correct zijn aangesloten in Interface Builder. Zoals altijd is het een goed idee om ze uit te voeren voordat u doorgaat om ervoor te zorgen dat ze allemaal slagen.

Vervolgens implementeer je de selecteer kleur methode in PaintingController. Wanneer deze methode wordt aangeroepen, wordt de knop die werd getikt geselecteerd en wordt de eerder geselecteerde knop gedeselecteerd.

def select_color (afzender) [black_button, purple_button, green_button, blue_button, white_button]. each do | button | button.selected = false end sender.selected = true end

Voeg de specificaties toe aan spec / controllers / painting_controller_spec.rb.

beschrijf "#select_color" doe het eerder doe controller.select_color (controller.green_button) einde het "deselecteert de andere kleuren" do controller.black_button.state.should == UIControlStateNormal controller.purple_button.state.should == UIControlStateNormal controller.blue_button.state .should == UIControlStateNormal controller.white_button.state.should == UIControlStateNormal end it "selecteert de kleur" do controller.green_button.state.should == UIControlStateSelected end end 

Start de applicatie en controleer of de knopselectie werkt. Wanneer u op een knop tikt, moet deze groter worden. Hoewel dit cool is, wil je echt dat een kleur wordt geselecteerd wanneer op de knop wordt getikt. Dit is gemakkelijk te bereiken met een paar toevoegingen.

Sugarcube is een verzameling iOS-extensies voor RubyMotion die verschillende taken, zoals het maken van kleuren, eenvoudiger maken. Toevoegen gem 'sugarcube' naar je Gemfile en rennen bundel installeren. Dan toevoegen vereisen "sugarcube-color" aan jouw Rakefile bovenstaande Motion :: Project :: App.setup.

De edelsteen maakt het gemakkelijk om kleuren te maken met behulp van hun hex-code. In de PaintingController klasse, voeg het volgende codefragment toe onder de verklaring van de winkels:

KLEUREN = ["# 333333" .uicolor, "# 7059ac" .uicolor, "# 196e76" .uicolor, "# 80a9cc" .uicolor, "#fafafa" .uicolor] 

Werk vervolgens de reeks knoppen bij selecteer kleur in een privéhulpmethode:

def select_color (zender) buttons.each do | button | button.selected = false end sender.selected = true @color = COLOURS [sender.tag] einde private def-knoppen [black_button, purple_button, green_button, blue_button, white_button] end 

Voeg ten slotte hieronder een nieuwe methode toe selecteer kleur die de geselecteerde kleur retourneert.

def selected_color KLEUREN [buttons.find_index | button | button.state == UIControlStateSelected] end 

Deze methode grijpt de index van de geselecteerde knop en selecteert de kleur die daarmee overeenkomt. Natuurlijk zou deze methode niet volledig zijn zonder tests.

beschrijf "#selected_color" doe voordat je controller.select_color (controller.green_button) doet eindigen "geeft de juiste kleur terug" do controller.selected_color.should == PaintingController :: COLORS [2] end end 

Voer uw toepassing opnieuw uit om te controleren of alles werkt zoals verwacht.

Conclusie

Je hebt veel aandacht besteed aan deze tutorial. U hebt geleerd hoe u een RubyMotion-toepassing opzet en uitvoert, dat u met Interface Builder hebt gewerkt en dat u een gebruikersinterface hebt gebouwd.

In het tweede deel van deze zelfstudie gaat u dieper in op het patroon Model-View-Controller op iOS en de organisatie van uw toepassing. Je voegt ook een schilderijweergave toe en schrijft de code waarmee de gebruiker kan tekenen. Blijf kijken.