Fondations d'UIKit : Maîtriser UIView et la composition d'interfaces

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];

Étiquettes: uikit UIView UIButton Objective-C Interface Builder

Publié le 23 octobre à 18h25