RubyMotion - les cellules personnalisées

Publié le 5 novembre 2013 par Nicolas Cavigneaux | mobile

Cet article est publié sous licence CC BY-NC-SA

Dans cet article à propos de RubyMotion, nous allons voir comment mettre en place des cellules personnalisées qui seront utilisées dans un UITableView. Nous utiliserons donc comme base l’application de todo développée à l’occasion de l’article concernant rubymotion et les tableviews.

Si vous prenez cette suite d’article en cours de route, RubyMotion est un framework permettant d’écrire des applications iOS et OS X natives en Ruby.

Vous pouvez récupérer le code de notre application sur GitHub et vous placer sur le commit “7cf07001ae” qui correspond à l’état dans lequel nous avons laissé l’application à la fin de l’article précédent.

L’idée ici est de démontrer que nous ne sommes pas cantonnés aux types de cellules disponibles par défaut lorsqu’on construit une table. Il est possible d’être tout à fait original et d’aller beaucoup plus loin. Il s’avère d’ailleurs qu’en pratique, beaucoup d’applications utilisent leurs propres cellules personnalisées pour présenter leurs informations au sein d’une table.

Création de cellule personnalisées

Lorsque vous souhaitez sortir des sentiers battus, que votre designer vous propose une présentation des éléments élaborée, autre que celle livrée de base avec le SDK iOS, il vous faut créer des éléments personnalisés.

Ici nous souhaitons créer des cellules de tableau avec une présentation bien spécifique, pour ce faire nous allons créer une classe héritant de UITableViewCell.

Dans le répertoire app/views, créons le fichier todo_table_view_cell.rb :

class TodoTableViewCell < UITableViewCell
  TODO_CELL_REUSE_ID = "TodoTableViewCell"

  attr_accessor :title, :date, :priority
end

Nous créons donc une classe héritant de UITableViewCell contenant trois attributs qui serviront pour stocker le titre de la tâche, sa date de création ainsi que sa priorité ce qui permettra d’afficher un badge en fonction de celle-ci. Vous noterez qu’on définit également une constante qui sera utilisée comme identifiant de ré-utilisation des cellules comme nous l’avons déjà fait dans l’article précédent.

Jusque là nous avons créé nos cellules directement depuis ListViewController en passant par la méthode tableView:cellForRowAtIndexPath:, nous allons maintenant déplacer cette logique dans notre classe dédiée à la création de cellules. Voici donc la méthode à ajouter à TodoTableViewCell :

def self.cellForTask(task, inTableView:tableView)
  cell = tableView.dequeueReusableCellWithIdentifier(TODO_CELL_REUSE_ID) || TodoTableViewCell.alloc.initWithStyle(UITableViewCellStyleDefault, reuseIdentifier:TODO_CELL_REUSE_ID)
  cell.title = task.name
  cell.date = task.created_at
  cell.priority = task.priority
  cell.selectionStyle = UITableViewCellSelectionStyleNone
  cell
end

Cette méthode permet de vérifier si la cellule correspondante est cachée. Si elle ne l’est pas, nous la créons.

Nous stockons ensuite le titre, la date de création et la priorité grâce à l’objet task qui a été passé. Nous définissons un style vide pour la cellule puisque nous voulons gérer nous même son aspect visuel. Finalement nous retournons cette cellule.

Il ne nous reste plus qu’à utiliser ces cellules personnalisées dans notre contrôleur ListViewController :

def tableView(tableView, cellForRowAtIndexPath:indexPath)
  task = @tasks[indexPath.row]
  TodoTableViewCell.cellForTask(task, inTableView:tableView)
end

Vous pouvez lancer l’application, voici ce que vous obtenez :

UITableViewCell vide

La table est vide, aucune des cellules n’est remplie. En effet, lorsque vous utilisez des cellules personnalisées, il faut leur préciser comment afficher les informations au sein de la cellule. Nous allons donc, comme pour n’importe quel UIView, placer nos différent éléments en utilisant la méthode addSubview.

Ajout des éléments

Image de fond

Les UIView mettent à disposition la méthode layoutSubviews qui permet de définir comment seront disposées les vues enfants, nous allons l’utiliser pour disposer nos informations. Commençons par ajouter une image de fond à nos cellules :

def layoutSubviews
  @background_image = UIImageView.alloc.initWithImage(UIImage.imageNamed("bgCell"))
  self.addSubview(@background_image)
end

et dans app/controllers/list_view_controller.rb :

def tableView(tableView, heightForRowAtIndexPath:indexPath)
  50
end

Nous ajoutons donc notre image de fond mais comme vous pouvez le remarquer, nous prenons le soin de préciser au contrôleur la hauteur de nos cellules pour qu’elle corresponde à la hauteur de notre image (50px ici). Par défaut, la hauteur appliquée aux cellules est de 44px ce qui masquerait une partie de notre image de fond.

Voici ce que vous obtenez en lançant l’application :

UITableViewCell avec fond

Nom de la tâche

Nous pouvons maintenant passer à l’ajout du nom de la tâche dans la cellule. Pour ce faire, nous allons enrichir la méthode layoutSubviews et ajouter une méthode dédiée à la création du label :

def layoutSubviews
  @background_image = UIImageView.alloc.initWithImage(UIImage.imageNamed("bgCell"))
  self.addSubview(@background_image)

  self.addSubview(titleLabel)
end

def titleLabel
  titleLabel = UILabel.alloc.initWithFrame([[10, 0], [300, 40]])
  titleLabel.font = UIFont.fontWithName("AmericanTypewriter-Bold", size: 18)
  titleLabel.textColor = UIColor.blueColor
  titleLabel.adjustsFontSizeToFitWidth = true
  titleLabel.backgroundColor = UIColor.clearColor
  titleLabel.text = @title
  titleLabel
end

Voici le résultat :

UITableViewCell avec titre

Ajout de la date

Aucune surprise ici puisque le principe va être exactement le même que pour l’ajout du nom de la tâche :

def layoutSubviews
  @background_image = UIImageView.alloc.initWithImage(UIImage.imageNamed("bgCell"))
  self.addSubview(@background_image)

  self.addSubview(titleLabel)
  self.addSubview(dateLabel)
end

def dateLabel
  dateLabel = UILabel.alloc.initWithFrame([[10, 10], [300, 40]])
  dateLabel.font = UIFont.fontWithName("AmericanTypewriter-Bold", size: 14)
  dateLabel.textColor = UIColor.blackColor
  dateLabel.adjustsFontSizeToFitWidth = true
  dateLabel.backgroundColor = UIColor.clearColor
  dateLabel.text = @date.strftime("%d/%M/%Y")
  dateLabel
end

UITableViewCell avec date

Ajout du marqueur de priorité

Nous allons maintenant ajouter à droite dans notre cellule un marqueur de priorité qui sera représentée par une image que nous ajoutons donc à notre dossier resources. Ce marqueur ne sera à afficher que si la tâche est en priorité haute :

def layoutSubviews
  @background_image = UIImageView.alloc.initWithImage(UIImage.imageNamed("bgCell"))
  self.addSubview(@background_image)

  self.addSubview(titleLabel)
  self.addSubview(dateLabel)
  self.addSubview(priorityImage) if @priority == "Haut"
end

def priorityImage
  priorityImage = UIImageView.alloc.initWithImage(UIImage.imageNamed("important"))
  priorityImage.frame = ([[290, 8], [29, 29]])
  priorityImage
end

La méthode d’ajout de l’image n’est appelée que si notre tâche en cours de rendu à un priorité haute, information que nous stockons dans une variable d’instance à la création d’une nouvelle cellule personnalisée.

Voyons un exemple de rendu avec deux tâches en priorité basse et une en priorité haute :

UITableViewCell avec priorité

Conclusion

Nous avons vu dans cet article comment mettre en place un UITableViewCell pour pouvoir dépasser les limites des types de cellule disponibles par défaut. Vous pouvez donc exploiter cette possibilité pour intégrer un design personnalisé et mettre en œuvre toutes vos idées.

Un exercice intéressant pourrait être d’ajouter un bouton dans la cellule qui permettrait de modifier le statut (fait / à faire) de la tâche. Le bouton serait représenté par deux images qui alterneraient en fonction du statut.

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


L’équipe Synbioz.

Libres d’être ensemble.