Auto Layout — систем адаптивног позиционирања елемената интерфејса од Apple-а, заснован на математичким ограничењима (constraints). Развијен за iOS 6 (2012), Auto Layout омогућава креирање интерфејса који се коректно приказују на свим уређајима — од iPhone SE (4.7″) до iPad Pro (12.9″) и Dynamic Island. Према Apple WWDC Session 202 (2024), више од 90% апликација у App Store користи Auto Layout или његову декларативну алтернативу — SwiftUI layout system. Constraints описују зависности између UI елемената кроз линеарне једначине: view1.leading = view2.trailing + 8.
Главно
Auto Layout — је систем адаптивног распореда Apple-а, који користи математичка ограничења (constraints) за позиционирање UI елемената. За разлику од оквирног распореда (frame-based layout), где сваки елемент има фиксне координате x, y, width, height, Auto Layout описује односе између елемената: «дугме се налази 8pt од десне ивице родитеља» или «ширина текстуалног поља је једнака половини ширине екрана». Механизам се заснива на Cassowary алгоритму, развијеном на University of Washington (Greg J. Badros, 1999) и имплементираном од стране Apple-а у iOS 6. Cassowary решава систем линеарних неједнакости са приоритетима — Required (1000), Default High (750), Default Low (250) — што омогућава управљање конфликтима ограничења. Auto Layout подржава три типа величина: intrinsic (природна величина елемента одређена садржајем), explicit (експлицитно задато constraint) и compressible/stretchable (еластични режим кроз Content Hugging Priority и Compression Resistance Priority).
Сваки UI елемент у Auto Layout-у има Intrinsic Content Size — природну величину одређену својим садржајем. За UILabel то зависи од текста и фонта, за UIImageView — од димензија слике. Content Hugging Priority (отпор истезању) и Compression Resistance Priority (отпор сабијању) управљају понашањем елемента при промени расположивог простора. Стандардне вредности: 251 за hugging и 749 за compression resistance. Ако се два елемента такмиче за простор, приоритет одређује који ће се први растегнути. Разумевање ових приоритета је кључ за уклањање Ambiguous Layout (нејасног распореда), који Xcode истиче у дебагеру.
Constraint се описује једначином: view1.attribute = multiplier × view2.attribute + constant. Атрибути укључују leading, trailing, top, bottom, centerX, centerY, width, height, firstBaseline, lastBaseline. Multiplier се користи за пропорционалне односе (ширина view1 = 0.5 × ширина superview). Constant задаје фиксни размак (leading = superview.leading + 16). Алати Interface Builder-а омогућавају визуелно задавање constraints кроз Ctrl-превлачење, али сложени распореди захтевају програмско креирање кроз NSLayoutConstraint или VFL (Visual Format Language), који Apple препоручује да се замени са NSLayoutConstraint од iOS 9.
Систем constraints се решава као задатак линеарног програмирања: Cassowary алгоритам проналази оптимални положај свих елемената, задовољавајући сва ограничења узимајући у обзир њихове приоритете. Ако су constraints у међусобној супротности, настаје Unsatisfiable Layout — изузетак који Xcode логира са детаљним описом конфликта. Ако constraints нису довољни за одређивање положаја макар једног елемента, настаје Ambiguous Layout — елементи се приказују на произвољном положају. Apple препоручује минимални скуп: за сваки елемент морају бити одређени position (x, y) и size (width, height) — експлицитно или кроз intrinsic content size. Constraints могу бити first-class: водећи елемент (нпр. superview) и зависни (приказујући view) стварају хијерархију.
Cassowary користи метод Sequential Quadratic Programming за решавање система линеарних неједнакости. Сваки constraint има приоритет од 1 до 1000. Required (1000) — обавезно ограничење; ако се не може испунити, апликација пада са NSConstraintException. Default High (750) — препоручујуће; Default Low (250) — најмање важно. При конфликту Cassowary опушта ограничења са нижим приоритетом. На пример, ако два елемента захтевају фиксну ширину, а екран је преузак, опушта се constraint са нижим приоритетом. У Xcode Debug View Hierarchy (алат за отклањање грешака доступан од Xcode 6) истиче само проблеме са Required constraints — остали се обрађују без грешке.
UIStackView — контејнер представљен у iOS 9 (2015), који аутоматски креира и управља constraints за угњеждене arrangedSubviews. UIStackView подржава две осе: horizontal (хоризонтални распоред) и vertical (вертикални). Подешавања distribution одређују расподелу простора: fill (пуњење пропорционално hugging priority), fillEqually (једнаке величине), fillProportionally (пропорционално intrinsic content size), equalSpacing (једнаки размаци), equalCentering (једнаке удаљености између центара). Alignment задаје поравнање попречно на осу: fill, leading, center, trailing (за horizontal) или fill, top, center, bottom (за vertical). UIStackView аутоматски управља spacing, baseline alignment и адаптацијом на 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 водич"
label.font = UIFont.preferredFont(forTextStyle: .headline)
let button = UIButton(type: .system)
button.setTitle("Примени", 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)
])
}
}Код креира вертикални UIStackView са два елемента (UILabel и UIButton), равномерно распоређена (fillEqually) са размаком од 8pt. Stack је центриран на екрану са маргинама од ивица од најмање 16pt. translatesAutoresizingMaskIntoConstraints = false је обавезан при програмском креирању constraints — без њега Auto Layout не ради. У IT Sectr-у, UIStackView се користи у 80% екрана iOS пројеката за изградњу адаптивних форми, листа подешавања и картица.
UIStackView-ови могу бити угњеждени: хоризонтални стек унутар вертикалног — стандардни образац за сложене распореде. Спољашњи стек управља редовима, унутрашњи — колонама унутар реда. Комбинација axis, alignment и distribution на сваком нивоу даје практично неограничену флексибилност без иједног ручног constraint-а. Apple препоручује коришћење UIStackView-а као главног алата за распоред у UIKit-у, прибегавајући ручним NSLayoutConstraint само за случајеве које стек не покрива: преклапајући view, прецизно позиционирање по пикселима, прилагођена анимација bounds.
NSLayoutConstraint — програмски API за креирање појединачних ограничења у коду. Сваки constraint се креира кроз иницијализатор са параметрима: item, attribute, relatedBy, toItem, attribute, multiplier, constant. Од iOS 9 Apple је представио Anchor API — читљивију синтаксу кроз својства view.leadingAnchor, view.trailingAnchor, view.topAnchor, view.bottomAnchor, view.centerXAnchor, view.centerYAnchor, view.widthAnchor, view.heightAnchor. Anchor API аутоматски поставља relatedBy = .equal и користи First Item/Second Item из Anchor-а, смањујући код за 40% у поређењу са класичним 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)
])
}
}Код позиционира childView са маргинама од safeAreaLayoutGuide (top) и ивица екрана (leading/trailing). lessThanOrEqualTo за bottom гарантује да view неће изаћи изван доње границе. Anchor API баца изузетак у фази компилације ако су anchor-и некомпатибилни (нпр. leadingAnchor се меша са rightAnchor), што спречава runtime грешке. Apple препоручује Anchor API као стандард за програмски Auto Layout од iOS 9.
Safe Area — област екрана коју не прекривају системски елементи: Dynamic Island, Notch, Status Bar, Home Indicator, заобљени углови. У iOS 11 Apple је заменио topLayoutGuide/bottomLayoutGuide са safeAreaLayoutGuide, који се аутоматски прилагођава оријентацији уређаја и присуству изреза екрана. Layout Margins — унутрашње маргине view-а подразумевано (16pt на iOS, 20pt на iPadOS). За UILayoutGuide се могу подесити прилагођени directionalLayoutMargins узимајући у обзир RIGHT-TO-LEFT локализацију. Auto Layout аутоматски узима у обзир safe area при коришћењу safeAreaLayoutGuide у anchor-има.
На уређајима са Dynamic Island (iPhone 14 Pro и новији) и Notch (iPhone X–13) Safe Area искључује 44pt одозго у портрету (59pt са Dynamic Island у активном стању). Home Indicator додаје 34pt одоздо. За коректну адаптацију, сви top-constraints морају бити везани за safeAreaLayoutGuide.topAnchor, а не за view.topAnchor. Bottom-constraints — за safeAreaLayoutGuide.bottomAnchor или view.bottomAnchor са резервом за Home Indicator. У IT Sectr-у тестирамо све екране на симулаторима iPhone SE (2022), iPhone 14 Pro Max и iPad Pro 12.9″ — три уређаја која покривају све варијанте safe area.
Најчешће грешке при раду са Auto Layout-ом: заборављено translatesAutoresizingMaskIntoConstraints = false, конфликтни Required constraints (приоритет 1000), Ambiguous Layout (недовољно constraints за одређивање положаја), нетачан Content Hugging Priority за UILabel са вишелинијским текстом и мешање leading/trailing са left/right anchors. Xcode 15+ приказује проблеме распореда у Runtime Issue Navigator-у и нуди аутоматске исправке. За сложене распореде користите Debug View Hierarchy: жуте ознаке указују на ambiguous layout, црвене — на unsatisfiable.
| Грешка | Узрок | Решење |
|---|---|---|
| translatesAutoresizingMaskIntoConstraints = true | Auto Layout није активиран за view | Поставите false за све програмске view |
| Unsatisfiable Layout | Конфликт Required (1000) constraints | Смањите приоритет једног на Default High (750) |
| Ambiguous Layout | Недовољно constraints за x/y/w/h | Додајте недостајући constraint или проверите intrinsic size |
| Скраћивање текста у UILabel | Content Hugging Priority нижи од конкурента | Повећајте hugging priority на 252+ |
| Мешање LTR/RTL anchor-а | leadingAnchor са rightAnchor | Користите само leading/trailing за подршку RTL |
Често постављана питања
Оквирни распоред поставља фиксне координате x, y, width, height за сваки елемент. Auto Layout користи математичка ограничења (constraints) — односе између елемената: «label.leading = button.trailing + 8». Оквирни распоред се не прилагођава величини екрана; Auto Layout аутоматски прерачунава позиције при ротацији, Split View или промени Dynamic Type.
UIStackView је оптималан за линеарне распореде: редови, колоне, форме, листе параметара. NSLayoutConstraint је неопходан за преклапајуће view, прецизно позиционирање по пикселима, прилагођену анимацију bounds и случајеве када је расподела простора неравномерна и није покривена distribution UIStackView-а. У пракси 80% распореда се решава UIStackView-ом, 20% — ручним constraints.
Content Hugging Priority (отпор истезању) — приоритет који одређује колико се елемент опире повећању своје величине изнад Intrinsic Content Size. Подразумевана вредност — 251. Ако се два елемента такмиче за слободан простор, елемент са вишим hugging priority остаје своје величине, а други се растеже. Compression Resistance Priority (подразумевано 749) ради аналогно за сабијање.
Auto Layout се аутоматски прилагођава Dynamic Type ако constraints користе intrinsic content size ознака. При повећању величине фонта, UILabel се шири, померајући суседне елементе кроз constraints. UIStackView са distribution = fillProportionally прерасподељује простор пропорционално новим intrinsic size. Safe Area и Layout Margins такође узимају у обзир accessibility подешавања.
Unsatisfiable Layout настаје када су два Required (priority = 1000) constraints у међусобној супротности: на пример, view.leading = superview.leading + 16 и view.trailing = superview.leading + 200 при superview ширине 100pt. Cassowary алгоритам не може да пронађе решење и апликација пада са NSConstraintException. Решење — смањите приоритет једног од конфликтних constraints на Default High (750).
Закључак
Развићемо мобилну апликацију под кључ
IT Sectr креира iOS и Android апликације за стартапе и предузећа од 2017. године. Саветоваћемо вас и предложити најбоље решење.
Прочитајте такође