Cette partie a été très riche et je suis sûr que vous avez fait de sérieux progrès en programmation Objective-C. C'est pourquoi je vous propose de naviguer en solo (rassurez-vous, je ne serai pas très loin) pour créer une application à partir d'une idée et de rien d'autre !
Je ne sais pas si vous utilisez votre iPod Touch/iPhone/iPad pour naviguer sur le Web. Personnellement, cela m'arrive fréquemment, et je me rends compte que je vais toujours sur les mêmes sites. Pour faciliter l'accès à vos sites préférés, pourquoi ne pas créer une application dans laquelle il vous suffirait de cliquer sur un élément dans une liste pour accéder au site correspondant ?
Principe du TP
Ce TP est tout à fait à votre portée si vous avez parcouru les chapitres de la troisième partie. Au besoin, n'hésitez pas à vous reporter à certains passages pour avoir des pistes lorsque vous développerez ce projet.
Mais avant toute chose, je vous propose de vous montrer à quoi ressemblera notre application. Pour vous faire une idée, regardez la figure suivante.

Ça vous tente ? Alors, voici quelques conseils qui devraient bien débroussailler le terrain.
Je vais volontairement être bref pour vous montrer que vous pouvez réaliser de grandes choses… facilement !
Bien sûr, il est tout à fait possible de créer cette application à partir du modèle Single View Application
, mais il semble plus judicieux et plus facile d'utiliser le modèle Master-Detail Application
. En effet, si le modèle Single View Application
est en quelque sorte « universel », puisqu'il permet de réaliser toutes sortes d'applications, le modèle Master-Detail Application
vous épargnera une partie du travail en prédéfinissant les deux vues de l'application. Il vous suffira donc de les compléter en y ajoutant les contrôles et les contenus nécessaires.
Vous serez amenés à créer une vue détaillée dans laquelle vous placerez un contrôle Web View
. Pour que la vue principale communique l'adresse du site choisi par l'utilisateur à la vue détaillée, vous devrez créer une variable d'instance. Par exemple dans la classe de la vue détaillée, et vous arranger pour que la vue principale puisse accéder à cette variable.
Et maintenant, c'est à vous de jouer !
Correction
J'espère que vous n'avez pas eu trop de problèmes en développant cette application. Si vous n'avez rencontré aucune difficulté, je vous tire mon chapeau. Dans le cas contraire, je vous rassure, c'est tout à fait normal : le langage Objective-C est capricieux et les messages d'aide ne sont pas toujours explicites (du moins pour un néophyte). Il n'est pas rare de passer des heures sur une erreur qui, finalement, était tout autre que celle à laquelle on pensait de prime abord !
Je vais vous accompagner par étapes dans la correction de ce TP et vous verrez que ce n'était pas si complexe que ça.
Définition de l'application et de la vue secondaire
Définissez une nouvelle application de type Master-Detail Application
et donnez-lui le nom « favoris ». Comme le montre le canevas MainStoryboard.storyboard
(figure suivante), cette simple opération a créé une vue Master
et une vue Detail
.

Définition des favoris
Les sites favoris seront affichés dans la vue Master
. Le but est d'obtenir le résultat visible à la figure suivante.

Voici les informations qui correspondent aux sites Web que j'ai choisis :
Google :
http://www.google.fr
Le Site du Zéro :
http://www.siteduzero.com
Mediaforma :
http://www.mediaforma.com
Bien entendu, rien ne vous empêche de choisir d'autres sites Web ou de compléter la liste comme vous l'entendez.
Les données de la vue Master
seront directement définies dans le code. Cliquez sur MasterViewController.m
dans le volet de navigation afin de compléter la méthode ViewDidLoad
comme suit :
(void)viewDidLoad
{
[super viewDidLoad];
mesFavoris = [[NSMutableArray alloc] init];
[mesFavoris addObject:@"Google"];
[mesFavoris addObject:@"Le Site du Zéro"];
[mesFavoris addObject:@"Mediaforma"];
self.navigationItem.title = @"Mes sites Web préférés";
adressesWeb = [[NSMutableArray alloc] init];
[adressesWeb addObject:@"http://www.google.fr"];
[adressesWeb addObject:@"http://www.siteduzero.com"];
[adressesWeb addObject:@"http://www.mediaforma.com"];
}
Le premier bloc d'instructions (lignes 4 à 8) définit les éléments qui seront affichés dans le contrôle Table View
. Dans un premier temps, un espace mémoire est réservé pour l'objet NSMutableArray mesFavoris
:
mesFavoris = [[NSMutableArray alloc] init];
Les lignes 5 à 7 définissent les trois noms de sites affichés dans le Table View
:
[mesFavoris addObject:@"Google"];
[mesFavoris addObject:@"Le Site du Zéro"];
[mesFavoris addObject:@"Mediaforma"];
L'instruction de la ligne 8 définit le titre qui sera affiché dans la partie supérieure du Table View
:
self.navigationItem.title = @"Mes sites Web préférés";
Le bloc d'instructions suivant (lignes 10 à 13) initialise un autre NSArray
dans lequel seront stockées les adresses URL qui correspondent aux sites affichés dans le Table View
. La première instruction réserve de l'espace en mémoire pour accueillir l'objet NSArray adressesWeb
:
adressesWeb = [[NSMutableArray alloc] init];
Les trois instructions suivantes définissent les adresses des trois sites favoris :
[adressesWeb addObject:@"http://www.google.fr"];
[adressesWeb addObject:@"http://www.siteduzero.com"];
[adressesWeb addObject:@"http://www.mediaforma.com"];
Pour que cette méthode fonctionne, vous devez déclarer les variables mesFavoris
et adressesWeb
dans le fichier d'en-têtes. Cliquez sur MasterViewController.h
dans le volet de navigation et définissez les deux variables d'instance :
#import <UIKit/UIKit.h>
@interface MasterViewController : UITableViewController
{
NSMutableArray *mesFavoris;
NSMutableArray *adressesWeb;
}
@end
Si vous avez des souvenirs du chapitre 3 de la partie III, consacré aux informations tabulaires, vous savez que la définition du NSArray
ne suffit pas. Vous devez également le relier au Table View
pour que les données s'affichent. Pour cela, vous devez :
indiquer que le contenu du
Table View
sera défini dans le code ;donner un nom au prototype des cellules ;
indiquer au contrôle
Table View
combien de données il doit afficher ;relier l'objet
Table View
et l'objetmaListe
.
Pour indiquer que le contenu du Table View
sera défini dans le code, cliquez sur MainStoryboard.storyboard
dans le volet de navigation (1), comme indiqué à la figure suivante. Cliquez sur le Table View
dans le canevas (2), affichez le volet des utilitaires en cliquant sur Show or hide the Utilities
(3), affichez l'inspecteur des attributs en cliquant sur Show the Attributes inspector
(4) puis choisissez Dynamic Properties
dans le paramètre Content
(5).

Pour donner un nom au prototype des cellules et ainsi éviter l'affichage d'un avertissement dans la barre d'outils de Xcode, cliquez sur la cellule affichée sous Prototype Cells
dans le canevas (1) et renseignez la zone de texte Identifier
(2), comme à la figure suivante.

Enfin, pour indiquer au contrôle Table View
combien de données il doit afficher, et afin de relier les objets Tab View
et maListe
, définissez les méthodes numberOfRowsInSection
et cellForRowAtIndexPath
dans le fichier MasterViewController.m
:
(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [mesFavoris count];
}
(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = @"MyIdentifier";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
// Configuration des cellules
NSString *cellValue = [mesFavoris objectAtIndex:indexPath.row];
cell.textLabel.text = cellValue;
return cell;
}
Si vous cliquez sur Run
, la fenêtre du simulateur iOS affiche triomphalement vos sites favoris.
Insertion du contrôle Web View
et liaison au code
Cliquez sur MainStoryboard.storyboard
dans le volet de navigation, cliquez sur Detail view
dans le canevas, supprimez le contrôle Label
dans lequel apparaît le texte « Detail view content goes here » et remplacez-le par un contrôle Web View
que vous redimensionnerez pour lui donner tout l'espace disponible dans la vue.
Pour que le contrôle Web View
puisse communiquer avec le code, vous allez définir un outlet. Dans la barre d'outils de Xcode, au-dessus du libellé Editor
, cliquez sur l'icône Show the Assistant editor
. Contrôle-glissez-déposez le contrôle Web View
dans le fichier d'en-têtes DetailViewController.h
, juste au-dessus du @end
final. Au relâchement du bouton gauche de la souris, donnez le nom « pageWeb » à l'outlet et validez en cliquant sur Connect
. Le code du fichier d'en-têtes DetailViewController.h
doit maintenant ressembler à ceci :
#import <UIKit/UIKit.h>
@interface DetailViewController : UIViewController
@property (strong, nonatomic) id detailItem;
@property (strong, nonatomic) IBOutlet UILabel *detailDescriptionLabel;
@property (weak, nonatomic) IBOutlet UIWebView *pageWeb;
@end
Mise en relation des deux vues
Vous allez maintenant relier la vue Master
à la vue Detail
. Comme d'habitude, cliquez sur MainStoryboard.storyboard
dans le volet de navigation. Comme le montre la figure suivante, cliquez sur l'élément qui représente une cellule dans la vue Master
(1) puis contrôle-glissez-déposez cet élément sur la vue Detail
(2). Au relâchement du bouton gauche de la souris, sélectionnez Push
dans le menu (3).

Pour pouvoir faire référence à cette liaison dans le code, vous allez lui donner un nom. Comme à la figure suivante, cliquez sur le symbole qui identifie la liaison dans le canevas (1), sur l'icône Hide or show the Utilities
dans la barre d'outils (2), sur Show the Attributes inspector
dans le volet des utilitaires (3) puis donnez le nom « detailSegue » à la liaison (4).

Partage de données entre les deux vues
Lorsque l'utilisateur sélectionne un élément dans le contrôle Table View
, la vue Detail
remplace la vue Master
. Pour passer des informations de la vue Master
à la vue Detail
, vous allez utiliser la méthode prepareForSegue
. Pour cela, vous devez passer par une variable intermédiaire. Cliquez sur DetailViewController.h
dans le volet de navigation et définissez la propriété siteSelectionne
comme suit :
@property (strong, nonatomic) id siteSelectionne;
Le fichier DetailViewController.h
doit maintenant contenir les instructions suivantes :
#import <UIKit/UIKit.h>
@interface DetailViewController : UIViewController
@property (strong, nonatomic) id detailItem;
@property (strong, nonatomic) id siteSelectionne;
@property (strong, nonatomic) IBOutlet UILabel *detailDescriptionLabel;
@property (weak, nonatomic) IBOutlet UIWebView *pageWeb;
@end
Cliquez sur DetailViewController.m
dans le volet de navigation et ajoutez une instruction synthesize
pour pouvoir accéder à la propriété siteSelectionne
:
@synthesize siteSelectionne = _siteSelectionne;
Ah, encore une petite chose : comme vous allez faire référence à la vue Detail
dans la vue Master
, vous devez également ajouter une instruction #import
au début du fichier MasterViewController.m
. Cliquez sur MasterViewController.m
dans le volet de navigation et insérez l'instruction suivante, juste après l'instruction #import
existante :
#import "DetailViewController.h"
Vous pouvez maintenant insérer la méthode prepareForSegue
. Si ce n'est pas déjà fait, cliquez sur MasterViewController.m
dans le volet de navigation et insérez les instructions suivantes dans le code (peu importe l'endroit) :
(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender{
if([[segue identifier] isEqualToString:@"detailSegue"])
{
NSInteger selectedIndex = [[self.tableView indexPathForSelectedRow] row];
DetailViewController *dvc = [segue destinationViewController];
dvc.siteSelectionne = [NSString stringWithFormat:@"%@", [adressesWeb objectAtIndex:selectedIndex]];
}
}
L'adresse du site sélectionné est mémorisée dans la variable d'instance siteSelectionne
de la vue Detail
(dvc.siteSelectionne
). L'adresse est obtenue à partir du tableau adressesWeb
, et plus précisément de la cellule dont l'index correspond à celui de la cellule sélectionnée ([adressesWeb objectAtIndex:selectedIndex
]). L'élément obtenu est converti en un NSString
avant d'être mémorisé dans la variable siteSelectionne
([NSString stringWithFormat:@"%@", …
) :
dvc.siteSelectionne = [NSString stringWithFormat:@"%@", [adressesWeb objectAtIndex:selectedIndex]];
Vous devez enfin récupérer le site sélectionné dans DetailViewController.m
et afficher la page correspondante. Cliquez sur DetailViewController.m
dans le volet de navigation et complétez la méthode viewDidLoad
comme ceci :
[_pageWeb loadRequest:[NSURLRequest requestWithURL: [NSURL URLWithString:_siteSelectionne]]];
Cette instruction demande l'affichage du site d'adresse siteSelectionne
dans le contrôle Web View
.
Vous pouvez exécuter l'application en cliquant sur Run
. Quelle réussite !
Les fichiers de cette application se trouvent dans le dossier favoris
.
MasterViewController.h
#import <UIKit/UIKit.h>
@interface MasterViewController : UITableViewController
{
NSMutableArray *mesFavoris;
NSMutableArray *adressesWeb;
}
@end
MasterViewController.m
#import "MasterViewController.h"
#import "DetailViewController.h"
@implementation MasterViewController
(void)awakeFromNib
{
[super awakeFromNib];
}
(void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Release any cached data, images, etc that aren't in use.
}
#pragma mark - View lifecycle
(void)viewDidLoad
{
[super viewDidLoad];
mesFavoris = [[NSMutableArray alloc] init];
[mesFavoris addObject:@"Google"];
[mesFavoris addObject:@"Le Site du Zéro"];
[mesFavoris addObject:@"Mediaforma"];
self.navigationItem.title = @"Mes sites Web préférés";
adressesWeb = [[NSMutableArray alloc] init];
[adressesWeb addObject:@"http://www.google.fr"];
[adressesWeb addObject:@"http://www.siteduzero.com"];
[adressesWeb addObject:@"http://www.mediaforma.com"];
}
(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [mesFavoris count];
}
(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = @"MyIdentifier";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
// Configuration des cellules
NSString *cellValue = [mesFavoris objectAtIndex:indexPath.row];
cell.textLabel.text = cellValue;
return cell;
}
(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender{
if([[segue identifier] isEqualToString:@"detailSegue"])
{
NSInteger selectedIndex = [[self.tableView indexPathForSelectedRow] row];
DetailViewController *dvc = [segue destinationViewController];
dvc.siteSelectionne = [NSString stringWithFormat:@"%@", [adressesWeb objectAtIndex:selectedIndex]];
}
}
(void)viewDidUnload
{
[super viewDidUnload];
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
}
(void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
}
(void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
}
(void)viewWillDisappear:(BOOL)animated
{
[super viewWillDisappear:animated];
}
(void)viewDidDisappear:(BOOL)animated
{
[super viewDidDisappear:animated];
}
(BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
// Return YES for supported orientations
return (interfaceOrientation != UIInterfaceOrientationPortraitUpsideDown);
}
@end
DetailViewController.h
#import <UIKit/UIKit.h>
@interface DetailViewController : UIViewController
@property (strong, nonatomic) id detailItem;
@property (strong, nonatomic) id siteSelectionne;
@property (strong, nonatomic) IBOutlet UILabel *detailDescriptionLabel;
@property (weak, nonatomic) IBOutlet UIWebView *pageWeb;
@end
DetailViewController.m
#import "DetailViewController.h"
@interface DetailViewController ()
(void)configureView;
@end
@implementation DetailViewController
@synthesize detailItem = _detailItem;
@synthesize detailDescriptionLabel = _detailDescriptionLabel;
@synthesize pageWeb = _pageWeb;
@synthesize siteSelectionne = _siteSelectionne;
#pragma mark - Managing the detail item
(void)setDetailItem:(id)newDetailItem
{
if (_detailItem != newDetailItem) {
_detailItem = newDetailItem;
// Update the view.
[self configureView];
}
}
(void)configureView
{
// Update the user interface for the detail item.
if (self.detailItem) {
self.detailDescriptionLabel.text = [self.detailItem description];
}
}
(void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Release any cached data, images, etc that aren't in use.
}
#pragma mark - View lifecycle
(void)viewDidLoad
{
[super viewDidLoad];
[self configureView];
[_pageWeb loadRequest:[NSURLRequest requestWithURL: [NSURL URLWithString:_siteSelectionne]]];
}
(void)viewDidUnload
{
[self setPageWeb:nil];
[super viewDidUnload];
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
}
(void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
}
(void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
}
(void)viewWillDisappear:(BOOL)animated
{
[super viewWillDisappear:animated];
}
(void)viewDidDisappear:(BOOL)animated
{
[super viewDidDisappear:animated];
}
(BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
// Return YES for supported orientations
return (interfaceOrientation != UIInterfaceOrientationPortraitUpsideDown);
}
@end
Aller plus loin
Vous voulez aller plus loin ? Pas de problème, mais attention, cela sort du cadre de ce TP. Vous pourriez :
autoriser l'ajout de sites via le clavier ;
permettre à l'utilisateur de mémoriser le site en cours de visualisation dans les favoris ;
définir des favoris sur deux niveaux, en utilisant deux
Table View
;permettre une gestion avancée des favoris en autorisant (par exemple) les déplacements, suppressions et modifications de noms.