Skip to content

9 novembre 2010 | Rédigé par Mathieu

49

Liste et détails : créer une application multivues (1/3) [Tutoriel iPhone n°8] [XCode 4]

Nouveau (mai 2011) : Ce tutoriel a été mis à jour et été testé pour XCode 4.

Tous les utilisateurs d’iPhone sont habitués à naviguer dans des listes, à toucher un élément pour afficher le détail d’un objet, et utiliser la barre de navigation supérieure pour revenir à l’écran principal. Ce fonctionnement est homogène dans toutes les applications proposées en standard par Apple sur les appareils exécutant iOS (iPhone, iPod Touch, iPad), mais pas seulement. En effet, Cocoa Touch facilite considérablement la vie du développeur qui souhaite programmer une App dans cette idée, ce qui explique l’omniprésence d’une telle approche.

Ce tutoriel sera séparée en trois parties. La première vous proposera un pas à pas pour construire une liste à partir d’un fichier à plat, puis détaillera le fonctionnement pour ouvrir une fenêtre de détails. Dans la seconde, nous verrons comment enrichir le contenu de l’application et aborderons les fenêtres modales. Enfin dans la troisième et dernière, nous verrons l’enchaînement d’objet à mettre en place pour enregistrer les modifications dans la mémoire de l’iPhone et verrons comment, en quelques clics, nous pouvons utiliser les comportements avancés des tableaux.

L’objectif

Avant de commencer, je souhaiterais vous montrer où nous souhaitons arriver à la fin de ce tutoriel. Notre application a pour ambition de présenter des sites internet de référence. Cette liste de sites sera stockée dans un fichier et présentée à l’utilisateur sous forme d’une table avec le titre du site et le début de la description. En touchant une ligne, l’utilisateur accèdera à une vue de détails présentant la description complète et une webview du site en question.

Créer le projet

On est parti ! Lancez XCode, démarrez la création d’un nouveau projet et choisissez une « Navigation-based application ». Ce template, nous ne l’avons pas utilisé dans les tutoriels précédents parce qu’il va préparer pas mal de choses pour vous :

  • une première classe RootViewController (un .m, un .h et un fichier InterfaceBuilder .xib)
  • cette classe sera instanciée dans un UINavigationController (nous l’avions créé nous-mêmes dans le tutoriel n°5)
  • de plus cette classe RootViewController est une sous-classe de UITableViewController, ce qui signifie qu’elle propose, par défaut, une liste d’éléments.

Appelez ce projet « MyWebSites ». Vous pouvez le compiler tout de suite. Vous verrez alors apparaître un écran présentant la barre bleue de navigation en haut et une liste, vide, mais qui réagit déjà au toucher (vous pouvez « scroller » dedans).

Personnalisons cela rapidement. Ouvrez MainWindow.xib, accédez au « Navigation Controller » dans la barre de gauche. Vous remarquez que la vue qui sera affichée dans un premier temps est gérée par RootViewController. Double-cliquez dans la barre bleue pour mettre le titre en surbrillance et le modifier selon votre choix. Ceci revient à modifier la propriété title, comme nous l’avions vu dans le tutoriel précédent, et comme nous n’allons pas tarder à le faire pour la vue de détails.

Ajouter un fichier .plist contenant les informations de sites

Cette liste est bien vide, il est temps de trouver des données à y présenter. Apple propose plusieurs façons de stocker des données, comme des fichiers textes, des accès à une base de données SQLite, les mettre sur un serveur Internet et les récupérer, etc… Mais il y a mieux. Les fichiers de propriétés (les fameux .plist) sont des fichiers XML qui présentent de nombreux avantages dont celui d’être supportés nativement par les objets du framework (NSArray, NSDictionnary, …) ; cela signifie que nous allons pouvoir récupérer avec une simple ligne de code des objets entiers depuis un fichier .plist. La seconde option est CoreData, que nous aborderons en détails dans un prochain tutoriel.

Assez parlé ! Pour créer un fichier .plist qui contiendra les données de notre liste de sites Internet favoris, utilisez la commande « New File… » du menu File, et choisissez « Property List » qui se trouve dans la rubrique « Ressource ». Donnez-lui un nom (j’ai appelé le mien « websites.plist ») et retrouvez-le dans l’arborescence de XCode (il est en fin de projet).

Ici, nul besoin de connaître le moindre début de XML, XCode s’occupe de tout et vous présente le contenu du fichier dans une interface super simple et efficace à utiliser. Notre objectif, ici : obtenir un tableau d’objets (NSArray), ces objets étant des dictionnaires clé-valeur (NSDictionnary) permettant, pour chaque site, de préciser : le titre du site, son URL et une description à afficher.

Bon je m’épargne de vous expliquer par le menu comment fonctionne l’interface (petite astuce : clic-droit pour ajouter les premières lignes, après, vous avez droit à des icônes « plus » et « moins ». Voici le résultat final : un NSArray contenant des objets de types NSDictionnary ayant tous la même structure : trois NSStrings title, url et description. Courage !

(Cliquez sur l’image pour voir le résultat attendu)

Créer une classe modèle

Il est temps de coder un peu et de nous attaquer à la partie « modèle » de notre fameux MVC. Nous allons faire une « classe métier » pour représenter chacun des sites. Cette classe a pour objectifs :

  • de représenter un site (avec son titre, son URL, sa description)
  • de prendre en charge les actions à mener sur un site

Nous aurons, par la suite, besoin de créer un objet métier pour chacun des NSDictionnary détaillé dans notre fichier .plist. Il faut donc créer pour cette classe :

  • Trois attributs (titre, URL et description)
  • Trois propriétés pour pouvoir accéder aux valeurs des attributs
  • Une méthode d’initialisation à partir d’un NSDictionnary

Pour créer la nouvelle classe, choisissez « New File… » dans le menu « File » et choisissez une « Objective-C class » simple. Appelez-la Website, XCode se chargera de créer les deux fichier .h (en-tête) et .m (implémentation).

Dans le fichier .h, nous allons créer les trois attributs, déclarer les propriétés ainsi que la méthode d’initialisation :

#import UIKit/UIKit.h
 
@interface Website : NSObject {
    NSString *webSiteTitle;
    NSURL *webSiteURL;
    NSString *webSiteDescription;
}
 
@property (nonatomic,retain) NSString *webSiteTitle;
@property (nonatomic,retain) NSURL *webSiteURL;
@property (nonatomic,retain) NSString *webSiteDescription;
 
- (id) initWithDictionaryFromPlist: (NSDictionary *) dictionnary;
 
@end

Dans le fichier .m, nous allons synthétiser les trois propriétés (sans oublier de faire les release correspondants dans la méthode dealloc) et implémenter la méthode initWithDictionaryFromPlist :

#import "Website.h"
 
@implementation Website
 
@synthesize webSiteTitle;
@synthesize webSiteURL;
@synthesize webSiteDescription;
 
- (id) initWithDictionaryFromPlist: (NSDictionary *) dictionnary {
    [self init];
 
    self.webSiteTitle = [dictionnary objectForKey:@"title"];
    self.webSiteURL = [NSURL URLWithString:[dictionnary objectForKey:@"url"]];
    self.webSiteDescription = [dictionnary objectForKey:@"description"];
 
    return self;
}
 
- (void)dealloc {
    [webSiteTitle release];
    [webSiteURL release];
    [webSiteDescription release];
    [super dealloc];
}
 
@end

Vous pouvez essayer de compiler, mais pour le moment rien n’a changé, ceci permettra simplement de corriger les éventuelles petites fautes de frappe.

Charger le contenu dans la liste

Il est largement temps de faire en sorte que cette liste se remplisse avec les sites que nous avons saisis dans notre fichier .plist. Retournons sur la classe RootViewController, et plus précisément dans le fichier header (.h) pour ajouter un attribut : tabWebSites qui contiendra tous les objets que nous allons charger de notre fichier .plist

#import UIKit/UIKit.h
 
@interface RootViewController : UITableViewController {
    NSArray *tabWebSites;
}
 
@property (nonatomic, retain) NSArray *tabWebSites;
 
@end

Dans le fichier .m, ajoutez les éléments suivants (ça doit commencer à devenir un reflexe quand vous déclarez une propriété) :

  • le @synthetise pour la propriété
  • le message release dans la méthode dealloc de la classe

(il y a un morceau de code similaire un peu plus haut dans le tutoriel).

Intéressons-nous maintenant à la méthode viewDidLoad de RootViewController.m. Cette méthode est appelée une fois que la vue est chargée, c’est à dire, dans notre cas, une fois que la liste est préparée en mémoire pour que nous puissions y ajouter des éléments. Elle est déjà pré-codée par XCode lors de la création du projet. Repérez-la : elle est en général quasi en haut du fichier.

C’est dans cette méthode que nous allons :

  • Charger le fichier .plist et le parcourir
  • Créer un objet Website à partir de chaque NSDictionnary extrait du fichier .plist
  • Enregistrer ces objets Website dans le tableau tabWebSites que nous venons de créer

Voici le code à implémenter, j’ai détaillé les trois points :

#import "RootViewController.h"
#import "Website.h"
 
@implementation RootViewController
@synthesize tabWebSites;
 
- (void)viewDidLoad {
    [super viewDidLoad];
 
    // Charger le fichier .plist dans un tableau que l'on appelera  arrayFromFile
    NSString *path = [[NSBundle mainBundle] pathForResource:@"websites" ofType:@"plist"];
    NSDictionary *dictFromFile = [[NSDictionary alloc] initWithContentsOfFile:path];
    NSArray *arrayFromFile = [dictFromFile objectForKey:@"Root"];
 
    // Créons un tableau temporaire que nous allons remplir avec un objet Website par NSDictionnary contenu dans le fichier .plist
    // Notez l'utilisation de NSEnumerator pour parcourir un tableau
    NSMutableArray *websitesToAdd = [[NSMutableArray alloc] init];
    NSEnumerator *enumerator = [arrayFromFile objectEnumerator];
    NSDictionary *anObject;
    while ((anObject = [enumerator nextObject])) {
        Website *ws = [[Website alloc] initWithDictionaryFromPlist: anObject];
        [websitesToAdd addObject: ws];
        [ws release];
    }
 
    // Remplir la propriété tabWebSites avec le contenu du NSMutableArray précédent
    self.tabWebSites = [NSArray arrayWithArray:websitesToAdd];
 
    // Gestion de la mémoire : pour chaque alloc, n'oubliez pas le release qui va avec !
    [websitesToAdd release];
    [arrayFromFile release];
}
 
// [...]

Compilez, et tadaaa ! Non toujours rien ? Bon, c’est normal : on a chargé notre tableau, mais on a rien demandé à afficher.

En savoir plus sur UITableView

XCode a déjà préparé pas mal de méthodes dans le fichier RootViewController.m qui n’attendent qu’à être activée et personnalisées. Avant de faire en sorte que nos sites apparaissent vraiment dans la liste, laissez-moi vous parler un peu plus de UITableView (cette magnifique liste).

Le composant UITableView propose une des façons les plus simples et les plus rapides pour traiter les données : le tableau, et plus précisément la liste. Un UITableView propose en fait un ensemble de cellules (les UITableViewCell) qui sont organisées en sections :

Pour présenter des données, il ne s’agit pas de « charger » un UITableView en mettant des données dedans (comme dans un tableau NSArray, par exemple). Un UITableView a besoin d’un objet qui l’accompagne : sa source de données : un DataSource. N’importe quel objet peut être un DataSource, il suffit pour cela qu’il implémente le protocole : UITableViewDataSource. Lorsqu’il sera temps d’afficher le contenu d’une cellule, par exemple celle de la troisième ligne de la première section, le UITableView demandera alors à son DataSource : « Qu’est-ce que je dois afficher dans la ligne 3 de la section 1 ? ».

XCode a bien fait les choses quand il a préparé notre projet. Le DataSource de notre UITableView est déjà déclaré : c’est notre classe RootViewController. Pour s’en convaincre, il suffit d’aller dans RootViewController.h, constater que cette classe étend la classe UITableViewController. Si vous appuyez sur Commande (Pomme) et cliquez sur « UITableViewController », l’écran présentera la déclaration de cette classe, et vous verrez qu’elle implémente bien le protocole UITableViewDataSource.

Afficher les données dans la liste

Les méthodes du protocole UITableViewDataSource que nous avons à implémenter pour afficher les informations sont numberOfSectionsInTableView, numberOfRowsInSection et cellForRowAtIndexPath, elles sont déjà préparées dans RootViewController.m. Voici le code, commenté pour détailler les actions mises en place :

// Customize the number of sections in the table view.
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
    // On n'a besoin que d'une section pour nos sites Internet
    return 1;
}
 
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    // Nous ne tenons pas compte du numéro de section puisqu'il n'y en a qu'une
    // Dans cette unique section il y a tous les éléments du tableau, on retourne donc le nombre
    return [self.tabWebSites count];
}
 
// Customize the appearance of table view cells.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
 
    static NSString *CellIdentifier = @"Cell";
 
    // Les cellules sont mises en cache pour accélérer le traitement, sous l'identifiant "Cell",
    // on essaie récupère ce modèle de cellule s'il est déjà en cache
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
 
    // Si on n'a pas réussi à sortir une cellule du cache, on crée un nouveau modèle de cellule
    // et on l'enregistre dans le cache
    if (cell == nil) {
        cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier] autorelease];
    }
 
    // On récupère l'objet Website qui correspon à la ligne que l'on souhaite afficher
    Website *ws = [self.tabWebSites objectAtIndex:indexPath.row];
 
    // On configure la cellule avec le titre du site et sa description
    cell.textLabel.text = ws.webSiteTitle;
    cell.detailTextLabel.text = ws.webSiteDescription;
 
    // On renvoie la cellule configurée pour l'affichage
    return cell;
}

Il est temps de compiler tout cela, et de faire notre premier test. Si tout va bien, voilà le résultat affiché :

Créer une vue de détail

Nous allons maintenant créer la vue qui sera affichée quand on touchera un élément de la liste. Il s’agit d’un UIViewController que nous allons pousser, plus tard, dans le UINavigationController. On va aller un peut plus vite pour cette partie, parce que, si vous vous êtes déjà frottés aux tutoriels précédents, ça devrait être une partie de plaisirs.

Créez une nouvelle classe, héritée de UIViewController, appelez-la DetailViewController et n’oubliez pas d’associer un fichier .h et un fichier .xib. Dans DetailViewController.h, créez trois attributs et autant de propriétés :

#import
#import "Website.h"
 
@interface DetailViewController : UIViewController {
	IBOutlet UITextView *descriptionTextView;
	IBOutlet UIWebView *webView;
	Website *ws;
}
 
@property (nonatomic,retain) UIWebView *webView;
@property (nonatomic,retain) UITextView *descriptionTextView;
@property (nonatomic,retain) Website *ws;
 
@end

Dans le fichier .m, synthétisez les trois propriétés, n’oubliez pas les release de rigueur dans la méthode dealloc et personnalisez la méthode viewDidLoad pour personnaliser l’affichage de la vue et charger la page dans la webview :

#import "DetailViewController.h"
 
@implementation DetailViewController
@synthesize descriptionTextView, ws, webView;
 
/*...*/
 
- (void)dealloc {
    [webView release];
    [descriptionTextView release];
    [ws release];	
 
    [super dealloc];
}
 
/*...*/
 
- (void)viewDidLoad {
    [super viewDidLoad];
 
    // Titre du navigation controller
    self.title = ws.webSiteTitle;
 
    // Afficher le contenu de la page web
    NSURLRequest *requestObject = [NSURLRequest requestWithURL: ws.webSiteURL];
    [webView loadRequest:requestObject];
 
    // Affecter le champ de description avec la description du site
    descriptionTextView.text = ws.webSiteDescription;
}
 
@end

Enfin, ouvrez le fichier .xib : ajoutez, un UIWebView en bas de la vue, ainsi qu’un UITextView qui présentera la description, au dessus de la vue Web.

Cliquez-droit sur l’objet « File’s owner » dans la colonne de gauche pour ouvrir l’outil vous permettant de faire les connexions. Connectez la propriété descriptionTextView :

Ainsi que l’attribut webview de notre objet :

A l’aide l’inspecteur de la colonne de droite, personnalisez l’affichage des deux éléments (n’oubliez pas, par exemple, de cocher « Scales Page To Fit » pour la WebView). Sauvegardez le tout avant de continuer.

Afficher les bonnes données dans la vue de détail

Quand l’utilisateur touche un site de la liste, il faut que la popup de détails soit poussée dans le UINavigationController. Nous avons vu ce mode de fonctionnement dans le tutoriel numéro 5 : s’agit d’instancier une classe UIViewController et d’utiliser la méthode pushViewController. Retournez donc sur le code de RootViewController.m.

Commencez par créer un lien vers la nouvelle classe avec la commande #import :

#import "DetailViewController.h"

Puis repérez la méthode didSelectRowAtIndexPath qui est appelée quand l’utilisateur touche une cellule du UITableView. Cette méthode prend en paramètre un objet NSIndexPath qui contient, dans sa propriété row, le numéro de la ligne qui a été touchée (et qui correspond, de fait à la position dans notre tableau tabWebSites). Modifiez la comme suit :

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
	DetailViewController *detailVC = [[DetailViewController alloc] initWithNibName:@"DetailViewController" bundle:nil];
	detailVC.ws = [self.tabWebSites objectAtIndex:indexPath.row];
 
	[self.navigationController pushViewController:detailVC animated:YES];
	[detailVC release];
}

Il est presque temps de compiler et d’admirer le résultat ! Mais juste avant, une petite remarque d’importance : si par hasard, vous souhaitez mettre cette application à disposition sur l’AppStore, il y a de fortes chances qu’elle soit refusée parce que qu’elle n’est pas conforme aux HIG : les fameuses et strictes iPhone Human Interface Guidelines. Je vous laisse trouver toute cette documentation dans XCode, et j’en profite vous parler des disclosure indicators elements. En effet, quand vous souhaitez indiquer à l’utilisateur que toucher une ligne va le faire avancer dans la hiérarchie des vues, vous devez le lui indiquer avec un Disclosure Indicator.

Pour ajouter ce petit point de détail (mais qui a son importance), il nous faut ajouter une dernière ligne dans la méthode cellForRowAtIndexPath de notre RootViewController (juste avant le return, par exemple) :

cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;

Cette fois c’est terminé ! Vous pouvez compiler et admirer le résultat ! :-)

Dans la seconde partie, nous parlerons de la modification des éléments de la liste. Vous verrez que ce n’est pas beaucoup plus difficile et qu’il suffit juste d’être un peu rigoureux pour ne pas perdre des modifications en route. Vous apprendrez, en outre, à gérer le magnifique glissé du doigt pour supprimer une ligne ! En attendant, n’hésitez pas à utiliser les commentaires si vous rencontrez des difficultés, et surtout, accrochez-vous : en général, la solution n’est pas très loin ! ;-)

Merci pour vos commentaires et encouragements ! A très vite !

Encore un peu de lecture :

49 Commentaires Poster un commentaire
  1. Vincent
    14 nov 2010

    Merci pour ce tutorial de qualité, par contre il y a un détail qui m’échappe, les lignes du tableau n’affiche pas la ligne détail en dessous du titre sur mon projet.
    Je n’ai pas trouvé ou on peut paramétrer ca.
    Merci encore !

  2. 16 nov 2010

    Oui en effet, j’ai oublié de détailler ce point. Par défaut, le style proposé pour les cellules n’affichent qu’une seule ligne de texte. Pour changer le style d’une cellule, il faut modifier le paramètre initWithStyle lors de la création de la cellule. Regarde dans le code, j’ai mis : initWithStyle:UITableViewCellStyleSubtitle, qui est le format affiché sur les captures d’écran. Regarde dans la doc, si tu veux avoir tous les styles de cellule disponible !
    Merci en tous cas pour tes encouragements ;-)

  3. Abdul
    17 nov 2010

    Bonsoir, ça marche super bien sauf que j’ai toujours la meme description qui s’affiche  » Lorem ipsum dolor iset … » et meme dans la liste des sites la description partielle ne s’affiche :( . j’attend votre reponse merci

  4. Vincent
    12 déc 2010

    Merci pour ta réponse, ca marche comme ca. Sinon, euh… comptes du bientôt sortir la suite ? J’ai hate :)

  5. 12 déc 2010

    Je m’y active actuellement. J’espère avoir terminé avant mercredi. Merci pour tes encouragements !

  6. Cyrille
    25 déc 2010

    Joyeux Noël

    de mon côté, je n’arrive pas à transmettre de la liste à la vue de détail le chemin d’une image stockée dans un fichier .plist et afficher cette image malgré la qualité de ce tuto. Je n’arrive pas à transposer.

    Pouvez-vous m’aider svp ?

    Merci
    Cyrille

  7. Sami
    7 mar 2011

    Merci super tuto et ça fonctionne nickel

  8. Raphael
    21 mar 2011

    Bonjour, merci beaucoup pour les tutos.
    Cependant, j’ai un petit souci. J’ai eu erreur lors de l’execution :
    Program received signal: “EXC_BAD_ACCESS”.
    J’ai essayé de l’isoler avec le debug et elle se trouverait dans cette ligne : NSURLRequest *requestObject = [[NSURLRequest alloc] initWithURL:ws.webSiteURL];
    ou celle la : [self.navigationController pushViewController:detailVC animated:YES];
    mais je n’arrive pas à la trouver.
    Merci d’avance

  9. 22 mar 2011

    @Raphael : Est-ce que tu peux me dire quelle version d’XCode et du SDK iOS tu utilises ? Et est-ce que tu peux me donner un peux plus de code pour que je puisse te donner un coup de main ?

  10. Raphael
    23 mar 2011

    J’ai la version 3.2.6 d’Xcode et ios4.3.
    J’ai recommencé le tuto à zéro et cette fois-ci ça marche. J’ai comparé mes deux projets ensuite mais je n’ai pas trouvé la solution.

    Par contre j’ai une autre question concernant le gestion de la mémoire. Il me semblait qu’après avoir fini d’utiliser un objet, on fait un release sur celui-ci.
    Par exemple, dans la fonction cellForRowAtIndexPath, il n’y a pas de release sur ws. Est-ce normal ? Même question pour le requestObject dans le detailViewController.

    Autre question : Serait-il judicieux de créer une fonction d’initialisation du detailViewController avec comme paramètre un Website pour eviter sa création sans Website ?

    Merci beaucoup, je vais m’attaquer à la partie suivante du tuto.

  11. Shon
    24 mar 2011

    Je viens d’essayer ce tuto avec le nouvel Xcode 4 et iOS4.2, aucun résultat … J’ai recommencé le tuto 2 fois, aucune erreur de compilation, mais rien ne s’affiche dans la liste …
    Quelqu’un pourrait m’éclairer ?

    Merci d’avance ! Et merci aussi pour ce tuto !

  12. Ich'
    25 mar 2011

    Salut à tous,

    15 fois que je suis le tuto à la lettre. Moi l’UITableView ne se charge absolument pas.
    J’ai bien le fichier .plist
    Je le retrouve via :
    NSString *path = [[NSBundle mainBundle] pathForResource:@ »websites » ofType:@ »plist »];
    NSArray *arrayFromFile = [[NSMutableArray alloc] initWithContentsOfFile:path];

    J’ai aussi ces trois lignes :
    NSMutableArray *websitesToAdd = [[NSMutableArray alloc] init];
    NSEnumerator *enumerator = [arrayFromFile objectEnumerator];
    NSDictionary *object;

    Et ensuite ça remplis pas alors j’ai fais un test
    if ([arrayFromFile count] == 0) {
    NSLog(@ »ERROR !!! »);
    }

    et il rentre dans le if.
    Je suis sous Xcode 4.

    Des idées ? Du coup je suis bloqué à l’étape : « Afficher les données dans la liste ».

  13. 26 mar 2011

    @Raphael : Concernant la mémoire. Il faut en effet faire un release à chaque fois que le compteur de référence d’un objet est augmenté. Ce compteur est augmenté lors de la création d’un objet (alloc ou copy) ou lorsque l’on fait un retain.
    Dans le cas de la méthode cellForRowAtIndexPath, on récupère une référence vers l’objet de cellule et on récupère des données qu’elle contient. Mais la cellule est « retenue » par le UITableView qui est le seul propriétaire de la cellule.

    Pour résumer : je crée un objet, je dois alors gérer son impact mémoire. Si on me prête un objet pour appeler une de ses méthodes ou récupérer une de ses valeurs : ce n’est pas moi qui gère sa mémoire, je ne m’en occupe pas.

    Concernant les méthodes d’initialisation, on peut tout à fait en créer une comme tu le décris. A vrai dire, j’ai fait cette méthode comme ça parce qu’elle sert pour le tuto n°2 ;-)

    Bon courage !

  14. 26 mar 2011

    @Shon et @Ich’
    Manifestement, le passage à XCode 4 n’est pas le mieux pour ce tutoriel. Je vais rapidement télécharger cette version, refaire le tuto sur XCode 4 et apporter les corrections ici.
    je vous tiens au courant !

  15. Ich'
    28 mar 2011

    @Matthieu
    Ok je n’ai pas eu le temps de vraiment m’y pencher non plus mais je vais y jeter un oeil les prochains jours.
    Si jamais je trouve avant toi je te mail un pseudo-tuto que tu le mette ici :)

  16. Ich'
    28 mar 2011

    Alors déjà y’a un soucis à ce niveau là :
    - (void)viewDidLoad
    {
    [super viewDidLoad];

    NSString *path = [[NSBundle mainBundle] pathForResource:@ »websites » ofType:@ »plist »];
    NSArray *root = [[NSArray alloc] initWithContentsOfFile:path];


    }

    En fait à la dernière ligne affichée (init de mon « root »), il vaut nil. Que j’alloc un NSArray ou Mutable ça vaut « 0×0″ donc adresse nulle.
    Alors je sais pas d’où vient le soucis mais il à l’air de pas réussir à récupérer les valeurs de la source.

    Je cherche encore je vous tiens au jus.

  17. Ich'
    28 mar 2011

    Premier soucis résolu.
    En fait il faut savoir que les « .plist » sont de base (quoi que vous mettez) des Dictionary.
    Il suffit donc de créer une instance de NSDictionary de lui affecter le path.
    Ensuite de donner au NSArray la « key » (donc le Array créé).

    Voici ce que ça donne :
    - (void)viewDidLoad
    {
    [super viewDidLoad];

    //Make the path and init the array with elements in websites.plist
    NSString *path = [[NSBundle mainBundle] pathForResource:@ »websites » ofType:@ »plist »];
    //NSString *path = [[NSString alloc] initWithFormat:@ »websites.plist »];

    NSDictionary *dict = [[NSDictionary alloc] initWithContentsOfFile:path];
    NSArray *arrayFromFile = [dict objectForKey:@"root"];

    NSMutableArray *websitesToAdd = [[NSMutableArray alloc] init];
    NSEnumerator *enumerator = [arrayFromFile objectEnumerator];
    NSDictionary *object;

    while ((object = [enumerator nextObject])) {
    Website *ws = [[Website alloc] initWithDictionaryFromPlist:object];
    [websitesToAdd addObject:ws];
    [ws release];
    }

    tabWebSites = [NSArray arrayWithArray:websitesToAdd];

    [websitesToAdd release];
    [dict release];
    }

    Bon au moins ça ne plante pas, par contre j’ai encore un mini soucis au niveau de la création des objets Website je suis en train de vérifier.
    Mais normalement en faisant le début comme je l’ai indiqué vous avez un tableau avec le bon nombre d’éléments.

  18. 28 mar 2011

    @Ich’ Merci pour ta solution !
    Mais ça me semble quand même bizarre. Est-ce que sur ton éditeur de PLIST, tu as bien fait en sorte que l’élément racine (Root) soit du type Array ? Il faut en effet le changer, ce n’est pas la config par défaut. Jette un oeil sur le tuto et agrandit l’image en cliquant dessus, tu verras que j’ai mis un Array. Ceci pourrait en effet être la source de ton problème.

  19. Ich'
    28 mar 2011

    @Mat :
    Oui tu as mis un ARRAY.
    Mais quand tu ouvres le plist avec Xcode 4 en ayant mit un ARRAY bien sur tu as ça :

    root

    description
    Tutoriels en français pour le développement d'applications mobiles
    title
    ….etc

    Le point de départ AVANT d’arriver sur le ROOT c’est une branche de type NSDictionary !!

    Donc ta ligne « NSArray *arrayFromFile = [[NSMutableArray alloc] initWithContentsOfFile:path]; » ne fonctionne plus.
    Il faut d’abord récupérer le fichier « *.plist » dans un Dictionary puis lui dire que la racine root en tant qu’Array doit aller dans ton array.

    Par contre j’ai un nouveau soucis.
    C’est quand je sors du while d’affectation.
    La variable « websitesToAdd » est bonne et contient les infos.
    Juste au dessous y’a une ligne du style :
    « tabWebSites = [[NSArray alloc] initWithArray:websitesToAdd]; »

    J’ai tout essayé peu importe comment je transforme cette ligne quand je l’exécute (breakpoint oblige) bah mon tabWebSite (ou self.tabWebSite) contient le même tableau avec des NSObjects vides pour cause : « out of scope »…
    Pourtant l’Array n’a pas besoin de retain ou autre… Je galère sur ça si tu as une idée.

  20. Ich'
    28 mar 2011

    pardon petit bug de xml interprété par le site….
    Je vais y remédier.
    Je sais pas quel style de code vous avez mis en place BB, XML,… donc je vais remplacer les par ( ) :

    (plist version= »1.0″)
    (dict)
    (key)root(/key)
    (array)
    (dict)
    (key)description(/key)
    (string)Tutoriels en français pour le développement d'applications mobiles(/string)
    (key)title(/key)
    (string)Tutomobile(/string)
    (key)url(/key)
    (string)http://www.tutomobile.fr(/string)
    (/dict)

  21. Shon
    28 mar 2011

    Bonsoir,

    Je viens de tester la méthode de Ich’ (étant aussi sous xcode 4) mais pour moi aucun changement !
    Toujours aucune erreur dans le projet, mais au lancement de l’application toujours aucune liste… La je sèche :/

  22. Ich'
    28 mar 2011

    @Shon : avec ma méthode la liste (ton Array) se remplit (le temporaire avec les bonnes valeurs) mais l’attribut de la classe à que des « out of scope » pour chaque objets. Si tu as fait les bonnes manips ça doit être comme ça!

    Sinon je suis en train de chercher la solution encore :)

  23. Ich'
    28 mar 2011

    @Shon : PS : Moi non plus j’ai pas la liste mais passe en mode debug tu verra que déjà y’a du changement dans le prog :D

  24. Ich'
    29 mar 2011

    It’s done !!!!!!!!
    Pour ceux qui veulent la solution me contacter par mail ! J’essaierai de faire un tuto rapidement pour Xcode 4.

    xo xo =]

  25. Ich'
    29 mar 2011

    J’avoue j’ai un mini soucis, il m’affiche pas la description ! Pourtant tout est bon là y’a pas d’erreur de code et de compile… bizarre !

  26. Ich'
    29 mar 2011

    Réglé pour le dernier :D Tout fonctionne :D haha
    Erreur de ma part ce coup ci.

  27. Shon
    29 mar 2011

    Ah une bonne nouvelle :)
    Ta solution m’intéresse bien alors !
    Par contre pour te contacter par mail, il n’apparait pas !
    Je te donne le mien, si tu peux me contacter :
    john.troszynski@gmail.com

    Merci d’avance ! ;)

  28. Ich'
    29 mar 2011

    J’ai écris à l’admin du site, je fais demain un tuto vidéo pour Xcode 4. Si des gens sont intéressés motivez les admins pour me répondre :D
    Je m’en occupe demain.

    Ce tuto sera fait en parallèle avec celui-ci. Je le reprendrais pas en pas en expliquant les modifications pour Xcode 4.

  29. Ich'
    30 mar 2011

    J’ai préparé cet aprem’ le tuto vidéo.
    Donc disponibilité rapidement…

  30. Shon
    31 mar 2011

    Pas de news des modos ? :(

  31. Ich'
    31 mar 2011

    Si alors disons que le tuto est fini, je suis en train de le mettre sur internet là !
    Il fait quand même 50 min alors on voit justement avec les modos comment gérer ça :)

    Mais on vous promet ça rapidement :)

  32. Shon
    1 avr 2011

    Pas besoin d’un testeur pour le tuto ? :)
    Je pose ma candidature sinon ! ^^

  33. pako
    18 avr 2011

    salut
    je veu savoir comment je peu faire pour avoir le code source de ce tutoriel
    merci

  34. Keroh
    4 mai 2011

    Bonjour … je suis sur votre tuto depuis 2-3 jours et je ne suis jamais arrivé à afficher le contenu de mon .plist !

    J’ai même repris que le tout début du tuto en laissant les mêmes noms que vous, mais ça ne marche quand même pas!

    Une idée de ce que j’ai pu oublié ??

    merciii

  35. a3116b
    14 mai 2011

    Bonjour
    Merci pour vos tuto super interessant.

    Mais j’ai un soucis
    j’ai refait par 3X le tuto ci-dessus
    2X à partir de la vidéo et une 1X à partir du tuto WEB

    et bien à chaque fois, rien ne s’affiche
    j’ai tout bien suivi, pas d’erreur au niveau de la compilation
    en fait, l’application ne charge pas les infos du fichiers plist
    ou alors je les vois pas

    merci pour vos réponses

  36. 21 mai 2011

    Salut tout le monde !
    Ce tutoriel est à jour et a été modifié pour fonctionner sous XCode 4 (notamment le problème des plist désormais célèbre dans les commentaires).

    Il faut savoir à ce sujet qu’avant XCode 4, le type racine des plist était présenté dans l’affichage et pouvait être modifié. Désormais ce type racine ne peut plus être modifié sans toucher directement au XML. Il faut donc s’habituer à avoir un plist qui a un NSDictionnary comme élément racine.

    @a3116b : regarde avec cette version, si tu as encore un souci, répond dans les commentaires avec le contenu de ta méthode viewDidLoad !

  37. a3116b
    28 mai 2011

    c’est bon ça fonctionne

    merci

  38. a3116b
    28 mai 2011

    Une question tout de même
    je galère à intégrer un activity Indicator dans le DetailViewController

    merci d’avance pour vos précieux renseignements

  39. BeginnerDeveloper
    3 juin 2011

    Great tutorial, thanks a lot of :)

    Just une petite remarque :
    dans certaine version du SDK la racine du fichier plist est un Array, donc l’objet qui reçoit le contenu de la plist est un NSArray et pas un NSDictionary. (en plus dans cette exemple le Root est un Array)

    Donc il faut remplacer ces deux lines :

    NSDictionary *dictFromFile = [[NSDictionary alloc] initWithContentsOfFile:path];
    NSArray *arrayFromFile = [dictFromFile objectForKey:@"Root"];

    par celle-ci :

    NSArray *arrayFromFile = [[NSArray alloc] initWithContentsOfFile:path];
    et ça marche

    Bonne continuation :)

  40. Christophe
    20 juil 2011

    Merci énormément pour ces tuto !!

    @BeginnerDeveloper, ton tuyau fonctionne parfaitement (je débute et je suis sous xcode 3 et donc le soucis de l’affichage des éléments de la plist commençait à me faire blanchir les cheveux.)

  41. 14 sept 2011

    Super, trop cool ton tuto!!!
    Vivement d’autres tutos!

  42. tiovince1
    27 oct 2011

    Salut,
    Pour le didSelectRowAtIndexPath:, si je veux appeler différentes webviews selon le choix de la ligne, que dois je faire car je suis un peu perdu la .
    Merci par avance

  43. 3 jan 2012

    Salut
    Bravo pour le Tuto.
    je viens de faire le tuto, tout c’est bien passé sauf au niveau de l’affichage du detail.
    le contenu du site web ne s’afficha pas. est ce que quelqu’un a eu ce même problème.

    Pour ceux qui ont des soucis lors de l’affichage da liste des site au début j’ai du modifier le code source du fichier website.plist pour regler ce problème.
    j’ai du rajouter ces balise et Root apres la balise

    -====>
    -====>Root

    ….
    -====>>

    bonne continuation et encore bravo à tutomobile

  44. 14 jan 2012

    exact @BeginnerDeveloper
    ton tuyau fonctionne parfaitement

  45. momosad
    29 avr 2012

    Merci pour le tuto,
    J’arrive presque au bout en ayant adapté quelques petites choses.

    Mon PS, je n’affiche pas le detailViewController . Ma ligne sélectionnée reste en bleu et rien ne s’affiche. Pourtant en mode débug je passe bien sur la ligne [self.navigationController pushViewController:detailVC animated:YES];

    JE pense à un problème de delegate ou autre mal renseigné à partir du XIB…Si qqn a une idée.

    Bon courage à tous

  46. AMENI
    25 avr 2013

    bnjr svp je cherche un exemple de muliview avec des bouttons

  47. AMENI
    28 avr 2013

    bnjr j’ai un probleme error: Semantic Issue: Property implementation must have its declaration in interface ‘iostunisairAppDelegate’
    comment je peux le ressoudre

Trackbacks & Pingbacks

  1. Les tweets qui mentionnent Liste et détails : créer une application multivues (1/2) [Tutoriel iPhone n°8] | Tuto Mobile -- Topsy.com
  2. Application Multivue avec XCode 4 (partie 1) [Tutoriel iPhone n°10] | Tuto Mobile

Une question, une suggestion, une opinion? Partagez ce que vous pensez, laissez un commentaire.

(obligatoire)
(obligatoire)

Note: Votre adresse email ne sera jamais publiée.

Suivez les réponses aux commentaires