Au fil de différents articles, Nicolas vous a permis de découvrir le framework RubyMotion permettant d’écrire des applications iOS et OS X natives en Ruby. Si vous les avez manqués et que le sujet vous intéresse je vous conseille d’en prendre connaissance sur cette page.
Dans cet article, nous allons aborder l’utilisation de l’interface builder d’ Xcode au sein d’une application RubyMotion. L’interface builder permet de créer les différentes vues de notre application par le biais de widgets. Nous retrouvons des objets comme des champs de texte, des tableaux, des menus et bien plus. La modification du design est également possible.
La création des différentes vues via un outil visuel permet un gain de productivité assez impressionnant et permet également d’avoir une vision globale de notre application via l’utilisation des storyboards. Ceci est l’une des caractéristiques les plus intéressantes, introduites dans Xcode 4.2 et iOS 5 SDK. Il rend la vie du développeur iOS plus simple et vous permet de facilement concevoir l’interface utilisateur de votre application iOS.
Dans les articles précédents, vous avez pu constater que pour générer un label dans un contrôleur, le code ressemblait à cela :
class RootViewController < UIViewController
def viewDidLoad
@label = UILabel.alloc.initWithFrame [[10, 10], [100, 30]]
@label.backgroundColor = UIColor.clearColor
@label.textColor = UIColor.whiteColor
@label.text = "Votre nom"
view.addSubview @label
end
En utilisant l’interface builder (IB), la création se réalise dans l’interface visuelle et nous sélectionnons l’élément via son tag (que l’on détermine dans l’IB) :
class RootViewController < UIViewController
def viewDidLoad
@label = self.view.viewWithTag(TAG_DE_NOTRE_ELEMENT)
end
end
Afin d’illustrer cet article, nous allons créer une application permettant de calculer son indice de masse corporelle (IMC). Cet exemple nous permettra d’aborder les points suivants:
Une vidéo sera créée à la suite de cette article afin d’en améliorer la compréhension. Les fonctionnalités de l’IB y seront parcourues en détails.
Pour la création du projet, nous allons utiliser la gem ‘ib’ qui permet de générer le fichier ib.xcodeproj
à la racine de notre projet et par la même occasion ouvrir l’IB. Cette gem permet de faire connaître à l’IB nos différentes classes, méthodes et outlets définis dans notre code RubyMotion. La notion d’outlet sera abordée dans le prochain article.
Comme vu dans les précédents articles, créons notre application RubyMotion via la commande motion create storyboard_article
.
Ajoutons la gem 'ib'
à notre Gemfile
. Après installation, nous avons 3 nouvelles tâches rake
à disposition :
rake ib # Same as 'ib:open'
rake ib:open # Generates ib.xcodeproj and opens it in ...
rake ib:project # Generates ib.xcodeproj
Lançons donc rake ib:open
pour créer notre projet Xcode et ouvrir l’IB.
Une fois l’IB lancé, créons notre storyboard via le menu d’Xcode, File -> New -> File…
et choisissons Storyboard
.
Nous enregistrons ce fichier dans le dossier resources
de notre application.
Voici à quoi ressemble notre interface builder une fois notre premier View Controller
ajouté (en glissant-déposant le widget View Controller dans notre storyboard - Cadre vert de l’image suivante).
Afin de pouvoir identifier les éléments de notre storyboard dans notre code RubyMotion, nous allons spécifier les noms de classe de nos contrôleurs mais également les tags des éléments que nous allons intégrer dans ce contrôleur (cadre bleu).
Via le moteur de recherche de widgets (en bas à droite), nous allons rechercher et ajouter les éléments suivants :
Pour ajouter la navigation à notre vue, il suffit de lui rajouter une Navigation Controller
via le menu Editor -> Embed In -> Navigation Controller
.
Nous devons spécifier le contrôleur d’entrée de notre storyboard et lui renseigner un StoryBoardID
afin de pouvoir l’identifier lors du chargement du storyboard dans notre application RubyMotion.
Afin de pouvoir identifier chaque élément de notre contrôleur via notre code RubyMotion, il faut utiliser la notion de tag. Les tags doivent être uniques au sein de notre storyboard.
Sélectionnons chacun des éléments dont nous allons vouloir récupérer ou changer la valeur et spécifions lui son tag.
Créons le deuxième contrôleur dans lequel nous allons afficher le résultat de l’IMC utilisateur. Avec la même démarche que précédemment, nous renseignons le nom de la classe, ImcViewController
et lui ajoutons une navigation.
Sur le deuxième contrôleur nous avons ajouté les éléments suivants, toujours en leur spécifiant leur tag :
Maintenant nous souhaitons mettre en place la transition entre ces deux contrôleurs. Concrètement au clic du bouton Calcul
nous voulons afficher le controller ImcViewController
avec le résultat du calcul IMC.
Dans l’IB, il suffit de sélectionner le bouton Calcul
en maintenant la touche Ctrl
et en le glissant déposant sur le contrôleur de destination pour créer la relation que l’on appelle Segue
.
Plusieurs relations sont possibles, voici un tableau récapitulatif concernant les iOS Segues
:
Une fois notre relation créée, il est important de l’identifier en spécifiant le nom du Segue
afin de pouvoir implémenter le traitement des données dans notre code Ruby.
Astuce: l’utilisation des contraintes
permet de garder le positionnement de vos éléments selon les différents devices utilisés (ipad, iphone 3G …). Pour cela il suffit de placer vos éléments, de tous les sélectionner et d’utiliser la fonction Add missing constraints
. Vous pouvez bien évidemment enregistrer ces contraintes manuellement lorsque vous avez des besoins spécifiques.
Voilà ce que nous retrouvons dans notre storyboard :
NavigationController
pour le contrôleur PersonViewController
PersonViewController
NavigationController
pour le contrôleur ImcViewController
ImcViewController
CalculYourImcSegue
- relation entre PersonViewController
et ImcViewController
Le fichier app/app_delegate.rb
est le point d’entrée de l’application. C’est dans la méthode application
que nous pouvons instancier notre fenêtre principale en chargeant notre storyboard.
class AppDelegate
def application(application, didFinishLaunchingWithOptions:launchOptions)
@window = UIWindow.alloc.initWithFrame(UIScreen.mainScreen.bounds)
storyboard = UIStoryboard.storyboardWithName("main", bundle:nil)
rootVC = storyboard.instantiateViewControllerWithIdentifier("main")
@window.rootViewController = rootVC
@window.makeKeyAndVisible
true
end
end
Ici nous chargeons le fichier storyboard et déterminons notre root controller via UIStoryboard.storyboardWithName("main", bundle:nil).instantiateViewControllerWithIdentifier("main")
. main
correspondant au StoryBoardID
de notre contrôleur d’entrée, ici il s’agit bien du NavigationController
associé à la première vue souhaitée soit le contrôleur PersonViewController
.
Il faut ensuite créer les deux contrôleurs PersonViewController
et ImcViewController
au sein de notre code ruby.
Concernant PersonViewController
, dans la méthode viewDidLoad
qui est appelé juste après le rendu de notre contrôleur, nous allons identifier les différents éléments par le biais des tags.
Pour cela, nous utilisons :
self.view.viewWithTag(TAG_DE_NOTRE_ELEMENT)
que nous pouvons mettre dans un helper afin de pouvoir le réutiliser dans toute notre application :
# app/helpers/application_helper.rb
def retrieve_subview_with_tag(topview,tag)
retval = topview.view.subviews.find { |v| v.tag == tag }
retval ||= topview.view
end
Voici à quoi ressemble notre contrôleur :
# app/controllers/person_view_controller.rb
class PersonViewController < UIViewController
GENDER_SELECT = 1
NAME_TEXT = 2
WEIGHT_TEXT = 3
SIZE_TEXT = 4
AGE_TEXT = 5
def viewDidLoad
@gender = retrieve_subview_with_tag(self, GENDER_SELECT)
@name = retrieve_subview_with_tag(self, NAME_TEXT)
@weight = retrieve_subview_with_tag(self, WEIGHT_TEXT)
@size = retrieve_subview_with_tag(self, SIZE_TEXT)
@age = retrieve_subview_with_tag(self, AGE_TEXT)
end
def prepareForSegue(segue, sender:sender)
if segue.identifier == "CalculYourImcSegue"
custom_imc = calcul_imc(@weight.text, @size.text)
custom_title = generate_custom_title(@name.text)
custom_description = generate_description_text(@name.text,
@gender.selectedSegmentIndex,
@age.text,
@weight.text,
@size.text)
custom_imc_category = imc_category(custom_imc)
imc_view_controller = segue.destinationViewController
imc_view_controller.custom_title.text = custom_title
imc_view_controller.custom_description.text = custom_description
imc_view_controller.custom_imc.text = custom_imc
imc_view_controller.custom_imc_category.text = custom_imc_category
end
end
end
La méthode prepareForSegue
est appelée avant d’initier la transition de contrôleur et nous permet d’identifier quelle transition est appelée via le nom que nous avons renseigné sur notre Segue
dans l’IB (if segue.identifier == "CalculYourImcSegue"
).
L’objet segue
passé à la méthode prepareForSegue
nous permet également d’identifier le contrôleur de destination. Nous le stockons dans une variable local afin de pouvoir appelé les méthodes disponibles dans ce contrôleur(imc_view_controller = segue.destinationViewController
).
Les méthodes calcul_imc
, generate_custom_title
, generate_description_text
et imc_category
sont des helpers renvoyant du texte(l’indice IMC, un récapitulatif des informations utilisateurs et la catégorie de votre IMC).
Dans notre contrôleur de destination ImcViewController
, nous allons également identifié les éléments de notre vue par leur tag. Puis nous allons créer des méthodes qui seront appelées depuis la méthode prepareForSegue
du contrôleur PersonViewController
afin de leur passer les données voulues.
# app/controllers/imc_view_controller.rb
class ImcViewController < UIViewController
TITLE_LABEL = 20
DESCRIPTION_TEXT = 21
IMC_TEXT = 22
IMC_CATEGORY = 23
def awakeFromNib
# make sure our views are loaded
self.view
end
def viewDidLoad
@title_label = retrieve_subview_with_tag(self, TITLE_LABEL)
@description_text = retrieve_subview_with_tag(self, DESCRIPTION_TEXT)
@imc = retrieve_subview_with_tag(self, IMC_TEXT)
@custom_imc_categorie = retrieve_subview_with_tag(self, IMC_CATEGORY)
end
def custom_title
@title_label
end
def custom_description
@description_text
end
def custom_imc
@imc
end
def custom_imc_category
@custom_imc_categorie
end
end
Ici il est important de mentionné la méthode awakeFromNib
car elle permet de s’assurer que les informations de notre storyboard sont bien disponibles au sein de notre contrôleur RubyMotion.
Vous pouvez retrouver le code de l’application sur Github.
Voici les deux écrans de l’application :
L’interface builder et l’utilisation du storyboard permet de concevoir rapidement l’UI et le workflow de notre application. L’intégration à RubyMotion se réalise très bien et permet d’éviter de créer les différents éléments à la main. Une vidéo vous sera prochainement proposée afin de vous donner plus de détails.
Maintenant il n’y a plus qu’à tester et créer sa propre application iOS en 20 minutes…
L’équipe Synbioz.
Libres d’être ensemble.