Interface Builder est un éditeur visuel d'interfaces intégré à Xcode pour le développement iOS et macOS. Il permet de créer une UI par glisser-déposer, de configurer Auto Layout et de connecter du code via IBOutlet et IBAction. Analysons le fonctionnement d'IB, les différences entre Storyboard et XIB, et l'utilité de @IBDesignable.
Points clés
Interface Builder est un composant de Xcode conçu pour la conception visuelle d'interfaces utilisateur. L'histoire d'IB a commencé en 1988 chez NeXT, bien avant l'apparition d'iOS. Stefan Pope a développé la première version pour NeXTSTEP — le système d'exploitation devenu la base de macOS et iOS. En 1996, Apple a acquis NeXT et intégré Interface Builder dans Xcode.
Dans Xcode moderne, Interface Builder prend en charge trois formats de fichier : Storyboard, XIB (Xcode Interface Builder) et les fichiers XIB pour les cellules de tableau et les vues personnalisées. Chacun de ces formats stocke une description XML de la hiérarchie des éléments d'UI, de leurs propriétés, contraintes et connexions au code.
IB fonctionne au niveau UIKit : boutons, étiquettes, champs de texte, tableaux, collections et contraintes sont glissés à la souris sur le canevas. Xcode compile les fichiers .storyboard et .xib en archives nib (Interface Builder compilé) lors de la construction, ce qui réduit la taille du bundle et accélère le chargement.
Selon Apple, plus de 70% des projets iOS sur UIKit utilisent Interface Builder à différentes étapes du développement. Malgré la croissance de SwiftUI, IB reste la norme pour les applications commerciales prenant en charge iOS 12 et inférieur, ainsi que pour les interfaces personnalisées complexes nécessitant un réglage fin d'Auto Layout.
Avant Xcode 4, Interface Builder était une application séparée lancée parallèlement à l'éditeur de code. Dans Xcode 4 (2011), Apple a fusionné IB et l'éditeur de code dans un seul IDE. Cela a permis de basculer entre le code et la mise en page sans changer de fenêtre, et de voir les modifications de propriétés en temps réel via le panneau Attributes Inspector.
| Version Xcode | Année | Changements dans Interface Builder |
|---|---|---|
| Xcode 3 | 2008 | IB — application séparée, support iOS 2.0 |
| Xcode 4 | 2011 | IB intégré à l'IDE, apparition de Storyboard |
| Xcode 5 | 2013 | Auto Layout avec menu de contraintes, aperçu des écrans |
| Xcode 6 | 2014 | Size Classes, @IBDesignable, Preview Assistant |
| Xcode 11 | 2019 | SwiftUI Canvas, IB reste pour UIKit |
| Xcode 15 | 2023 | SwiftUI Preview comme outil principal, mode legacy d'IB |
Avec l'introduction de SwiftUI en 2019, Apple a déplacé l'attention vers le développement déclaratif, cependant Interface Builder reste intégré à Xcode pour prendre en charge les projets UIKit. Des milliers d'applications existantes continuent d'utiliser IB, et Apple n'a pas annoncé sa suppression.
Interface Builder prend en charge deux formats principaux : Storyboard (.storyboard) et XIB (.xib). La différence réside dans la portée et le cas d'utilisation.
Storyboard est un fichier contenant toute la scène de l'application : plusieurs écrans (UIViewController), transitions entre eux (segues), contrôleurs de navigation, barres d'onglets et tous les éléments d'UI. Un Storyboard est chargé une fois au démarrage depuis Info.plist via la clé UIMainStoryboardFile (k). C'est pratique pour visualiser le flux des écrans, mais cela crée des problèmes de conflits de fusion dans git, car la description XML de l'application entière est stockée dans un seul fichier.
XIB (signifie Xcode Interface Builder) est un fichier pour un seul composant : une UIView individuelle, UITableViewCell, UICollectionViewCell ou un ViewController. XIB est chargé à la demande via UINib(nibName:bundle:) (k) ou la méthode Bundle.loadNibNamed (k). Les fichiers XIB sont plus faciles à fusionner, plus compacts et se chargent plus rapidement car ils ne contiennent pas la description de toute l'application.
| Critère | Storyboard | XIB |
|---|---|---|
| Portée | Plusieurs écrans + transitions | Un écran ou composant |
| Segues | Prend en charge (push, modal, unwind) | Ne prend pas en charge |
| Fusion git | Difficile (un grand XML) | Simple (beaucoup de petits fichiers) |
| Chargement | Au démarrage de l'application | À la demande (paresseux) |
| Réutilisabilité | Uniquement via les références storyboard | Élevée (cellules, en-têtes, vues) |
| Recommandation Apple | Déconseillé pour les grands projets | Recommandé pour les composants |
Depuis Xcode 11, Apple recommande d'utiliser XIB pour les composants individuels et d'éviter les Storyboards monolithiques. Pour la navigation entre écrans, la navigation basée sur le code via UIStoryboardSegue (k) manuellement ou les coordinateurs est préférée.
Les fichiers .storyboard et .xib stockent le XML au format Interface Builder Cocoa Touch XIB (dt). Exemple d'une structure simplifiée :
<!-- XIB file with UIView and UILabel -->
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB"
version="3.0">
<objects>
<view id="abc-123"
userLabel="CustomHeaderView"
contentMode="scaleToFill">
<subviews>
<label id="def-456"
text="Title"
textColor="darkTextColor"
fontDescription="title1"/>
</subviews>
</view>
</objects>
</document>Chaque élément a un id (an) unique, par lequel IB relie le nœud XML à l'objet d'exécution. Lors de la compilation, Xcode convertit le XML en format nib binaire (.nib), réduisant la taille du fichier d'environ 40%.
Auto Layout est un système de positionnement des éléments à l'écran via des relations mathématiques (contraintes). Interface Builder fournit une interface visuelle pour créer, éditer et déboguer des contraintes sans écrire de code. Chaque contrainte décrit une dépendance : view.leading = superview.leading + 16 (k) ou view.width = 2 * otherView.height (k).
Dans IB, les contraintes sont créées via le menu Pin (fixation des marges, largeur, hauteur) et le menu Align (centrage, bords, ligne de base). Le panneau Size Inspector affiche toutes les contraintes de l'élément sélectionné, leurs priorités (required/high/low) et permet de modifier les multiplicateurs et constantes.
IB prend également en charge UIStackView — un conteneur qui gère automatiquement la disposition des vues enfants. Il suffit de placer des éléments dans une vue en pile sur le canevas, et IB générera automatiquement les contraintes nécessaires. Cela accélère considérablement la mise en page par rapport au placement manuel des contraintes.
Size Classes sont une abstraction qui regroupe les appareils par largeur et hauteur d'écran : Compact et Regular. Les combinaisons (wC hR pour iPhone portrait, wR hR pour iPad) permettent de spécifier différentes contraintes et dispositions d'éléments pour différents scénarios. Dans Interface Builder, basculer entre les size classes modifie l'ensemble des contraintes actives sur le canevas.
| Appareil | Orientation | Width Class | Height Class |
|---|---|---|---|
| iPhone (sauf Max/Plus) | Portrait | Compact | Regular |
| iPhone (sauf Max/Plus) | Paysage | Compact | Compact |
| iPhone Plus/Max | Paysage | Regular | Compact |
| iPad | Quelconque | Regular | Regular |
| iPad Split View | 1/3 écran | Compact | Regular |
Exemple de contrainte avec variation de size class :
import UIKit
class AdaptiveViewController: UIViewController {
@IBOutlet weak var titleLabel: UILabel!
@IBOutlet weak var leadingConstraint: NSLayoutConstraint!
private func updateConstraints() {
let isRegular = traitCollection.horizontalSizeClass == .regular
leadingConstraint.constant = isRegular ? 40 : 16
titleLabel.font = isRegular
? UIFont.preferredFont(forTextStyle: .largeTitle)
: UIFont.preferredFont(forTextStyle: .title1)
}
override func traitCollectionDidChange(
_ previousTraitCollection: UITraitCollection?
) {
super.traitCollectionDidChange(previousTraitCollection)
if traitCollection.horizontalSizeClass != previousTraitCollection?.horizontalSizeClass {
updateConstraints()
}
}
}Dans le code ci-dessus, traitCollectionDidChange réagit au changement de size class, mettant à jour la contrainte et la police. Interface Builder permet de définir des valeurs par défaut pour chaque size class via l'inspecteur, tandis que le code est utilisé pour les scénarios dynamiques qui ne peuvent pas être décrits statiquement.
La connexion entre l'interface visuelle dans Interface Builder et le code Swift/Objective-C s'effectue via deux mécanismes : IBOutlet (Interface Builder Outlet) et IBAction (Interface Builder Action). Les deux sont créés en faisant glisser avec la touche Ctrl enfoncée du canevas IB vers le fichier du contrôleur.
IBOutlet est une annotation déclarant une référence à un élément d'UI. Xcode la connecte automatiquement à l'objet correspondant dans l'archive nib lors du chargement. Si la connexion est rompue (par exemple, un élément est renommé), l'application plante avec une erreur NSUnknownKeyException (k). IBOutlet est marqué comme weak (k), car le nib possède l'objet et le contrôleur n'est qu'un observateur.
IBAction est une méthode appelée lors d'un événement d'élément d'UI : pression d'un bouton, modification de texte, basculement d'interrupteur. IB connecte UIControlEvent (k) à la méthode via addTarget:action:forControlEvents: (k). Dans le code, IBAction ressemble à une méthode normale avec le type de retour IBAction (dt).
import UIKit
final class LoginViewController: UIViewController {
@IBOutlet weak var emailTextField: UITextField!
@IBOutlet weak var passwordTextField: UITextField!
@IBOutlet weak var loginButton: UIButton!
@IBOutlet weak var spinner: UIActivityIndicatorView!
@IBAction private func loginButtonTapped(_ sender: UIButton) {
guard let email = emailTextField.text, !email.isEmpty,
let password = passwordTextField.text, !password.isEmpty
else {
showAlert(message: "Fill in all fields")
return
}
loginButton.isEnabled = false
spinner.startAnimating()
performLogin(email: email, password: password)
}
private func performLogin(email: String, password: String) {
/// API call via URLSession
let request = LoginRequest(email: email, password: password)
APIClient.shared.login(request) { [weak self] result in
DispatchQueue.main.async {
guard let self else { return }
self.spinner.stopAnimating()
self.loginButton.isEnabled = true
switch result {
case .success:
self.navigateToMainScreen()
case .failure(let error):
self.showAlert(message: error.localizedDescription)
}
}
}
}
private func showAlert(message: String) {
let alert = UIAlertController(
title: "Error",
message: message,
preferredStyle: .alert
)
alert.addAction(UIAlertAction(title: "OK", style: .default))
present(alert, animated: true)
}
}L'exemple montre une configuration standard : IBOutlet pour les champs de texte, le bouton et le spinner, IBAction pour gérer l'appui. Toutes ces connexions sont définies dans Interface Builder via Ctrl+glisser. Si la connexion n'est pas configurée, IBOutlet sera nil (v) à l'exécution, provoquant un plantage lors de l'accès — c'est pourquoi IBOutlet est déclaré comme weak var (k s) avec déballage implicite.
@IBDesignable est une annotation Swift qui permet d'afficher une UIView personnalisée directement sur le canevas d'Interface Builder en temps réel. Le développeur voit les modifications de code sans exécuter l'application. @IBInspectable est une annotation pour les propriétés, les ajoutant au panneau Attributes Inspector d'IB, où les valeurs peuvent être modifiées interactivement.
Ces annotations sont particulièrement utiles lors de la création de bibliothèques de composants d'UI : boutons personnalisés, champs de saisie masqués, indicateurs animés. IBDesignable utilise prepareForInterfaceBuilder() (fn) pour la compilation séparée du code de construction, sans affecter le binaire principal de l'application.
import UIKit
@IBDesignable
final class GradientButton: UIButton {
@IBInspectable var startColor: UIColor = .systemBlue {
didSet { updateGradient() }
}
@IBInspectable var endColor: UIColor = .systemPurple {
didSet { updateGradient() }
}
@IBInspectable var cornerRadius: CGFloat = 12 {
didSet {
layer.cornerRadius = cornerRadius
layer.masksToBounds = true
}
}
private let gradientLayer = CAGradientLayer()
override init(frame: CGRect) {
super.init(frame: frame)
setupGradient()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
setupGradient()
}
override func layoutSubviews() {
super.layoutSubviews()
gradientLayer.frame = bounds
}
private func setupGradient() {
layer.insertSublayer(gradientLayer, at: 0)
updateGradient()
}
private func updateGradient() {
gradientLayer.colors = [startColor.cgColor, endColor.cgColor]
gradientLayer.startPoint = CGPoint(x: 0, y: 0.5)
gradientLayer.endPoint = CGPoint(x: 1, y: 0.5)
}
override func prepareForInterfaceBuilder() {
super.prepareForInterfaceBuilder()
setupGradient()
}
}Dans le code ci-dessus, GradientButton est un composant IBDesignable avec des propriétés IBInspectable startColor (v), endColor (v) et cornerRadius (v). En faisant glisser une UIView sur le canevas IB et en changeant la classe en GradientButton dans Identity Inspector, un bouton avec dégradé s'affichera sur le canevas en temps réel. Toutes les propriétés IBInspectable apparaîtront dans le panneau Attributes Inspector à droite.
Important : @IBDesignable compile tout le code pour l'affichage dans IB, donc les requêtes réseau ou les opérations longues ne doivent pas y être exécutées. Pour la séparation, #if TARGET_INTERFACE_BUILDER (k) est utilisé — une compilation conditionnelle qui exclut le code non destiné à IB.
Le processus de transformation des fichiers Interface Builder de la création du nib à l'affichage à l'écran comprend plusieurs étapes. Comprendre ce cycle aide à diagnostiquer les problèmes liés à IB.
Lors de la phase de construction, Xcode exécute l'outil ibtool (k, fn) — un utilitaire en ligne de commande pour compiler les fichiers .storyboard et .xib en format nib binaire. ibtool effectue également une validation : vérifie l'exactitude des contraintes, l'existence de toutes les classes, les types de connexion IBOutlet/IBAction. Les erreurs de validation s'affichent dans l'Issue Navigator de Xcode.
L'archive .nib finale est placée dans le bundle de l'application dans le dossier .nib (s). La taille du fichier nib est considérablement inférieure au XML original : le format binaire utilise une représentation optimisée avec remplacement des chaînes par des jetons et compression des valeurs numériques. La compression typique est de 50 à 60% de la taille XML originale.
À l'exécution, le nib est chargé via UINib(nibName:bundle:) (k) ou automatiquement via UIStoryboard.instantiateViewController(withIdentifier:) (k). Le processus de chargement comprend :
awakeFromNib() (fn) pour chaque objet — point d'entrée pour la configuration post-chargementLa méthode awakeFromNib() (fn) est appelée après que tous les IBOutlet sont déjà définis, mais avant le premier layoutSubviews. C'est pratique pour la configuration initiale : arrondir les coins, ajouter des ombres, localiser le texte. Cependant, tous les IBOutlet sont garantis non nuls dans awakeFromNib.
Avec la sortie de SwiftUI en 2019, les développeurs iOS ont obtenu une alternative à Interface Builder — un framework déclaratif avec Canvas Preview en temps réel. Examinons les principales différences entre les deux approches.
Interface Builder génère une description XML qui est compilée en nib. L'interface est créée visuellement ; le code ne gère que la logique. IB nécessite un seuil d'entrée plus bas pour les designers sans compétences en programmation, mais est difficile pour la révision de code (les modifications XML ne sont pas visibles dans le diff).
SwiftUI Preview est un développement entièrement basé sur le code. L'interface est décrite en Swift, l'aperçu se met à jour à chaque sauvegarde. Pas de XML, pas de nib, pas de risque de connexions IBOutlet rompues. SwiftUI Preview fonctionne plus rapidement qu'IB car il ne nécessite pas la compilation d'un fichier séparé.
| Critère | Interface Builder (UIKit) | SwiftUI Preview |
|---|---|---|
| Format de fichier | XML (.storyboard / .xib) → nib binaire | Code Swift (pas de fichier intermédiaire) |
| Aperçu | Canevas IB avec délai pour les vues complexes | Canvas Preview en temps réel |
| Support des versions iOS | iOS 2.0+ (toutes les versions) | iOS 13+ |
| Fusion git | Problématique (un seul fichier XML) | Simple (code Swift normal) |
| Données dynamiques | Via IBOutlet + code | @State (k), @Observable (k) |
| Vues personnalisées | @IBDesignable (compilation) | SwiftUI View avec PreviewProvider |
| Performance | Chargement rapide du nib | Compilation Swift à la volée |
En pratique, le choix entre IB et SwiftUI Preview dépend des exigences du projet. Interface Builder est indispensable pour les applications UIKit prenant en charge les anciennes versions d'iOS, ainsi que pour les projets commerciaux où les designers travaillent dans Xcode sans compétences Swift. SwiftUI est préféré pour les nouveaux projets ciblant iOS 17+, où la vitesse de développement et la réactivité comptent.
Apple ne prévoit pas de supprimer Interface Builder de Xcode. De plus, dans Xcode 16, l'entreprise a amélioré les performances du canevas IB et ajouté la prise en charge des composants SwiftUI via UIViewRepresentable Bridge. IB devrait être pris en charge au moins jusqu'en 2030.
Des années d'expérience en développement iOS ont formé un ensemble de recommandations réduisant le nombre de problèmes lors de l'utilisation d'Interface Builder dans les projets commerciaux.
Utilisez XIB au lieu de Storyboard pour les composants réutilisables. Chaque cellule de tableau personnalisée, en-tête ou pied doit être dans un XIB séparé. Cela facilite la fusion, accélère le chargement et permet de réutiliser les composants entre projets via Swift Package Manager ou CocoaPods.
Configurez des Storyboard References pour diviser les grands storyboards en modules. Au lieu d'un Main.storyboard avec 100 écrans, créez un storyboard par module (Auth, Profile, Feed) et reliez-les via Storyboard Reference. Cela réduira le temps de compilation d'ibtool et simplifiera le travail d'équipe.
Évitez les connexions IBOutlet vers File's Owner (k) sans vérification. Chaque connexion doit être weak (k) et optionnelle (l'optionnelle implicitement déballée n'est géniale que dans les playgrounds). Lors du renommage d'un IBOutlet dans une vue, Xcode met automatiquement à jour la connexion, mais l'édition manuelle du XML peut facilement introduire des erreurs.
Show Connection Panel (k) après avoir modifié un fichier IB — les indicateurs rouges signalent les connexions rompuesUser Defined Runtime Attributes (k) pour définir des propriétés sans code : layer.cornerRadius, layer.borderWidth, tintColorIdentifier (k) à chaque contrainte dans Size Inspector — cela aide à déboguer les conflitsimport UIKit
final class ProfileHeaderView: UIView {
@IBOutlet weak var avatarImageView: UIImageView!
@IBOutlet weak var nameLabel: UILabel!
@IBOutlet weak var bioLabel: UILabel!
@IBOutlet weak var editButton: UIButton!
override func awakeFromNib() {
super.awakeFromNib()
avatarImageView.layer.cornerRadius = avatarImageView.bounds.width / 2
avatarImageView.layer.masksToBounds = true
nameLabel.font = UIFont.preferredFont(forTextStyle: .headline)
bioLabel.font = UIFont.preferredFont(forTextStyle: .subheadline)
}
func configure(with profile: UserProfile) {
nameLabel.text = profile.fullName
bioLabel.text = profile.bio
/// Loading avatar via SDWebImage or Kingfisher
}
static func instantiateFromNib() -> ProfileHeaderView {
let nib = UINib(nibName: String(describing: self), bundle: nil)
return nib.instantiate(withOwner: nil).first as! ProfileHeaderView
}
}L'exemple montre une meilleure pratique pour les vues XIB : une méthode statique instantiateFromNib (fn) charge la vue depuis XIB avec le même nom que la classe. La méthode awakeFromNib (fn) configure l'UI (coins arrondis, polices), et la méthode configure(with:) (fn) accepte un modèle de données pour le remplissage. La séparation des responsabilités simplifie les tests et la réutilisation.
Questions fréquentes
Interface Builder est un éditeur visuel pour UIKit avec format Storyboard/XIB, fonctionnant par glisser-déposer. SwiftUI Preview est un aperçu déclaratif en temps réel où l'interface est décrite en code Swift. Les deux outils sont intégrés à Xcode, mais IB génère du XML tandis que SwiftUI compile Swift directement. IB supporte iOS 2.0+, SwiftUI supporte iOS 13+.
Non, Interface Builder n'est pas directement compatible avec SwiftUI. SwiftUI utilise sa propre syntaxe déclarative et Canvas Preview. Cependant, les projets UIKit créés via IB peuvent être intégrés dans SwiftUI via UIViewRepresentable, et les vues SwiftUI peuvent être intégrées dans UIKit via UIHostingController. Cela permet une migration progressive d'IB vers SwiftUI.
@IBDesignable est une annotation Swift qui affiche une UIView personnalisée directement dans Interface Builder en temps réel sans exécuter l'application. @IBInspectable est une annotation pour les propriétés, les ajoutant au panneau Attributes Inspector d'IB. Les deux annotations accélèrent le développement de composants d'UI personnalisés : modifiez simplement une propriété dans l'inspecteur et le changement est immédiatement visible sur le canevas.
Auto Layout dans Interface Builder définit des contraintes via le menu Pin (marges, largeur, hauteur) et le menu Align (centrage, ligne de base). Chaque contrainte est une relation mathématique entre vues. IB affiche les erreurs avec des lignes rouges et les conflits avec des avertissements jaunes. Les Size Classes dans IB permettent de spécifier différentes contraintes pour différents appareils et orientations sans écrire de code.
IBOutlet est une annotation pour une référence à un élément d'UI depuis le code (par exemple, @IBOutlet weak var label: UILabel!). IBAction est une annotation pour une méthode appelée sur un événement (par exemple, @IBAction func buttonTapped(_ sender: UIButton)). La connexion est créée en faisant glisser avec Ctrl enfoncé du canevas IB vers le fichier du contrôleur. Xcode génère automatiquement le code de connexion lors du relâchement de la souris.
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