Låt oss skriva en RubyMotion App Del 1

Vad du ska skapa

RubyMotion är ett ramverk som låter dig bygga iOS-applikationer i Ruby. Det ger dig alla fördelarna med Ruby-språket, men för att din kod är sammanställd till maskinkod får du all den råa prestanda som utvecklas i Objective-C. RubyMotion låter dig använda iOS SDK direkt, vilket innebär att du har tillgång till alla de senaste funktionerna på plattformen. Du kan inkludera Mål-C-kod i ditt projekt och RubyMotion fungerar även med CocoaPods.

I den här handledningen bygger du en målningsprogram från början. Jag ska visa dig hur du integrerar gränssnittsbyggare i ditt arbetsflöde och hur du testar din ansökan korrekt. Om du inte har någon tidigare iOS- eller Ruby-upplevelse, rekommenderar jag dig att lära dig mer om de första. Tuts + Ruby for Newbies och Learning iOS SDK Development from Scratch guider är ett bra ställe att börja.

1. Projekt Setup

Innan du kan börja kodning måste du ha RubyMotion installerat och installerat. För mer information om hur du gör det, kolla in förutsättningarna i RubyMotion Getting Started guide.

När du har gjort det, öppna din terminal och skapa ett nytt RubyMotion-projekt genom att köra:

rörelse skapa färg cd-färg

Detta skapar en måla katalog och flera filer:

  • .gitignore: Den här filen berättar Git vilka filer som ska ignoreras. Eftersom RubyMotion genererar bygga filer när det körs, är den här filen användbar för att hålla dina genererade byggfiler ur källkontrollen.
  • Gemfile: Den här filen innehåller programmets beroenden.
  • Rakefile: RubyMotion använder Rake för att bygga och driva din ansökan. De Rakefile konfigurerar din ansökan och laddar dess beroenden. Du kan se alla uppgifter som är tillgängliga för din ansökan genom att köra rake -T från kommandoraden.
  • app / app_delegate.rb: Ansökningsdelegationen är startpunkten för din ansökan. När iOS avslutar laddar din ansökan i minnet, meddelas programdelegationen.

RubyMotion genererar också a spec / main_spec.rb fil. Jag ska visa dig hur du ska testa din ansökan lite senare i den här handledningen. För tillfället kan du radera den här filen genom att köra rm spec / main_spec.rb från kommandoraden.

Installera programmets beroenden genom att köra buntinstallation följd av bunt exec rake att starta din ansökan.

Woohoo! En svart skärm. Du kommer att göra det mer intressant om en minut.

2. Första ändringen

Även om det är trevligt att få en löpande app, är en svart skärm lite tråkig. Låt oss lägga till en liten färg.

Precis som den inbyggda iOS SDK, tvingar RubyMotion dig inte att organisera dina filer på något visst sätt. Det är dock användbart att skapa några mappar i app katalog för att hålla ditt projekt organiserat. Kör följande kommandon från kommandoraden för att skapa en katalog för dina modeller, visningar och kontroller.

mkdir app / models mkdir app / visningar mkdir app / controllers

Ta en titt inuti app / app_delegate.rb fil:

klass AppDelegate def application (ansökan, didFinishLaunchingWithOptions: launchOptions) true end end

Om du är bekant med iOS-utveckling kommer du märka att den här metoden tillhör UIApplicationDelegate protokoll, vilket ger flera krokar till applikationens livscykel. Observera att AppDelegate klassen förklarar inte att den implementerar UIApplicationDelegate protokoll. Ruby är beroende av ankstyp eftersom det inte stöder protokoll. Det betyder att det inte bryr sig om din klass säger att det implementerar ett protokoll, det bryr sig bara om det genomför de korrekta metoderna.

Definitionen av applikations: didFinishLaunchingWithOptions: metod inuti AppDelegate klassen kan se lite konstig ut. I Mål-C skulle denna metod skrivas så här:

- (BOOL) ansökan: (UIApplication *) ansökan gjordeFinishLaunchingWithOptions: (NSDictionary *) launchOptions;

Eftersom Metod-C-metodenamn kan delas upp i flera delar, implementerar Ruby dem på ett unikt sätt. Den första delen av applikations: didFinishLaunchingWithOptions: är vad som skulle vara metodnamnet i MR. Resten av metoden signaturen är skriven som sökordsargument. I RubyMotion, applikations: didFinishLaunchingWithOptions: är skrivet så här:

def ansökan (ansökan, didFinishLaunchingWithOptions: launchOptions) slut 

Låt oss genomföra denna metod.

klass AppDelegate def application (application, didFinishLaunchingWithOptions: launchOptions) @window = UIWindow.alloc.initWithFrame (UIScreen.mainScreen.bounds) @ ​​window.makeKeyAndVisible @ window.rootViewController = UIViewController.alloc.initWithNibName (noll, bunt: noll)

De första två raderna av applikations: didFinishLaunchingWithOptions: Metod skapa ett nytt fönsterobjekt och gör det till huvudfönstret för programmet. Varför är @fönster en instansvariabel? RubyMotion kommer skräp att samla in fönstret om vi inte lagrar det. Den sista raden i metoden sätter fönstrets root view-kontroller till en ny, tom visningskontroll.

Kör programmet för att få allt som fortfarande fungerar.

Hmm. Applikationen körs, men skärmen är fortfarande svart. Hur vet du att din kod fungerar? Du kan göra en snabb sanitetskontroll genom att lägga till följande i botten av applikations: didFinishLaunchingWithOptions:, innan Sann. Var noga med att ta bort det innan du går vidare.

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

3. Testning

Ingen ansökan är komplett utan en solid testpaket. Testning kan du vara säker på att din kod fungerar och det låter dig göra ändringar utan att oroa dig för att bryta befintlig kod.

RubyMotion skickas med en port i Bacon testbiblioteket. Om du är bekant med Rspec kommer Bacon att känna sig väldigt bekant.

För att komma igång speglar du app katalogstruktur i spec katalog genom att köra följande kommandon från kommandoraden.

mkdir spec / models mkdir spec / visningar mkdir spec / controllers

Skapa sedan AppDelegates specifikationsfil på spec / app_delegate_spec.rb. Enligt konventionen är källfiler speglar i spec-katalogen och har _spec bifogas till slutet av deras filnamn.

Starta denna klass genom att definiera a beskriva block som berättar läsaren vad din fil testar.

beskriv AppDelegate göra slut 

Lägg sedan till en sekund beskriva blockera inom den första för att visa att du vill testa applikations: didFinishLaunchingWithOptions: metod.

beskriv AppDelegate beskriva "#application: didFinishLaunchingWithOptions:" gör slutänden

Såg du på # i början av metodens signatur? Genom konventionen börjar exempelmetoder med hash och klassmetoder börja med en period.

Lägg sedan till en specifik med en Det blockera.

beskriv AppDelegate beskriva "#application: didFinishLaunchingWithOptions:" gör det "skapar fönstret" do UIApplication.sharedApplication.windows.size.should == 1 slutet slutet

En av de bästa sakerna om Bacon-och andra BDD-testramar-är att specifikationerna är mycket tydliga om vad de testar. I det här fallet ser du till att applikations: didFinishLaunchingWithOptions: Metod skapar ett fönster.

Din spec behöver inte ringa applikations: didFinishLaunchingWithOptions: metod direkt. Den heter automatiskt när Bacon lanserar din ansökan.

Kör dina programspecifikationer genom att köra bunt exec rake spec från kommandoraden. Du ska se produktionen så här:

1 specifikationer (1 krav), 0 fel, 0 fel 

Detta berättar att Bacon körde ett test och hittade inga fel. Om en av dina specifikationer misslyckas ser du 1 fel och Bacon kommer att skriva ut en detaljerad beskrivning av problemet.

Ovanstående verk, men du ska använda UIApplication.sharedApplication för alla dina specifikationer. Skulle det inte vara trevligt om du kunde ta tag i det här objektet en gång och använda det i alla specifikationer? Du kan med en innan blockera.

beskriv AppDelegate beskriva "#application: didFinishLaunchingWithOptions:" gör före gör @application = UIApplication.sharedApplication avsluta det "skapar fönstret" do @ application.windows.size.should == 1 slutet slutet

Nu kan du enkelt lägga till resten av programmets specifikationer.

beskriv AppDelegate beskriva "#application: didFinishLaunchingWithOptions:" gör före gör @application = UIApplication.sharedApplication avsluta det "skapar fönstret" do @ application.windows.size.should == 1 avsluta "gör fönsternyckeln" göra @application .windows.first.isKeyWindow.should.be.true avsluta det "ställer in root view controller" gör @ application.windows.first.rootViewController.should.be.instance_of UIViewController slutänden

Kör dessa för att se till att allt fungerar innan du går vidare.

4. Lägga till användargränssnittet

Det finns flera sätt att skapa användargränssnittet med RubyMotion. Min personliga favorit är att använda Gränssnittsbyggare med IB pärla. Öppna din Gemfile och lägg till IB-pärlan.

källa 'https://rubygems.org' pärla 'rake' pärla 'ib'

Springa buntinstallation från kommandoraden för att installera pärlan. Om du använder Git, lägg till ib.xcodeproj till din .gitignore fil.

Gränssnittsbyggare är en del av Xcode. Starta gränssnittsbyggaren genom att köra bunt exec rake ib: öppen. Detta skapar ett Xcode-projekt skräddarsytt för din ansökan. Skapa en ny användargränssnittfiler genom att välja Ny> Fil ... från Xcode s Fil menyn och välj Storyboard från Användargränssnitt kategori till vänster. Klick Nästa två gånger för att slutföra detta steg.

Spara storyboardet i Medel katalog som main.storyboard. Öppna storyboardet i Xcode och dra ett nytt Visa kontrollenhet in i den från Objektbibliotek till höger. Ställ in Storyboard ID styrenhetens område till PaintingController.

Dra en etikett i vyens kontrollvy från Objektbibliotek till höger och ange texten till Hej.

Öppna sedan upp app / app_delegateoch ersätt den sista raden av applikations: didFinishLaunchingWithOptions: med följande:

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

Därefter kör programmets test igen med bunt exec rake spec för att se till att de fortfarande passerar. Lägg märke till hur du inte behövde byta någon av dem? Goda specifikationer testa kodens beteende, inte dess genomförande. Det betyder att du borde kunna ändra på vilket sätt Din kod är implementerad och dina specifikationer ska fortfarande fungera. Kör din ansökan för att prova ditt nya användargränssnitt.

5. Knappar

Vad du har byggt hittills är bra, men skulle det inte vara trevligt om din app faktiskt gjorde något? I det här avsnittet lägger du till kontrollerna för att byta färg på penseln. Skapa två nya filer, en kontroller och dess spec, genom att köra följande kommandon.

peka app / controllers / painting_controller.rb touch spec / controllers / painting_controller_spec.rb

Implementera PaintingControllers skelett tillsammans med dess spec.

klass PaintingController < UIViewController end
beskriv PaintingController gör tester PaintingController,: storyboard => 'main',: id => 'PaintingController' slutet

RubyMotion hanterar controllerspecifikationer på ett speciellt sätt. De tester PaintingController,: storyboard => 'main',: id => 'PaintingController' rad av spec-filen berättar RubyMotion att använda kontrollenheten med ett storyboard-ID på PaintingController i huvud storyboard. Du kan använda kontrollant variabel för att testa den.

Därefter måste du lägga till uttag till din controller. Dessa låter dig ansluta objekt till din controller i Interface Builder.

klass 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

förlänga IB lägger till flera metoder till din controller, inklusive utlopp. Du har lagt till fem uttag, en för varje knapp.

Bilderna för knapparna ingår i källfilerna i denna handledning. Ladda ner bilderna och kopiera dem till Medel katalogen. Du måste regenerera ditt Xcode-projekt så att gränssnittsbyggaren kan hämta de ändringar vi har gjort. Det enklaste sättet att göra detta är att stänga Xcode och springa bunt exec rake ib: öppen, som kommer att återuppta projektet.

Välj vykontrollen och ändra klassen till PaintingController.

Öppna spec / app_delegate_spec.rb och ändra den senaste specifikationen för att söka efter PaintingController klass.

det "ställer in root view controller" gör @ application.windows.first.rootViewController.should.be.instance_of PaintingController slutet

Lägg till fem knappar för vyens kontroll genom att dra Knapp föremål på utsikten från Objektbibliotek till höger.

Dessa knappar är lite tråkiga. Välj den första knappen, ändra dess typ till Beställnings i Attribut Inspector till höger och ta bort dess titel. Var säker på Standard tillståndet är valt i State Config rullgardinsmenyn och sätt bakgrundsbilden till button_black.png. Ställ in Nyans egenskapen hos knappen till transparent.

Ställ in State Config rullgardinsmeny till Vald och ändra bakgrundsbilden till button_black_selected.png.

I Storleksinspektör, ändra bredden och höjden på knappen till 50.

Upprepa denna process för de andra knapparna.

Nästa steg är att haka knapparna upp till utsiktsregulatorens utlopp vi förklarade tidigare. Håll ner Kontrollera tangent på tangentbordet och dra från vykontrollen till den första knappen. En meny kommer dyka upp när du släpper ut musen. Välj black_button från menyn. Håll sedan ned Kontrollera tangenten och dra från knappen till vykontrollen och välj select_color Metod från menyn som dyker upp. Upprepa dessa två steg för de andra knapparna.

Slutligen, välj den första knappen och klicka på Vald kryssrutan under Kontrollera i Attribut Inspector.

Nu är det bra att lägga till några användbara specifikationer till spec / painting_controller_spec.rb.

beskriv PaintingController gör tester PaintingController,: storyboard => 'main',: id => 'PaintingController' beskriver "#black_button" gör det "är anslutet i storyboardet" do controller.black_button.should.not.be.nil slutänden beskriver "#purple_button" gör det "är anslutet i storyboard" do controller.purple_button.should.not.be.nil änden beskriver "#green_button" gör det "är anslutet i storyboardet" do controller.green_button.should.not. be.nil slutet ändan beskriva "#blue_button" gör det "är anslutet i storyboard" do controller.blue_button.should.not.be.nil änden beskriver "#white_button" gör det "är anslutet i storyboard" do controller ". white_button.should.not.be.nil slutet slutet

Dessa specifikationer säkerställer att uttagen är ordentligt anslutna i Interface Builder. Som alltid är det en bra idé att köra dem innan du fortsätter för att se till att alla passerar.

Därefter implementerar du select_color metod i PaintingController. När den här metoden kallas väljs den knapp som valts och den tidigare valda knappen avmarkeras.

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

Lägg till specifikationerna till spec / controllers / painting_controller_spec.rb.

beskriv "#select_color" gör innan gör controller.select_color (controller.green_button) avsluta det "avmarkerar de andra färgerna" gör controller.black_button.state.should == UIControlStateNormal controller.purple_button.state.should == UIControlStateNormal controller.blue_button.state .should == UIControlStateNormal controller.white_button.state.should == UIControlStateNormal avsluta det "väljer färg" gör controller.green_button.state.should == UIControlStateSelected slutänden 

Kör programmet och kontrollera att knappvalet fungerar. När du trycker på en knapp, ska den öka i storlek. Medan det här är coolt, är det som du verkligen vill ha en färg som ska väljas när knappen trycks in. Detta är lätt att åstadkomma med några tillägg.

Sugarcube är en uppsättning iOS-tillägg för RubyMotion som gör flera uppgifter, till exempel att skapa färger, enklare. Lägg till pärla "sockercube" till din Gemfile och springa buntinstallation. Sen Lägg till kräver "sockercube-color" till din Rakefile ovan Motion :: Project :: App.setup.

Ädelstenen gör det enkelt att skapa färger med hjälp av deras hex-kod. I PaintingController klass, lägg till följande kodsedel nedanför deklarationerna för uttag:

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

Därefter refactor arrayen av knappar i select_color in i en privat hjälpar metod:

def select_color (avsändare) buttons.each do | button | button.selected = false end sender.selected = true @color = COLORS [sender.tag] avslutar privata def knappar [black_button, purple_button, green_button, blue_button, white_button] avsluta 

Slutligen lägg till en ny metod nedan select_color som returnerar den valda färgen.

def selected_color COLORS [buttons.find_index | knapp | button.state == UIControlStateSelected] slutet 

Denna metod tar tag i indexet för den valda knappen och väljer den färg som motsvarar den. Naturligtvis skulle denna metod inte vara komplett utan test.

beskriv "#selected_color" gör innan gör controller.select_color (controller.green_button) avsluta det "returnerar rätt färg" gör controller.selected_color.should == PaintingController :: COLORS [2] änden 

Kör din ansökan igen för att se till att allt fungerar som förväntat.

Slutsats

Du har täckt mycket av den här handledningen. Du har lärt dig hur du installerar och kör ett RubyMotion-program, du har arbetat med Interface Builder, och du har byggt ett användargränssnitt.

I den andra delen av denna handledning dyker du djupare in i modell-View-Controller-mönstret på IOS och din applikations organisation. Du lägger också till en målningsvy och skriver den kod som tillåter användaren att rita. Håll dig igång.