Auto Layout — system adaptacyjnego pozycjonowania elementów interfejsu od Apple, oparty na matematycznych ograniczeniach (constraints). Opracowany dla iOS 6 (2012), Auto Layout pozwala tworzyć interfejsy, które poprawnie wyświetlają się na wszystkich urządzeniach — od iPhone SE (4,7″) do iPad Pro (12,9″) i Dynamic Island. Według Apple WWDC Session 202 (2024), ponad 90% aplikacji w App Store używa Auto Layout lub jego deklaratywnej alternatywy — SwiftUI layout system. Constraints opisują zależności między elementami UI poprzez równania liniowe: view1.leading = view2.trailing + 8.
Najważniejsze
Auto Layout — to system adaptacyjnego układu Apple, wykorzystujący matematyczne ograniczenia (constraints) do pozycjonowania elementów UI. W przeciwieństwie do układu ramkowego (frame-based layout), gdzie każdy element ma stałe współrzędne x, y, width, height, Auto Layout opisuje relacje między elementami: «przycisk znajduje się 8pt od prawej krawędzi rodzica» lub «szerokość pola tekstowego jest równa połowie szerokości ekranu». Mechanizm oparty jest na algorytmie Cassowary, opracowanym na University of Washington (Greg J. Badros, 1999) i wdrożonym przez Apple w iOS 6. Cassowary rozwiązuje system nierówności liniowych z priorytetami — Required (1000), Default High (750), Default Low (250) — co pozwala zarządzać konfliktami ograniczeń. Auto Layout obsługuje trzy typy rozmiarów: intrinsic (naturalny rozmiar elementu określany przez treść), explicit (jawnie określone constraint) i compressible/stretchable (elastyczny tryb przez Content Hugging Priority i Compression Resistance Priority).
Każdy element UI w Auto Layout ma Intrinsic Content Size — naturalny rozmiar określany przez jego zawartość. Dla UILabel zależy to od tekstu i czcionki, dla UIImageView — od rozmiarów obrazu. Content Hugging Priority (opór przed rozciąganiem) i Compression Resistance Priority (opór przed ściskaniem) zarządzają zachowaniem elementu przy zmianie dostępnego miejsca. Standardowe wartości: 251 dla hugging i 749 dla compression resistance. Jeśli dwa elementy konkurują o miejsce, priorytet określa, który rozciągnie się pierwszy. Zrozumienie tych priorytetów jest kluczem do eliminacji Ambiguous Layout (niejednoznacznego układu), który Xcode podświetla w debuggerze.
Constraint opisuje równanie: view1.attribute = multiplier × view2.attribute + constant. Atrybuty obejmują leading, trailing, top, bottom, centerX, centerY, width, height, firstBaseline, lastBaseline. Multiplier jest używany do relacji proporcjonalnych (szerokość view1 = 0,5 × szerokość superview). Constant określa stały odstęp (leading = superview.leading + 16). Narzędzia Interface Builder pozwalają określać constraints wizualnie przez Ctrl-przeciąganie, ale złożone układy wymagają programowego tworzenia przez NSLayoutConstraint lub VFL (Visual Format Language), który Apple zaleca zastępować na NSLayoutConstraint od iOS 9.
System constraints jest rozwiązywany jako zadanie programowania liniowego: algorytm Cassowary znajduje optymalne rozmieszczenie wszystkich elementów, spełniające wszystkie ograniczenia z uwzględnieniem ich priorytetów. Jeśli constraints są ze sobą sprzeczne, powstaje Unsatisfiable Layout — wyjątek, który Xcode loguje ze szczegółowym opisem konfliktu. Jeśli constraints jest niewystarczająco do określenia pozycji choć jednego elementu, powstaje Ambiguous Layout — elementy wyświetlają się w dowolnej pozycji. Apple zaleca minimalny zestaw: dla każdego elementu muszą być określone position (x, y) i size (width, height) — jawnie lub przez intrinsic content size. Constraints mogą być first-class: element wiodący (np. superview) i podrzędny (widok potomny) tworzą hierarchię.
Cassowary używa metody Sequential Quadratic Programming do rozwiązywania układów nierówności liniowych. Każdy constraint ma priorytet od 1 do 1000. Required (1000) — obowiązkowe ograniczenie; jeśli nie można go spełnić, aplikacja ulega awarii z NSConstraintException. Default High (750) — zalecane; Default Low (250) — najmniej ważne. W przypadku konfliktu Cassowary rozluźnia ograniczenia o niższym priorytecie. Na przykład, jeśli dwa elementy wymagają stałej szerokości, a ekran jest zbyt wąski, rozluźniane jest constraint o niższym priorytecie. W Xcode Debug View Hierarchy (narzędzie debugowania dostępne od Xcode 6) podświetla tylko problemy z Required constraints — pozostałe są obsługiwane bez błędu.
UIStackView — kontener przedstawiony w iOS 9 (2015), który automatycznie tworzy i zarządza constraints dla zagnieżdżonych arrangedSubviews. UIStackView obsługuje dwie osie: horizontal (układ poziomy) i vertical (pionowy). Ustawienia distribution określają rozkład miejsca: fill (wypełnienie proporcjonalne do hugging priority), fillEqually (równe rozmiary), fillProportionally (proporcjonalnie do intrinsic content size), equalSpacing (równe odstępy), equalCentering (równe odległości między środkami). Alignment określa wyrównanie w poprzek osi: fill, leading, center, trailing (dla horizontal) lub fill, top, center, bottom (dla vertical). UIStackView automatycznie zarządza spacing, baseline alignment i adaptacją do 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 = "Przewodnik Auto Layout"
label.font = UIFont.preferredFont(forTextStyle: .headline)
let button = UIButton(type: .system)
button.setTitle("Zastosuj", 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)
])
}
}Kod tworzy pionowy UIStackView z dwoma elementami (UILabel i UIButton), rozmieszczonymi równomiernie (fillEqually) z odstępem 8pt. Stack jest wyśrodkowany na ekranie z odstępami od krawędzi co najmniej 16pt. translatesAutoresizingMaskIntoConstraints = false jest obowiązkowy przy programowym tworzeniu constraints — bez niego Auto Layout nie działa. W IT Sectr UIStackView jest używany w 80% ekranów projektów iOS do budowy adaptacyjnych formularzy, list ustawień i kart.
UIStackView mogą być zagnieżdżone: poziomy stos wewnątrz pionowego — standardowy wzorzec dla złożonych układów. Zewnętrzny stos zarządza wierszami, wewnętrzny — kolumnami wewnątrz wiersza. Kombinacja axis, alignment i distribution na każdym poziomie daje praktycznie nieograniczoną elastyczność bez ani jednego ręcznego constraint. Apple zaleca używanie UIStackView jako głównego narzędzia układu w UIKit, sięgając po ręczne NSLayoutConstraint tylko w przypadkach nieobjętych stosem: nakładające się view, precyzyjne pozycjonowanie w pikselach, niestandardowa animacja bounds.
NSLayoutConstraint — programowe API do tworzenia pojedynczych ograniczeń w kodzie. Każde constraint jest tworzone przez inicjalizator z parametrami: item, attribute, relatedBy, toItem, attribute, multiplier, constant. Od iOS 9 Apple przedstawiło Anchor API — bardziej czytelną składnię przez właściwości view.leadingAnchor, view.trailingAnchor, view.topAnchor, view.bottomAnchor, view.centerXAnchor, view.centerYAnchor, view.widthAnchor, view.heightAnchor. Anchor API automatycznie ustawia relatedBy = .equal i używa First Item/Second Item z Anchor, skracając kod o 40% w porównaniu z klasycznym NSLayoutConstraint.
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)
])
}
}Kod pozycjonuje childView z odstępami od safeAreaLayoutGuide (top) i krawędzi ekranu (leading/trailing). lessThanOrEqualTo dla bottom gwarantuje, że view nie wyjdzie poza dolną granicę. Anchor API zgłasza wyjątek na etapie kompilacji, jeśli anchory są niekompatybilne (np. leadingAnchor miesza się z rightAnchor), co zapobiega błędom runtime. Apple zaleca Anchor API jako standard dla programowego Auto Layout od iOS 9.
Safe Area — obszar ekranu nieprzykrywany przez elementy systemowe: Dynamic Island, Notch, Status Bar, Home Indicator, zaokrąglone rogi. W iOS 11 Apple zastąpiło topLayoutGuide/bottomLayoutGuide na safeAreaLayoutGuide, który automatycznie dostosowuje się do orientacji urządzenia i obecności wycięcia ekranu. Layout Margins — wewnętrzne marginesy view domyślnie (16pt na iOS, 20pt na iPadOS). Dla UILayoutGuide można ustawić niestandardowe directionalLayoutMargins z uwzględnieniem lokalizacji RIGHT-TO-LEFT. Auto Layout automatycznie uwzględnia safe area przy użyciu safeAreaLayoutGuide w anchors.
Na urządzeniach z Dynamic Island (iPhone 14 Pro i nowsze) i Notch (iPhone X–13) Safe Area wyklucza 44pt od góry w orientacji pionowej (59pt z Dynamic Island w stanie aktywnym). Home Indicator dodaje 34pt od dołu. Dla prawidłowej adaptacji wszystkie top-constraints powinny być powiązane z safeAreaLayoutGuide.topAnchor, a nie z view.topAnchor. Bottom-constraints — z safeAreaLayoutGuide.bottomAnchor lub view.bottomAnchor z zapasem na Home Indicator. W IT Sectr testujemy wszystkie ekrany na symulatorach iPhone SE (2022), iPhone 14 Pro Max i iPad Pro 12,9″ — trzech urządzeniach pokrywających wszystkie warianty safe area.
Najczęstsze błędy przy pracy z Auto Layout: zapomniane translatesAutoresizingMaskIntoConstraints = false, konfliktujące Required constraints (priorytet 1000), Ambiguous Layout (niewystarczająca liczba constraints do określenia pozycji), nieprawidłowy Content Hugging Priority dla UILabel z wieloliniowym tekstem i mieszanie leading/trailing z left/right anchors. Xcode 15+ wyświetla problemy układu w Runtime Issue Navigator i oferuje automatyczne poprawki. W przypadku złożonych układów używaj Debug View Hierarchy: żółte znaczniki wskazują ambiguous layout, czerwone — unsatisfiable.
| Błąd | Przyczyna | Rozwiązanie |
|---|---|---|
| translatesAutoresizingMaskIntoConstraints = true | Auto Layout nie jest aktywowany dla view | Ustaw false dla wszystkich programowych view |
| Unsatisfiable Layout | Konflikt Required (1000) constraints | Obniż priorytet jednego do Default High (750) |
| Ambiguous Layout | Niewystarczająca liczba constraints dla x/y/w/h | Dodaj brakujące constraint lub sprawdź intrinsic size |
| Obcinanie tekstu w UILabel | Content Hugging Priority niższy niż u konkurenta | Podnieś hugging priority do 252+ |
| Mieszanie anchorów LTR/RTL | leadingAnchor z rightAnchor | Używaj tylko leading/trailing dla obsługi RTL |
Często zadawane pytania
Układ ramkowy określa stałe współrzędne x, y, width, height dla każdego elementu. Auto Layout używa matematycznych ograniczeń (constraints) — relacji między elementami: «label.leading = button.trailing + 8». Układ ramkowy nie dostosowuje się do rozmiaru ekranu; Auto Layout automatycznie przelicza pozycje przy obrocie, Split View lub zmianie Dynamic Type.
UIStackView jest optymalny dla układów liniowych: wiersze, kolumny, formularze, listy parametrów. NSLayoutConstraint jest niezbędny dla nakładających się view, precyzyjnego pozycjonowania pikseli, niestandardowej animacji bounds i przypadków, gdy rozkład miejsca jest nierównomierny i nie jest pokrywany przez distribution UIStackView. W praktyce 80% układów rozwiązuje UIStackView, 20% — ręczne constraints.
Content Hugging Priority (opór przed rozciąganiem) — priorytet określający, jak bardzo element opiera się zwiększeniu swojego rozmiaru powyżej Intrinsic Content Size. Wartość domyślna — 251. Jeśli dwa elementy konkurują o wolne miejsce, element z wyższym hugging priority pozostanie swojego rozmiaru, a drugi się rozciągnie. Compression Resistance Priority (749 domyślnie) działa analogicznie dla ściskania.
Auto Layout automatycznie dostosowuje się do Dynamic Type, jeśli constraints używają intrinsic content size etykiet. Przy zwiększeniu rozmiaru czcionki UILabel rozszerza się, przesuwając sąsiednie elementy przez constraints. UIStackView z distribution = fillProportionally redystrybuuje miejsce proporcjonalnie do nowych intrinsic size. Safe Area i Layout Margins również uwzględniają ustawienia dostępności.
Unsatisfiable Layout powstaje, gdy dwa Required (priority = 1000) constraints są ze sobą sprzeczne: na przykład view.leading = superview.leading + 16 i view.trailing = superview.leading + 200 przy superview o szerokości 100pt. Algorytm Cassowary nie może znaleźć rozwiązania, a aplikacja ulega awarii z NSConstraintException. Rozwiązanie — obniżyć priorytet jednego z konfliktujących constraints do Default High (750).
Podsumowanie
Opracujemy aplikację mobilną pod klucz
IT Sectr tworzy aplikacje na iOS i Androida dla startupów i firm od 2017 roku. Doradzimy Ci i zaproponujemy najlepsze rozwiązanie.
Przeczytaj również