Le modèle
Master-Detail Application est particulièrement bien adapté aux iPad. Leur surface d'affichage, bien plus grande que celle offerte par les iPhone et iPod Touch, permet aux applications basées sur ce modèle de rassembler deux vues : une liste et une vue détaillée de l'élément sélectionné dans la liste. L'application iPad Mail (figure suivante) est un parfait exemple d'utilisation de ce modèle.
Et maintenant, passons sans plus attendre à la pratique.
Définissez une nouvelle application basée sur le modèle
Master-Detail Application, donnez-lui le nom « masterDetail » et choisissez
iPad dans la liste déroulante
Device Family.
Cliquez sur
MainStoryboard.storyboard dans le volet de navigation et observez les nombreux objets qui ont été créés, visibles à la figure suivante.
Comme vous pouvez le voir, l'application contient :
- un contrôleur de vue Split View ;
- un contrôleur de navigation et un Table View liés au Master View Controller ;
- un contrôleur de navigation et un contrôleur de vue détaillée.
Exécutez l'application en cliquant sur l'icône
Run. La figure suivante représente le résultat obtenu en mode portrait et en mode paysage. Pas si mal pour un début !
La vue
Master consiste en un contrôle
Table View. La vue
Detail représente les détails de l'élément sélectionné dans la vue
master.
Je sens que vous avez envie de vous dégourdir les doigts. Ça tombe bien, nous allons personnaliser l'application qui vient d'être générée par Xcode pour qu'elle affiche trois éléments dans la liste et qu'un clic sur l'un d'entre eux provoque l'affichage d'une image et d'un texte dans la vue détaillée. À la figure suivante se trouve le résultat à obtenir.
Pour arriver à ce résultat, voici les étapes à accomplir :
- suppression du texte affiché dans la vue détaillée au lancement de l'application ;
- modification du texte affiché dans la barre d'outils ;
- définition des entrées textuelles du contrôle Table View ;
- ajout de trois images dans les ressources de l'application et d'un contrôle Image View dans la vue détaillée ;
- dans la vue détaillée, affichage de l'image et du texte correspondant à l'entrée cliquée dans le contrôle Table View.
Commençons par supprimer le texte affiché dans la vue détaillée au lancement de l'application. Cette étape est très simple : cliquez sur l'entrée
MainStoryboard.storyboard dans le volet de navigation. Repérez le
Label dans la vue
Detail, cliquez dessus puis appuyez sur la touche
Suppr du clavier pour le supprimer.
Nous allons maintenant modifier le texte affiché dans la barre d'outils. Cliquez sur
DetailViewController.m dans le volet de navigation. Repérez la méthode suivante :
Code : Objective-C | splitViewController:willHideViewController:withBarButtonItem:forPopoverController:
|
… et modifiez sa première instruction comme suit :
Code : Objective-C | barButtonItem.title = @"À vous de choisir";
|
Nous en sommes déjà à la troisième étape, à savoir, la définition des entrées affichées dans le contrôle
Table View. Nous allons pour cela utiliser la méthode
viewDidLoad du fichier
MasterViewController.m. Cliquez sur ce fichier dans le volet de navigation et complétez la méthode
viewDidLoad comme suit :
Code : Objective-C 1
2
3
4
5
6
7
8
9
10
11
12 | - (void)viewDidLoad
{
laListe = [[NSMutableArray alloc] init];
[laListe addObject: @"Chat"];
[laListe addObject: @"Chien"];
[laListe addObject: @"Cheval"];
self.navigationItem.title = @"Choisissez un animal";
[super viewDidLoad];
self.clearsSelectionOnViewWillAppear = NO;
self.contentSizeForViewInPopover = CGSizeMake(320.0, 600.0);
}
|
La première instruction initialise le
NSMutableArray laListe :
Code : Objective-C | laListe = [[NSMutableArray alloc] init];
|
Les trois instructions suivantes ajoutent trois entrées dans le tableau
laListe :
Code : Objective-C | [laListe addObject: @"Chat"];
[laListe addObject: @"Chien"];
[laListe addObject: @"Cheval"];
|
Enfin, la dernière instruction ajoutée définit le titre qui sera affiché dans la fenêtre popup :
Code : Objective-C | self.navigationItem.title = @"Choisissez un animal";
|
L'objet
laListe a été initialisé dans la méthode
viewDidLoad, mais il n'a pas été défini. Pour réparer cette lacune, cliquez sur
MasterViewController.h dans le volet de navigation, puis ajoutez les lignes 2 à 4 sous la définition de l'interface :
Code : Objective-C | @interface MasterViewController : UITableViewController
{
NSMutableArray *laListe;
}
|
Pour afficher les données dans le
Table View, vous devez encore insérer deux méthodes 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 [laListe count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *MyIdentifier = @"MyIdentifier";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:MyIdentifier];
if (cell == nil)
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:MyIdentifier];
// Configuration de la cellule
NSString *cellValue = [laListe objectAtIndex:indexPath.row];
cell.textLabel.text = cellValue;
return cell;
}
|
Attention, si vous essayez d'exécuter l'application, vous obtiendrez une erreur lors de la compilation. Il reste en effet plusieurs petits détails à régler avant que l'affichage des données dans le
Table View soit opérationnel :
- le contenu des cellules étant défini dans le code, il faut utiliser un Dynamic Prototypes ;
- un identifiant doit être affecté au prototype.
Vous pouvez dès maintenant exécuter l'application et vérifier que le
Table View affiche bien les trois cellules qui ont été renseignées.
Maintenant, ajoutez trois images dans les ressources de l'application. Si vous voulez utiliser les mêmes images que moi,
vous pouvez les télécharger ici.
Ensuite, cliquez sur l'entrée
MainStoryboard.storyboard dans le volet de navigation. Repérez la vue
Detail et ajoutez-y un contrôle
Image View et un contrôle
Label. Redimensionnez et repositionnez ces contrôles afin d'obtenir quelque chose comme la figure suivante.
Définissez l'outlet
uneImage pour le contrôle
Image View et l'outlet
laLegende pour le contrôle
Label. Ces opérations doivent maintenant vous être familières. Au besoin, reportez-vous aux chapitres précédents pour avoir plus de détails sur la technique à utiliser.
L'application est presque terminée : il ne reste plus que la cinquième étape, l'affichage de l'image et du texte qui correspondent à l'élément choisi par l'utilisateur dans le
Table View.
Avant tout, vous devez déterminer quel élément a été choisi par l'utilisateur en définissant la méthode
didSelectRowAtIndexPath dans la vue
Master. Cliquez sur
MasterViewController.m et définissez la méthode
didSelectRowAtIndexPath comme suit :
Code : Objective-C | - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
self.detailViewController.detailItem = [NSString stringWithFormat: @"%@", [laListe objectAtIndex: indexPath.row]];
}
|
L'unique instruction de cette méthode récupère l'élément choisi, le convertit en un
NSString et le stocke dans la variable
detailItem de la vue détaillée.
Il ne reste plus qu'à récupérer la donnée passée dans la vue détaillée et à afficher l'image et le texte correspondants. Cela se fera dans la méthode
configureView de la vue
Detail.
Cliquez sur
DetailViewController.m dans le volet de navigation. Repérez la méthode
configureView et complétez-la comme suit :
Code : Objective-C | - (void)configureView
{
self.laLegende.text = [self.detailItem description];
self.uneImage.image = [UIImage imageNamed:[NSString stringWithFormat:@"%@%@",[self.detailItem description],@".jpg"]];
}
|
Cette méthode est également très courte.
La ligne 3 récupère la chaîne stockée dans l'objet
detailItem. Cette chaîne est affectée à la propriété texte du
Label laLegende, ce qui provoque son affichage dans la vue détaillée.
La ligne 4 vous semble peut-être un peu compliquée. Cela vient de l'imbrication de trois messages dans la deuxième partie de l'instruction. N'ayez crainte, il n'y a rien d'insurmontable dans cette instruction. Pour bien comprendre ce qui se passe, il faut toujours partir du message le plus interne. Ici :
Code : Objective-C | [self.detailItem description]
|
Ce message a déjà été utilisé dans l'instruction précédente. Il renvoie la version texte du contenu de l'objet
detailItem. Jusque-là, tout va bien !
Passons au message qui engloble
[self.detailItem description] :
Code : Objective-C | [NSString stringWithFormat:@"%@%@",[self.detailItem description],@".jpg"]
|
Ce message crée un objet
NSString en concaténant la valeur sélectionnée par l'utilisateur et la chaîne « .jpg ».
Supposons que l'utilisateur choisisse « chat » dans le
Table View. Ce message produira un
NSString contenant la chaîne « chat.jpg ».
Passons enfin au troisième message :
Code : Objective-C | [UIImage imageNamed: ...]
|
Ce message définit un objet
UIImage en piochant dans les ressources de l'application. Le fichier sélectionné est précisément celui dont le nom a été calculé à l'étape précédente.
Pour résumer, ces trois messages imbriqués fabriquent un objet
UIImage dont le nom est égal au contenu de la cellule sélectionnée par l'utilisateur, auquel on ajoute « .jpg ». Vous voyez, tout cela est très simple
Ah oui, j'allais oublier l'essentiel : une fois l'objet
UIImage obtenu, il est affecté à la propriété
image du contrôle I
mage View (
self.uneImage.image = …). Ainsi, l'image est affichée dans la vue
Detail.
Il ne vous reste plus qu'à cliquer sur
Run et à profiter du résultat (figure suivante).
Les fichiers de l'application se trouvent dans le dossier
masterDetail.
MasterViewController.h
Secret (cliquez pour afficher)
Code : Objective-C 1
2
3
4
5
6
7
8
9
10
11
12 | #import <UIKit/UIKit.h>
@class DetailViewController;
@interface MasterViewController : UITableViewController
{
NSMutableArray *laListe;
}
@property (strong, nonatomic) DetailViewController *detailViewController;
@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 | #import "MasterViewController.h"
#import "DetailViewController.h"
@implementation MasterViewController
@synthesize detailViewController = _detailViewController;
- (void)awakeFromNib
{
self.clearsSelectionOnViewWillAppear = NO;
self.contentSizeForViewInPopover = CGSizeMake(320.0, 600.0);
[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];
laListe = [[NSMutableArray alloc] init];
[laListe addObject: @"Chat"];
[laListe addObject: @"Chien"];
[laListe addObject: @"Cheval"];
self.navigationItem.title = @"Choisissez un animal";
self.detailViewController = (DetailViewController *)[[self.splitViewController.viewControllers lastObject] topViewController];
[self.tableView selectRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:0] animated:NO scrollPosition:UITableViewScrollPositionMiddle];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [laListe count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *MyIdentifier = @"MyIdentifier";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:MyIdentifier];
if (cell == nil)
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:MyIdentifier];
// Configuration de la cellule
NSString *cellValue = [laListe objectAtIndex:indexPath.row];
cell.textLabel.text = cellValue;
return cell;
}
- (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 YES;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
self.detailViewController.detailItem = [NSString stringWithFormat: @"%@", [laListe objectAtIndex: indexPath.row]];
}
@end
|
DetailViewController.h
Secret (cliquez pour afficher)
Code : Objective-C | #import <UIKit/UIKit.h>
@interface DetailViewController : UIViewController <UISplitViewControllerDelegate>
@property (strong, nonatomic) id detailItem;
@property (strong, nonatomic) IBOutlet UILabel *detailDescriptionLabel;
@property (weak, nonatomic) IBOutlet UIImageView *uneImage;
@property (weak, nonatomic) IBOutlet UILabel *laLegende;
@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
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104 | #import "DetailViewController.h"
@interface DetailViewController ()
@property (strong, nonatomic) UIPopoverController *masterPopoverController;
- (void)configureView;
@end
@implementation DetailViewController
@synthesize detailItem = _detailItem;
@synthesize detailDescriptionLabel = _detailDescriptionLabel;
@synthesize uneImage = _uneImage;
@synthesize laLegende = _laLegende;
@synthesize masterPopoverController = _masterPopoverController;
#pragma mark - Managing the detail item
- (void)setDetailItem:(id)newDetailItem
{
if (_detailItem != newDetailItem) {
_detailItem = newDetailItem;
// Update the view.
[self configureView];
}
if (self.masterPopoverController != nil) {
[self.masterPopoverController dismissPopoverAnimated:YES];
}
}
- (void)configureView
{
self.laLegende.text = [self.detailItem description];
self.uneImage.image = [UIImage imageNamed:[NSString stringWithFormat:@"%@%@",[self.detailItem description],@".jpg"]];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Release any cached data, images, etc that aren't in use.
}
#pragma mark - View lifecycle
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
[self configureView];
}
- (void)viewDidUnload
{
[self setUneImage:nil];
[self setLaLegende: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 YES;
}
#pragma mark - Split view
- (void)splitViewController:(UISplitViewController *)splitController willHideViewController:(UIViewController *)viewController withBarButtonItem:(UIBarButtonItem *)barButtonItem forPopoverController:(UIPopoverController *)popoverController
{
barButtonItem.title = @"A vous de choisir";
[self.navigationItem setLeftBarButtonItem:barButtonItem animated:YES];
self.masterPopoverController = popoverController;
}
- (void)splitViewController:(UISplitViewController *)splitController willShowViewController:(UIViewController *)viewController invalidatingBarButtonItem:(UIBarButtonItem *)barButtonItem
{
// Called when the view is shown again in the split view, invalidating the button and popover controller.
[self.navigationItem setLeftBarButtonItem:nil animated:YES];
self.masterPopoverController = nil;
}
@end
|