Auto Layout è il sistema di posizionamento adattivo degli elementi dell'interfaccia di Apple, basato su vincoli matematici (constraints). Sviluppato per iOS 6 (2012), Auto Layout consente di creare interfacce che vengono visualizzate correttamente su tutti i dispositivi — da iPhone SE (4.7″) a iPad Pro (12.9″) e Dynamic Island. Secondo Apple WWDC Session 202 (2024), oltre il 90% delle app nell'App Store utilizza Auto Layout o la sua alternativa dichiarativa — SwiftUI layout system. I Constraints descrivono le dipendenze tra elementi UI tramite equazioni lineari: view1.leading = view2.trailing + 8.
Punti chiave
Auto Layout è il sistema di layout adattivo di Apple che utilizza vincoli matematici (constraints) per posizionare gli elementi UI. A differenza del layout basato su frame (frame-based layout), dove ogni elemento ha coordinate fisse x, y, width, height, Auto Layout descrive relazioni tra elementi: «il pulsante è a 8pt dal bordo destro del genitore» o «la larghezza del campo di testo è uguale alla metà della larghezza dello schermo». Il meccanismo si basa sull'algoritmo Cassowary, sviluppato presso l'University of Washington (Greg J. Badros, 1999) e implementato da Apple in iOS 6. Cassowary risolve un sistema di disuguaglianze lineari con priorità — Required (1000), Default High (750), Default Low (250) — consentendo la gestione dei conflitti di vincoli. Auto Layout supporta tre tipi di dimensione: intrinsic (dimensione naturale dell'elemento determinata dal contenuto), explicit (vincolo esplicitamente impostato) e compressible/stretchable (modalità elastica tramite Content Hugging Priority e Compression Resistance Priority).
Ogni elemento UI in Auto Layout ha un Intrinsic Content Size — una dimensione naturale determinata dal suo contenuto. Per UILabel questo dipende dal testo e dal font, per UIImageView — dalle dimensioni dell'immagine. Content Hugging Priority (resistenza all'allungamento) e Compression Resistance Priority (resistenza alla compressione) controllano il comportamento dell'elemento quando lo spazio disponibile cambia. Valori standard: 251 per hugging e 749 per compression resistance. Se due elementi competono per lo spazio, la priorità determina quale si allunga per primo. Comprendere queste priorità è la chiave per risolvere Ambiguous Layout (layout ambiguo), che Xcode evidenzia nel debugger.
Un constraint è descritto dall'equazione: view1.attribute = multiplier × view2.attribute + constant. Gli attributi includono leading, trailing, top, bottom, centerX, centerY, width, height, firstBaseline, lastBaseline. Multiplier è usato per relazioni proporzionali (larghezza di view1 = 0.5 × larghezza di superview). Constant imposta un offset fisso (leading = superview.leading + 16). Gli strumenti di Interface Builder consentono di creare constraint visivamente tramite Ctrl-transcinamento, ma layout complessi richiedono la creazione programmatica tramite NSLayoutConstraint o VFL (Visual Format Language), che Apple raccomanda di sostituire con NSLayoutConstraint da iOS 9.
Il sistema di constraint viene risolto come un problema di programmazione lineare: l'algoritmo Cassowary trova la disposizione ottimale di tutti gli elementi che soddisfa tutti i vincoli considerando le loro priorità. Se i constraint si contraddicono, si verifica un Unsatisfiable Layout — un'eccezione che Xcode registra con una descrizione dettagliata del conflitto. Se non ci sono constraint sufficienti per determinare la posizione di almeno un elemento, si verifica Ambiguous Layout — gli elementi vengono visualizzati in posizioni arbitrarie. Apple raccomanda un insieme minimo: per ogni elemento, position (x, y) e size (width, height) devono essere impostati — esplicitamente o tramite intrinsic content size. I constraint possono essere di prima classe: l'elemento leader (ad esempio, superview) e quello seguace (vista figlia) creano una gerarchia.
Cassowary utilizza il metodo Sequential Quadratic Programming per risolvere sistemi di disuguaglianze lineari. Ogni constraint ha una priorità da 1 a 1000. Required (1000) è un vincolo obbligatorio; se non può essere soddisfatto, l'app si blocca con NSConstraintException. Default High (750) è raccomandato; Default Low (250) è il meno importante. In caso di conflitto, Cassowary rilassa i vincoli con priorità inferiore. Ad esempio, se due elementi richiedono una larghezza fissa ma lo schermo è troppo stretto, viene rilassato il constraint con priorità inferiore. In Xcode Debug View Hierarchy (strumento di debug disponibile da Xcode 6) vengono evidenziati solo i problemi con i constraint Required — gli altri vengono gestiti senza errore.
UIStackView è un contenitore introdotto in iOS 9 (2015) che crea e gestisce automaticamente i constraint per gli arrangedSubviews annidati. UIStackView supporta due assi: orizzontale e verticale. Le impostazioni di distribution determinano la distribuzione dello spazio: fill (riempimento proporzionale in base all'hugging priority), fillEqually (dimensioni uguali), fillProportionally (proporzionale all'intrinsic content size), equalSpacing (spaziatura uguale), equalCentering (distanze uguali tra i centri). Alignment definisce l'allineamento trasversale: fill, leading, center, trailing (per orizzontale) o fill, top, center, bottom (per verticale). UIStackView gestisce automaticamente la spaziatura, l'allineamento della linea di base e l'adattamento al Dynamic Type.
import UIKit
class StackViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let stack = UIStackView()
stack.axis = NSLayoutConstraint.Axis.vertical
stack.distribution = .fillEqually
stack.spacing = 8
stack.translatesAutoresizingMaskIntoConstraints = false
let label = UILabel()
label.text = "Auto Layout Guide"
label.font = UIFont.preferredFont(forTextStyle: .headline)
let button = UIButton(type: .system)
button.setTitle("Apply", for: .normal)
stack.addArrangedSubview(label)
stack.addArrangedSubview(button)
view.addSubview(stack)
NSLayoutConstraint.activate([
stack.centerXAnchor.constraint(equalTo: view.centerXAnchor),
stack.centerYAnchor.constraint(equalTo: view.centerYAnchor),
stack.leadingAnchor.constraint(greaterThanOrEqualTo: view.leadingAnchor, constant: 16),
stack.trailingAnchor.constraint(lessThanOrEqualTo: view.trailingAnchor, constant: -16)
])
}
}Il codice crea un UIStackView verticale con due elementi (UILabel e UIButton), distribuiti uniformemente (fillEqually) con una spaziatura di 8pt. Lo stack è centrato sullo schermo con margini di almeno 16pt dai bordi. translatesAutoresizingMaskIntoConstraints = false è obbligatorio quando si creano constraint programmaticamente — senza di esso, Auto Layout non funziona. In IT Sectr, UIStackView viene utilizzato nell'80% degli schermi dei progetti iOS per costruire moduli adattivi, elenchi di impostazioni e schede.
Gli UIStackView possono essere annidati: uno stack orizzontale dentro uno verticale è uno schema standard per layout complessi. Lo stack esterno gestisce le righe, lo stack interno gestisce le colonne all'interno di ogni riga. La combinazione di axis, alignment e distribution a ogni livello offre una flessibilità praticamente illimitata senza un singolo constraint manuale. Apple raccomanda UIStackView come strumento di layout principale in UIKit, ricorrendo a NSLayoutConstraint manuale solo per i casi non coperti dagli stack: viste sovrapposte, posizionamento preciso dei pixel, animazione personalizzata dei bounds.
NSLayoutConstraint è un'API programmatica per creare vincoli individuali nel codice. Ogni constraint viene creato tramite un inizializzatore con parametri: item, attribute, relatedBy, toItem, attribute, multiplier, constant. Da iOS 9, Apple ha introdotto l'Anchor API — una sintassi più leggibile tramite le proprietà view.leadingAnchor, view.trailingAnchor, view.topAnchor, view.bottomAnchor, view.centerXAnchor, view.centerYAnchor, view.widthAnchor, view.heightAnchor. L'Anchor API imposta automaticamente relatedBy = .equal e utilizza First Item/Second Item dagli Anchors, riducendo il codice del 40% rispetto al NSLayoutConstraint classico.
import UIKit
class ConstraintViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let childView = UIView()
childView.backgroundColor = .systemBlue
childView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(childView)
NSLayoutConstraint.activate([
childView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 24),
childView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 16),
childView.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -16),
childView.heightAnchor.constraint(equalToConstant: 120),
childView.bottomAnchor.constraint(lessThanOrEqualTo: view.bottomAnchor, constant: -24)
])
}
}Il codice posiziona childView con margini da safeAreaLayoutGuide (top) e dai bordi dello schermo (leading/trailing). lessThanOrEqualTo per bottom garantisce che la vista non superi il limite inferiore. L'Anchor API lancia un'eccezione in fase di compilazione se gli anchor sono incompatibili (ad esempio, leadingAnchor mescolato con rightAnchor), prevenendo errori a runtime. Apple raccomanda l'Anchor API come standard per Auto Layout programmatico da iOS 9.
Safe Area è l'area dello schermo non coperta dagli elementi di sistema: Dynamic Island, Notch, Status Bar, Home Indicator, angoli arrotondati. In iOS 11, Apple ha sostituito topLayoutGuide/bottomLayoutGuide con safeAreaLayoutGuide, che si adatta automaticamente all'orientamento del dispositivo e alla presenza del ritaglio dello schermo. I Layout Margins sono i margini interni predefiniti della vista (16pt su iOS, 20pt su iPadOS). Per UILayoutGuide è possibile impostare directionalLayoutMargins personalizzati considerando la localizzazione RIGHT-TO-LEFT. Auto Layout rispetta automaticamente la safe area quando si utilizza safeAreaLayoutGuide negli anchor.
Sui dispositivi con Dynamic Island (iPhone 14 Pro e successivi) e Notch (iPhone X–13), Safe Area esclude 44pt in alto in modalità ritratto (59pt con Dynamic Island in stato attivo). Home Indicator aggiunge 34pt in basso. Per un corretto adattamento, tutti i constraint superiori devono essere collegati a safeAreaLayoutGuide.topAnchor, non a view.topAnchor. I constraint inferiori devono essere collegati a safeAreaLayoutGuide.bottomAnchor o view.bottomAnchor con un margine per Home Indicator. In IT Sectr, testiamo tutti gli schermi sui simulatori iPhone SE (2022), iPhone 14 Pro Max e iPad Pro 12.9″ — tre dispositivi che coprono tutte le variazioni di safe area.
Gli errori più frequenti quando si lavora con Auto Layout: dimenticare translatesAutoresizingMaskIntoConstraints = false, constraint Required in conflitto (priorità 1000), Ambiguous Layout (constraint insufficienti per determinare la posizione), Content Hugging Priority errato per UILabel multilinea e mescolanza di anchor leading/trailing con left/right. Xcode 15+ mostra i problemi di layout nel Runtime Issue Navigator e offre correzioni automatiche. Per layout complessi, utilizzare Debug View Hierarchy: i marcatori gialli indicano ambiguous layout, quelli rossi indicano unsatisfiable.
| Errore | Causa | Soluzione |
|---|---|---|
| translatesAutoresizingMaskIntoConstraints = true | Auto Layout non attivato per la vista | Impostare false per tutte le viste programmatiche |
| Unsatisfiable Layout | Conflitto di constraint Required (1000) | Ridurre una priorità a Default High (750) |
| Ambiguous Layout | Constraint insufficienti per x/y/w/h | Aggiungere constraint mancante o verificare intrinsic size |
| Troncamento del testo in UILabel | Content Hugging Priority inferiore al concorrente | Aumentare hugging priority a 252+ |
| Mescolanza di anchor LTR/RTL | leadingAnchor con rightAnchor | Utilizzare solo leading/trailing per supporto RTL |
Domande frequenti
Il layout basato su frame imposta coordinate fisse x, y, width, height per ogni elemento. Auto Layout utilizza vincoli matematici (constraints) — relazioni tra elementi: «label.leading = button.trailing + 8». Il layout basato su frame non si adatta alla dimensione dello schermo; Auto Layout ricalcola automaticamente le posizioni in caso di rotazione, Split View o modifica del Dynamic Type.
UIStackView è ottimale per layout lineari: righe, colonne, moduli, elenchi di parametri. NSLayoutConstraint è necessario per viste sovrapposte, posizionamento preciso dei pixel, animazione personalizzata dei bounds e casi in cui la distribuzione dello spazio non è uniforme e non è coperta dalla distribution di UIStackView. In pratica, l'80% dei layout viene risolto con UIStackView, il 20% con constraint manuali.
Content Hugging Priority (resistenza all'allungamento) è una priorità che determina quanto un elemento resiste all'aumento delle sue dimensioni oltre l'Intrinsic Content Size. Il valore predefinito è 251. Se due elementi competono per lo spazio libero, l'elemento con hugging priority più alta mantiene le sue dimensioni mentre il secondo si allunga. Compression Resistance Priority (749 predefinito) funziona in modo simile per la compressione.
Auto Layout si adatta automaticamente a Dynamic Type se i constraint utilizzano l'intrinsic content size delle etichette. Quando la dimensione del carattere aumenta, UILabel si espande, spostando gli elementi vicini attraverso i constraint. UIStackView con distribution = fillProportionally ridistribuisce lo spazio proporzionalmente ai nuovi intrinsic size. Safe Area e Layout Margins rispettano anche le impostazioni di accessibilità.
Unsatisfiable Layout si verifica quando due constraint Required (priority = 1000) si contraddicono: ad esempio, view.leading = superview.leading + 16 e view.trailing = superview.leading + 200 con una superview larga 100pt. L'algoritmo Cassowary non riesce a trovare una soluzione e l'app si blocca con NSConstraintException. La soluzione è ridurre la priorità di uno dei constraint in conflitto a Default High (750).
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