{"id":60,"date":"2013-10-15T18:24:18","date_gmt":"2013-10-15T17:24:18","guid":{"rendered":"http:\/\/tim.cstj.qc.ca\/cours\/xcode\/wp\/?page_id=60"},"modified":"2013-10-15T18:24:18","modified_gmt":"2013-10-15T17:24:18","slug":"timflix-delegation-et-uitableview-personnalisee","status":"publish","type":"page","link":"https:\/\/ve2cuy.com\/xcode\/labo\/timflix-delegation-et-uitableview-personnalisee\/","title":{"rendered":"TIMFlix &#8211; D\u00e9l\u00e9gation et UITableView personnalis\u00e9e"},"content":{"rendered":"<p>Description:<br \/>\nAvec ce tutoriel, nous apprendrons \u00e0 construire une application qui affiche, grace \u00e0 &lsquo;UITableView&rsquo; et une cellule personnalis\u00e9e, une liste de titres vid\u00e9os renseign\u00e9e par un fichier de propri\u00e9t\u00e9s (plist).<br \/>\nUne page contenant le d\u00e9tail de la vid\u00e9o sera affich\u00e9e &#8211; par un &lsquo;segue&rsquo; &#8211; suite \u00e0 la s\u00e9lection d&rsquo;un \u00e9l\u00e9ment de la table.<br \/>\nVoici le &lsquo;storyboard&rsquo;:<br \/>\n<div id=\"attachment_64\" style=\"width: 810px\" class=\"wp-caption alignnone\"><a href=\"\/xcode\/index.php\/labo\/timflix-delegation-et-uitableview-personnalisee\/timflix-storyboard\/\" rel=\"attachment wp-att-64\"><img loading=\"lazy\" decoding=\"async\" aria-describedby=\"caption-attachment-64\" class=\"size-medium wp-image-64\" src=\"\/xcode\/wp-content\/uploads\/2013\/10\/TIMFlix-storyboard-800x370.png\" alt=\"\" width=\"800\" height=\"370\" \/><\/a><p id=\"caption-attachment-64\" class=\"wp-caption-text\">Figure 1<\/p><\/div><br \/>\nAinsi qu&rsquo;une vid\u00e9o du r\u00e9sultat final:<br \/>\n<iframe loading=\"lazy\" title=\"Projet TIMFlix\" width=\"700\" height=\"394\" src=\"https:\/\/www.youtube.com\/embed\/Kq_U0TWATdo?feature=oembed\" frameborder=\"0\" allow=\"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture\" allowfullscreen><\/iframe><br \/>\nObjectifs:<br \/>\nSavoir cr\u00e9er un tableau de type &lsquo;NSArray&rsquo; \u00e0 partir d&rsquo;un fichier de propri\u00e9t\u00e9s. \u00a0Comprendre et utiliser la d\u00e9l\u00e9gation de l&rsquo;objet &lsquo;UITableView&rsquo;. \u00a0Etre capable de pr\u00e9parer et lancer par programmation, une transition vers une autre sc\u00e8ne (&lsquo;segue&rsquo;). \u00a0Savoir programmer une &lsquo;UITableViewCell&rsquo; personnalis\u00e9e (design et classe).<br \/>\n\u00c9l\u00e9ments de contenu:<\/p>\n<ul>\n<li>Storyboard<\/li>\n<li>UITableView<\/li>\n<li>UITableViewCell &#8211; dequeueReusableCellWithIdentifier<\/li>\n<li>UITableViewDelegate &#8211; didSelectRowAtIndexPath<\/li>\n<li>UITableViewDataSource &#8211; numberOfRowsInSection, cellForRowAtIndexPath<\/li>\n<li>Seque &#8211; prepareForSegue, performSegueWithIdentifier:@\u00a0\u00bbidentificateur\u00a0\u00bb<\/li>\n<li>performSelector:@selector(unSelecteur) afterDelay:<\/li>\n<li>NSArray &#8211;\u00a0arrayWithContentsOfFile:[NSBundle&#8230;]<\/li>\n<\/ul>\n<p>Le projet de d\u00e9part est <a title=\"TIMFlix\" href=\"\/cours\/xcode\/contenu\/ateliers\/timflix\/projet-timflix-depart.zip\">ici<\/a><br \/>\n&nbsp;<\/p>\n<hr \/>\n<h3>\u00c9tape 1<\/h3>\n<p>\u00c0 cette \u00e9tape, nous allons mettre en place les sc\u00e8nes du projet, programmer une transition (segue), de la page de chargement vers la liste, positionner un UITableView puis finalement, renseigner une transition de la liste vers la sc\u00e8ne de d\u00e9tail.<br \/>\n<strong><em>ACTION &#8211; Ouvrons le &lsquo;Storyboard&rsquo; du projet de d\u00e9part:<\/em><\/strong><br \/>\n<div id=\"attachment_104\" style=\"width: 549px\" class=\"wp-caption alignnone\"><a href=\"\/xcode\/index.php\/labo\/timflix-delegation-et-uitableview-personnalisee\/timflix-figure01\/\" rel=\"attachment wp-att-104\"><img loading=\"lazy\" decoding=\"async\" aria-describedby=\"caption-attachment-104\" class=\" wp-image-104\" src=\"\/xcode\/wp-content\/uploads\/2013\/10\/timflix-figure01-539x600.png\" alt=\"\" width=\"539\" height=\"600\" \/><\/a><p id=\"caption-attachment-104\" class=\"wp-caption-text\">figure 2<\/p><\/div><br \/>\n&nbsp;<br \/>\n<strong><em><strong><em>ACTION &#8211; A<\/em><\/strong>joutons deux nouvelles sc\u00e8nes avec leur classe: VCListeDesVideos et VCDetailVideoCourante:<\/em><\/strong><br \/>\n&nbsp;<br \/>\n<div id=\"attachment_112\" style=\"width: 810px\" class=\"wp-caption alignnone\"><a href=\"\/xcode\/wp-content\/uploads\/2013\/10\/timflix-figure021.png\"><img loading=\"lazy\" decoding=\"async\" aria-describedby=\"caption-attachment-112\" class=\"size-medium wp-image-112\" src=\"\/xcode\/wp-content\/uploads\/2013\/10\/timflix-figure021-800x400.png\" alt=\"timflix-figure02\" width=\"800\" height=\"400\" \/><\/a><p id=\"caption-attachment-112\" class=\"wp-caption-text\">figure 3<\/p><\/div><\/p>\n<h4>Vid\u00e9o \u00e9tape01<\/h4>\n<p><iframe loading=\"lazy\" title=\"TIMFlix-E\u0301tape01\" width=\"700\" height=\"394\" src=\"https:\/\/www.youtube.com\/embed\/8a5Qhrv97Qo?feature=oembed\" frameborder=\"0\" allow=\"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture\" allowfullscreen><\/iframe><br \/>\n<strong><em><strong><em>ACTION &#8211; A<\/em><\/strong>joutons de la couleur, un UITableView ainsi qu&rsquo;un d\u00e9placement (segue) \u00e0 partir la liste des vid\u00e9os vers la sc\u00e8ne de d\u00e9tail:<\/em><\/strong><br \/>\n<div id=\"attachment_121\" style=\"width: 810px\" class=\"wp-caption alignnone\"><a href=\"\/xcode\/wp-content\/uploads\/2013\/10\/timflix-figure03.png\"><img loading=\"lazy\" decoding=\"async\" aria-describedby=\"caption-attachment-121\" class=\"size-medium wp-image-121\" src=\"\/xcode\/wp-content\/uploads\/2013\/10\/timflix-figure03-800x434.png\" alt=\"timflix-figure03\" width=\"800\" height=\"434\" \/><\/a><p id=\"caption-attachment-121\" class=\"wp-caption-text\">figure 4<\/p><\/div><\/p>\n<h4>\u00a0Vid\u00e9o \u00e9tape01.1<\/h4>\n<p><iframe loading=\"lazy\" title=\"TIMFlix-E\u0301tape01.1\" width=\"700\" height=\"394\" src=\"https:\/\/www.youtube.com\/embed\/p6eVOvuDqXo?feature=oembed\" frameborder=\"0\" allow=\"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture\" allowfullscreen><\/iframe><br \/>\nLa programmation de la transition, de la sc\u00e8ne d&rsquo;introduction vers la sc\u00e8ne \u00a0de la liste des vid\u00e9os, est r\u00e9alis\u00e9e avec les m\u00e9thodes &lsquo;performSelector: afterDelay:&rsquo; et &lsquo;performSegueWithIdentifier&rsquo;.<br \/>\nVoici le code \u00e0 ajouter \u00e0 la classe de la sc\u00e8ne d&rsquo;introduction:<\/p>\n<pre class=\"toolbar:1 lang:default decode:true\">- (void)viewDidLoad\n{\n    [super viewDidLoad];\n    [self performSelector:@selector(transitionVersListeVideos) withObject:self afterDelay:3];\n}\n-(void) transitionVersListeVideos {\n    [self performSegueWithIdentifier:@\"versListeVideos\" sender:self];\n}<\/pre>\n<p>L&rsquo;\u00e9tape 1 compl\u00e9t\u00e9e est\u00a0<a title=\"TIMFlix\" href=\"https:\/\/docs.google.com\/uc?export=download&amp;id=0Bxk2a0khnQQ7aWg2WU1PRkQ5c0U\">ici<\/a><\/p>\n<hr \/>\n<h3>\u00c9tape 2<\/h3>\n<p>Objectifs:<\/p>\n<ul>\n<li>Cr\u00e9er un tableau contenant les enregistrements du fichier\u00a0\u00a0&lsquo;Listes des videos.plist&rsquo;.<\/li>\n<li>Renseigner la d\u00e9l\u00e9gation des protocoles &lsquo;donn\u00e9es&rsquo; et &lsquo;d&rsquo;\u00e9v\u00e9nements&rsquo; de la classe UITableView.<\/li>\n<li>Programmer les m\u00e9thodes protocoles &lsquo;tableView:numberOfRowsInSection:&rsquo; et &lsquo;tableView: cellForRowAtIndexPath:&rsquo;<\/li>\n<li>Renseigner les propri\u00e9t\u00e9s &lsquo;labelText&rsquo; et &lsquo;image&rsquo; de la &lsquo;UITableViewCell&rsquo; \u00e0 partir des donn\u00e9es du tableau.<\/li>\n<li>Tester l&rsquo;affichage de la table.<\/li>\n<\/ul>\n<p>\u00c9tape 2.1<br \/>\nCommen\u00e7ons par cr\u00e9er un tableau &lsquo;<strong>NSArray<\/strong>&lsquo; \u00e0 partir de la liste de propri\u00e9t\u00e9s &lsquo;<strong>Listes des videos.plist&rsquo;<\/strong>. \u00a0Note, ce fichier est dans le projet de d\u00e9part.<br \/>\nVoici un extrait du fichier contenant les donn\u00e9es des vid\u00e9os:<\/p>\n<pre class=\"toolbar:1 lang:default decode:true\">&lt;array&gt;\n\t&lt;dict&gt;\n\t\t&lt;key&gt;titre&lt;\/key&gt;\n\t\t&lt;string&gt;Pirates du cyberspace&lt;\/string&gt;\n\t\t&lt;key&gt;duree&lt;\/key&gt;\n\t\t&lt;string&gt;1 h 48 m&lt;\/string&gt;\n\t\t&lt;key&gt;classement&lt;\/key&gt;\n\t\t&lt;string&gt;18A&lt;\/string&gt;\n\t\t&lt;key&gt;pochette&lt;\/key&gt;\n\t\t&lt;string&gt;pochette-000.jpg&lt;\/string&gt;\n\t\t&lt;key&gt;annee&lt;\/key&gt;\n\t\t&lt;string&gt;1995&lt;\/string&gt;\n\t\t&lt;key&gt;description&lt;\/key&gt;\n\t\t&lt;string&gt;L&amp;apos;utilisation des ordinateurs lui \u00e9tant bannie pour des ann\u00e9es, un ex-enfant prodige replonge dans le monde du crime informatique avec trois autres pirates.&lt;\/string&gt;\n\t\t&lt;key&gt;cote&lt;\/key&gt;\n\t\t&lt;string&gt;4&lt;\/string&gt;\n\t&lt;\/dict&gt;\n\t&lt;dict&gt;\n\t\t&lt;key&gt;titre&lt;\/key&gt;\n\t\t&lt;string&gt;Star trek: The Next Generation &lt;\/string&gt;\n\t\t&lt;key&gt;duree&lt;\/key&gt;\n\t\t&lt;string&gt;1 h 48 m&lt;\/string&gt;\n\t\t&lt;key&gt;classement&lt;\/key&gt;\n\t\t&lt;string&gt;18A&lt;\/string&gt;\n\t\t&lt;key&gt;pochette&lt;\/key&gt;\n\t\t&lt;string&gt;pochette-007.jpg&lt;\/string&gt;\n\t\t&lt;key&gt;annee&lt;\/key&gt;\n\t\t&lt;string&gt;1987&lt;\/string&gt;\n\t\t&lt;key&gt;description&lt;\/key&gt;\n\t\t&lt;string&gt;In the year 2364, Capt. Jean-Luc Picard leads the new Enterprise on missions of discovery. First Officer William Riker, engineer Geordi La Forge, and Klingon crewmember Worf join Picard as they explore the universe and interact with alien species.&lt;\/string&gt;\n\t\t&lt;key&gt;cote&lt;\/key&gt;\n\t\t&lt;string&gt;4&lt;\/string&gt;\n\t&lt;\/dict&gt;\n...\n&lt;array&gt;<\/pre>\n<p>Ce fichier va nous permettre de cr\u00e9er un tableau qui contiendra des objets de type &lsquo;NSDictionary&rsquo;<br \/>\nNous pourrons acc\u00e9der aux \u00e9l\u00e9ments du tableau avec la syntaxe:<br \/>\ntableauDesVideos[noElement]<br \/>\net<br \/>\ntableauDesVideos[noElement][@\u00a0\u00bbpochette\u00a0\u00bb], &#8230;<br \/>\n<em><strong><strong><em>ACTION &#8211;\u00a0<\/em><\/strong>Modifions la classe de la sc\u00e8ne &lsquo;Liste des vid\u00e9os&rsquo;.<\/strong><\/em><br \/>\nNote: \u00a0nous voulons cr\u00e9er le tableau des vid\u00e9os au chargement de la sc\u00e8ne &lsquo;Liste des vid\u00e9os&rsquo;, c&rsquo;est pourquoi nous modifions le fichier &lsquo;VCListeDesVideos.m&rsquo; et non pas le fichier &lsquo;ViewController.m&rsquo;.<br \/>\n&nbsp;<\/p>\n<pre class=\"toolbar:1 lang:default decode:true\">@interface VCListeDesVideos (){\n\/\/ Variables de classe\n     NSArray * tableauDesVideos;\n}\n@end\n@implementation VCListeDesVideos\n- (void)viewDidLoad\n{\n    [super viewDidLoad];\n    tableauDesVideos = [[NSArray alloc]initWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@\"Listes des videos\" ofType:@\"plist\"]];\n    \/\/ NSLog(@\"tableauDesVideos = %@\", tableauDesVideos);\n    NSLog(@\"Pochette de la premi\u00e8re vid\u00e9o = %@\", tableauDesVideos[0][@\"pochette\"]);\n}\n@end<\/pre>\n<p>&nbsp;<br \/>\nNote: Nous aurions pu cr\u00e9er un objet de type NSDictionary \u00e0 partir d&rsquo;un des \u00e9l\u00e9ments du &lsquo;tableauDesVideos&rsquo; en proc\u00e9dant comme suit:<\/p>\n<pre class=\"toolbar:1 lang:default decode:true\">    tableauDesVideos = [[NSArray alloc]initWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@\"Listes des videos\" ofType:@\"plist\"]];\n    NSDictionary * uneVideo = tableauDesVideos[0];\n    NSLog(@\"Titre = %@, dur\u00e9e = %@\", uneVideo[@\"titre\"], uneVideo[@\"duree\"]);<\/pre>\n<p>&nbsp;<br \/>\n&nbsp;<\/p>\n<h4>Vid\u00e9o \u00e9tape02.1<\/h4>\n<p><iframe loading=\"lazy\" title=\"TIMFlix-E\u0301tape02.1\" width=\"700\" height=\"394\" src=\"https:\/\/www.youtube.com\/embed\/CRC0Uav5lEQ?feature=oembed\" frameborder=\"0\" allow=\"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture\" allowfullscreen><\/iframe><\/p>\n<h3><\/h3>\n<h3>\u00c9tape 2.2 &#8211;\u00a0Renseigner et programmer la d\u00e9l\u00e9gation de l&rsquo;objet UITableView<\/h3>\n<p>Pour \u00eatre en mesure d&rsquo;utiliser un objet de type UITableView dans un projet, il faudra se d\u00e9clarer d\u00e9l\u00e9gu\u00e9 d&rsquo;au moins le protocole &lsquo;UITableViewDataSource&rsquo; et au besoin &lsquo;UITableViewDataSource&rsquo;.<br \/>\nEtre d\u00e9l\u00e9gu\u00e9 pour un objet, c&rsquo;est devoir impl\u00e9menter des m\u00e9thodes dont les signatures sont prescrites par la super classe.<br \/>\nPar exemple, pour \u00eatre en mesure de populer notre UITableView \u00a0\u00e0 partir du tableau des vid\u00e9os, une de nos classes devra souscrire au protocole &lsquo;UITableViewDataSource&rsquo; et programmer les deux m\u00e9thodes obligatoires suivante:<\/p>\n<blockquote><p>&#8211; (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:<br \/>\n&#8211; (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:<\/p><\/blockquote>\n<p>Le fichier.h d&rsquo;une classe \u00a0nous renseigne sur les m\u00e9thodes de d\u00e9l\u00e9gation, le niveau de prescription (@required ou @optional) ainsi que sur la syntaxe.<br \/>\n&nbsp;<br \/>\nVoici un extrait du fichier UITableView.h<\/p>\n<pre class=\"toolbar:1 lang:default decode:true\">@protocol UITableViewDataSource&lt;NSObject&gt;\n@required\n- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section;\n\/\/ Row display. Implementers should *always* try to reuse cells by setting each cell's reuseIdentifier and querying for available reusable cells with dequeueReusableCellWithIdentifier:\n\/\/ Cell gets various attributes set automatically based on table (separators) and data source (accessory views, editing controls)\n- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath;\n@optional\n- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView;              \/\/ Default is 1 if not implemented\n...\n@protocol UITableViewDelegate&lt;NSObject, UIScrollViewDelegate&gt;\n@optional\n...\n\/\/ Called after the user changes the selection.\n- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath;\n...<\/pre>\n<p>&nbsp;<br \/>\nUne source d&rsquo;information sur les protocoles et m\u00e9thodes de d\u00e9l\u00e9gation d&rsquo;une classe est la documentation developpeur Apple.<br \/>\nPar exemple, pour UITableView:<br \/>\nhttps:\/\/developer.apple.com\/library\/ios\/documentation\/uikit\/reference\/UITableViewDataSource_Protocol\/Reference\/Reference.html<br \/>\nhttps:\/\/developer.apple.com\/library\/ios\/documentation\/uikit\/reference\/UITableViewDelegate_Protocol\/Reference\/Reference.html<br \/>\nPassons \u00e0 l&rsquo;action. \u00a0Dans la prochaine vid\u00e9o, nous allons localiser le fichier ent\u00eate de la classe UITableView pour indentifier et impl\u00e9meter les m\u00e9thodes d\u00e9l\u00e9gu\u00e9es qui vont nous permettre de renseigner les donn\u00e9es \u00e0 afficher via le UITableView de la sc\u00e8ne &lsquo;Liste des vid\u00e9os&rsquo;.<br \/>\n&nbsp;<br \/>\n<iframe loading=\"lazy\" title=\"TIMFlix-E\u0301tape02.2\" width=\"700\" height=\"394\" src=\"https:\/\/www.youtube.com\/embed\/mf1q_ZjN6KU?feature=oembed\" frameborder=\"0\" allow=\"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture\" allowfullscreen><\/iframe><br \/>\n&nbsp;<br \/>\n<strong><em>Action &#8211; Ajoutons le code suivant au fichier &lsquo;VCListeDesVideos.m&rsquo;:<\/em><\/strong><\/p>\n<pre class=\"toolbar:1 lang:default decode:true\">\/\/ Les m\u00e9thodes de d\u00e9l\u00e9gation de donn\u00e9es d'un UITableView\n- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{\n    return tableauDesVideos.count;\n}\n- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{\n    \/\/ le param\u00e8tre 'indexPath.row' renseigne sur la cellule courante.\n    \/\/ Il peut-\u00eatre utilis\u00e9 pour indicer le tableau des vid\u00e9os.\n    UITableViewCell * celluleCourante = [[UITableViewCell alloc]init];\n    celluleCourante.textLabel.text = tableauDesVideos[indexPath.row][@\"titre\"];\n    \/\/ Ajoutons une image par programmation ...\n    celluleCourante.imageView.image = [UIImage imageNamed:tableauDesVideos[indexPath.row][@\"pochette\"]];\n    return celluleCourante;\n}<\/pre>\n<p>&nbsp;<br \/>\n<em><strong>Action &#8211; Tra\u00e7ons un lien entre la propri\u00e9t\u00e9 &lsquo;dataSource&rsquo; de notre objet &lsquo;UITableView&rsquo; et le &lsquo;ViewController&rsquo; de la sc\u00e8ne &lsquo;Liste des vid\u00e9os&rsquo;<\/strong><\/em><br \/>\n&nbsp;<br \/>\n<div id=\"attachment_169\" style=\"width: 566px\" class=\"wp-caption alignnone\"><a href=\"\/xcode\/wp-content\/uploads\/2013\/10\/timflix-figure04.png\"><img loading=\"lazy\" decoding=\"async\" aria-describedby=\"caption-attachment-169\" class=\" wp-image-169\" src=\"\/xcode\/wp-content\/uploads\/2013\/10\/timflix-figure04-556x600.png\" alt=\"timflix-figure04\" width=\"556\" height=\"600\" \/><\/a><p id=\"caption-attachment-169\" class=\"wp-caption-text\">figure 5<\/p><\/div><br \/>\n&nbsp;<br \/>\nSi nous testons l&rsquo;application nous allons constater que le bouton, permettant de passer \u00e0 la sc\u00e8ne de d\u00e9tail n&rsquo;est pas affich\u00e9. \u00a0Il n&rsquo;est donc pas possible de lancer la transition suite \u00e0 la s\u00e9lection d&rsquo;un item de la liste.<br \/>\nCela s&rsquo;explique par le fait que nous n&rsquo;avons pas utilis\u00e9 le mod\u00e8le de la cellule personnalis\u00e9e pour dessiner les cellules. \u00a0Dans ce cas, le UITableView utilise le mod\u00e8le par d\u00e9faut de UITableViewCell. \u00a0Ce mod\u00e8le poss\u00e8de des propri\u00e9t\u00e9s visuelles comme celles que nous avons utilis\u00e9es pr\u00e9c\u00e9demment; celluleCourante.imageView, celluleCourante.textLabel &#8230;<br \/>\nPour utiliser le mod\u00e8le personnalis\u00e9 de notre UITableViewCell il faut:<\/p>\n<ol>\n<li>Donner un identificateur \u00e0 la cellule.<\/li>\n<li>Cr\u00e9er la cellule avec la m\u00e9thode &lsquo;dequeueReusableCellWithIdentifier&rsquo; du param\u00e8tre &lsquo;tableView&rsquo;<\/li>\n<\/ol>\n<p>&nbsp;<br \/>\n<em><strong>Action &#8211; Renseignons l&rsquo;identificateur de UITableViewCell<\/strong><\/em><br \/>\n<div id=\"attachment_175\" style=\"width: 634px\" class=\"wp-caption alignnone\"><a href=\"\/xcode\/wp-content\/uploads\/2013\/10\/timflix-figure06.png\"><img loading=\"lazy\" decoding=\"async\" aria-describedby=\"caption-attachment-175\" class=\" wp-image-175\" src=\"\/xcode\/wp-content\/uploads\/2013\/10\/timflix-figure06.png\" alt=\"timflix-figure06\" width=\"624\" height=\"325\" \/><\/a><p id=\"caption-attachment-175\" class=\"wp-caption-text\">figure 6<\/p><\/div><br \/>\n&nbsp;<br \/>\n<em><strong>Action &#8211; Modifions la m\u00e9thode &lsquo;cellForRowAtIndexPath: indexPath:&rsquo; du fichier &lsquo;VCListeDesVideos.m&rsquo;<\/strong><\/em><br \/>\n&nbsp;<\/p>\n<pre class=\"toolbar:1 lang:default decode:true\" mark=\"4\">\/\/ tableView: cellForRowAtIndexPath\n- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{\n    UITableViewCell * celluleCourante = [tableView dequeueReusableCellWithIdentifier:@\"modeleCellule\" forIndexPath:indexPath];\n    celluleCourante.textLabel.text = tableauDesVideos[indexPath.row][@\"titre\"];\n    celluleCourante.imageView.image = [UIImage imageNamed:tableauDesVideos[indexPath.row][@\"pochette\"]];\n    return celluleCourante;\n}<\/pre>\n<p><em><strong>Action &#8211; Il ne reste maintenant plus qu&rsquo;\u00e0 d\u00e9finir un lien IBAction sur le bouton de la sc\u00e8ne d\u00e9tail (m\u00e9thode: revenirScenePrecedente) et y ajouter le code suivant:<\/strong><\/em><\/p>\n<pre class=\"toolbar:1 lang:default decode:true\">\/\/  VCDetailVideoCourante.m\n- (IBAction)revenirScenePrecedente:(id)sender {\n    [self dismissViewControllerAnimated:YES completion:nil];\n}<\/pre>\n<h4>\u00a0R\u00e9sultat final de l&rsquo;\u00e9tape 2<\/h4>\n<p><iframe loading=\"lazy\" title=\"TIMFlix-E\u0301tape02.2-demo\" width=\"700\" height=\"394\" src=\"https:\/\/www.youtube.com\/embed\/eMGri_tk-TM?feature=oembed\" frameborder=\"0\" allow=\"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture\" allowfullscreen><\/iframe><br \/>\nL&rsquo;\u00e9tape 2 compl\u00e9t\u00e9e est\u00a0<a title=\"TIMFlix\" href=\"\/cours\/xcode\/contenu\/ateliers\/timflix\/projet-timflix-etape02.zip\">ici<\/a><\/p>\n<hr \/>\n<p>&nbsp;<\/p>\n<h3>\u00c9tape 3 &#8211; Personnaliser la cellule du UITableView<\/h3>\n<p>Objectifs:<\/p>\n<ul>\n<li>\u00c9laborer, en mode &lsquo;Interface Builder&rsquo; un design personnalis\u00e9 pour la cellule de notre &lsquo;UITableView&rsquo;.<\/li>\n<li>\u00c9laborer le design de la sc\u00e8ne &lsquo;D\u00e9tail&rsquo;<\/li>\n<li>D\u00e9finir des propri\u00e9t\u00e9s \u00e0 la sc\u00e8ne &lsquo;d\u00e9tail&rsquo; qui permettront de recevoir le d\u00e9tail de la s\u00e9lection courante.<\/li>\n<li>Cr\u00e9er et associer une classe personnalis\u00e9e, \u00e0 partir de &lsquo;UITableViewCell&rsquo;, \u00e0 la cellule de\u00a0\u00a0notre &lsquo;UITableView&rsquo;.<\/li>\n<li>D\u00e9finir des liens MVC sur les \u00e9l\u00e9ments visuelles de la cellule.<\/li>\n<li>Pr\u00e9parer et envoyer les donn\u00e9es (titre, dur\u00e9e, pochette, cote de la vid\u00e9o, &#8230;) vers la sc\u00e8ne &lsquo;D\u00e9tail&rsquo;<\/li>\n<\/ul>\n<p>&nbsp;<br \/>\n\u00c9tape 3.1<br \/>\n<em><strong>Action &#8211; R\u00e9alisons le design de la cellule<\/strong><\/em><br \/>\n<div id=\"attachment_181\" style=\"width: 331px\" class=\"wp-caption alignnone\"><a href=\"\/xcode\/wp-content\/uploads\/2013\/10\/timflix-figure06.5.png\"><img loading=\"lazy\" decoding=\"async\" aria-describedby=\"caption-attachment-181\" class=\"size-medium wp-image-181\" src=\"\/xcode\/wp-content\/uploads\/2013\/10\/timflix-figure06.5-321x600.png\" alt=\"timflix-figure06.5\" width=\"321\" height=\"600\" \/><\/a><p id=\"caption-attachment-181\" class=\"wp-caption-text\">figure 7<\/p><\/div><br \/>\nTestons<br \/>\n<div id=\"attachment_180\" style=\"width: 332px\" class=\"wp-caption alignnone\"><a href=\"\/xcode\/wp-content\/uploads\/2013\/10\/timflix-figure07.png\"><img loading=\"lazy\" decoding=\"async\" aria-describedby=\"caption-attachment-180\" class=\"size-medium wp-image-180\" src=\"\/xcode\/wp-content\/uploads\/2013\/10\/timflix-figure07-322x600.png\" alt=\"timflix-figure07\" width=\"322\" height=\"600\" \/><\/a><p id=\"caption-attachment-180\" class=\"wp-caption-text\">figure 8<\/p><\/div><br \/>\nNote: Les objets visuels du mod\u00e8le de cellule par d\u00e9faut entrent en conflits avec nos \u00e9l\u00e9ments de design. \u00a0Il n&rsquo;y a que la cellule s\u00e9lectionn\u00e9e qui est affich\u00e9e correctement.<br \/>\n<em><strong>Action &#8211; Pour l&rsquo;instant, pla\u00e7ons la ligne suivante en commentaire:<\/strong><\/em><\/p>\n<pre class=\"toolbar:1 lang:default decode:true\" mark=\"5\">\/\/  VCListeDesVideos.m\n- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{\n    UITableViewCell * celluleCourante = [tableView dequeueReusableCellWithIdentifier:@\"modeleCellule\" forIndexPath:indexPath];\n    \/\/ ---- &gt; celluleCourante.textLabel.text = tableauDesVideos[indexPath.row][@\"titre\"];\n    celluleCourante.imageView.image = [UIImage imageNamed:tableauDesVideos[indexPath.row][@\"pochette\"]];\n    return celluleCourante;\n}<\/pre>\n<p>Ce qui produit ceci \u00e0 l&rsquo;ex\u00e9cution de l&rsquo;application:<br \/>\n<div id=\"attachment_183\" style=\"width: 316px\" class=\"wp-caption alignnone\"><a href=\"\/xcode\/wp-content\/uploads\/2013\/10\/timflix-figure08.png\"><img loading=\"lazy\" decoding=\"async\" aria-describedby=\"caption-attachment-183\" class=\" wp-image-183\" src=\"\/xcode\/wp-content\/uploads\/2013\/10\/timflix-figure08-306x600.png\" alt=\"timflix-figure08\" width=\"306\" height=\"600\" \/><\/a><p id=\"caption-attachment-183\" class=\"wp-caption-text\">figure 9<\/p><\/div><br \/>\n\u00c9tape 3.2<br \/>\n<em><strong>Action &#8211; R\u00e9alisons le design de la sc\u00e8ne &lsquo;D\u00e9tail&rsquo;<\/strong><\/em><br \/>\n<em><strong>\u00a0<\/strong><\/em><br \/>\n<div id=\"attachment_185\" style=\"width: 682px\" class=\"wp-caption alignnone\"><a href=\"\/xcode\/wp-content\/uploads\/2013\/10\/timflix-figure09.png\"><img loading=\"lazy\" decoding=\"async\" aria-describedby=\"caption-attachment-185\" class=\"size-medium wp-image-185\" src=\"\/xcode\/wp-content\/uploads\/2013\/10\/timflix-figure09-672x600.png\" alt=\"timflix-figure09\" width=\"672\" height=\"600\" \/><\/a><p id=\"caption-attachment-185\" class=\"wp-caption-text\">figure 10<\/p><\/div><br \/>\n&nbsp;<\/p>\n<h4>\u00c9tape 3.3 &#8211; Classe personnalis\u00e9e pour notre &lsquo;UITableViewCell&rsquo;<\/h4>\n<p>&nbsp;<br \/>\nNous allons ajouter une nouvelle classe \u00e0 notre projet. \u00a0Cette classe sera d\u00e9riv\u00e9e de la classe UITableViewCell. \u00a0Elle deviendra la classe de notre cellule personnalis\u00e9e. \u00a0Cela nous permettra de d\u00e9finir des liens MVC et de renseigner correctement les \u00e9l\u00e9ments visuels \u00e0 l&rsquo;int\u00e9rieur de la m\u00e9thode &lsquo;cellForRowAtIndexPath: indexPath:\u00a0\u00bb.<br \/>\n&nbsp;<br \/>\n<em><strong>Action &#8211; R\u00e9alisons l&rsquo;\u00e9tape 3.3 \u00a0(la recette est dans la vid\u00e9o suivante)<\/strong><\/em><br \/>\n&nbsp;<br \/>\n<iframe loading=\"lazy\" title=\"TIMFlix-E\u0301tape03.3\" width=\"700\" height=\"394\" src=\"https:\/\/www.youtube.com\/embed\/H14kNuHtpQU?feature=oembed\" frameborder=\"0\" allow=\"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture\" allowfullscreen><\/iframe><br \/>\n&nbsp;<br \/>\nVoici les liens MVC de la cellule:<br \/>\n&nbsp;<\/p>\n<pre class=\"toolbar:1 lang:default decode:true\">\/\/  CelluleVideo.h\n\/\/  projet-TIMFlix-etape03\n@property (weak, nonatomic) IBOutlet UIImageView *videoImage;\n@property (weak, nonatomic) IBOutlet UILabel *videoTitre;\n@property (weak, nonatomic) IBOutlet UILabel *videoAnnee;\n@property (weak, nonatomic) IBOutlet UIImageView *videoCote;<\/pre>\n<p>&nbsp;<br \/>\nEt les modifications au code du fichier &lsquo;VCListeDesVideos.m&rsquo;<br \/>\n&nbsp;<\/p>\n<pre class=\"toolbar:1 lang:default decode:true\" mark=\"1,6-10\">#import \"CelluleVideo.h\"\n\/\/ tableView: cellForRowAtIndexPath\n- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{\n    CelluleVideo * celluleCourante = [tableView dequeueReusableCellWithIdentifier:@\"modeleCellule\" forIndexPath:indexPath];\n    celluleCourante.videoTitre.text = tableauDesVideos[indexPath.row][@\"titre\"];\n    celluleCourante.videoImage.image = [UIImage imageNamed:tableauDesVideos[indexPath.row][@\"pochette\"]];\n    celluleCourante.videoAnnee.text = tableauDesVideos[indexPath.row][@\"annee\"];\n    celluleCourante.videoCote.image = [UIImage imageNamed:[NSString stringWithFormat:@\"%@-star-rating.png\", tableauDesVideos[indexPath.row][@\"cote\"]]];\n    return celluleCourante;\n}<\/pre>\n<p>&nbsp;<br \/>\nL&rsquo;\u00e9tape 3 compl\u00e9t\u00e9e est\u00a0<a title=\"TIMFlix\" href=\"\/cours\/xcode\/contenu\/ateliers\/timflix\/projet-timflix-etape03.zip\">ici<\/a><\/p>\n<hr \/>\n<p>&nbsp;<\/p>\n<h3>\u00c9tape 4<\/h3>\n<p>Objectifs:<\/p>\n<ul>\n<li>D\u00e9finir des propri\u00e9t\u00e9s, \u00e0 la sc\u00e8ne &lsquo;d\u00e9tail&rsquo;, ce qui permettra de recevoir les informations de la s\u00e9lection courante.<\/li>\n<li>D\u00e9finir des liens MVC sur les \u00e9l\u00e9ments visuels de la\u00a0\u00a0sc\u00e8ne &lsquo;d\u00e9tail&rsquo;.<\/li>\n<li>Pr\u00e9parer et envoyer &#8211; &lsquo;<strong>prepareForSegue&rsquo;<\/strong> &#8211; \u00a0les donn\u00e9es (titre, dur\u00e9e, pochette, cote de la vid\u00e9o, &#8230;) vers la sc\u00e8ne &lsquo;D\u00e9tail&rsquo;<\/li>\n<\/ul>\n<p>&nbsp;<br \/>\nContenu du fichier VCDetailVideoCourante.h<\/p>\n<pre class=\"toolbar:1 lang:default decode:true\">\/\/  VCDetailVideoCourante.h\n#import &lt;UIKit\/UIKit.h&gt;\n@interface VCDetailVideoCourante : UIViewController\n@property NSDictionary *videoInfo;\n@property (weak, nonatomic) IBOutlet UILabel *titre;\n@property (weak, nonatomic) IBOutlet UIImageView *pochette;\n@property (weak, nonatomic) IBOutlet UILabel *annee;\n@property (weak, nonatomic) IBOutlet UILabel *duree;\n@property (weak, nonatomic) IBOutlet UILabel *classement;\n@property (weak, nonatomic) IBOutlet UIImageView *cote;\n@property (weak, nonatomic) IBOutlet UITextView *description;\n- (IBAction)revenirScenePrecedente:(id)sender;\n@end<\/pre>\n<p>&nbsp;<br \/>\nContenu du fichier VCDetailVideoCourante.m<br \/>\n&nbsp;<\/p>\n<pre class=\"toolbar:1 lang:default decode:true\">\/\/  VCDetailVideoCourante.m\n#import \"VCDetailVideoCourante.h\"\n@interface VCDetailVideoCourante ()\n@end\n@implementation VCDetailVideoCourante\n- (void)viewDidLoad\n{\n    [super viewDidLoad];\n    NSLog(@\"detail = %@\", self.videoInfo);\n    self.titre.text = self.videoInfo[@\"titre\"];\n    self.pochette.image = [UIImage imageNamed:self.videoInfo[@\"pochette\"]];\n    self.annee.text = self.videoInfo[@\"annee\"];\n    self.cote.image = [UIImage imageNamed:[NSString stringWithFormat:@\"%@-star-rating.png\", self.videoInfo[@\"cote\"]]];\n    self.duree.text = self.videoInfo[@\"duree\"];\n    self.classement.text = self.videoInfo[@\"classement\"];\n    self.description.text = self.videoInfo[@\"description\"];\n}\n- (IBAction)revenirScenePrecedente:(id)sender {\n    [self dismissViewControllerAnimated:YES completion:nil];\n}\n@end<\/pre>\n<p>&nbsp;<br \/>\nCode \u00e0 ajouter au fichier VCListeDesVideos.m<\/p>\n<pre class=\"toolbar:1 lang:default decode:true\">\/\/  VCListeDesVideos.m\n#import \"VCDetailVideoCourante.h\"\n\/\/ Lanc\u00e9e automatiquement avant une transition 'segue'\n-(void) prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender{\n    \/\/ Pointer sur la sc\u00e8ne de destination\n    VCDetailVideoCourante * vcDetail = [segue destinationViewController];\n    \/\/ sender -&gt; pointe sur la cellule s\u00e9lectionn\u00e9e\n    int selectionCourante = [[self.TViewVideos indexPathForCell:sender] row];\n    \/\/ Envoyer les informations de la s\u00e9lection courante\n    vcDetail.videoInfo = tableauDesVideos[selectionCourante];\n}<\/pre>\n<p>&nbsp;<br \/>\n<em><strong>Action &#8211; tester l&rsquo;application<\/strong><\/em><br \/>\n&nbsp;<br \/>\nAm\u00e9liorations \u00e0 apporter:<\/p>\n<ul>\n<li>Placer un bouton invisible sur 66% de la sc\u00e8ne de d\u00e9tail pour permettre le retour sans avoir \u00e0 appuyer sur le bouton du haut.<\/li>\n<li>Lancer \u00a0la transition &lsquo;segue&rsquo; en s\u00e9lectionnant un \u00e9l\u00e9ment du &lsquo;UITableView&rsquo;, au lieu d&rsquo;avoir \u00e0 appuyer sur le bouton accessoire.<\/li>\n<li>Obtenir la liste des vid\u00e9os via un service web.<\/li>\n<li>Permettre \u00e0 l&rsquo;utilisateur d&rsquo;\u00e9valuer \u00a0les vid\u00e9os (cote).<\/li>\n<li>Permettre \u00e0 l&rsquo;utilisateur d&rsquo;envoyer un message sur &lsquo;twitter&rsquo; indiquant qu&rsquo;il a aim\u00e9 la vid\u00e9o s\u00e9lectionn\u00e9e<\/li>\n<\/ul>\n<p>&nbsp;<br \/>\nL&rsquo;\u00e9tape 4 compl\u00e9t\u00e9e est\u00a0<a title=\"TIMFlix\" href=\"\/cours\/xcode\/contenu\/ateliers\/timflix\/projet-timflix-etape04.zip\">ici<\/a><\/p>\n<hr \/>\n<p>&nbsp;<br \/>\n&nbsp;<\/p>\n<h6 style=\"text-align: right;\">Didacticiel pr\u00e9par\u00e9 par Alain Boudreault, enseignant en Techniques d&rsquo;int\u00e9gration multim\u00e9dia du c\u00e9gep de Saint-J\u00e9r\u00f4me. \u00a0Version 1.0<\/h6>\n","protected":false},"excerpt":{"rendered":"<p>Description: Avec ce tutoriel, nous apprendrons \u00e0 construire une application qui affiche, grace \u00e0 &lsquo;UITableView&rsquo; et une cellule personnalis\u00e9e, une liste de titres vid\u00e9os renseign\u00e9e par un fichier de propri\u00e9t\u00e9s (plist). Une page contenant le d\u00e9tail de la vid\u00e9o sera affich\u00e9e &#8211; par un &lsquo;segue&rsquo; &#8211; suite \u00e0 la s\u00e9lection d&rsquo;un \u00e9l\u00e9ment de la table. [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"parent":26,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"","meta":{"footnotes":""},"class_list":["post-60","page","type-page","status-publish","hentry"],"_links":{"self":[{"href":"https:\/\/ve2cuy.com\/xcode\/wp-json\/wp\/v2\/pages\/60","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/ve2cuy.com\/xcode\/wp-json\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/ve2cuy.com\/xcode\/wp-json\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/ve2cuy.com\/xcode\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/ve2cuy.com\/xcode\/wp-json\/wp\/v2\/comments?post=60"}],"version-history":[{"count":0,"href":"https:\/\/ve2cuy.com\/xcode\/wp-json\/wp\/v2\/pages\/60\/revisions"}],"up":[{"embeddable":true,"href":"https:\/\/ve2cuy.com\/xcode\/wp-json\/wp\/v2\/pages\/26"}],"wp:attachment":[{"href":"https:\/\/ve2cuy.com\/xcode\/wp-json\/wp\/v2\/media?parent=60"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}