Adaptive Layout : concepts clés, classes de taille et qualificateurs

Auteur : IT Sectr Publié le : 2026-02-26 Temps de lecture : 10 min

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 — commutation discrète des mises en page selon la taille d'écran, l'orientation et le type d'appareil
  • Size Classes sous iOS classent l'espace comme Compact ou Regular horizontalement et verticalement
  • UITraitCollection — conteneur de caractéristiques d'environnement iOS (taille, échelle, force touch, gamme de couleurs)
  • sw600dp — qualificateur Android de largeur minimale pour tablettes (7" et plus)
  • layout-w600dp — qualificateur de largeur disponible qui s'active lorsqu'il y a suffisamment d'espace dans l'orientation actuelle

Qu'est-ce que l'Adaptive Layout ?

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 (swdp) dans Android 3.2 (2011). Les deux mécanismes résolvent le même problème : donner au développeur un outil pour créer différentes IU pour différents écrans sans duplication de code.

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 et UITraitCollection sous iOS

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 / OrientationHorizontalVertical
iPhone portrait (tous modèles)CompactRegular
iPhone Plus/Pro Max paysageRegularCompact
iPhone SE / mini paysageCompactCompact
iPad portraitRegularRegular
iPad paysageRegularRegular
iPad Split View (1/3 écran)CompactRegular

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.

Exemple de mise en page adaptative en Swift

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.

swift
// 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.

Qualificateurs Android : sw600dp et w600dp

Android fournit deux qualificateurs de largeur principaux : swdp (smallestWidth) et wdp (available width). La différence est cruciale : sw est la largeur minimale de l'écran indépendamment de l'orientation, w est la largeur disponible compte tenu de l'orientation actuelle. sw garantit que la mise en page se charge pour un appareil qui possède physiquement la largeur minimale spécifiée. w répond à la rotation — en paysage, w600dp peut s'activer même si la largeur en portrait est de 360 dp.

QualificateurConditionExemple d'appareil
layout-sw600dpLargeur minimale ≥ 600 dpiPad (768 dp), Pixel C (900 dp)
layout-w600dpLargeur actuelle ≥ 600 dpiPad en Split View (600 dp), téléphone en paysage
layout-landOrientation paysageTout appareil tourné horizontalement
layout-sw720dpLargeur minimale ≥ 720 dpiPad 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.

Exemple de mise en page adaptative en Kotlin

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").

kotlin
// 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).

Adaptive Layout dans SwiftUI

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.

swift
// 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.

Adaptive Layout dans Jetpack Compose

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.

kotlin
// 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

Quelle est la différence entre sw600dp et w600dp ?

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.

Qu'est-ce que UITraitCollection sous iOS ?

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.

Comment adapter une application aux appareils pliables ?

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.

Dois-je créer des storyboards séparés pour iPad ?

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.

Que sont les mises en page canoniques dans Material 3 ?

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é

  • Adaptive Layout — commutation discrète des mises en page aux points de rupture pour différents appareils
  • Size Classes iOS (Compact/Regular) définissent la mise en page pour iPhone, iPad et Split View
  • UITraitCollection — mécanisme de transmission des caractéristiques d'environnement à travers la hiérarchie des vues
  • sw600dp — qualificateur Android de largeur minimale pour tablettes 7"+
  • w600dp — qualificateur de largeur disponible qui s'active lors de la rotation en paysage
  • Jetpack WindowManager fournit WindowSizeClass (WindowWidthSizeClass : COMPACT/MEDIUM/EXPANDED)
  • Material 3 Adaptive — composants prêts à l'emploi (ListDetailScaffold) avec adaptation automatique

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.

Discuter du projet

Lisez aussi