Interface Builder — un editor visivo di interfacce integrato in Xcode per lo sviluppo iOS e macOS. Permette di creare UI tramite drag-and-drop, configurare Auto Layout, collegare il codice tramite IBOutlet e IBAction. Analizziamo come funziona IB, le differenze tra Storyboard e XIB e a cosa servono @IBDesignable.
Punti principali
Interface Builder — un componente di Xcode progettato per la progettazione visiva di interfacce utente. La storia di IB iniziò nel 1988 in NeXT, molto prima dell'arrivo di iOS. Stefan Pope sviluppò la prima versione per NeXTSTEP — il sistema operativo che divenne la base di macOS e iOS. Nel 1996 Apple acquisì NeXT e integrò Interface Builder in Xcode.
Nell'Xcode moderno, Interface Builder supporta tre formati di file: Storyboard, XIB (Xcode Interface Builder) e file XIB per celle di tabella e view personalizzate. Ciascuno di questi formati memorizza una descrizione XML della gerarchia degli elementi UI, delle loro proprietà, dei constraint e dei collegamenti con il codice.
IB lavora a livello UIKit: pulsanti, label, campi di testo, tabelle, collezioni e constraint vengono trascinati con il mouse sulla canvas. Xcode compila i file .storyboard e .xib in archivi nib (compiled Interface Builder) in fase di build, riducendo le dimensioni del bundle e accelerando il caricamento.
Secondo Apple, oltre il 70% dei progetti iOS su UIKit utilizza Interface Builder in varie fasi di sviluppo. Nonostante la crescita di SwiftUI, IB rimane lo standard per le applicazioni commerciali con supporto iOS 12 e versioni precedenti, nonché per interfacce personalizzate complesse che richiedono una regolazione fine di Auto Layout.
Prima di Xcode 4, Interface Builder era un'applicazione separata, eseguita parallelamente all'editor di codice. In Xcode 4 (2011), Apple ha integrato IB e l'editor di codice in un unico IDE. Ciò ha permesso di passare dal codice al layout senza cambiare finestra e di vedere le modifiche alle proprietà in tempo reale tramite il pannello Attributes Inspector.
| Versione Xcode | Anno | Modifiche in Interface Builder |
|---|---|---|
| Xcode 3 | 2008 | IB — applicazione separata, supporto iOS 2.0 |
| Xcode 4 | 2011 | IB integrato nell'IDE, introdotti gli Storyboard |
| Xcode 5 | 2013 | Auto Layout con menu dei constraint, anteprima schermate |
| Xcode 6 | 2014 | Size Classes, @IBDesignable, Preview Assistant |
| Xcode 11 | 2019 | SwiftUI Canvas, IB rimane per UIKit |
| Xcode 15 | 2023 | SwiftUI Preview come strumento principale, IB modalità legacy |
Con l'arrivo di SwiftUI nel 2019, Apple ha spostato l'attenzione sullo sviluppo dichiarativo, tuttavia Interface Builder rimane integrato in Xcode per supportare i progetti UIKit. Migliaia di applicazioni esistenti continuano a utilizzare IB e Apple non ne ha annunciato la rimozione.
Interface Builder supporta due formati principali: Storyboard (.storyboard) e XIB (.xib). La differenza tra loro risiede nell'ambito di utilizzo e nello scenario d'uso.
Storyboard — un file che contiene l'intera scena dell'applicazione: più schermate (UIViewController), transizioni tra di esse (segues), controller di navigazione, tab-bar e tutti gli elementi UI. Lo Storyboard viene caricato una volta all'avvio da Info.plist tramite la chiave UIMainStoryboardFile (k). È comodo per visualizzare il flusso delle schermate, ma crea problemi durante i conflitti di merge in git, poiché la descrizione XML dell'intera applicazione è contenuta in un unico file.
XIB (acronimo di Xcode Interface Builder) — un file per un singolo componente: una UIView separata, UITableViewCell, UICollectionViewCell o un singolo ViewController. XIB viene caricato su richiesta tramite UINib(nibName:bundle:) (k) o il metodo Bundle.loadNibNamed (k). I file XIB sono più facili da mergiare, sono più compatti e si caricano più velocemente, poiché non contengono la descrizione dell'intera applicazione.
| Criterio | Storyboard | XIB |
|---|---|---|
| Ambito | Più schermate + transizioni | Una schermata o componente |
| Segues | Supporta (push, modal, unwind) | Non supporta |
| Merge in git | Complesso (un unico grande XML) | Semplice (molti file piccoli) |
| Caricamento | All'avvio dell'applicazione | Su richiesta (lazy) |
| Riutilizzo | Solo tramite storyboard references | Elevato (celle, header, view) |
| Raccomandazione Apple | Sconsigliato per progetti grandi | Consigliato per componenti |
Dalla versione Xcode 11, Apple consiglia di utilizzare XIB per componenti singoli ed evitare Storyboard monolitici. Per la navigazione tra schermate è preferibile la navigazione tramite codice con UIStoryboardSegue (k) manualmente o coordinatori.
I file .storyboard e .xib memorizzano XML nel formato Interface Builder Cocoa Touch XIB (dt). Esempio di struttura semplificata:
<!-- File XIB con UIView e 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="Titolo"
textColor="darkTextColor"
fontDescription="title1"/>
</subviews>
</view>
</objects>
</document>Ogni elemento ha un id univoco (an), tramite il quale IB collega il nodo XML all'oggetto runtime. Durante la compilazione, Xcode converte l'XML in formato nib binario (.nib), riducendo la dimensione del file di circa il 40%.
Auto Layout — un sistema di posizionamento degli elementi sullo schermo tramite relazioni matematiche (constraint). Interface Builder fornisce un'interfaccia visiva per creare, modificare e debuggare i constraint senza scrivere codice. Ogni constraint descrive una dipendenza: view.leading = superview.leading + 16 (k) o view.width = 2 * otherView.height (k).
In IB i constraint vengono creati tramite i menu Pin (fissaggio di margini, larghezza, altezza) e Align (allineamento al centro, ai bordi, baseline). Il pannello Size Inspector mostra tutti i constraint dell'elemento selezionato, le loro priorità (required/high/low) e permette di modificare moltiplicatori e costanti.
IB supporta anche UIStackView — un contenitore che gestisce automaticamente la disposizione delle view figlie. Basta posizionare gli elementi in uno stack view sulla canvas e IB genererà automaticamente i constraint necessari. Questo accelera significativamente il layout rispetto al posizionamento manuale dei constraint.
Size Classes — un'astrazione che raggruppa i dispositivi per larghezza e altezza dello schermo: Compact e Regular. Le combinazioni (wC hR per iPhone in verticale, wR hR per iPad) permettono di impostare diversi constraint e disposizioni degli elementi per vari scenari. In Interface Builder, il passaggio tra size classes modifica l'insieme di constraint attivi sulla canvas.
| Dispositivo | Orientamento | Width Class | Height Class |
|---|---|---|---|
| iPhone (tranne Max/Plus) | Verticale | Compact | Regular |
| iPhone (tranne Max/Plus) | Orizzontale | Compact | Compact |
| iPhone Plus/Max | Orizzontale | Regular | Compact |
| iPad | Qualsiasi | Regular | Regular |
| iPad Split View | 1/3 schermo | Compact | Regular |
Esempio di constraint con variazione per 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()
}
}
}Nel codice sopra, traitCollectionDidChange reagisce al cambiamento di size class, aggiornando il constraint e il font. Interface Builder permette di impostare valori predefiniti per ogni size class tramite l'ispettore, mentre il codice viene utilizzato per scenari dinamici che non possono essere descritti staticamente.
La connessione tra l'interfaccia visiva in Interface Builder e il codice Swift/Objective-C avviene tramite due meccanismi: IBOutlet (Interface Builder Outlet) e IBAction (Interface Builder Action). Entrambi vengono creati trascinando con il tasto Ctrl premuto dalla canvas IB al file del controller.
IBOutlet — un'annotazione che dichiara un riferimento a un elemento UI. Xcode la collega automaticamente all'oggetto corrispondente nell'archivio nib al momento del caricamento. Se la connessione si interrompe (ad esempio, l'elemento viene rinominato), l'applicazione si blocca con l'errore NSUnknownKeyException (k). IBOutlet viene dichiarato come weak (k), poiché il nib possiede l'oggetto e il controller è solo un osservatore.
IBAction — un metodo chiamato in risposta a un evento di un elemento UI: pressione di un pulsante, modifica del testo, attivazione di uno switch. IB collega UIControlEvent (k) al metodo tramite addTarget:action:forControlEvents: (k). Nel codice, IBAction appare come un metodo normale con tipo restituito 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: "Compila tutti i campi")
return
}
loginButton.isEnabled = false
spinner.startAnimating()
performLogin(email: email, password: password)
}
private func performLogin(email: String, password: String) {
/// Chiamata API tramite 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: "Errore",
message: message,
preferredStyle: .alert
)
alert.addAction(UIAlertAction(title: "OK", style: .default))
present(alert, animated: true)
}
}Nell'esempio viene mostrata una connessione standard: IBOutlet per i campi di testo, il pulsante e lo spinner, IBAction per gestire la pressione del pulsante. Tutte queste connessioni vengono stabilite in Interface Builder tramite Ctrl+drag. Se la connessione non è configurata, IBOutlet sarà nil (v) al runtime, causando un crash al momento dell'accesso — pertanto IBOutlet viene dichiarato come weak var (k s) con implicit unwrap.
@IBDesignable — un'annotazione Swift che permette di visualizzare una UIView personalizzata direttamente sulla canvas di Interface Builder in tempo reale. Lo sviluppatore vede il risultato delle modifiche al codice senza avviare l'applicazione. @IBInspectable — un'annotazione per le proprietà che le aggiunge al pannello Attributes Inspector di IB, dove è possibile modificarne i valori in modo interattivo.
Queste annotazioni sono particolarmente utili nella creazione di librerie di componenti UI: pulsanti personalizzati, campi di input con maschera, indicatori animati. IBDesignable utilizza prepareForInterfaceBuilder() (fn) per una compilazione separata del codice di build che non influisce sul binario principale dell'applicazione.
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()
}
}Nel codice sopra, GradientButton è un componente IBDesignable con proprietà IBInspectable startColor (v), endColor (v) e cornerRadius (v). Trascinando una UIView sulla canvas IB e cambiando la classe in GradientButton nell'Identity Inspector, sulla canvas verrà visualizzato un pulsante con gradiente in tempo reale. Tutte le proprietà IBInspectable appariranno nel pannello Attributes Inspector a destra.
Importante: @IBDesignable compila l'intero codice per la visualizzazione in IB, pertanto al suo interno non devono essere eseguite richieste di rete o operazioni lunghe. Per la distinzione si utilizza #if TARGET_INTERFACE_BUILDER (k) — una compilazione condizionale che esclude il codice non destinato a IB.
Il processo di trasformazione dei file Interface Builder dalla creazione del nib alla visualizzazione sullo schermo comprende diverse fasi. Comprendere questo ciclo aiuta a diagnosticare i problemi con IB.
In fase di build, Xcode esegue lo strumento ibtool (k, fn) — un'utilità a riga di comando per compilare i file .storyboard e .xib in formato nib binario. ibtool esegue anche la validazione: verifica la correttezza dei constraint, la presenza di tutte le classi, i tipi di connessioni IBOutlet/IBAction. Gli errori di validazione vengono visualizzati nell'Issue Navigator di Xcode.
L'archivio .nib finale viene inserito nel bundle dell'applicazione nella cartella .nib (s). La dimensione del file nib è significativamente inferiore rispetto all'XML originale: il formato binario utilizza una rappresentazione ottimizzata con sostituzione delle stringhe con token e compressione dei valori numerici. La compressione tipica è del 50–60% rispetto alla dimensione XML originale.
Al runtime, il nib viene caricato tramite UINib(nibName:bundle:) (k) o automaticamente tramite UIStoryboard.instantiateViewController(withIdentifier:) (k). Il processo di caricamento include:
awakeFromNib() (fn) per ogni oggetto — punto di ingresso per la configurazione post-caricamentoIl metodo awakeFromNib() (fn) viene chiamato dopo che tutti gli IBOutlet sono stati impostati, ma prima del primo layoutSubviews. È comodo per la configurazione iniziale: impostazione degli arrotondamenti, aggiunta di ombre, localizzazione del testo. Tuttavia, tutti gli IBOutlet sono già garantitamente non nil in awakeFromNib.
Con l'uscita di SwiftUI nel 2019, gli sviluppatori iOS hanno ottenuto un'alternativa a Interface Builder: un framework dichiarativo con Canvas Preview in tempo reale. Analizziamo le differenze chiave tra i due approcci.
Interface Builder genera una descrizione XML che viene compilata in nib. L'interfaccia viene creata visivamente, il codice si occupa solo della logica. IB richiede una soglia di ingresso più bassa per i designer senza competenze di programmazione, ma è complesso per la revisione del codice (le modifiche XML non sono visibili nel diff).
SwiftUI Preview — sviluppo interamente basato sul codice. L'interfaccia viene descritta in Swift, l'anteprima si aggiorna a ogni salvataggio. Niente XML, niente nib, nessun rischio di connessioni IBOutlet interrotte. SwiftUI Preview funziona più velocemente di IB, poiché non richiede la compilazione di un file separato.
| Criterio | Interface Builder (UIKit) | SwiftUI Preview |
|---|---|---|
| Formato file | XML (.storyboard / .xib) → nib binario | Codice Swift (senza file intermedio) |
| Anteprima | Canvas IB con ritardo per view complesse | Canvas Preview in tempo reale |
| Supporto versioni iOS | iOS 2.0+ (tutte le versioni) | iOS 13+ |
| Merge in git | Problematico (un unico file XML) | Semplice (normale codice Swift) |
| Dati dinamici | Tramite IBOutlet + codice | @State (k), @Observable (k) |
| View personalizzate | @IBDesignable (compilazione) | SwiftUI View con PreviewProvider |
| Prestazioni | Caricamento nib veloce | Compilazione Swift al volo |
In pratica, la scelta tra IB e SwiftUI Preview dipende dai requisiti del progetto. Interface Builder è indispensabile per applicazioni UIKit con supporto di vecchie versioni iOS, nonché per progetti commerciali in cui i designer lavorano in Xcode senza competenze Swift. SwiftUI è preferibile per nuovi progetti orientati a iOS 17+, dove contano velocità di sviluppo e reattività.
Apple non prevede di rimuovere Interface Builder da Xcode. Anzi, in Xcode 16 l'azienda ha migliorato le prestazioni della canvas IB e aggiunto il supporto per componenti SwiftUI tramite UIViewRepresentable Bridge. Ci si aspetta che IB sarà supportato almeno fino al 2030.
L'esperienza pluriennale nello sviluppo iOS ha formato una serie di raccomandazioni che riducono il numero di problemi durante l'utilizzo di Interface Builder in progetti commerciali.
Utilizza XIB invece di Storyboard per componenti riutilizzabili. Ogni cella personalizzata della tabella, header o footer deve essere in un XIB separato. Questo facilita il merge, accelera il caricamento e permette di riutilizzare i componenti tra progetti tramite Swift Package Manager o CocoaPods.
Configura Storyboard References per suddividere gli storyboard grandi in moduli. Invece di un unico Main.storyboard con 100 schermate, crea uno storyboard per ogni modulo (Auth, Profile, Feed) e collegali tramite Storyboard Reference. Questo ridurrà il tempo di compilazione di ibtool e semplificherà il lavoro di squadra.
Evita connessioni IBOutlet a File's Owner (k) senza controllo. Ogni connessione deve essere weak (k) e opzionale (l'implicitly unwrapped optional va bene solo in playground). Quando si rinomina un IBOutlet nella view, Xcode aggiorna automaticamente la connessione, ma con la modifica manuale dell'XML è facile commettere un errore.
Show Connection Panel (k) dopo aver modificato un file IB — gli indicatori rossi segnalano connessioni interrotteUser Defined Runtime Attributes (k) per impostare proprietà senza codice: layer.cornerRadius, layer.borderWidth, tintColorIdentifier (k) a ogni constraint in Size Inspector — aiuta nel debug in caso di conflittiimport 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
/// Caricamento avatar tramite SDWebImage o Kingfisher
}
static func instantiateFromNib() -> ProfileHeaderView {
let nib = UINib(nibName: String(describing: self), bundle: nil)
return nib.instantiate(withOwner: nil).first as! ProfileHeaderView
}
}Nell'esempio viene mostrata una best practice per le view XIB: il metodo statico instantiateFromNib (fn) carica la view dall'XIB con lo stesso nome della classe. Il metodo awakeFromNib (fn) configura l'UI (arrotondamenti, font), mentre il metodo configure(with:) (fn) accetta un modello di dati per il riempimento. La separazione delle responsabilità semplifica il testing e il riutilizzo.
Domande frequenti
Interface Builder — editor visivo per UIKit con formato Storyboard/XIB, funzionante tramite drag-and-drop. SwiftUI Preview — anteprima dichiarativa in tempo reale, dove l'interfaccia viene descritta con codice Swift. Entrambi gli strumenti sono integrati in Xcode, ma IB genera XML mentre SwiftUI compila Swift direttamente. IB supporta iOS 2.0+, SwiftUI — iOS 13+.
No, Interface Builder non è direttamente compatibile con SwiftUI. SwiftUI utilizza la propria sintassi dichiarativa e Canvas Preview. Tuttavia, i progetti UIKit creati tramite IB possono essere integrati in SwiftUI attraverso UIViewRepresentable, e le view SwiftUI possono essere incorporate in UIKit tramite UIHostingController. Questo permette di migrare gradualmente da IB a SwiftUI.
@IBDesignable — un'annotazione Swift che visualizza una UIView personalizzata direttamente in Interface Builder in tempo reale senza avviare l'applicazione. @IBInspectable — un'annotazione per proprietà che le aggiunge al pannello Attributes Inspector di IB. Entrambe le annotazioni accelerano lo sviluppo di componenti UI personalizzati: basta modificare la proprietà nell'ispettore e la modifica è subito visibile sulla canvas.
Auto Layout in Interface Builder imposta i constraint tramite i menu Pin (margini, larghezza, altezza) e Align (centratura, baseline). Ogni constraint è una relazione matematica tra le view. IB mostra gli errori con linee rosse e i conflitti con avvisi gialli. Le Size Classes in IB permettono di impostare diversi constraint per diversi dispositivi e orientamenti senza scrivere codice.
IBOutlet — un'annotazione per un riferimento a un elemento UI dal codice (ad esempio, @IBOutlet weak var label: UILabel!). IBAction — un'annotazione per un metodo chiamato in risposta a un evento (ad esempio, @IBAction func buttonTapped(_ sender: UIButton)). La connessione viene creata tramite Ctrl+drag dalla canvas IB al file del controller. Xcode genera automaticamente il codice di connessione al rilascio del mouse.
Riepilogo
Svilupperemo un'applicazione mobile chiavi in mano
IT Sectr crea applicazioni iOS e Android per startup e aziende dal 2017. Ti consulteremo e ti proporremo la soluzione migliore.
Leggi anche