RubyMotion - les onglets de navigation

Synbioz

Dans cet article à propos de RubyMotion, nous allons voir comment mettre en place des onglets de navigation pour améliorer le confort utilisateur de notre application de Todo.

Nous allons améliorer notre application en proposant à l'utilisateur de naviguer comme bon lui semble grâce à un UITabBar bien plus convivial.

Vous pouvez récupérer le code de notre application sur GitHub et vous placer sur le commit « dd7a7bcebd » qui correspond à l'état dans lequel nous avons laissé l'application à la fin de l'article précédent.

Cet article est publié avec l'aimable autorisation de Synbioz, l'article original peut être lu sur le blog de Synbioz : RubyMotion - les onglets de navigation.

Commentez ♪

- Titre : RubyMotion - les onglets de navigation

- Auteur : Synbioz

- Parution : 21 janvier 2014

- Licence : Les sources présentées sur cette page sont libres de droits et vous pouvez les utiliser à votre convenance. Par contre, la page de présentation constitue une œuvre intellectuelle protégée par les droits d'auteur. Copyright ® 2013 Synbioz. Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.

Sommaire

- I. Création du contrôleur de navigation

- II. Hauteur de barre de navigation

- III. Entête non désiré

- III-A. Masquer la barre de statut

- III-B. Changer l'entête du UITabBarController

- IV. Correction du bouton d'ajout de tâche

- V. Conclusion

- VI. Remerciements

I - Création du contrôleur de navigation

Jusqu'ici, nous étions par défaut sur le contrôleur permettant l'ajout d'une tâche. Une fois la tâche ajoutée, nous étions automatiquement redirigés vers le contrôleur de liste des tâches. Depuis cette liste, une icône dans la barre d'entête nous permettait de revenir vers le formulaire.

La première chose à faire pour améliorer l'ergonomie est d'indiquer à notre application que nous voulons utiliser une navigation à base d'onglets plutôt que le système d'application à vue unique.

C'est donc le fichier app/app_delegate.rb qui va être concerné. Plutôt que d'instancier le TaskViewController et de le déclarer comme rootViewController, nous allons créer une méthode qui instancie un UITabBarController et définit ses différents onglets. C'est ensuite cette méthode qui sera utilisée pour définir le rootViewController :

class AppDelegate
def application(application, didFinishLaunchingWithOptions:launchOptions)
@window = UIWindow.alloc.initWithFrame(UIScreen.mainScreen.bounds)
@window.rootViewController = appTabBarController
@window.makeKeyAndVisible

true
end

def appTabBarController
tabBarController = UITabBarController.alloc.init
tabBarController.viewControllers = [
TaskViewController.alloc.init,
UINavigationController.alloc.initWithRootViewController(ListViewController.alloc.initWithStyle(UITableViewStylePlain))
]
tabBarController
end
end

Le menu à onglet est en place et fonctionnel, mais sans titre, ni icône :

Modifions le code pour nommer nos onglets :

class AppDelegate
def application(application, didFinishLaunchingWithOptions:launchOptions)
@window = UIWindow.alloc.initWithFrame(UIScreen.mainScreen.bounds)
@window.rootViewController = appTabBarController
@window.makeKeyAndVisible

true
end

def appTabBarController
taskViewController = TaskViewController.alloc.init
taskViewController.title = "Ajouter"

listViewController = ListViewController.alloc.initWithStyle(UITableViewStylePlain)
listViewController.title = "Tâches"

tabBarController = UITabBarController.alloc.init
tabBarController.viewControllers = [
taskViewController,
UINavigationController.alloc.initWithRootViewController(listViewController)
]

tabBarController
end
end

Comme vous pouvez le voir, pour résoudre notre problème, nous n'avons eu qu'à donner un titre à nos contrôleurs. Ce titre est utilisé automatiquement par les UITabBarItems.

Voici le résultat :

Nous pourrions également définir des icônes pour chaque contrôleur via la propriété .tabBarItem.image,mais nous ne le ferons pas ici.

Quelques problèmes entachent notre application suite à ces modifications :

- la barre de navigation couvre partiellement notre bouton d'ajout de tâche ;

- l'affichage de l'onglet « Tâches » fait apparaître un entête non désiré généré par la UITabBarController ;

- le bouton de validation de tâche fait planter l'application, car elle utilise un système de transition incompatible avec le nouveau fonctionnement ;

- depuis la liste des tâches, le bouton de retour à la liste ne fonctionne que partiellement (il affiche le bon contenu, mais sans changer d'onglet).

Corrigeons ces quelques problèmes.

II - Hauteur de barre de navigation

Pour corriger le problème de la barre de navigation qui couvre partiellement le bouton d'ajout de tâche, deux solutions s'offrent à nous. Nous pourrions simplement remonter le bouton ou alors réduire la hauteur de la barre de navigation.

Optons pour cette seconde solution qui nous permettra de découvrir de nouvelles choses.

Pour agir sur la position et la taille de la UITabBar, il faut redéfinir sa propriété frame. Nous allons donc la définir dans notre méthode appTabBarController du fichier app/app_delegate.rb juste après l'avoir instanciée :

def appTabBarController
…

tabBarController = UITabBarController.alloc.init
tabBarController.tabBar.frame = [[0, UIScreen.mainScreen.bounds.size.height - 20], [UIScreen.mainScreen.bounds.size.width, 20]]

…
end

On positionne donc la UITabBar sur le bord gauche de l'écran et à 20px de la bordure du bas. On définit la taille avec une largeur égale à celle de l'écran et une hauteur de 20px.

Voici ce que nous obtenons :

III - Entête non désiré

Deux problèmes existent sur l'onglet « Tâches ». Premièrement, la barre de statut d'iOS s'affiche. Il y a également l'entête autogénéré par le UITabBarController.

III-A - Masquer la barre de statut

Nous n'avons pas le souci avec notre contrôleur d'ajout de tâches, car nous lui avons déjà demandé explicitement de masquer la barre de statut. Appliquons le même principe au contrôleur de liste des tâches.

Dans le fichier app/controllers/list_view_controller.rb, il faut ajouter la méthode suivante :

def prefersStatusBarHidden
true
end

Je l'ajoute en ce qui me concerne dans les méthodes privées. Voici le résultat obtenu :

III-B - Changer l'entête du UITabBarController

Il faut maintenant remplacer l'entête généré par le UITabBarController par notre entête personnalisé qui est actuellement affiché comme entête de notre tableau de tâches.

Commençons donc par supprimer les deux méthodes qui servent à la génération de cet entête dans notre tableau. Les deux méthodes concernées sont tableView:viewForHeaderInSection et tableView:heightForHeaderInSection:.

Notre tableau ne nous affiche donc plus que son contenu, sans entête :

Nous devons changer l'entête par défaut, pour cela nous allons, dans le viewDidLoad, définir une image de fond pour la navigationBar puis changer son texte pour y mettre le titre de notre application :

def viewDidLoad
self.navigationController.navigationBar.setBackgroundImage(UIImage.imageNamed("bgHeader.png"), forBarMetrics:UIBarMetricsDefault)

self.navigationController.navigationBar.titleTextAttributes = {
NSForegroundColorAttributeName => UIColor.colorWithRed(0.702, green: 0.702, blue: 0.702, alpha: 1.000),
NSFontAttributeName => UIFont.fontWithName("AvenirNext-Bold", size: 25)
}

self.title = "RubyMotion Todo"
self.navigationController.tabBarItem.title = "Tâches"
end

La première ligne nous permet d'utiliser notre image de fond. Ensuite nous définissons les attributs du texte de titre pour la navigationBar. Attention à bien utiliser les noms de classe comme clé, sinon cette ligne n'aura aucun effet. C'est pour cette raison que j'utilise l'ancienne syntaxe de Hash.

Les deux dernières lignes permettent de définir le titre. En changeant le title du contrôleur, la chaîne est mise à jour dans l'entête, mais cette manipulation a pour effet de bord de redéfinir le titre du bouton d'onglet en bas. C'est pourquoi la dernière ligne redéfinit le titre de ce bouton.

Il nous manque toujours le bouton de suppression d'une tâche. Nous ne remettrons pas le bouton de retour au formulaire puisque la navigation par onglet nous le permet.

Cet ajout est très simple puisqu'il consiste à ajouter le code suivant à la fin de la méthode viewDidLoad :

leftButtonItem = UIBarButtonItem.alloc.initWithCustomView(deleteButton)
self.navigationItem.setLeftBarButtonItem(leftButtonItem)

Les navigationBars sont prévus pour recevoir un bouton à gauche et à droite. Notre méthode générant le bouton existant déjà nous n'avions plus qu'à la réutiliser pour instancier un UIBarButtonItem et le placer à gauche.

Noter toutefois que j'ai légèrement réduit la taille du bouton pour qu'il s'intègre mieux visuellement :

IV - Correction du bouton d'ajout de tâche

Notre bouton d'ajout de tâche pose un souci. Au moment de la transition du contrôleur d'ajout vers le contrôleur de liste des tâches, l'application plante, car le contrôleur de liste initialisé ligne 18 de app/controllers/task_view_controller.rb n'est pas appelé dans le contexte attendu. Il n'intègre pas la notion de navigationController qui est requis dans notre nouveau mode de fonctionnement.

Il n'y a maintenant plus aucune raison d'initialiser ce contrôleur à cet endroit. En effet, il est déjà initialisé dans app/app_delegate.rb au chargement de l'application et intégré en tant que contrôleur lié au UITabBarController.

On peut donc retirer le code suivant de la méthode addTask :

@listViewController = ListViewController.alloc.initWithStyle(UITableViewStylePlain)
@listViewController.view.frame = self.view.frame

UIView.transitionFromView(self.view,
toView: @listViewController.view,
duration: 0.5,
options: UIViewAnimationOptionTransitionFlipFromLeft,
completion: nil)

Si vous testez à nouveau l'application, vous verrez que l'ajout de tâche fonctionne à nouveau. Il reste une chose à gérer : la transition automatique vers l'onglet de la liste des tâches. Nous retournons donc dans la méthode addTask pour ajouter le code suivant :

self.tabBarController.selectedIndex = 1

Cette ligne suffira à indiquer l'index de l'onglet souhaité et à l'afficher.

V - Conclusion

Nous avons pu mettre en place une navigation à base d'onglets, bien plus intuitive et flexible pour les utilisateurs.

Passer d'une application « single-page » à une application basée sur les onglets implique quelques changements dans la gestion, la logique et la présentation, mais se fait finalement sans trop d'efforts.

L'utilisation des UITabBars est très courante dans les applications iOS, c'est donc un élément clé à maîtriser.

Cette simple modification donne plus de cachet à l'application et permet également d'étendre nos possibilités en tant que développeur. On évite également d'entasser les éléments de navigation dans le peu d'endroits qu'il nous reste à disposition dans notre interface.

Vous trouverez l'ensemble du code d'exemple de cet article, découpé en commits, sur GitHub.

VI - Remerciements

Cet article est publié avec l'aimable autorisation de Synbioz, l'article original peut être lu sur le blog de Synbioz : RubyMotion - les onglets de navigation.