La oss skrive en RubyMotion App Del 1

Hva du skal skape

RubyMotion er et rammeverk som lar deg bygge iOS-applikasjoner i Ruby. Det gir deg alle fordelene med Ruby-språket, men fordi koden din er kompilert til maskinkode, får du all den raske ytelsen til å utvikle seg i Objective-C. RubyMotion lar deg bruke iOS SDK direkte, noe som betyr at du har tilgang til alle de nyeste funksjonene på plattformen. Du kan inkludere Mål-C-kode i prosjektet ditt, og RubyMotion fungerer også med CocoaPods.

I denne opplæringen vil du bygge et maleri program fra bunnen av. Jeg skal vise deg hvordan du integrerer grensesnittbygger i arbeidsflyten din og hvordan du skal teste programmet på riktig måte. Hvis du ikke har noen tidligere iOS- eller Ruby-opplevelse, vil jeg anbefale at du lærer mer om de første. Tuts + Ruby for Newbies og Learning iOS SDK Development from Scratch guider er et flott sted å starte.

1. Prosjektoppsett

Før du kan starte kodingen, må du ha RubyMotion installert og satt opp. For detaljer om hvordan du gjør dette, se delen Forutsetninger i RubyMotion Komme i gang-veiledningen.

Når du har gjort det, åpner du terminalen din og oppretter et nytt RubyMotion-prosjekt ved å kjøre:

bevegelse skape maling cd-maling

Dette skaper en maling katalog og flere filer:

  • .gitignore: Denne filen forteller Git hvilke filer som skal ignoreres. Fordi RubyMotion genererer byggefiler når det kjører, er denne filen nyttig for å holde dine genererte byggfiler ut av kildekontroll.
  • Gemfile: Denne filen inneholder programmets avhengigheter.
  • Rakefile: RubyMotion bruker Rake til å bygge og kjøre din søknad. De Rakefile konfigurerer søknaden din og laster avhengighetene sine. Du kan se alle oppgavene som er tilgjengelige for søknaden din, ved å kjøre rake -T fra kommandolinjen.
  • app / app_delegate.rb: Programdelegatet er inngangspunktet for søknaden din. Når iOS er ferdig med å laste inn programmet i minnet, blir programdelegatet varslet.

RubyMotion genererer også a spec / main_spec.rb fil. Jeg skal vise deg hvordan du kan teste programmet ditt litt senere i denne opplæringen. For nå kan du slette denne filen ved å kjøre rm spec / main_spec.rb fra kommandolinjen.

Installer programmets avhengigheter ved å kjøre bunt installasjon etterfulgt av bundle exec rake å starte søknaden din.

Woohoo! En svart skjerm. Du vil gjøre det mer interessant om et minutt.

2. Første endring

Selv om det er fint å ha en løpende app, er en svart skjerm litt kjedelig. La oss legge til en liten farge.

Som den native iOS SDK, tvinger RubyMotion deg ikke til å organisere filene dine på en bestemt måte. Det er imidlertid nyttig å lage noen få mapper i app katalog for å holde prosjektet ditt organisert. Kjør følgende kommandoer fra kommandolinjen for å lage en katalog for modeller, visninger og kontroller.

mkdir app / models mkdir app / visninger mkdir app / controllers

Deretter ta en titt inne i app / app_delegate.rb fil:

klasse AppDelegate def søknad (søknad, didFinishLaunchingWithOptions: launchOptions) true end end

Hvis du er kjent med iOS-utviklingen, vil du legge merke til at denne metoden tilhører UIApplicationDelegate protokollen, som gir flere kroker inn i applikasjonens livssyklus. Legg merke til at AppDelegate Klassen erklærer ikke at den implementerer UIApplicationDelegate protokoll. Ruby er avhengig av å skrive ender som det ikke støtter protokoller. Dette betyr at det ikke bryr seg om klassen din sier at det implementerer en protokoll, det bryr seg bare om det implementerer de riktige metodene.

Definisjonen av applikasjons: didFinishLaunchingWithOptions: metode inne i AppDelegate klassen kan se litt rart ut. I Mål-C vil denne metoden bli skrevet slik:

- (BOOL) søknad: (UIApplication *) søknad didFinishLaunchingWithOptions: (NSDictionary *) launchOptions;

Fordi Objective-C-metodenavnene kan deles i flere deler, implementerer Ruby dem på en unik måte. Den første delen av applikasjons: didFinishLaunchingWithOptions: er det som ville være metodenavnet i MR. Resten av metoden signaturen er skrevet som søkeord argumenter. I RubyMotion, applikasjons: didFinishLaunchingWithOptions: er skrevet slik:

def søknad (søknad, didFinishLaunchingWithOptions: launchOptions) avslutte 

La oss implementere denne metoden.

klasse AppDelegate def søknad (program, didFinishLaunchingWithOptions: launchOptions) @window = UIWindow.alloc.initWithFrame (UIScreen.mainScreen.bounds) @ ​​window.makeKeyAndVisible @ window.rootViewController = UIViewController.alloc.initWithNibName (null, bunt: null) sann ende

De to første linjene i applikasjons: didFinishLaunchingWithOptions: Metode oppretter et nytt vindu objekt og gjør det til hovedvinduet av programmet. Hvorfor er @vindu en instansvariabel? RubyMotion vil søppel samle vinduet med mindre vi lagrer det. Den siste linjen i metoden setter vinduets rotasjonsvisningskontroller til en ny, tom visningskontroller.

Kjør programmet for å få alt som fortsatt fungerer.

Hmm. Programmet kjører, men skjermen er fortsatt svart. Hvordan vet du at koden din fungerer? Du kan gjøre en rask sunnhetskontroll ved å legge til følgende nederst på siden applikasjons: didFinishLaunchingWithOptions:, før ekte. Pass på at du fjerner dette før du går videre.

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

3. Testing

Ingen søknad er fullført uten en solid pakke tester. Testing kan du være trygg på at koden din fungerer, og det lar deg gjøre endringer uten å bekymre deg for å bryte eksisterende kode.

RubyMotion leveres med en port i Bacon testing biblioteket. Hvis du er kjent med Rspec, vil Bacon føle seg veldig kjent.

For å komme i gang, speil på app katalogstruktur i spec katalog ved å kjøre følgende kommandoer fra kommandolinjen.

mkdir spec / models mkdir spec / visninger mkdir spec / controllers

Deretter lager du AppDelegates spesifikasjonsfil på spec / app_delegate_spec.rb. Ved konvensjon er kildefiler speil i spesifikasjonsmappen og har _spec vedlagt til slutten av filnavnet.

Start denne klassen ved å definere en beskrive blokkere som forteller leseren hva filen din tester.

beskriv AppDelegate gjør slutten 

Deretter legger du til et sekund beskrive blokkere innenfor den første som viser at du vil teste applikasjons: didFinishLaunchingWithOptions: metode.

beskriv AppDelegate gjør beskrivelsen "#application: didFinishLaunchingWithOptions:" gjør slutten

La du merke til # i begynnelsen av metoden signatur? Ved konvensjon begynner eksempelmetoder med hash og klassemetoder som begynner med en periode.

Deretter legger du til en spesifikasjon ved hjelp av en den blokkere.

beskriv AppDelegate gjør beskrivelsen "#application: didFinishLaunchingWithOptions:" gjør det "skaper vinduet" do UIApplication.sharedApplication.windows.size.should == 1 slutten av enden

En av de beste tingene med Bacon-og andre BDD-testrammer-er at spesifikasjonene er veldig klare om hva de tester. I dette tilfellet sørger du for at applikasjons: didFinishLaunchingWithOptions: Metoden skaper et vindu.

Din spesifikasjon trenger ikke å ringe applikasjons: didFinishLaunchingWithOptions: metode direkte. Den kalles automatisk når Bacon starter søknaden din.

Kjør programmets spesifikasjoner ved å kjøre bundle exec rake spec fra kommandolinjen. Du bør se utgang slik:

1 spesifikasjoner (1 krav), 0 feil, 0 feil 

Dette forteller deg at Bacon løp en test og fant ingen feil. Hvis en av dine spesifikasjoner mislykkes, vil du se 1 feil og Bacon vil skrive ut en detaljert beskrivelse av problemet.

Ovennevnte arbeider, men du skal bruke UIApplication.sharedApplication for alle dine spesifikasjoner. Ville det ikke vært fint hvis du kunne gripe dette objektet en gang og bruke det i alle spesifikasjonene? Du kan med en før blokkere.

beskriv AppDelegate gjør beskjed "#application: didFinishLaunchingWithOptions:" gjør før du gjør @application = UIApplication.sharedApplication avslutte det "oppretter vinduet" do @ application.windows.size.should == 1 slutten av enden

Nå kan du enkelt legge til resten av programmets spesifikasjoner.

beskriv AppDelegate gjør beskrive "#application: didFinishLaunchingWithOptions:" gjør før du gjør @application = UIApplication.sharedApplication avslutte det "oppretter vinduet" gjør @ application.windows.size.should == 1 avslutte det "gjør vinduetasten" gjør @applikasjon .windows.first.isKeyWindow.should.be.true avslutte det "setter rotvinkontrolleren" gjør @ application.windows.first.rootViewController.should.be.instance_of UIViewController endeendens ende

Kjør disse for å sikre at alt fungerer før du går videre.

4. Legge til brukergrensesnittet

Det er flere måter å opprette brukergrensesnittet ved hjelp av RubyMotion. Min personlige favoritt er å bruke Grensesnittbygger med IB-perlen. Åpne din Gemfile og legg til IB-perlen.

kilde 'https://rubygems.org' perle 'rake' perle 'ib'

Løpe bunt installasjon fra kommandolinjen for å installere perlen. Hvis du bruker Git, legg til ib.xcodeproj til din .gitignore fil.

Interface Builder er en del av Xcode. Start grensesnittbyggeren ved å kjøre bunt exec rake ib: åpen. Dette skaper et Xcode-prosjekt skreddersydd for søknaden din. Opprett en ny brukergrensesnittfiler ved å velge Ny> Fil ... fra Xcode er Fil menyen og velg Storyboard fra Brukergrensesnitt kategori til venstre. Klikk neste to ganger for å fullføre dette trinnet.

Lagre storyboardet i ressurser katalog som main.storyboard. Åpne storyboardet i Xcode og dra et nytt Vis kontrolleren inn i den fra Objektbibliotek til høyre. Sett Storyboard ID feltet til kontrolleren til PaintingController.

Dra en etikett til visningskontrollens visning fra Objektbibliotek til høyre og sett teksten til Hallo.

Deretter åpner du opp app / app_delegateog erstatt den siste linjen av applikasjons: didFinishLaunchingWithOptions: med følgende:

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

Kjør deretter programtestene dine igjen med bundle exec rake spec for å sørge for at de fortsatt passerer. Legg merke til hvordan du ikke måtte endre noen av dem? Gode ​​spesifikasjoner teste oppførselen til koden, ikke dens implementering. Dette betyr at du bør kunne endre hvordan Koden din er implementert og dine spesifikasjoner skal fortsatt fungere. Kjør din applikasjon for å teste ditt nye brukergrensesnitt.

5. Knapper

Det du har bygget så langt er bra, men ville det ikke vært fint hvis appen din faktisk gjorde noe? I denne delen legger du til kontrollene for å bytte fargen på penselen. Opprett to nye filer, en kontroller og dens spesifikasjoner, ved å kjøre følgende kommandoer.

berør app / controllers / painting_controller.rb touch spec / controllers / painting_controller_spec.rb

Gjennomfør PaintingControllers skjelett sammen med sin spec.

klasse PaintingController < UIViewController end
beskriv PaintingController gjør tester PaintingController,: storyboard => 'main',: id => 'PaintingController' slutten

RubyMotion håndterer kontrollerens spesifikasjoner på en spesiell måte. De tester PaintingController,: storyboard => 'main',: id => 'PaintingController' linje av spesifikasjonsfilen forteller RubyMotion å bruke kontrolleren med et storyboard ID på PaintingController i hoved- storyboard. Du kan bruke kontrolleren variabel for å teste den.

Deretter må du legge til utsalgssteder for kontrolleren din. Disse lar deg koble objekter til kontrolleren din i 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

utvide IB legger til flere metoder for kontrolleren din, inkludert stikkontakt. Du har lagt til fem uttak, en for hver knapp.

Bildene for knappene er inkludert i kildefilene i denne opplæringen. Last ned bildene og kopier dem til ressurser katalogen. Du må regenerere Xcode-prosjektet ditt slik at grensesnittbyggeren kan hente de endringene vi har gjort. Den enkleste måten å gjøre dette på er å lukke Xcode og kjører bunt exec rake ib: åpen, som vil gjenåpne prosjektet.

Velg visningsregulatoren og endre klassen til PaintingController.

Åpen spec / app_delegate_spec.rb og endre siste spesifikasjon for å sjekke for PaintingController klasse.

det "setter rot view view controller" gjør @ application.windows.first.rootViewController.should.be.instance_of PaintingController end

Legg til fem knapper i visningsregulatorens visning ved å dra Knapp objekter på utsikten fra Objektbibliotek til høyre.

Disse knappene er litt kjedelige. Velg den første knappen, endre dens type til Tilpasset i Attributtsinspektør til høyre og fjern tittelen. Pass på at Misligholde tilstand er valgt i State Config rullegardinmenyen og sett bakgrunnsbildet til button_black.png. Sett Tint Egenskapen til knappen for gjennomsiktig.

Sett State Config rullegardinmenyen til valgt og endre bakgrunnsbildet til button_black_selected.png.

I Størrelsesinspektør, endre bredden og høyden på knappen til 50.

Gjenta denne prosessen for de andre knappene.

Det neste trinnet er å hekte knappene opp til utsiktsregulatorens utsalgssteder som vi erklærte tidligere. Hold nede Styre tasten på tastaturet ditt, og dra fra visningsregulatoren til den første knappen. En meny vil dukke opp når du slipper musen. Å velge black_button fra menyen. Deretter holder du nede Styre tast og dra av knappen til visningsregulatoren og velg select_color metode fra menyen som dukker opp. Gjenta disse to trinnene for de andre knappene.

Til slutt velger du den første knappen og klikker på valgt boksen under Styre i Attributtsinspektør.

Nå er det en god tid å legge til noen nyttige spesifikasjoner til spec / painting_controller_spec.rb.

Beskriv PaintingController gjør tester PaintingController,: storyboard => 'main',: id => 'PaintingController' beskriver "#black_button" gjør det "er koblet til i storyboardet" do controller.black_button.should.not.be.nil slutten enden beskrive "#purple_button" gjør det "er tilkoblet i storyboardet" do controller.purple_button.should.not.be.nil slutten beskriver "#green_button" gjør det "er koblet i storyboardet" do controller.green_button.should.not. be.nil end-end beskriver "#blue_button" gjør det "er tilkoblet i storyboardet" do controller.blue_button.should.not.be.nil slutten beskriver "#white_button" gjør det "er koblet i storyboard" gjør kontrolleren. white_button.should.not.be.nil slutten slutten

Disse spesifikasjonene sikrer at uttakene er riktig tilkoblet i Interface Builder. Som alltid er det en god ide å kjøre dem før du fortsetter å sørge for at de alle passerer.

Deretter skal du implementere select_color metode i PaintingController. Når denne metoden kalles, er knappen som ble tappet valgt, og den tidligere valgte knappen er ikke valgt.

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

Legg til spesifikasjonene til spec / kontrollere / painting_controller_spec.rb.

beskriv "#select_color" gjør før gjør controller.select_color (controller.green_button) avslutte det "fjerner de andre fargene" gjø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 avslutte det "velger farge" gjør controller.green_button.state.should == UIControlStateSelected end end 

Kjør programmet og kontroller at knappvalget fungerer. Når du trykker på en knapp, bør den øke i størrelse. Mens dette er kult, er det virkelig en farge som skal velges når knappen er tappet. Dette er lett å oppnå med noen tilføyelser.

Sugarcube er et sett med iOS-utvidelser for RubyMotion som gjør flere oppgaver, som å skape farger, enklere. Legg til perle 'sukkerrør' til Gemfile og løp bunt installasjon. Legg så til krever "sukkerrør-farge" til din Rakefile ovenfor Motion :: Prosjekt :: App.setup.

Perlen gjør det enkelt å lage farger med heksekoden. I PaintingController klasse, legg til følgende kodestykke under utsagnsstedet:

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

Deretter refactor rekke knapper i select_color inn i en privat hjelpemetode:

def select_color (sender) buttons.each do | button | button.selected = false end sender.selected = true @color = COLORS [sender.tag] avslutter private def knapper [black_button, purple_button, green_button, blue_button, white_button] avslutte 

Til slutt legg til en ny metode nedenfor select_color som returnerer den valgte fargen.

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

Denne metoden tar tak i indeksen for den valgte knappen og velger fargen som tilsvarer den. Selvfølgelig ville denne metoden ikke være komplett uten tester.

beskriv "#selected_color" gjør før gjør controller.select_color (controller.green_button) avslutte det "returnerer riktig farge" gjør controller.selected_color.should == PaintingController :: FARGER [2] endeenden 

Kjør din søknad igjen for å sikre at alt fungerer som forventet.

Konklusjon

Du har dekket mye bakken i denne opplæringen. Du har lært hvordan du konfigurerer og kjører et RubyMotion-program, du har jobbet med Interface Builder, og du har bygget et brukergrensesnitt.

I den andre delen av denne veiledningen vil du dykke dypere inn i modell-View-Controller-mønsteret på iOS og programmets organisasjon. Du vil også legge til et målvisning og skrive koden som tillater brukeren å tegne. Følg med.