Lar Skriv en RubyMotion App: Part 2

Let oss skrive en RubyMotion App: Part 2
10
Del
6
Del
Dette Cyber ​​Monday Envato Tuts + kurs vil bli redusert til bare $ 3. Ikke gå glipp av.
Hva du skal lage

RubyMotion er et fantastisk rammeverk for å bygge performant iOS-applikasjoner ved hjelp av Ruby språket. I den første delen av denne opplæringen, lært deg hvordan du setter opp og implementere en RubyMotion søknad. Du jobbet med Interface Builder for å lage programmets brukergrensesnitt, implementert en visning kontrolleren, og lærte å skrive tester for søknaden din.

I denne opplæringen vil du lære om Model-View-Controller eller MVC designe mønster og hvordan du kan bruke den til å strukturere din søknad. Du vil også gjennomføre et maleri visning og legge en gest gjenkjenner som gjør at brukeren kan tegne på skjermen. Når du er ferdig, vil du ha en komplett, fullt arbeidsprogrammet.

1. Model-View-Controller

Apple oppfordrer iOS utviklere å bruke Model-View-Controller design mønster på sine søknader. Dette mønsteret bryter klasser i en av tre kategorier, modeller, synspunkter og kontrollere.

  • Modeller inneholde programmets forretningslogikk, koden som bestemmer reglene for håndtering og samspill med data. Modellen er der kjernen logikken for deg applikasjons liv.
  • Visninger vise informasjon til brukeren og tillate dem å samhandle med søknaden.
  • Kontrollere er ansvarlig for å binde modeller og utsikten sammen. IOS SDK bruker view kontrollere, spesialiserte kontrollere med litt mer kunnskap om visninger enn andre MVC rammeverk.

    Hvordan MVC gjelder søknaden din? Du har allerede begynt å implementere PaintingController klasse, som vil koble modeller og visninger sammen. For modellen lag, vil du legge til to klasser:

    Stroke Denne klassen representerer et enkelt slag i maleriet

    Maleri Denne klassen representerer hele maleriet, og inneholder ett eller flere. slag.

    For visningen lag, vil du opprette en PaintingView klasse som er ansvarlig for å vise et maleri objekt for brukeren. Du vil også legge til en StrokeGestureRecongizer som fanger touch input fra brukeren.

    2. Strokes

    La oss starte med den Stroke modell. Et hjerneslag vil bestå av en farge og flere punkter som representerer hjerneslag. For å starte, opprette en fil for Strek klassen, app /modeller /stroke.rb
    , og en annen for sin spec, spec /modeller /stroke.rb
    .
    < p> Deretter implementere hjerneslag klassen skjelett og en konstruktør
    klasse Stroke attr_reader. punkter: colorend

    Stroke klasse har to attributter, poeng, en samling av poeng, og farge, fargen på Stroke objekt . Deretter implementere en konstruktør
    klasse Stroke attr_reader. Punkter: farge def initial (start_point, farge)points = [start_point]color = farge Sluttslutt

    Det ser bra ut så langt. Konstruktøren godtar to argumenter, start_point og farge. Det setter poeng til en rekke punkter som inneholder start_point og farge til den medfølgende farge.

    Når en bruker avleser fingeren over skjermen, trenger du en måte å legge til punkter i Strek objektet. Tilsett add_point metode for å Stroke
    def add_point (punkt) poeng. ≪ < pointend

    Det var lett. For enkelhets skyld legge til én metode for å Strek klassen som returnerer startpunktet
    def start_point points.firstend

    Selvfølgelig., Er ingen modell komplett uten et sett med spesifikasjoner for å gå sammen med det.
    Beskrive Stroke gjøre før dostart_point = CGPoint.new (0,0, 50,0)middle_point = CGPoint.new (50,0, 100,0)end_point = CGPoint.new (100,0, 0,0)color = UIColor.blueColorstroke = Stroke.new ( start_point,color) @ stroke.add_point (middle_point) @ stroke.add_point (end_point) end beskrive "#initialize" gjøre før dostroke = Stroke.new (start_point,color) avslutte den "setter farge "gjør @ stroke.color.should ==color end end beskrive" #start_point "gjør det" returnerer hjerneslag startpunktet "gjør @ stroke.start_point.should ==start_point end end beskrive" #add_point "do it" legger punktene i strøk "gjør @ stroke.points.should == [start_point,middle_point,end_point] end end beskrive" #start_point "gjør det" returnerer startpunktet "gjør @ stroke.start_point.should == @ start_point slutten Sluttslutt

    Dette bør begynne å føle kjent. Du har lagt til fire beskrive blokker som tester de initialize, start_point, add_point, og start_point metoder. Det er også en før blokk som setter noen instansvariabler for specs. Legg merke til beskrive blokk for #initialize har en før blokk som nullstillerstroke objektet. Det er bra. Med spesifikasjoner, trenger du ikke å være så opptatt av ytelse som du gjør med en vanlig applikasjon.

    3. Tegning

    Det er sannhetens øyeblikk, det er på tide å gjøre programmet tegne noe. Start med å lage en fil for PaintingView klasse på app /visninger /painting_view.rb
    . Fordi vi gjør noen spesialisert tegning, er PaintingView klasse vanskelig å teste. For enkelhets skyld, kommer jeg til å hoppe over spesifikasjoner for nå

    Deretter implementere PaintingView klassen
    klasse PaintingView <..; UIView attr_accessor: stroke def drawRect (rektangel) super # sikre slaget blir gitt avkastning dersom stroke.nil? # Satt opp tegningen sammenheng sammenheng = UIGraphicsGetCurrentContext () CGContextSetStrokeColorWithColor (kontekst, stroke.color.CGColor) CGContextSetLineWidth (kontekst, 20,0) CGContextSetLineCap (kontekst, KCGLineCapRound) CGContextSetLineJoin (kontekst, KCGLineJoinRound) # flytte linjen til startpunktet CGContextMoveToPoint (kontekst , stroke.start_point.x, stroke.start_point.y) # legg hver linje i banen stroke.points.drop (1) .Hver gjøre | punkt | CGContextAddLineToPoint (kontekst, point.x, point.y) end # hjerneslag banen CGContextStrokePath (sammenheng); Sluttslutt

    Puh, det er mye kode. La oss bryte det ned bit for bit. Den PaintingView klassen utvider UIView klassen. Dette gjør PaintingView å bli lagt til som en subview av PaintingController syn. Den PaintingView klassen har ett attributt, hjerneslag, som er en forekomst av Strek modellen klassen.

    Med hensyn til MVC mønsteret, når du arbeider med iOS SDK, er det akseptabelt for en visning for å vite om en modell, men det er ikke greit for en modell for å vite om en visning

    I PaintingView klasse, har vi overstyres UIView sin drawRect. metode. Denne metoden gjør det mulig å implementere tilpassede tegning kode. Den første linjen av denne metoden, super, kaller metoden på super klasse, UIView i dette eksempelet med de angitte argumentene.

    I drawRect :, vi sjekker også at streken attributtet er ikke null. Dette hindrer feil hvis hjerneslag ikke er satt ennå. Vi deretter hente den nåværende tegning sammenheng ved å påberope UIGraphicsGetCurrentContext, konfigurere slag at vi er i ferd med å tegne, flytte grafikk sammenheng til start_point av hjerneslag, og legger til linjer for hvert punkt i slaget objektet. Endelig påberope vi CGContextStrokePath å stryke banen, tegne den i visningen

    Legg et uttak til PaintingController for maleriet visning
    utløp.. Painting_view

    Fyr opp Interface Builder ved å kjøre bunt exec rake ib: åpne og legge en UIView objekt til PaintingController syn fra Ojbect Bibliotek
    til høyre. Still visningen klasse til PaintingView i Identity Inspector. Sørg for at maleriet visningen er plassert under knappene du har lagt tidligere. Du kan justere bestilling av subviews ved å endre plasseringen av visningens i visningen hierarkiet til venstre.

    Kontroll og dra fra visningen kontrolleren til PaintingView og velg painting_view utløpet fra menyen som vises.

    Velg maleriet visningen og sette bakgrunnsfarge til 250 rød, 250 grønt, og 250 blå.

    Ikke glem å legge en spec til spec /kontrollere /painting_controller_spec. rb
    for painting_view uttaket.
    beskrive "#painting_view" do it "er koblet i dreieboken" gjør controller.painting_view.should.not.be.nil Sluttslutt

    For å være sikker på tegningen koden fungerer riktig, legge inn følgende kode til PaintingController klassen og kjøre programmet. Du kan slette denne kodebiten når du har bekreftet alt fungerer som forventet.
    Def viewDidLoad stroke = Stroke.new (CGPoint.new (80, 100), '# ac5160'.uicolor) stroke.add_point (CGPoint. ny (240, 100)) stroke.add_point (CGPoint.new (240, 428)) stroke.add_point (CGPoint.new (80, 428)) stroke.add_point (CGPoint.new (80, 100)) painting_view.stroke = hjerneslag painting_view.setNeedsDisplayend
    4. Maleri

    Nå som du kan tegne en strek, er det på tide å nivå opp til hele maleriet. La oss starte med den Maleri modell. Lag en fil for klassen på app /modeller /painting.rb Hotell og implementere Maleri klassen
    klasse Maleri attr_accessor. Slag def initialstrokes = [] end def start_stroke (punkt, farge) slag < < Stroke.new (punkt, farge) end def continue_stroke (punkt) current_stroke.add_point (punkt) end def current_stroke strokes.last Sluttslutt

    Maleri modellen er lik Strek klassen. Konstruktøren initialiserer slag til en tom array. Når en person berører skjermen, vil programmet starte et nytt slag ved å ringe start_stroke. Så, som brukeren drar fingeren, vil det legge poeng med continue_stroke. Ikke glem spesifikasjoner for Maleri klassen.
    Beskrive Maleri gjøre før do @ point1 = CGPoint.new (10, 60) @ poeng2 = CGPoint.new (20, 50) @ point3 = CGPoint.new (30, 40) @ point4 = CGPoint.new (40, 30) @ point5 = CGPoint.new (50, 20) @ point6 = CGPoint.new (60, 10)painting = Painting.new slutten beskrive "#initialize" gjøre før do painting = Painting.new avslutte det "setter strek til en tom array" gjøre @ painting.strokes.should == [] end end beskrive "#start_stroke" gjøre før do @ painting.start_stroke (@ point1, UIColor.redColor) @ painting.start_stroke (@ poeng2, UIColor.blueColor) ender det "starter nye slag" gjøre @ painting.strokes.length.should == 2 @ painting.strokes [0] .points.should == [@ point1]painting .strokes [0] .color.should == UIColor.redColor @ painting.strokes [1] .points.should == [@ poeng2] @ painting.strokes [1] .color.should == UIColor.blueColor end end beskrive "#continue_stroke" gjøre før do @ painting.start_stroke (@ point1, UIColor.redColor) @ painting.continue_stroke (@ poeng2) @ painting.start_stroke (@ point3, UIColor.blueColor) @ painting.continue_stroke (@ point4) ender det " legger poeng til dagens slag "gjøre @ painting.strokes [0] .points.should == [@ point1, @ poeng2] @ painting.strokes [1] .points.should == [@ point3, @ point4] slutten Sluttslutt

    Deretter endrer PaintingView klassen til å tegne et maleri objekt i stedet for en Stroke objekt
    klasse PaintingView <.; UIView attr_accessor: maleri def drawRect (rektangel) super # sikre maleriet er gitt avkastning dersom painting.nil? painting.strokes.each gjøre | stryke | draw_stroke (slag) end end def draw_stroke (slag) # satt opp tegningen sammenheng sammenheng = UIGraphicsGetCurrentContext () CGContextSetStrokeColorWithColor (kontekst, stroke.color.CGColor) CGContextSetLineWidth (kontekst, 20,0) CGContextSetLineCap (kontekst, KCGLineCapRound) CGContextSetLineJoin (kontekst, KCGLineJoinRound) # flytte linjen til startpunktet CGContextMoveToPoint (sammenheng stroke.start_point.x, stroke.start_point.y) # legg hver linje i banen stroke.points.drop (1) .Hver gjøre | punkt | CGContextAddLineToPoint (kontekst, point.x, point.y) end # hjerneslag banen CGContextStrokePath (sammenheng); Sluttslutt

    Du har endret slag attributtet til maleriet. Den drawRect: måte nå gjentar enn alle slag i maleriet og trekker hver enkelt bruker draw_stroke, som inneholder tegningen koden du skrev tidligere

    Du må også oppdatere visningen kontrolleren til å inneholde et maleri modell.. På toppen av PaintingController klassen, legge attr_reader: maleri. Som navnet tilsier, den viewDidLoad metoden i UIViewController klassesuper av PaintingController klasse kalles når visningen kontrolleren er ferdig lastet sitt syn. Den viewDidLoad metoden er derfor et godt sted å skape et maleri forekomst og sette maleriet egenskap av PaintingView objektet.
    Def viewDidLoadpainting = Painting.new painting_view.painting = paintingend

    Som alltid, ikke glem å legge til tester for viewDidLoad til spec /kontrollere /painting_controller_spec.rb
    .
    beskrive "#viewDidLoad" gjør det "setter maleriet" do controller.painting.should.be.instance_of Maleri avslutte det "setter maleri egenskap av maleriet view "gjøre controller.painting_view.painting.should == controller.painting Sluttslutt
    5. Gest Gjenkjennere

    Din søknad vil være ganske kjedelig med mindre du tillater folk å tegne på skjermen med fingrene. La oss legge til at stykke funksjonalitet nå. Lag en fil for StrokeGestureRecognizer klassen sammen med sin spec ved å kjøre følgende kommandoer fra kommandolinjen.
    Berøring app /visninger /stroke_gesture_recognizer.rbtouch spec /synspunkter /stroke_gesture_recognizer_spec.rb

    Deretter oppretter skjelettet for klassen .
    klasse StrokeGestureRecognizer < UIGestureRecognizer attr_reader: positionend

    StrokeGestureRecognizer klassen utvider UIGestureRecognizer klassen, som håndterer touch input. Den har en posisjon tilskriver at PaintingController klassen vil bruke for å bestemme plasseringen av brukerens finger

    Det er fire metoder du trenger for å implementere i StrokeGestureRecognizer klassen, touchesBegan. WithEvent :, touchesMoved: withEvent :, touchesEnded : withEvent :, og touchesCancelled: withEvent :. Den touchesBegan: withEvent: metoden kalles når brukeren starter å berøre skjermen med fingeren. Den touchesMoved: withEvent: Metoden kalles gjentatte ganger når brukeren beveger fingeren og touchesEnded: withEvent: metode kalles når brukeren løfter fingeren fra skjermen. Endelig touchesCancelled: withEvent: er metoden påberopes dersom gest blir avbrutt av brukeren

    Din gest kjenneren må gjøre to ting for hver hendelse, oppdatere posisjonen attributtet og endre statens eiendom
    .. klasse StrokeGestureRecognizer < UIGestureRecognizer attr_accessor: posisjon def touchesBegan (berører, withEvent: event) superposition = touches.anyObject.locationInView (self.view) self.state = UIGestureRecognizerStateBegan slutten def touchesMoved (berører, withEvent: event) superposition = touches.anyObject. locationInView (self.view) self.state = UIGestureRecognizerStateChanged end def touchesEnded (berører, withEvent: event) superposition = touches.anyObject.locationInView (self.view) self.state = UIGestureRecognizerStateEnded end def touchesCancelled (berører, withEvent: event) superposition = touches.anyObject.locationInView (self.view) self.state = UIGestureRecognizerStateEnded Sluttslutt

    Både touchesEnded: withEvent: og touchesCancelled: withEvent: metoder satt staten å UIGestureRecognizerStateEnded. Dette er fordi det spiller ingen rolle hvis brukeren blir avbrutt, skal tegningen forbli urørt.

    For å teste StrokeGestureRecognizer klassen, må du være i stand til å opprette en forekomst av UITouch. Dessverre, det er ingen offentlig tilgjengelig API for å oppnå dette. For å gjøre det arbeidet, vil vi gjøre bruk av Facon tentamen biblioteket.

    Legg perle 'motion-facon "til din Gemfile og kjøre bunt installere. Deretter legger du krever "motion-facon" nedenfor krever "sugarcube-farge" i prosjektets Rakefile.

    Deretter implementere StrokeGestureRecognizer spec.
    Beskrive StrokeGestureRecognizer trenger forlenge Facon :: SpecHelpers før dostroke_gesture_recognizer = StrokeGestureRecognizer.new @ touch1 = mock (UITouch,: "locationInView:" = > CGPoint.new (100, 200)) @ Touch2 = mock (UITouch,: "locationInView:" = > CGPoint.new (300, 400) ) @ touches1 = NSSet.setWithArray [@ touch1] @ touches2 = NSSet.setWithArray [@ Touch2] slutten beskrive "#touchesBegan: withEvent:" gjøre før do @ stroke_gesture_recognizer.touchesBegan (@ touches1, withEvent: null) ender det "setter stilling til gest posisjon "gjør @ stroke_gesture_recognizer.position.should == CGPoint.new (100, 200) ender det" setter staten gest kjenneren "gjør @ stroke_gesture_recognizer.state.should == UIGestureRecognizerStateBegan end end beskrive" #touchesMoved : withEvent: "gjøre før do @ stroke_gesture_recognizer.touchesBegan (@ touches1, withEvent: null) @ stroke_gesture_recognizer.touchesMoved (@ touches2, withEvent: null) ender det" setter stilling til gest posisjon "do @ stroke_gesture_recognizer.position.should = = CGPoint.new (300, 400) ender det "setter staten gest kjenneren" gjør @ stroke_gesture_recognizer.state.should == UIGestureRecognizerStateChanged end end beskrive "#touchesEnded: withEvent:" gjøre før do @ stroke_gesture_recognizer.touchesBegan (@ touches1 , withEvent: null) @ stroke_gesture_recognizer.touchesEnded (@ touches2, withEvent: null) ender det "setter stilling til gest posisjon" gjør @ stroke_gesture_recognizer.position.should == CGPoint.new (300, 400) ender det "setter state of the gest kjenneren "gjør @ stroke_gesture_recognizer.state.should == UIGestureRecognizerStateEnded end end beskriver" #touchesCancelled: withEvent: "gjøre før do @ stroke_gesture_recognizer.touchesBegan (@ touches1, withEvent: null) @ stroke_gesture_recognizer.touchesCancelled (@ touches2, withEvent : null) ender det "setter stilling til gest posisjon" gjør @ stroke_gesture_recognizer.position.should == CGPoint.new (300, 400) ender det "setter staten gest kjenneren" do @ stroke_gesture_recognizer.state.should = = UIGestureRecognizerStateEnded slutten Sluttslutt

    forlenge Facon :: SpecHelpers gjør flere metoder tilgjengelig i dine spesifikasjoner, inkludert mock. mock er en enkel måte å lage testobjekter som fungerer akkurat slik du vil ha dem. I før blokk i begynnelsen av specs, du spottet forekomster av UITouch med locationInView. Metode som returnerer en forhåndsdefinert punkt

    Deretter legger en stroke_gesture_changed metode til PaintingController klassen. Denne metoden vil motta en forekomst av StrokeGestureRecognizer klassen når gest oppdateres.
    Def stroke_gesture_changed (stroke_gesture_recognizer) hvis stroke_gesture_recognizer.state == UIGestureRecognizerStateBegan painting.start_stroke (stroke_gesture_recognizer.position, selected_color) ellers painting.continue_stroke (stroke_gesture_recognizer.position) ende painting_view.setNeedsDisplayend

    Når gest kjenneren tilstand er UIGestureRecognizerStateBegan, starter et nytt slag i Maleri objektet med StrokeGestureRecognizer posisjon og selected_color denne metoden. Ellers fortsetter det nåværende slag

    Legg til spesifikasjonene for denne metoden
    beskrive "#stroke_gesture_changed" do før gjøre dra (controller.painting_view.,:. Poeng = > [CGPoint.new (100, 100), CGPoint.new (150, 150), CGPoint.new (200, 200)]) avslutte den "legger punktene til hjerneslag" gjør controller.painting.strokes.first.points [0] .should == CGPoint .new (100, 100) controller.painting.strokes.first.points [1] .should == CGPoint.new (150, 150) controller.painting.strokes.first.points [2] .should == CGPoint.new (200, 200) ender det "setter strek farge til den valgte fargen" gjør controller.painting.strokes.first.color.should == controller.selected_color Sluttslutt

    RubyMotion gir flere hjelpe metoder for å simulere brukermedvirkning, inkludert drag . Ved hjelp av dra, kan du simulere en brukers interaksjon med skjermen. Punktene alternativet kan du gi en rekke punkter for drag.

    Hvis du skulle kjøre specs nå, ville de mislykkes. Det er fordi du må legge til gest kjenneren til dreieboken. Lansere Interface Builder ved å kjøre bunt exec rake ib: open. Fra objektbiblioteket, drar et objekt i scene, og endre sin klasse StrokeGestureRecognizer i Identity Inspektør til høyre.

    Kontroll og dra fra den StrokeGestureRecognizer objektet til PaintingController og velg select_color metoden fra menyen som vises. Dette vil sikre select_color metoden kalles når gest kjenneren utløses. Så, kontroll og dra fra den PaintingView objektet til StrokeGestureRecognizer objektet og velg gestureRecognizer fra menyen som vises.

    Legg til en spec for bevegelsen kjenneren til PaintingController specs i #painting_view beskrive blokken.
    beskrive "#painting_view" do it "er koblet i dreieboken" gjør controller.painting_view.should.not.be.nil avslutte den "har et hjerneslag gest kjenneren" gjør controller.painting_view.gestureRecognizers.length.should == 1 controller.painting_view.gestureRecognizers [0] .should.be.instance_of StrokeGestureRecognizer Sluttslutt

    Det var det. Med disse endringene programmene skal nå tillate en person å tegne på skjermen. Kjør programmet og ha det gøy.

    6. Siste hånd

    Det er noen siste hånd igjen å legge til før søknaden er ferdig. Fordi søknaden er oppslukende, er statuslinjen litt distraherende. Du kan fjerne den ved å sette UIStatusBarHidden og UIViewControllerBasedStatusBarAppearance verdier i programmets Info.plist. Dette er lett å gjøre i RubyMotion setup blokken inne i prosjektets Rakefile
    Motion :: Prosjekt :: App.setup gjøre |. Applikasjon | app.name = 'Paint' app.info_plist ['UIStatusBarHidden'] = true app.info_plist ['UIViewControllerBasedStatusBarAppearance'] = falseend

    programmets ikoner og lansere bilder er inkludert i kildefilene for denne opplæringen. Last ned bildene og kopiere dem til ressurser katalog av prosjektet. Deretter stiller programikonet i Rakefile konfigurasjon. . Du må kanskje rengjøre bygge ved å kjøre bunt exec rake rent: alt for å se den nye lanseringen bilde
    Motion :: Prosjekt :: App.setup gjøre | applikasjon | app.name = 'Paint' app.info_plist ['UIStatusBarHidden'] = true app.info_plist ['UIViewControllerBasedStatusBarAppearance'] = false app.icons = ["icon.png"] end

    Det er Konklusjon Anmeldelser den. Du har nå en komplett app som er klar for en million nedlastinger i App Store. Du kan se og laste ned kilde for dette programmet fra GitHub.

    Selv om din app er ferdig, det er så mye mer du kan legge til det. Du kan legge kurver mellom linjene, flere farger, forskjellige linjebredder, lagring, angre og gjøre om, og alt annet du kan tenke deg. Hva vil du gjøre for å gjøre app bedre? Gi meg beskjed i kommentarfeltet nedenfor.