Les éléments visuels fondamentaux
Tout composant affiché à l'écran dans une application iOS constitue une vue — qu'il s'agisse d'une étiquette textuelle (UILabel), d'une zone de saisie (UITextField) ou d'un élément interactif. Ces éléments partagent des caractéristiques communes : leur emplacement spatial, leurs dimensions et leur apparence visuelle.
Apple a centralisé ces propriétés universelles au sein d'une classe mère unique : UIView. L'ensemble des composants d'interface hérite donc, directement ou indirectement, de cette classe fondamentale.
La hiérarchie des vues
UIView joue le rôle de conteneur dans l'architecture d'UIKit. Chaque instance peut accueillir d'autres vues, créant ainsi une structure arborescente :
- La vue contenante est désignée comme vue parente (superview)
- Les vues incluses sont appelées vues enfants (subviews)
Tout contrôleur visuel (UIViewController) possède une propriété racine view qui sert de point d'ancrage. L'intégralité des éléments de l'interface est ensuite organisée sous cette vue principale.
Propriétés essentielles de UIView
| Propriété | Description |
|---|---|
superview |
Référence vers la vue parente |
subviews |
Tableau des vues enfants |
tag |
Identifiant numérique pour repérer une vue |
transform |
Transformation affine (rotation, échelle, translation) |
frame |
Position et taille dans le référentiel du parent |
bounds |
Dimensions dans son propre référentiel (x et y généralement nuls) |
center |
Coordonnées du centre dans le référentiel du parent |
Gestionnaire de coordonnées d'UIKit
Le système de coordonnées d'UIKit adopte le coin supérieur gauche comme origine (0, 0). L'axe X croît vers la droite, l'axe Y croît vers le bas.
Exemple : positionnement programmatique
#import "ViewController.h"
@interface ViewController ()
@property (weak, nonatomic) IBOutlet UIView *zoneReference;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
UIView *vignette = [[UIView alloc] init];
vignette.backgroundColor = [UIColor systemBlueColor];
vignette.frame = CGRectMake(20, 20, 80, 80);
[self.zoneReference addSubview:vignette];
}
@end
Exemple : redimensionnement dynamique
#import "ViewController.h"
@interface ViewController ()
@property (weak, nonatomic) IBOutlet UIView *panneau;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
CGFloat largeur = 20 + arc4random_uniform(250);
CGFloat hauteur = 20 + arc4random_uniform(250);
self.panneau.frame = CGRectMake(100, 100, largeur, hauteur);
}
@end
Les états d'interaction de UIButton
| État | Description | Constante |
|---|---|---|
| Normal | État par défaut, aucune interaction | UIControlStateNormal |
| Surbrillance | Doigt appuyé, non relâché | UIControlStateHighlighted |
| Désactivé | Non interactif (enabled = NO) |
UIControlStateDisabled |
Construction d'une grille adpatative
L'exemple suivant démontre la création d'une grille de type « grille magique » avec calcul automatique des positions :
#import "ViewController.h"
@interface ViewController ()
@property (weak, nonatomic) IBOutlet UIView *grilleProduits;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
[self creerBoutonAvecImage:@"ajouter"
imageSurvol:@"ajouter_actif"
imageInactif:@"ajouter_inactif"
cadre:CGRectMake(50, 50, 50, 50)
action:@selector(ajouterProduit:)];
[self creerBoutonAvecImage:@"retirer"
imageSurvol:@"retirer_actif"
imageInactif:@"retirer_inactif"
cadre:CGRectMake(300, 50, 50, 50)
action:@selector(retirerProduit:)];
}
- (void)creerBoutonAvecImage:(NSString *)normale
imageSurvol:(NSString *)survol
imageInactif:(NSString *)inactif
cadre:(CGRect)dimensions
action:(SEL)methode {
UIButton *bouton = [[UIButton alloc] init];
bouton.frame = dimensions;
[bouton setImage:[UIImage imageNamed:normale] forState:UIControlStateNormal];
[bouton setImage:[UIImage imageNamed:survol] forState:UIControlStateHighlighted];
[bouton setImage:[UIImage imageNamed:inactif] forState:UIControlStateDisabled];
[self.view addSubview:bouton];
[bouton addTarget:self action:methode forControlEvents:UIControlEventTouchUpInside];
}
- (void)ajouterProduit:(UIButton *)declencheur {
NSLog(@"Opération : ajout d'un article");
}
- (void)retirerProduit:(UIButton *)declencheur {
NSLog(@"Opération : suppression d'un article");
}
@end
Génération dynamique des cellules
- (void)ajouterProduit:(UIButton *)declencheur {
const CGFloat largeurCellule = 70;
const CGFloat hauteurCellule = 90;
const NSInteger nombreColonnes = 3;
const CGFloat espacementVertical = 25;
NSInteger position = self.grilleProduits.subviews.count;
NSInteger colonne = position % nombreColonnes;
NSInteger rangee = position / nombreColonnes;
CGFloat largeurDisponible = self.grilleProduits.frame.size.width;
CGFloat ecartHorizontal = (largeurDisponible - nombreColonnes * largeurCellule) / (nombreColonnes - 1);
CGFloat posX = colonne * (largeurCellule + ecartHorizontal);
CGFloat posY = rangee * (hauteurCellule + espacementVertical);
UIView *cellule = [[UIView alloc] init];
cellule.frame = CGRectMake(posX, posY, largeurCellule, hauteurCellule);
cellule.backgroundColor = [UIColor systemRedColor];
UIImageView *illustration = [[UIImageView alloc] init];
illustration.image = [UIImage imageNamed:@"sac_bandouliere"];
illustration.frame = CGRectMake(0, 0, largeurCellule, 70);
[cellule addSubview:illustration];
UILabel *legende = [[UILabel alloc] init];
legende.text = @"Sac à bandoulière";
legende.frame = CGRectMake(0, 70, largeurCellule, 20);
legende.textAlignment = NSTextAlignmentCenter;
legende.font = [UIFont systemFontOfSize:13];
[cellule addSubview:legende];
[self.grilleProduits addSubview:cellule];
}
Astuces de productivité Xcode
Pour visualiser les limites des vues durant le débogage : Debug → View Debugging → Show View Frames
Raccourcis essentiels :
- ⌘ + ⇧ + N : Nouveau fichier
Organisation du code avec les marqueurs de section :
#pragma mark - Configuration des boutons
#pragma mark - Gestionnaires d'événements
Formatage des chaînes en Objective-C :
NSInteger indice = 0;
indice++;
NSString *libelle = [NSString stringWithFormat:@"Article n°%ld", (long)indice];