• 20 heures
  • {0} Facile|{1} Moyenne|{2} Difficile

Ce cours est visible gratuitement en ligne.

Ce cours existe en livre papier.

J'ai tout compris !

Mis à jour le 21/06/2013

TP – Un navigateur Web très ciblé

Connectez-vous ou inscrivez-vous gratuitement pour bénéficier de toutes les fonctionnalités de ce cours !

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.

Voici à quoi ressemblera notre application

Ç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.

Une vue Master et une vue Detail ont été automatiquement créées

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.

Les sites favoris sont affichés dans la vue Master

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 :

  1. indiquer que le contenu du Table View sera défini dans le code ;

  2. donner un nom au prototype des cellules ;

  3. indiquer au contrôle Table View combien de données il doit afficher ;

  4. relier l'objet Table View et l'objet maListe.

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).

Le contenu du Table View doit être défini dans le code

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.

Il faut donner un nom au prototype des cellules afin d'éviter l'affichage d'un avertissement

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).

Il faut relier la vue Master à la vue Detail

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).

Il faut donner un nom à la liaison pour pouvoir y faire référence

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 :

  1. autoriser l'ajout de sites via le clavier ;

  2. permettre à l'utilisateur de mémoriser le site en cours de visualisation dans les favoris ;

  3. définir des favoris sur deux niveaux, en utilisant deux Table View ;

  4. permettre une gestion avancée des favoris en autorisant (par exemple) les déplacements, suppressions et modifications de noms.

Exemple de certificat de réussite
Exemple de certificat de réussite