Adaptive Layout est une approche de mise en page d'interface où l'application utilise différentes mises en page pour différents types d'appareils et orientations d'écran. Contrairement au responsive design, l'adaptive layout bascule entre des mises en page pré-définies à des points de rupture. Cet article examine Size Classes et UITraitCollection sous iOS, les qualificateurs sw600dp/layout-w600dp sous Android et fournit des exemples pratiques.
Points clés
Adaptive Layout est une stratégie d'interface utilisateur où le développeur crée des mises en page séparées pour chaque type d'appareil et bascule entre elles en fonction des caractéristiques de l'écran. Contrairement au responsive design où les éléments s'écoulent harmonieusement, l'adaptive layout utilise des commutations discrètes : téléphone — une mise en page, tablette — une autre, bureau — une troisième.
Apple a introduit Size Classes dans iOS 8 (2014) avec les applications universelles (binaires fonctionnant à la fois sur iPhone et iPad). Google a présenté le qualificateur smallestWidth (sw
Les directives modernes Apple HIG et Google Material Design s'accordent à dire que l'adaptive layout est essentiel pour les applications prenant en charge plus d'une taille d'écran. Google Play marque l'absence d'adaptation pour tablettes comme un défaut. L'App Store n'a pas d'exigence formelle, mais les utilisateurs d'iPad s'attendent à une expérience native, pas à une application iPhone étirée.
Size Classes sont une caractéristique iOS qui définit l'espace disponible comme Compact ou Regular sur chaque axe. UITraitCollection est un système iOS pour transmettre les caractéristiques d'environnement à travers la hiérarchie des vues : taille, échelle d'écran, capacité force touch, gamme de couleurs (clair/foncé), paramètres d'accessibilité.
| Appareil / Orientation | Horizontal | Vertical |
|---|---|---|
| iPhone portrait (tous modèles) | Compact | Regular |
| iPhone Plus/Pro Max paysage | Regular | Compact |
| iPhone SE / mini paysage | Compact | Compact |
| iPad portrait | Regular | Regular |
| iPad paysage | Regular | Regular |
| iPad Split View (1/3 écran) | Compact | Regular |
UITraitCollection est formé par le système et transmis de UIApplication à travers UIWindow jusqu'à chaque UIView. Lors d'un changement d'orientation ou de taille de fenêtre (Split View), le système génère un nouvel UITraitCollection et appelle traitCollectionDidChange. Dans SwiftUI, les changements sont suivis via Environment Values et la vue est automatiquement redessinée.
L'adaptation via UITraitCollection dans UIKit est implémentée en s'abonnant aux changements de trait collection et en reconstruisant la mise en page. L'exemple ci-dessous montre un contrôleur qui bascule entre une pile verticale pour iPhone et une mise en page fractionnée pour iPad.
// Contrôleur adaptatif avec Size Classes
import UIKit
final class AdaptiveViewController: UIViewController {
private let compactStack = UIStackView()
private let regularStack = UIStackView()
private let sidebar = UIView()
private let content = UIView()
override func viewDidLoad() {
super.viewDidLoad()
setupViews()
updateLayout(for: traitCollection)
}
override func traitCollectionDidChange(
_ previousTraitCollection: UITraitCollection?
) {
super.traitCollectionDidChange(previousTraitCollection)
if traitCollection.horizontalSizeClass
!= previousTraitCollection?.horizontalSizeClass {
updateLayout(for: traitCollection)
}
}
private func updateLayout(for traits: UITraitCollection) {
if traits.horizontalSizeClass == .regular {
showRegularLayout() // iPad : barre latérale + contenu
} else {
showCompactLayout() // iPhone : barre d'onglets + push
}
}
private func showRegularLayout() {
view.subviews.forEach { $0.removeFromSuperview() }
regularStack.addArrangedSubview(sidebar)
regularStack.addArrangedSubview(content)
regularStack.frame = view.bounds
regularStack.autoresizingMask = [.flexibleWidth, .flexibleHeight]
view.addSubview(regularStack)
}
private func showCompactLayout() {
view.subviews.forEach { $0.removeFromSuperview() }
compactStack.addArrangedSubview(content)
compactStack.frame = view.bounds
compactStack.autoresizingMask = [.flexibleWidth, .flexibleHeight]
view.addSubview(compactStack)
}
private func setupViews() {
sidebar.backgroundColor = .systemGray6
content.backgroundColor = .systemBackground
compactStack.axis = .vertical
regularStack.axis = .horizontal
}
}La méthode traitCollectionDidChange est appelée à tout changement de traits, mais nous ne vérifions que horizontalSizeClass — l'indicateur le plus significatif pour le changement de mise en page. Sous iOS 17+, il est recommandé d'utiliser UIViewController.horizontalSizeClass comme propriété calculée pour des mises à jour réactives sans abonnement aux changements.
Android fournit deux qualificateurs de largeur principaux : sw
| Qualificateur | Condition | Exemple d'appareil |
|---|---|---|
| layout-sw600dp | Largeur minimale ≥ 600 dp | iPad (768 dp), Pixel C (900 dp) |
| layout-w600dp | Largeur actuelle ≥ 600 dp | iPad en Split View (600 dp), téléphone en paysage |
| layout-land | Orientation paysage | Tout appareil tourné horizontalement |
| layout-sw720dp | Largeur minimale ≥ 720 dp | iPad Pro (833 dp), Galaxy Tab S9 (800 dp) |
Hiérarchie des ressources sous Android : res/layout/activity_main.xml (téléphone), res/layout-sw600dp/activity_main.xml (tablette 7"), res/layout-w600dp-land/activity_main.xml (téléphone en paysage). Android sélectionne le qualificateur le plus spécifique correspondant à la configuration actuelle de l'appareil. Lorsqu'aucun fichier correspondant n'existe, la ressource de base de res/layout/ est utilisée.
Le développement Android moderne utilise Jetpack WindowManager pour déterminer la taille de la fenêtre au lieu de lire directement les ressources. Ceci est particulièrement important pour les appareils pliables où la taille de l'écran change dynamiquement (Galaxy Fold se déplie de 6,2" à 7,6").
// Adaptation via Jetpack WindowManager
import androidx.window.core.layout.WindowHeightSizeClass
import androidx.window.core.layout.WindowSizeClass
import androidx.window.core.layout.WindowWidthSizeClass
@OptIn(ExperimentalLayoutApi::class)
@Composable
fun AdaptiveScreen() {
val windowSizeClass = currentWindowAdaptiveInfo().windowSizeClass
FlowRow(
verticalAlignment = Arrangement.Top,
horizontalArrangement = when (windowSizeClass.windowWidthSizeClass) {
WindowWidthSizeClass.EXPANDED -> Arrangement.SpaceEvenly
else -> Arrangement.Start
}
) {
when (windowSizeClass.windowWidthSizeClass) {
WindowWidthSizeClass.COMPACT -> {
CompactContent()
}
WindowWidthSizeClass.MEDIUM -> {
MediumContent()
}
WindowWidthSizeClass.EXPANDED -> {
ExpandedContent()
}
}
}
}
@Composable
private fun CompactContent() {
Column(modifier = Modifier.padding(16.dp)) {
Text("Téléphone", style = MaterialTheme.typography.headlineSmall)
ListContent()
}
}
@Composable
private fun MediumContent() {
Row(modifier = Modifier.padding(24.dp)) {
NavigationRail { ... }
Column { ListContent() }
}
}
@Composable
private fun ExpandedContent() {
Row(modifier = Modifier.padding(32.dp)) {
PermanentNavigationDrawer { ... }
Column {
ListContent()
}
DetailPane()
}
}Le composant currentWindowAdaptiveInfo() de la bibliothèque androidx.window adapte l'interface à tout appareil : téléphone, tablette, pliable, ChromeOS. Classes de largeur : COMPACT (0–600 dp), MEDIUM (600–840 dp), EXPANDED (>840 dp). Classes de hauteur : COMPACT (0–480 dp), MEDIUM (480–900 dp), EXPANDED (>900 dp).
SwiftUI fournit des outils intégrés pour la mise en page adaptative sans utiliser directement Size Classes. AnyLayout, ViewThatFits et GeometryReader permettent de construire des interfaces qui s'ajustent automatiquement à l'espace disponible.
// Adaptation SwiftUI via AnyLayout et ViewThatFits
import SwiftUI
struct AdaptiveGrid: View {
@Environment(\.horizontalSizeClass) private var hSizeClass
var body: some View {
let layout = hSizeClass == .regular
? AnyLayout(HStackLayout())
: AnyLayout(VStackLayout())
layout {
Label("Favoris", systemImage: "star")
Label("Récents", systemImage: "clock")
Label("Paramètres", systemImage: "gear")
}
.padding()
}
}
// ViewThatFits — sélection automatique de mise en page
struct SmartLayout: View {
var body: some View {
ViewThatFits {
HStack { // Priorité 1 : horizontal
CardView()
CardView()
}
VStack { // Si ne tient pas — vertical
CardView()
CardView()
}
}
}
}ViewThatFits est un outil puissant de SwiftUI qui sélectionne automatiquement la première vue enfant qui tient dans l'espace disponible sans rogner. Cela élimine le besoin de vérifier explicitement Size Classes pour les commutations adaptatives simples. AnyLayout permet de changer le type de mise en page (HStack/VStack) sans if-else dans le corps de la vue.
Jetpack Compose utilise WindowSizeClass (de la bibliothèque Material 3 adaptive) et BoxWithConstraints pour la mise en page adaptative. Contrairement aux qualificateurs XML, Compose détermine la taille de la fenêtre à l'exécution et recompose l'interface réactivement lors des changements de configuration.
// Jetpack Compose adaptatif via BoxWithConstraints
@Composable
fun AdaptiveList(items: List<String>) {
BoxWithConstraints {
val width = maxWidth
if (width >= 600.dp) {
// Tablette : deux colonnes (list-detail)
Row(modifier = Modifier.fillMaxSize()) {
LazyColumn(modifier = Modifier.weight(1f)) {
items(items) { item ->
ListItem(text = item)
}
}
var selected by remember { mutableStateOf(items.first()) }
DetailPanel(item = selected)
.weight(2f)
}
} else {
// Téléphone : une colonne avec navigation
LazyColumn(modifier = Modifier.fillMaxSize()) {
items(items) { item ->
ListItem(text = item, onClick = { navigateToDetail(item) })
}
}
}
}
}
@Composable
fun AdaptiveListMaterial3() {
val windowClass = currentWindowAdaptiveInfo().windowSizeClass
AdaptiveLayout(
layout = windowClass.windowWidthSizeClass
) {
when (windowClass.windowWidthSizeClass) {
WindowWidthSizeClass.COMPACT -> {
ListDetailScaffold(
isDetailOnly = false,
list = { ListPane() },
detail = { DetailPane() }
)
}
else -> {
ListDetailScaffold(
isDetailOnly = false,
list = { ListPane() },
detail = { DetailPane() }
)
}
}
}
}Material 3 Adaptive Layouts fournit des composants prêts à l'emploi : ListDetailScaffold, SupportingPaneScaffold, NavigationSuiteScaffold. Ces composants s'adaptent automatiquement à WindowWidthSizeClass — en passant du mode empilé (COMPACT) au mode côte à côte (MEDIUM/EXPANDED). Le développeur n'a qu'à choisir un motif et passer les panneaux de contenu.
Questions fréquentes
sw600dp (smallestWidth) — largeur minimale de l'écran indépendamment de l'orientation. S'active toujours sur les tablettes 7"+. w600dp (available width) — largeur disponible compte tenu de l'orientation actuelle. S'active sur les téléphones en paysage où la largeur peut dépasser 600 dp.
UITraitCollection est un objet iOS contenant les caractéristiques d'environnement : size classes (horizontal/vertical), échelle d'affichage, capacité force touch, idiome d'interface utilisateur (iPhone/iPad), gamme de couleurs, paramètres d'accessibilité. Il est transmis de UIApplication à travers la hiérarchie des vues et change lors d'un changement d'orientation ou de taille de fenêtre.
Utilisez Jetpack WindowManager (Android) et UIScreen nativeBounds + traitCollection (iOS). Les appareils pliables changent Size Class lors du dépliage. Une architecture basée sur des mises en page canoniques (list-detail, supporting pane) avec un abonnement réactif aux changements de taille de fenêtre sans redémarrage d'Activity est recommandée.
Non. Utilisez un seul storyboard avec des variations de contraintes par Size Class ou une mise en page programmatique via UIKit. SwiftUI ne nécessite pas de storyboards du tout. Des storyboards séparés pour iPad créent de la duplication et compliquent la maintenance — il est préférable d'adapter une seule mise en page via Size Classes.
Canonical layouts sont des motifs de mise en page adaptative prêts à l'emploi de Google : list-detail (liste + détail), supporting pane (contenu + panneau d'actions), feed (cartes). Chaque motif prend en charge trois états (compact/medium/expanded) et est implémenté dans la bibliothèque Material 3 Adaptive via ListDetailScaffold et SupportingPaneScaffold.
Résumé
Nous développerons une application mobile clé en main
IT Sectr crée des applications iOS et Android pour les startups et les entreprises depuis 2017. Nous vous conseillerons et vous proposerons la meilleure solution.
Lisez aussi