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 :
Code : Objective-C 1
2
3
4
5
6
7
8
9
10
11
12
13
14 | - (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 :
Code : Objective-C | mesFavoris = [[NSMutableArray alloc] init];
|
Les lignes 5 à 7 définissent les trois noms de sites affichés dans le
Table View :
Code : Objective-C | [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 :
Code : Objective-C | 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 :
Code : Objective-C | adressesWeb = [[NSMutableArray alloc] init];
|
Les trois instructions suivantes définissent les adresses des trois sites favoris :
Code : Objective-C | [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 :
Code : Objective-C | #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'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).
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 :
Code : Objective-C 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 | - (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
Pour ceux qui auraient la mémoire courte, les contenus Web sont affichés dans des contrôles Web View.
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 :
Code : Objective-C | #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 :
Code : Objective-C | @property (strong, nonatomic) id siteSelectionne;
|
Le fichier
DetailViewController.h doit maintenant contenir les instructions suivantes :
Code : Objective-C | #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 :
Code : Objective-C | @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 :
Code : Objective-C | #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) :
Code : Objective-C | -(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:@"%@", …) :
Code : Objective-C | 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 :
Code : Objective-C | [_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
Secret (cliquez pour afficher)
Code : Objective-C | #import <UIKit/UIKit.h>
@interface MasterViewController : UITableViewController
{
NSMutableArray *mesFavoris;
NSMutableArray *adressesWeb;
}
@end
|
MasterViewController.m
Secret (cliquez pour afficher)
Code : Objective-C 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98 | #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
Secret (cliquez pour afficher)
Code : Objective-C | #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
Secret (cliquez pour afficher)
Code : Objective-C 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84 | #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.