New tuto 6 : Utiliser une tableview pour présenter des données +Picker

Vous voilà familier avec quelques éléments du UIKit, et roi du delegate ! Il existe des manières élégantes de présenter des listes de données à l’utilisateur, encore faut-il savoir les mettre en place.

Maintenant que le delegate n’a plus de secrets pour vous, nous allons voir comment construire par le code une table view (liste de données) et des picker view (l’élément rotatif lorsque vous choisissez l’heure de votre réveil par exemple).
Commençons par créer une liste (UITableView). Il n’y a rien de plus simple !

Utilisation d’une TableView

Créez un projet Window-based Application que vous nommerez TableView et ajoutez un fichier, de type UIViewController > subclass of UITableViewController comme à la Figure 1, que vous nommerez DataListViewController.

 

Dans votre application delegate, ajoutez à la window la vue du DataListViewController. Petite piqûre de rappel au besoin :
Le .h

#import <UIKit/UIKit.h>
#import « DataListViewController.h »

@interface TableViewAppDelegate : NSObject <UIApplicationDelegate> {

DataListViewController *dataListViewController;

}

@property (nonatomic, retain) IBOutlet UIWindow *window;

@end

Le .m

– (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
dataListViewController = [[DataListViewController alloc] initWithStyle:UITableViewStylePlain]; // 1
[self.window addSubview:dataListViewController.view];
[self.window makeKeyAndVisible];
return YES;
}

– (void)dealloc
{
[dataListViewController release];
[_window release];
[super dealloc];
}

Ligne 1, nous pouvons remarquer que l’on peut choisir le style de la liste de données. Il en existe 2 : plain qui affiche les données comme dans le carnet d’adresses, ou grouped qui affiche les données par section, comme dans les préférences de votre iPhone/iPod Touch.
Il faut maintenant afficher des données. Dans le .h de DataListViewController, déclarer un NSArray nommé dataToShow. Dans le viewDidLoad (.m), initialisez-le comme suit :

– (void)viewDidLoad
{
[super viewDidLoad];

dataToShow = [[NSArray alloc] initWithObjects:@ »Element 1″, @ »Element 2″, @ »Element 3″, @ »Element 4″, @ »Element 5″,nil];
}

N’oubliez pas

– (void)dealloc
{
[dataToShow release];
[super dealloc];
}

Ensuite, modifiez ces quelques lignes de code :

– (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
// Return the number of sections.
return 1; // 1
}

– (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
// Return the number of rows in the section.
return [dataToShow count]; // 2
}

Ligne 1, nous spécifions le nombre de sections, comme dans la liste de votre musique dans l’application iPod (rangée par ordre alphabétique, chaque lettre correspondant à une section). Ici, nous ne nous en servons pas, il n’y a donc qu’une section. À la ligne 2, c’est le nombre de lignes que vous définissez. Ici, nous retournons le nombre d’objets de notre tableau.
Enfin, il faut afficher ces données. Pour cela, modifiez la méthode suivante :

– (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = @ »Cell »;

UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier]; // 1
if (cell == nil) { // 2
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
}

cell.textLabel.text = [dataToShow objectAtIndex:indexPath.row]; // 3

return cell;
}

Cette méthode porte bien son nom. Elle demande à retourner la cellule à afficher pour chaque ligne.
Pour que le défilement de votre table view soit le plus fluide possible, il faut faire très attention à la gestion mémoire de votre cellule. Par défaut, Apple introduit un mécanisme que vous retrouvez ligne 1 où la cellule est mise en cache en fonction de CellIdentifier.
Si cette cellule n’est pas allouée, on rentre dans la condition if ligne 2 pour l’allouer. On peut choisir le style de la cellule, n’hésitez pas à parcourir la documentation pour les voir tous. Nous apprendrons à en créer une personnalisée à la section « Créer une cellule personnalisée » quelques pages plus loin. Enfin, nous allons afficher le bon élément pour la ligne demandée.
NSIndexPath comprend deux propriétés : section et row (ligne). La cellule de type UITableViewCell- StyleDefault comprend entre autres un label textLabel. Nous nous en servons pour afficher ligne 3 l’élément correspondant.

Compilez et lancez.Vous devriez obtenir quelque chose de semblable à la Figure 2
http://www.ipup.fr/forum/userimages/12.2.png

Figure 2 : Votre première liste de données !

Cliquer sur une cellule

Maintenant, nous allons rendre cette liste de données cliquable. Pour cela, il faut tout d’abord utiliser un contrôleur de navigation (UINavigationController) qui va permettre de gérer la navigation entre les différentes vues très simplement.
Modifiez votre application delegate pour obtenir ceci :
Dans le .h

#import <UIKit/UIKit.h>
#import « DataListViewController.h »

@interface TableViewAppDelegate : NSObject <UIApplicationDelegate> {

DataListViewController *dataListViewController;
UINavigationController *navController;
}

@property (nonatomic, retain) IBOutlet UIWindow *window;

@end

Dans le .m

– (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
dataListViewController = [[DataListViewController alloc] initWithStyle:UITableViewStylePlain];
navController = [[UINavigationController alloc] initWithRootViewController:dataListViewController]; // 1
[self.window addSubview:navController.view]; // 2
[self.window makeKeyAndVisible];
return YES;
}

– (void)dealloc
{
[navController release];
[dataListViewController release];
[_window release];
[super dealloc];
}

En 1, on alloue le contrôleur de navigation, avec comme rootViewController dataListViewController, puis on affiche la vue du navController ainsi alloué à la place de la vue de dataListViewController ligne 2.
Ensuite, il faut créer un nouveau contrôleur (UIViewController) que vous nommerez DetailListViewController (cochez With XIB for User Interface). Dans ce contrôleur, affichez un label :
Dans le .h

#import <UIKit/UIKit.h>

@interface DetailListViewController : UIViewController {
IBOutlet UILabel *label;
}

@property (nonatomic, retain) UILabel *label;
@end

Dans le .m, ajoutez cette ligne

@synthesize label;

Et n’oubliez pas le dealloc

– (void)dealloc
{
[label release];
[super dealloc];
}

Revenez à DataListViewController.m et rendez-vous à la méthode – (void)tableView:(UITableView *) tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath que vous implémenterez comme suit : (n’oubliez pas l’import de DetailListViewController.h)

– (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
DetailListViewController *detailViewController = [[DetailListViewController alloc] initWithNibName:@ »DetailListViewController » bundle:nil];
[self.navigationController pushViewController:detailViewController animated:YES];

detailViewController.label.text = [dataToShow objectAtIndex:[indexPath row]];
detailViewController.title = @ »Détail »;

[detailViewController release];
}

Maintenant, en cliquant sur une cellule, vous affichez une nouvelle vue. Libre à vous de la remplir comme vous le souhaitez !

Créer une cellule personnalisée

Pour finir avec les table view, nous allons voir comment créer une cellule personnalisée. Ajoutez un nouveau fichier, iOS > Cocoa Touch Classes > Objective-C class > subclass of UITableViewCell que vous nommerez CustomCell. Ensuite, modifiez ces nouveaux fichiers pour y ajouter un label :

Dans le .h

#import <UIKit/UIKit.h>

@interface CustomCell : UITableViewCell {
UILabel *myLabel;
}

@property (nonatomic, retain) UILabel *myLabel;

@end

Dans le .m

#import « CustomCell.h »

@implementation CustomCell
@synthesize myLabel;

– (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self) {

myLabel = [[UILabel alloc] initWithFrame:CGRectMake(150, 5, 120, 30)];
myLabel.font = [UIFont fontWithName:@ »zapfino » size:12.0];
[self addSubview:myLabel];
}
return self;
}

– (void)setSelected:(BOOL)selected animated:(BOOL)animated
{
[super setSelected:selected animated:animated];

// Configure the view for the selected state
}

– (void)dealloc
{
[myLabel release];
[super dealloc];
}

@end

Enfin, dans DataListViewController.m, ajouter l’import de CustomCell.h, puis modifiez la méthode cellForRowAtIndexPath comme suit :

– (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = @ »Cell »;

CustomCell *cell = (CustomCell*)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[CustomCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
}

cell.myLabel.text = [dataToShow objectAtIndex:indexPath.row];

return cell;
}

Compilez et lancez, vous devriez obtenir une liste de données comme à la Figure 3.
http://www.ipup.fr/forum/userimages/12.3.png

Figure 3 : Une liste de cellules customisées

Une bonne chose de faite non ? Un petit café, ou alors continuez sur votre lancée !

Utiliser un picker view

Créez un nouveau projet Window-based Application, que vous nommerez Picker, pour utiliser les picker view.
Créez ensuite une nouvelle classe de type UIViewController que vous nommerez PickerViewController. Comme d’habitude, ajoutez la vue d’un objet PickerViewController (que vous aurez alloué dans votre application delegate) à la window.

Ensuite, dans le viewDidLoad de PickerViewController, ajoutez ceci :

– (void)viewDidLoad
{
[super viewDidLoad];

myPickerView = [[UIPickerView alloc] initWithFrame:CGRectZero]; // 1
CGSize pickerSize = [myPickerView sizeThatFits:CGSizeZero];
CGRect screenRect = [[UIScreen mainScreen] applicationFrame];
CGRect pickerRect = CGRectMake( 0.0,
screenRect.size.height – 84.0 – pickerSize.height,
pickerSize.width,
pickerSize.height);
myPickerView.frame = pickerRect; // 2
myPickerView.autoresizingMask = UIViewAutoresizingFlexibleWidth;

myPickerView.showsSelectionIndicator = YES; // 3
myPickerView.delegate = self; // 4
myPickerView.dataSource = self; // 5
[self.view addSubview:myPickerView];

label = [[UILabel alloc] initWithFrame:CGRectMake(10, 30, 200, 20)]; // 6
[self.view addSubview:label];

pickerViewArray = [[NSArray alloc] initWithObjects:@ »Element 1″, @ »Element 2″, @ »Element 3″, @ »Element 4″, @ »Element 5″, @ »Element 6″, @ »Element 7″, nil]; // 7
}

La taille d’un picker view est optimisée. Comprenez que vous ne pouvez pas vraiment le dimensionner comme vous le souhaitez. C’est pourquoi de la ligne 1 à la ligne 2, vous trouverez un mécanisme qui optimise sa taille, et va le centrer sur votre vue.
Vous pouvez choisir d’afficher une sorte de vitre, vous montrant la ligne sélectionnée. Par défaut, rien ne s’affiche.
Ligne 3, nous choisissons donc de montrer à l’utilisateur la sélection actuelle.
Le picker view nécessite d’être rempli par des données, c’est pourquoi nous définissons le view controller actuel comme source de données ligne 5, données qui seront dans un tableau, initialisé ligne 7. La classe UIPickerView contient un protocole UIPickerViewDelegate. À la ligne 4, on définit donc self comme étant delegate du picker view.
Pour montrer le bon fonctionnement du picker, on ajoute un label à la vue ligne 6, qui affichera les éléments sélectionnés.
Comme nous venons de définir la classe PickerViewController comme étant à la fois source de données et delegate de UIPickerView, vous devez écrire ceci dans le .h :

@interface PickerViewController : UIViewController <UIPickerViewDelegate, UIPickerViewDataSource> {

Ensuite, rappelez-vous, un protocole définit des méthodes optionnelles et d’autres requises. Vous les trouverez dans la documentation.
Nous souhaitons afficher deux colonnes dans le picker, une contenant Element 1, Element 2, etc. et une autre contenant le numéro de la ligne.
Ajoutez donc ces méthodes dans PickerViewController.m :

#pragma mark –
#pragma mark UIPickerViewDelegate

– (void)pickerView:(UIPickerView *)pickerView didSelectRow:(NSInteger)row inComponent:(NSInteger)component
{
label.text = [NSString stringWithFormat:@ »%@ – %d », [pickerViewArray objectAtIndex:[pickerView selectedRowInComponent:0]], [myPickerView selectedRowInComponent:1]]; // 1
}

#pragma mark –
#pragma mark UIPickerViewDataSource
– (NSString *)pickerView:(UIPickerView *)pickerView titleForRow:(NSInteger)row forComponent:(NSInteger)component
{
NSString *returnStr = @ » »;

if (component == 0) {
returnStr = [pickerViewArray objectAtIndex:row]; // 2
} else {
returnStr = [[NSNumber numberWithInt:row] stringValue]; // 3
}
return returnStr;
}
– (CGFloat)pickerView:(UIPickerView *)pickerView widthForComponent:(NSInteger) component {
CGFloat componentWidth = 0.0;

if (component == 0)
componentWidth = 240.0; // 4
else
componentWidth = 40.0; // 5
return componentWidth;
}

– (CGFloat)pickerView:(UIPickerView *)pickerView rowHeightForComponent:(NSInteger)component {
return 40.0; // 6

}
– (NSInteger)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component
{
return [pickerViewArray count]; // 7
}

– (NSInteger)numberOfComponentsInPickerView:(UIPickerView *)pickerView {
return 2; // 8
}

Nous choisissons d’afficher deux colonnes dans le picker, grâce à la ligne 8. Lignes 4 et 5, on doit définir la taille pour chaque colonne (nommée ici component, celle numérotée 0 étant à gauche).
Ligne 6, on définit la hauteur des lignes, et ligne 7, on détermine leur nombre qui est défini par le nombre d’éléments du tableau pickerViewArray.
Pour remplir chaque ligne du picker, on implémente la méthode – (NSString *)pickerView:(UIPicker- View *)pickerView titleForRow:(NSInteger)row forComponent:(NSInteger)component. Pour la colonne de gauche (0), le titre de la ligne sera l’élément correspondant dans le tableau, ligne 2. Pour celle de droite, le titre le de ligne est son numéro, ligne 3.
Enfin, ligne 1, on affiche dans le label les éléments sélectionnés dans chaque colonne.