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.