RubyMotion est un cadre fantastique pour la création d'applications iOS performantes utilisant le langage Ruby. Dans la première partie de ce didacticiel, vous avez appris à configurer et à mettre en œuvre une application RubyMotion. Vous avez travaillé avec Interface Builder pour créer l'interface utilisateur de l'application, implémenté un contrôleur de vue et appris à écrire des tests pour votre application..
Dans ce didacticiel, vous découvrirez le modèle de conception Model-View-Controller ou MVC et comment vous pouvez l'utiliser pour structurer votre application. Vous allez également implémenter une vue en peinture et ajouter un outil de reconnaissance des gestes permettant à l'utilisateur de dessiner à l'écran. Lorsque vous avez terminé, vous obtenez une application complète et pleinement opérationnelle..
Apple encourage les développeurs iOS à appliquer le modèle de conception Model-View-Controller à leurs applications. Ce modèle divise les classes en trois catégories, modèles, vues et contrôleurs..
Comment MVC s'applique-t-il à votre application? Vous avez déjà commencé à implémenter le PeintureContrôleur
classe, qui connectera vos modèles et vues ensemble. Pour la couche modèle, vous allez ajouter deux classes:
Accident vasculaire cérébral
Cette classe représente un seul trait dans la peinture.La peinture
Cette classe représente le tableau entier et contient un ou plusieurs traits.Pour la couche de vue, vous allez créer un PaintingView
classe qui est responsable de l'affichage d'un La peinture
objet à l'utilisateur. Vous allez aussi ajouter un StrokeGestureRecongizer
qui capture la saisie tactile de l'utilisateur.
Commençons par le Accident vasculaire cérébral
modèle. Un trait sera composé d'une couleur et de plusieurs points représentant le trait. Pour commencer, créez un fichier pour le Accident vasculaire cérébral
classe, app / models / stroke.rb, et un autre pour ses spécifications, spec / models / stroke.rb.
Ensuite, implémentez le squelette de classe de traits et un constructeur.
classe Stroke attr_reader: points,: couleur end
le Accident vasculaire cérébral
la classe a deux attributs, points
, une collection de points, et Couleur
, la couleur de la Accident vasculaire cérébral
objet. Ensuite, implémenter un constructeur.
classe Stroke attr_reader: points,: color def initialize (start_point, color) @points = [start_point] @color = color end end
Cela a l'air bien jusqu'à présent. Le constructeur accepte deux arguments, point de départ
et Couleur
. Il établit points
à un tableau de points contenant point de départ
et Couleur
à la couleur fournie.
Lorsqu'un utilisateur fait glisser son doigt sur l'écran, vous avez besoin d'un moyen d'ajouter des points à la Accident vasculaire cérébral
objet. Ajouter le add_point
méthode pour Accident vasculaire cérébral
.
def add_point (point) points << point end
C'était facile. Pour plus de commodité, ajoutez une méthode supplémentaire à la Accident vasculaire cérébral
classe qui retourne le point de départ.
def start_point points.first end
Bien sûr, aucun modèle n'est complet sans un ensemble de spécifications qui l'accompagnent.
decrire Stroke do before do @start_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.blueColor @stroke = Stroke.new (@start_point, @color) @ stroke.add_point (@middle_point) @ stroke.add_point (@end_point) end décrit "#initialize" do before do @stroke = Stroke.new (@start_point, @color) end it "définit les paramètres suivants: color "do @ stroke.color.should == @color end end" #start_point "do it" renvoie le point de départ du tracé "do @ stroke.start_point.should == @start_point end end" "add_point" ajoute les points au trait "do @ stroke.points.should == [@start_point, @middle_point, @end_point] end end décrit" #start_point "do it" renvoie le point de départ "do @ stroke.start_point.should == @start_point end end end
Cela devrait commencer à se sentir familier. Vous avez ajouté quatre blocs décrivant qui testent la initialiser
, point de départ
, add_point
, et point de départ
méthodes. Il y a aussi un avant
bloc qui définit quelques variables d'instance pour les spécifications. Remarquez le décrire
bloquer pour #initialiser
a un avant
bloquer qui réinitialise le @accident vasculaire cérébral
objet. C'est très bien. Avec les spécifications, vous n'avez pas à vous soucier de la performance comme vous le faites avec une application régulière.
C'est le moment de vérité, il est temps de faire dessiner quelque chose par votre application. Commencez par créer un fichier pour le PaintingView
cours à app / views / painting_view.rb. Parce que nous faisons un dessin spécialisé, le PaintingView
la classe est difficile à tester. Par souci de brièveté, je vais sauter les spécifications pour l'instant..
Ensuite, implémentez le PaintingView
classe.
classe PaintingView < UIView attr_accessor :stroke def drawRect(rectangle) super # ensure the stroke is provided return if stroke.nil? # set up the drawing context context = UIGraphicsGetCurrentContext() CGContextSetStrokeColorWithColor(context, stroke.color.CGColor) CGContextSetLineWidth(context, 20.0) CGContextSetLineCap(context, KCGLineCapRound) CGContextSetLineJoin(context, KCGLineJoinRound) # move the line to the start point CGContextMoveToPoint(context, stroke.start_point.x, stroke.start_point.y) # add each line in the path stroke.points.drop(1).each do |point| CGContextAddLineToPoint(context, point.x, point.y) end # stroke the path CGContextStrokePath(context); end end
Ouf, c'est beaucoup de code. Décomposons cela pièce par pièce. le PaintingView
la classe étend la UIView
classe. Ceci permet PaintingView
à ajouter en tant que sous-vue de PeintureContrôleur
la vue. le PaintingView
la classe a un attribut, accident vasculaire cérébral
, qui est une instance de la Accident vasculaire cérébral
classe de modèle.
En ce qui concerne le modèle MVC, lorsque vous utilisez le SDK iOS, il est acceptable pour une vue de connaître un modèle, mais ce n'est pas normal pour un modèle de connaître une vue..
dans le PaintingView
classe, nous avons remplacé UIView
de drawRect:
méthode. Cette méthode vous permet d'implémenter du code de dessin personnalisé. La première ligne de cette méthode, super
, appelle la méthode sur la super classe, UIView
dans cet exemple, avec les arguments fournis.
Dans drawRect:
, nous vérifions également que le accident vasculaire cérébral
attribut n'est pas néant
. Cela évite les erreurs si accident vasculaire cérébral
n'a pas encore été défini. Nous récupérons ensuite le contexte de dessin actuel en appelant UIGraphicsGetCurrentContext
, configurer le trait que nous sommes sur le point de dessiner, déplacez le contexte de dessin vers le point de départ
du trait et ajoute des lignes pour chaque point de la accident vasculaire cérébral
objet. Enfin, nous invoquons CGContextStrokePath
tracer le chemin en le dessinant dans la vue.
Ajouter un point de vente à PeintureContrôleur
pour la vue en peinture.
sortie: painting_view
Lancez Interface Builder en lançant bundle exec rake ib: ouvert
et ajouter un UIView
objecter à la PeintureContrôleur
vue depuis le Bibliothèque Ojbect sur la droite. Définissez la classe de la vue sur PaintingView
dans le Inspecteur d'identité. Assurez-vous que la vue de peinture est positionnée sous les boutons que vous avez ajoutés précédemment. Vous pouvez ajuster l'ordre des sous-vues en modifiant les positions des vues dans la hiérarchie des vues à gauche..
Contrôlez et faites glisser du contrôleur de vue vers le PaintingView
et sélectionnez le painting_view
sortie du menu qui apparaît.
Sélectionnez la vue en peinture et définissez sa couleur d'arrière-plan sur 250
rouge, 250
vert et 250
bleu.
N'oubliez pas d'ajouter une spécification à spec / controllers / painting_controller_spec.rb pour le painting_view
sortie.
décris "#painting_view" do it "est connecté au storyboard" do controller.painting_view.should.not.be.nil end end
Pour vous assurer que votre code de dessin fonctionne correctement, ajoutez l’extrait de code suivant à la PeintureContrôleur
classe et lance ton application. Vous pouvez supprimer cet extrait de code lorsque vous avez vérifié que tout fonctionne comme prévu..
def viewDidLoad stroke = Stroke.new (CGPoint.new (80, 100), '# ac5160'.uicolor) stroke.add_point (CGPoint.new (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 = stroke peinture_view.setNeedsAffichage de fin
Maintenant que vous pouvez dessiner un trait, il est temps de niveler le tableau en entier. Commençons par le La peinture
modèle. Créez un fichier pour la classe à app / models / painting.rb et mettre en œuvre le La peinture
classe.
class Peinture attr_accessor: strokes def initialize @strokes = [] end def start_stroke (point, couleur) traits << Stroke.new(point, color) end def continue_stroke(point) current_stroke.add_point(point) end def current_stroke strokes.last end end
le La peinture
le modèle est similaire à la Accident vasculaire cérébral
classe. Le constructeur s'initialise coups
à un tableau vide. Lorsqu'une personne touche l'écran, l'application commence un nouveau trait en appelant course_de_route
. Ensuite, lorsque l'utilisateur fait glisser son doigt, des points avec continuer_stroke
. N'oubliez pas les spécifications pour le La peinture
classe.
décrire Peinture faire avant de faire @ point1 = CGPoint.new (10, 60) @ point2 = 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 end décrit "#initialize" do beforepainting = Painting.new end it "définit le trait sur un tableau vide "do @ painting.strokes.should == [] end end décrit" #start_stroke "avant de faire @ painting.start_stroke (@ point1, UIColor.redColor) @ painting.start_stroke (@ point2, UIColor.blueColor) "commence de nouveaux traits" do @ painting.strokes.length.should == 2 @ painting.strokes [0] .points.should == [@ point1] @ painting.strokes [0] .color.should == UIColor.redColor @ painting.strokes [1] .points.should == [@ point2] @ painting.strokes [1] .color.should == UIColor.blueColor end end décrit "#continue_stroke" avant de faire @ painting.start_stroke (@ point1 , UIColor.redColor) @ painting.continue_stroke (@ point2) @ painting.start_stroke (@ point3, UIColor.blueColor) @ painting.con tinue_stroke (@ point4) end it "ajoute des points aux traits actuels" do @ painting.strokes [0] .points.should == [@point1, @point2] @ painting.strokes [1] .points.should == [ @ point3, @ point4] fin fin fin
Ensuite, modifiez le PaintingView
classe pour dessiner un La peinture
objet au lieu d'un Accident vasculaire cérébral
objet.
classe PaintingView < UIView attr_accessor :painting def drawRect(rectangle) super # ensure the painting is provided return if painting.nil? painting.strokes.each do |stroke| draw_stroke(stroke) end end def draw_stroke(stroke) # set up the drawing context context = UIGraphicsGetCurrentContext() CGContextSetStrokeColorWithColor(context, stroke.color.CGColor) CGContextSetLineWidth(context, 20.0) CGContextSetLineCap(context, KCGLineCapRound) CGContextSetLineJoin(context, KCGLineJoinRound) # move the line to the start point CGContextMoveToPoint(context, stroke.start_point.x, stroke.start_point.y) # add each line in the path stroke.points.drop(1).each do |point| CGContextAddLineToPoint(context, point.x, point.y) end # stroke the path CGContextStrokePath(context); end end
Vous avez changé le accident vasculaire cérébral
attribuer à La peinture
. le drawRect:
méthode itère maintenant sur tous les traits de la peinture et dessine chacun en utilisant draw_stroke
, qui contient le code de dessin que vous avez écrit précédemment.
Vous devez également mettre à jour le contrôleur de vue pour qu'il contienne La peinture
modèle. Au sommet de la PeintureContrôleur
classe, ajouter attr_reader: peinture
. Comme son nom l'indique, le viewDidLoad
méthode du UIViewController
classe-la super-classe de la PeintureContrôleur
class-est appelé lorsque le contrôleur de vue a fini de charger sa vue. le viewDidLoad
méthode est donc un bon endroit pour créer un La peinture
par exemple et définir le La peinture
attribut du PaintingView
objet.
def viewDidLoad @painting = Painting.new painting_view.painting = fin de peinture
Comme toujours, n’oubliez pas d’ajouter des tests pour viewDidLoad
à spec / controllers / painting_controller_spec.rb.
decrire "#viewDidLoad" do it "définit la peinture" do controller.painting.should.be.instance_of Peinture end it "définit l'attribut de peinture de la vue de peinture" do controller.painting_view.painting.should == controller.painting end end
Votre application sera assez ennuyeuse à moins que vous ne laissiez les gens dessiner à l'écran avec leurs doigts. Ajoutons cette fonctionnalité maintenant. Créer un fichier pour le StrokeGestureRecognizer
classe avec ses spécifications en exécutant les commandes suivantes à partir de la ligne de commande.
appuyez sur app / views / stroke_gesture_recognizer.rb appuyez sur spec / views / stroke_gesture_recognizer_spec.rb
Ensuite, créez le squelette pour la classe.
classe StrokeGestureRecognizer < UIGestureRecognizer attr_reader :position end
le StrokeGestureRecognizer
la classe étend la UIGestureRecognizer
classe, qui gère la saisie tactile. Il a un position
attribuer que le PeintureContrôleur
la classe utilisera pour déterminer la position du doigt de l'utilisateur.
Il y a quatre méthodes que vous devez implémenter dans le StrokeGestureRecognizer
classe, touchesBegan: withEvent:
, touchesMoved: withEvent:
, touchesEnded: withEvent:
, et touchesCancelled: withEvent:
. le touchesBegan: withEvent:
La méthode est appelée lorsque l'utilisateur commence à toucher l'écran avec son doigt. le touchesMoved: withEvent:
Cette méthode est appelée à plusieurs reprises lorsque l'utilisateur déplace son doigt et le touchesEnded: withEvent:
Cette méthode est appelée lorsque l'utilisateur lève son doigt de l'écran. Finalement, le touchesCancelled: withEvent:
La méthode est invoquée si le geste est annulé par l'utilisateur.
Votre reconnaissance de gestes doit faire deux choses pour chaque événement, mettre à jour le position
attribuer et changer le Etat
propriété.
classe StrokeGestureRecognizer < UIGestureRecognizer attr_accessor :position def touchesBegan(touches, withEvent: event) super @position = touches.anyObject.locationInView(self.view) self.state = UIGestureRecognizerStateBegan end def touchesMoved(touches, withEvent: event) super @position = touches.anyObject.locationInView(self.view) self.state = UIGestureRecognizerStateChanged end def touchesEnded(touches, withEvent: event) super @position = touches.anyObject.locationInView(self.view) self.state = UIGestureRecognizerStateEnded end def touchesCancelled(touches, withEvent: event) super @position = touches.anyObject.locationInView(self.view) self.state = UIGestureRecognizerStateEnded end end
Les deux touchesEnded: withEvent:
et touchesCancelled: withEvent:
les méthodes définissent l'état sur UIGestureRecognizerStateEnded
. En effet, peu importe que l'utilisateur soit interrompu, le dessin doit rester intact..
Afin de tester le StrokeGestureRecognizer
classe, vous devez pouvoir créer une instance de UITouch
. Malheureusement, il n'y a pas d'API disponible publiquement pour accomplir cela. Pour que cela fonctionne, nous allons utiliser la bibliothèque moqueuse Facon.
Ajouter gem 'motion-facon'
à votre Gemfile et courir installation groupée
. Puis ajouter nécessite "motion-facon"
au dessous de nécessite une "couleur sucre"
dans le rakefile du projet.
Ensuite, implémentez le StrokeGestureRecognizer
spec.
describe StrokeGestureRecognizer étend Facon :: SpecHelpers avant do @stroke_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] end décrivent" #touchesBegan: withEvent: "faire avant de faire @ stroke_gesture_recognizer.touchesBegan (@ touches1, withEvent: nil) end it "définit la position sur la position du geste" do @ stroke_gesture_recognizer.position.should == CGPoint.new (100, 200) end it "définit l'état de la reconnaissance de geste" do @ stroke_gesture_recognizer.state .should == UIGestureRecognizerStateBegan end end décrire "#touchesMoved: withEvent:" doit être précédé de @ stroke_gesture_recognizer.touchesBegan (@ touches1, avecEvénement: nil) définit le positionnement de la fonction * stroke_gesture_recognizer.touchesMoved (@ touches2, withEvent: nil). position du geste "do @ stroke_gesture_recognizer.pos ition.should == CGPoint.new (300, 400) end it "définit l’état de la reconnaissance de geste" do @ stroke_gesture_recognizer.state.should == UIGestureRecognizerStateChanged end décrivent "#touchesEnded: withEvent:" à faire avant: @stroke_gest_regognizer. touchesBegan (@ touches1, withEvent: nil) @ stroke_gesture_recognizer.touchesEnded (@ touches2, withEvent: nil) end it "définit la position sur la position du geste" do @ stroke_gesture_recognizer.position.should == CGPoint.new (300, 400) end it "définit l'état de la reconnaissance de geste" do @ stroke_gesture_recognizer.state.should == UIGestureRecognizerStateEnded end end décrit "#touchesCancelled: withEvent:" do before do_cut_gesture_recognizer.touchesBegan (@ touches1, withEvent: nil) @ touches2, withEvent: nil) end it "définit la position sur la position du geste" do @ stroke_gesture_recognizer.position.should == CGPoint.new (300, 400) end it "définit l'état de la reconnaissance de geste" do @stroke_gesture_r ecognizer.state.should == UIGestureRecognizerStateEnded end end end
extend Facon :: SpecHelpers
rend plusieurs méthodes disponibles dans vos spécifications, y compris moquer
. moquer
est un moyen simple de créer des objets de test qui fonctionnent exactement comme vous le souhaitez. dans le avant
bloquer au début des spécifications, vous vous moquez des instances de UITouch
avec le locationInView:
méthode qui retourne un point prédéfini.
Ensuite, ajoutez un stroke_gesture_changed
méthode à la PeintureContrôleur
classe. Cette méthode recevra une instance du StrokeGestureRecognizer
classe chaque fois que le geste est mis à jour.
def stroke_gesture_changed (stroke_gesture_recognizer) si stroke_gesture_recognizer.state == UIGestureRecognizerStateBegan painting.start_stroke (stroke_gesture_recognizer.position, selected_color) else painting.continue_stroke (stroke_gesture_recognizer.position).
Lorsque l'état de reconnaissance du geste est UIGestureRecognizerStateBegan
, cette méthode commence un nouveau coup dans le La peinture
objet en utilisant le StrokeGestureRecognizer
la position de et couleur_sélectionnée
. Sinon, il continue le trait en cours.
Ajouter les spécifications pour cette méthode.
décrivez "#stroke_gesture_changed" avant de faire glisser (controller.painting_view,: points => [CGPoint.new (100, 100), CGPoint.new (150, 150), CGPoint.new (200, 200)]) le terminez " ajoute les points au trait "do 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) end it "définit la couleur du trait sur la couleur sélectionnée" do controller.painting.strokes.first .color.should == controller.selected_color end end
RubyMotion fournit plusieurs méthodes d’aide pour simuler une interaction utilisateur, notamment: traîne
. En utilisant traîne
, vous pouvez simuler l'interaction d'un utilisateur avec l'écran. le points
l'option vous permet de fournir un tableau de points pour le glisser.
Si vous deviez exécuter les spécifications maintenant, elles échoueraient. C'est parce que vous devez ajouter la reconnaissance de geste au storyboard. Lancer Interface Builder en lançant bundle exec rake ib: ouvert
. Du Bibliothèque d'objets, traîner un Objet dans votre scène, et changer sa classe à StrokeGestureRecognizer
dans le Inspecteur d'identité sur la droite.
Contrôlez et faites glisser depuis le StrokeGestureRecognizer
objecter à la PeintureContrôleur
et choisissez le sélectionnez la couleur
méthode du menu qui apparaît. Cela assurera la sélectionnez la couleur
La méthode est appelée chaque fois que la reconnaissance de geste est déclenchée. Puis, contrôlez et faites glisser depuis le PaintingView
objecter à la StrokeGestureRecognizer
objet et sélectionnez gestureRecognizer
dans le menu qui apparaît.
Ajouter une spécification pour la reconnaissance de geste au PeintureContrôleur
spécifications dans le #painting_view
décrire
bloc.
décris "#painting_view" do it "est connecté dans le storyboard" do controller.painting_view.should.not.be.nil end it "a un identificateur de mouvements de traits" faire controller.painting_view.gestureRecognizers.length.should = shield == 1 controleur. painting_view.gestureRecognizers [0] .should.be.instance_of de StrokeGestureRecognizer end end
C'est tout. Avec ces modifications, votre application devrait maintenant permettre à une personne de dessiner à l'écran. Lancez votre application et amusez-vous.
Il reste quelques touches finales à ajouter avant la fin de votre candidature. Parce que votre application est immersive, la barre d'état est un peu gênante. Vous pouvez le supprimer en réglant la UIStatusBarHidden
et UIViewControllerBasedStatusBarAppearance
valeurs dans Info.plist de l'application. C'est facile à faire dans le RubyMotion installer
bloquer à l'intérieur du Rakefile du projet.
Motion :: Projet :: App.setup do | app | app.name = 'Paint' app.info_plist ['UIStatusBarHidden'] = true app.info_plist ['UIViewControllerBasedStatusBarAppearance'] = false end
Les icônes de l'application et les images de lancement sont incluses dans les fichiers source de ce didacticiel. Téléchargez les images et copiez-les sur le Ressources répertoire du projet. Ensuite, définissez l'icône de l'application dans la configuration de Rakefile. Vous devrez peut-être nettoyer la construction en exécutant bundle exec rake clean: tous
afin de voir la nouvelle image de lancement.
Motion :: Projet :: App.setup do | app | app.name = 'Paint' app.info_plist ['UIStatusBarHidden'] = true app.info_plist ['UIViewControllerBasedStatusBarAppearance'] = false app.icons = ["icon.png"] end
C'est tout. Vous avez maintenant une application complète prête pour un million de téléchargements dans l'App Store. Vous pouvez afficher et télécharger le code source de cette application à partir de GitHub..
Même si votre application est terminée, vous pouvez en ajouter beaucoup plus. Vous pouvez ajouter des courbes entre les lignes, davantage de couleurs, différentes largeurs de ligne, enregistrer, annuler et rétablir, et tout ce que vous pouvez imaginer. Que ferez-vous pour améliorer votre application? Laissez-moi savoir dans les commentaires ci-dessous.