UITableView : bases, listes et cellules dans les applications iOS

Auteur : IT Sectr Publié le : 2026-02-23 Temps de lecture : 9 min

UITableView — le composant principal d'UIKit pour afficher des listes verticales en iOS. Chaque ligne du tableau est une cellule UITableViewCell avec des styles prédéfinis. Nous expliquons les bases d'UITableView : DataSource, délégué, réutilisation des cellules et performances des tableaux dans les applications iOS. Selon Apple (Human Interface Guidelines, 2026), UITableView reste le composant d'interface utilisateur le plus utilisé en iOS — 85% des applications du top 100 de l'App Store contiennent au moins un tableau. Pour les mises en page complexes, lisez l'article sur UICollectionView.

Points clés

  • UITableView — un composant UIKit pour les listes verticales, disponible depuis iOS 2.0 (iPhone OS 2).
  • UITableViewCell — une cellule de tableau avec quatre styles intégrés et des options de personnalisation.
  • UITableViewDataSource — un protocole définissant le nombre de lignes et de cellules pour chaque section.
  • UITableViewDelegate — un protocole pour gérer la sélection, l'édition et la configuration de la hauteur des lignes.
  • UITableViewDiffableDataSource — une DataSource moderne (iOS 13+) avec calcul automatique des modifications.

Qu'est-ce qu'UITableView en iOS ?

UITableView est une classe du framework UIKit conçue pour afficher une liste défilable de lignes dans une seule colonne. Chaque ligne est représentée par un objet UITableViewCell. UITableView est apparue pour la première fois dans iPhone OS 2 (2008) et est depuis restée le composant principal pour les listes en iOS. L'architecture d'UITableView est basée sur le modèle MVC : les données sont gérées par la DataSource, l'apparence et le comportement par le Délégué, et la présentation par le tableau lui-même.

Deux styles de tableau : .plain (liste continue avec sections optionnelles) et .grouped (sections groupées avec coins arrondis et marges). iOS 13 a ajouté .insetGrouped — un style groupé avec des marges sur les bords, utilisé dans les applications Réglages et Santé. Le choix du style affecte l'apparence par défaut : les tableaux plain fixent les en-têtes de section en haut lors du défilement (en-tête collant), les tableaux grouped non.

Cellules à taille automatique

À partir d'iOS 8, UITableView prend en charge les cellules à taille automatique (self-sizing cells). Pour activer, définissez tableView.estimatedRowHeight (par exemple, 80) et tableView.rowHeight = UITableView.automaticDimension. Le tableau calcule automatiquement la hauteur de ligne en fonction des contraintes Auto Layout dans la cellule. L'auto-dimensionnement réduit les performances pour les cellules complexes — utilisez une hauteur fixe (rowHeight) pour les tableaux de plus de 500 éléments.

Styles UITableViewCell : Basic, Subtitle, Value1, Value2

UITableViewCell fournit quatre styles intégrés qui couvrent 80% des scénarios sans nécessiter la création d'une cellule personnalisée. Chaque style se compose d'une combinaison de textLabel, detailTextLabel et imageView. Le style est choisi lors de l'initialisation de la cellule via init(style:reuseIdentifier:).

StyletextLabeldetailTextLabelimageViewExemple
.default (.basic)Gauche, grasAucunOptionnelMenu, liste d'éléments
.subtitleGauche, grasSous textLabel, grisOptionnelContacts, listes de lecture
.value1Gauche, grasDroite, grisAucunRéglages avec valeurs
.value2Droite, bleuGauche, grisAucunAnnuaire téléphonique (style iOS 6)
swift
// Création d'un tableau avec des cellules personnalisées
class ContactListController: UIViewController {

    private let tableView = UITableView(frame: .zero, style: .insetGrouped)

    override func viewDidLoad() {
        super.viewDidLoad()

        tableView.register(UITableViewCell.self, forCellReuseIdentifier: "cell")
        tableView.dataSource = self
        tableView.delegate = self
        tableView.rowHeight = 60
        view.addSubview(tableView)

        // Auto Layout
        tableView.translatesAutoresizingMaskIntoConstraints = false
        NSLayoutConstraint.activate([
            tableView.topAnchor.constraint(equalTo: view.topAnchor),
            tableView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
            tableView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
            tableView.bottomAnchor.constraint(equalTo: view.bottomAnchor)
        ])
    }
}

extension ContactListController: UITableViewDataSource {

    func tableView(_ tableView: UITableView,
                         numberOfRowsInSection section: Int) -> Int {
        contacts.count
    }

    func tableView(_ tableView: UITableView,
                         cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
        let contact = contacts[indexPath.row]

        var content = cell.defaultContentConfiguration()
        content.text = contact.name
        content.secondaryText = contact.phone
        content.image = UIImage(systemName: "person.circle")
        cell.contentConfiguration = content

        return cell
    }
}

UIContentConfiguration (iOS 14+) — une façon moderne de configurer les cellules via contentConfiguration au lieu d'accéder directement à textLabel/detailTextLabel. Utilisez cell.defaultContentConfiguration() ou créez une UIContentConfiguration personnalisée. Avantages : prise en charge intégrée du Dynamic Type, du mode sombre et de VoiceOver. Apple (WWDC 2024) recommande contentConfiguration pour toutes les nouvelles UITableView.

DataSource et Delegate : méthodes obligatoires

UITableViewDataSource — un protocole qui fournit les données au tableau. Méthodes obligatoires : tableView(_:numberOfRowsInSection:) et tableView(_:cellForRowAt:). Sans elles, le tableau ne peut afficher aucune ligne. UITableViewDelegate — un protocole pour gérer l'apparence et le comportement : hauteur des lignes, sélection, édition, actions contextuelles.

swift
// Exemple avec plusieurs sections et édition
extension ContactListController: UITableViewDelegate {

    // Hauteur personnalisée pour chaque ligne
    func tableView(_ tableView: UITableView,
                         heightForRowAt indexPath: IndexPath) -> CGFloat {
        80
    }

    // Gestion de la sélection
    func tableView(_ tableView: UITableView,
                         didSelectRowAt indexPath: IndexPath) {
        tableView.deselectRow(at: indexPath, animated: true)
        let contact = contacts[indexPath.row]
        showDetail(for: contact)
    }

    // Swipe-to-delete
    func tableView(_ tableView: UITableView,
                         trailingSwipeActionsConfigurationForRowAt indexPath: IndexPath)
        -> UISwipeActionsConfiguration? {
        let deleteAction = UIContextualAction(style: .destructive, title: "Supprimer") {
            [weak self] _, _, completion in
            self?.contacts.remove(at: indexPath.row)
            tableView.deleteRows(at: [indexPath], with: .automatic)
            completion(true)
        }
        return UISwipeActionsConfiguration(actions: [deleteAction])
    }
}

Performances du Délégué : les méthodes de taille (heightForRowAt) sont appelées pour chaque ligne visible à chaque défilement. Pour une hauteur uniforme, définissez rowHeight directement — c'est 10 fois plus rapide que heightForRowAt. Pour les cellules à taille automatique, définissez estimatedRowHeight et automaticDimension — le tableau appelle heightForRowAt uniquement pour les lignes visibles et utilise estimatedRowHeight pour le reste.

Réutilisation des cellules : file d'attente et enregistrement

File d'attente de réutilisation — un pool de cellules UITableView réutilisables. Lorsqu'une cellule sort de l'écran, elle n'est pas supprimée mais placée dans la file d'attente. Les nouvelles cellules ne sont pas créées de zéro — le système appelle dequeueReusableCell(withIdentifier:for:), qui renvoie une cellule de la file ou en crée une nouvelle si la file est vide. C'est le mécanisme de performance clé d'UITableView.

swift
// Cellule personnalisée avec mise en cache
class ContactCell: UITableViewCell {

    private let avatarView = UIImageView()
    private let nameLabel = UILabel()
    private let subtitleLabel = UILabel()

    override init(style: UITableViewCell.Style, reuseIdentifier: String?) {
        super.init(style: style, reuseIdentifier: reuseIdentifier)
        setupViews()
    }

    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    private func setupViews() {
        contentView.addSubview(avatarView)
        contentView.addSubview(nameLabel)
        contentView.addSubview(subtitleLabel)

        // Configuration des contraintes Auto Layout
        avatarView.translatesAutoresizingMaskIntoConstraints = false
        // ... constraints ...
    }

    func configure(with contact: Contact) {
        nameLabel.text = contact.name
        subtitleLabel.text = contact.phone
        // Chargement asynchrone de l'avatar
    }

    override func prepareForReuse() {
        super.prepareForReuse()
        avatarView.image = nil
        nameLabel.text = nil
        subtitleLabel.text = nil
    }
}

// Enregistrement et utilisation
tableView.register(ContactCell.self, forCellReuseIdentifier: "contactCell")

// Dans cellForRowAt:
let cell = tableView.dequeueReusableCell(withIdentifier: "contactCell", for: indexPath) as! ContactCell
cell.configure(with: contacts[indexPath.row])
return cell

prepareForReuse() — une méthode appelée avant de placer une cellule dans la file d'attente de réutilisation. Réinitialisez tout l'état de la cellule : texte, images, animations, opérations de chargement. Sans prepareForReuse, une cellule réutilisée peut afficher d'anciennes données : l'image précédente sera visible pendant une fraction de seconde jusqu'au chargement de la nouvelle. Apple (WWDC 2023) recommande d'annuler les opérations asynchrones via URLSessionTask.cancel() dans prepareForReuse.

Sections, en-tête et pied de page : regroupement de données

Les sections dans UITableView regroupent les lignes logiquement liées. Chaque section peut avoir un en-tête (header) et un pied de page (footer). Le nombre de sections est déterminé par la méthode numberOfSections(in:) (la valeur par défaut est 1). Pour les styles grouped et insetGrouped, les sections sont visuellement séparées par des marges et des coins arrondis.

Méthode DataSourceDescriptionValeur de retour
numberOfSectionsNombre de sections (par défaut 1)Int
numberOfRowsInSectionNombre de lignes dans une sectionInt
titleForHeaderInSectionTexte du titre de l'en-tête de sectionString?
titleForFooterInSectionTexte du titre du pied de sectionString?
viewForHeaderInSectionVue personnalisée pour l'en-têteUIView?
heightForHeaderInSectionHauteur de l'en-têteCGFloat

Indexation : pour une navigation rapide entre les sections, ajoutez un titre d'index — un tableau de chaînes affichées sur le côté droit. Implémentez sectionIndexTitles(for:) — renvoie un tableau des premières lettres de chaque section. Pour 26 sections ou plus, cela améliore considérablement l'UX. Sous iOS 15+, les tableaux prennent en charge sectionHeaderTopPadding — le padding entre la barre d'état et le premier en-tête, qui peut être réinitialisé à 0 pour une disposition dense.

Performances d'UITableView : meilleures pratiques

Les performances d'UITableView sont cruciales pour les applications avec de longues listes. Problèmes courants : défilement lent (jank) dû à des opérations lourdes dans cellForRowAt, appels fréquents à heightForRowAt, absence de prefetching. Apple (WWDC 2025) met en avant cinq pratiques clés pour les tableaux de plus de 1000 éléments.

  • Hauteur fixe — utilisez rowHeight constante au lieu de heightForRowAt pour toutes les cellules. Différence : 60 FPS contre 30 FPS pour un tableau de 500 lignes.
  • Prefetching — implémentez UITableViewDataSourcePrefetching pour le chargement asynchrone des données (images, API) avant que la cellule n'apparaisse à l'écran.
  • Moins de sous-vues — chaque sous-vue dans contentView augmente le temps de rendu. Utilisez draw() pour les graphiques simples (séparateurs, icônes) au lieu d'UIImageView.
  • Opérations lourdes en arrière-plan — le formatage des dates, la localisation, les calculs doivent être effectués dans le modèle, pas dans cellForRowAt. Utilisez NSCache pour les résultats.
  • DiffableDataSource — remplacez reloadData() par apply(snapshot) — l'animation se produit gratuitement, le tableau n'est pas rechargé entièrement.
swift
// Optimisation avec le prefetching
extension ContactListController: UITableViewDataSourcePrefetching {

    func tableView(_ tableView: UITableView,
                         prefetchRowsAt indexPaths: [IndexPath]) {
        for indexPath in indexPaths {
            let contact = contacts[indexPath.row]
            ImageCache.shared.prefetch(url: contact.avatarUrl)
        }
    }

    func tableView(_ tableView: UITableView,
                         cancelPrefetchingForRowsAt indexPaths: [IndexPath]) {
        for indexPath in indexPaths {
            let contact = contacts[indexPath.row]
            ImageCache.shared.cancelPrefetch(url: contact.avatarUrl)
        }
    }
}

// Utilisation de DiffableDataSource pour des mises à jour fluides
class ModernTableController: UIViewController {

    private var dataSource: UITableViewDiffableDataSource<Section, Contact>!

    func applyContacts(_ contacts: [Contact]) {
        var snapshot = NSDiffableDataSourceSnapshot<Section, Contact>()
        snapshot.appendSections([.main])
        snapshot.appendItems(contacts)
        dataSource.apply(snapshot, animatingDifferences: true)
    }
}

Résultat pratique : l'application de ces optimisations augmente les FPS de défilement de 30 à 60 sur les appareils iPhone 12 et plus récents (selon les données de test d'Apple, 2025). Dans les projets IT Sectr, nous utilisons DiffableDataSource pour tous les nouveaux tableaux et ajoutons le prefetching pour les tableaux avec images (avatars, aperçus, icônes d'applications).

Questions fréquentes

UITableView ne défile pas — que faire ?

Vérifiez : (1) contentSize > frame.size — le tableau doit avoir plus de contenu que sa hauteur ; (2) le tableau n'est pas à l'intérieur d'un autre UIScrollView qui intercepte les gestes ; (3) isScrollEnabled = true ; (4) numberOfRowsInSection renvoie le nombre correct. Un problème typique est un tableau avec heightForRowAt = UITableView.automaticDimension sans estimatedRowHeight, ce qui entraîne un calcul infini de la hauteur.

Comment mettre à jour UITableView sans scintillement ?

Utilisez performBatchUpdates ou DiffableDataSource au lieu de reloadData(). reloadData() provoque un rechargement complet sans animation, créant un scintillement visuel. Pour les mises à jour ciblées, utilisez : reloadRows(at:with:), insertRows, deleteRows. Toutes les modifications dans performBatchUpdates sont animées simultanément. DiffableDataSource le fait automatiquement sans appels manuels.

Quelle est la différence entre UITableView et UICollectionView ?

UITableView est une liste verticale à une seule colonne avec des styles de cellule prédéfinis et une édition intégrée. UICollectionView est une mise en page flexible (grille, liste, carrousel, cascade) avec un positionnement arbitraire des éléments. Choisissez UITableView pour les listes simples (réglages, contacts, messages). Choisissez UICollectionView pour les galeries, catalogues, tableaux et toute mise en page non linéaire.

Comment supprimer les lignes vides sous le contenu dans UITableView ?

Définissez tableView.tableFooterView = UIView(frame: .zero). Par défaut, UITableView affiche des lignes vides (séparateurs) sous le dernier élément jusqu'au bord inférieur. Définir une UIView vide comme tableFooterView les supprime. Pour le style grouped, ce n'est pas nécessaire — le tableau n'affiche que les lignes réelles avec des coins arrondis dans la dernière section.

Comment faire une UITableView avec une hauteur constante ?

Définissez tableView.rowHeight = 80 et tableView.estimatedRowHeight = 0 ou omettez estimatedRowHeight. Avec une hauteur fixe, UITableView n'appelle pas heightForRowAt, ce qui accélère le rendu de 5 à 10 fois pour les listes de plus de 100 éléments. Pour les cellules avec une hauteur personnalisée (contenu différent), utilisez l'auto-dimensionnement : estimatedRowHeight + UITableView.automaticDimension.

Résumé

  • UITableView — le composant UIKit principal pour les listes verticales avec une architecture DataSource et Delegate.
  • UITableViewCell — 4 styles intégrés (.default, .subtitle, .value1, .value2) et personnalisés via contentConfiguration.
  • File d'attente de réutilisation — un pool de cellules réutilisables ; register + dequeueReusableCell + prepareForReuse sont obligatoires.
  • DiffableDataSource (iOS 13+) — une DataSource type-safe avec calcul automatique des modifications et animation.
  • Performances : rowHeight fixe, prefetching, moins de sous-vues, opérations lourdes en arrière-plan.
  • 85% des applications iOS du top 100 de l'App Store utilisent UITableView pour afficher du contenu.
  • Pour les grilles, carrousels et mises en page complexes, utilisez UICollectionView au lieu d'UITableView.

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