UIKit 10: Auto Layout Engine — Cassowary Algoritmi, IntrinsicContentSize va Prioritylar
📱 UIKit va SwiftUI — chuqur
UIKit 10: Auto Layout Engine — Cassowary Algoritmi, IntrinsicContentSize va Prioritylar
Holat: 2026-yil sentabr. Ko'plab iOS dasturchilar Auto Layout'ni faqat "NSLayoutConstraint qo'shish" deb bilishadi. Ammo ekranda o'nlab murakkab view'lar paydo bo'lganda, nega interfeys qotishi (CPU hang), matnlar nima sababdan uchta nuqta (
...) bo'lib qisqarishi yoki keraksiz cho'zilib ketishi Auto Layout Engine'ning ichki matematik mexanikasiga borib taqaladi. Ushbu darsda Cassowary algoritmi, IntrinsicContentSize, Content Hugging va Compression Resistance, hamda Ambiguity xatolarini xirurgdek aniqlash chuqur tahlil qilinadi.
Mundarija
- Auto Layout arxitekturasi va Cassowary algoritmi
- IntrinsicContentSize: Element qachon o'z o'lchamini biladi?
- Content Hugging vs Content Compression Resistance
- Constraint Prioritylar va ziddiyatlarni hal qilish
- Ambiguous Layout va Unsatisfiable Constraints diagnostikasi
- Amaliy kod: Dinamik va xavfsiz Auto Layout arxitekturasi
- Xulosa
- Manbalar
1. Auto Layout arxitekturasi va Cassowary algoritmi {#cassowary}
Auto Layout — bu qutichalar o'lchamini shunchaki hisoblovchi oddiy kalkulyator emas. U chiziqli tenglamalar va tengsizliklar tizimini yechuvchi (Linear Constraint Solver) matematik mexanizmdir.
Uning yuragida Cassowary algoritmi yotadi. Har bir yaratilgan constraint quyidagi matematik tenglamaga aylanadi:
view1.attribute = view2.attribute * multiplier + constant
Masalan:
button.leading = container.leading * 1.0 + 16.0
Auto Layout qanday ishlaydi:
- Siz kodda yoki Storyboard'da constraint'lar berasiz.
- Auto Layout Engine bu talablarni bitta katta matematik matritsaga joylaydi.
- Layout Pass boshlanganda, Cassowary algoritmi barcha tenglamalarni bir vaqtda qanoatlantiruvchi yagona yechimni (
frame: x, y, width, height) topadi. - Topilgan qiymatlar view'larning
centervaboundsxususiyatlariga beriladi.
[ Developer Constraints ]
│
▼
+─────────────────────────────────+
| Auto Layout Engine |
| (Cassowary Linear Solver) |
| Tenglamalar tizimini yechadi |
+─────────────────────────────────+
│
▼
[ UIView.bounds & UIView.center ]
│
▼
[ CALayer.frame (GPU chizishi) ]
Senior eslatmasi: Auto Layout murakkabligi eksponensial oshishi mumkin: bitta ekranda o'ta ko'p subview va bir-biriga bog'langan murakkab constraint'lar bo'lsa, Cassowary yechim izlashi ko'proq CPU vaqtini oladi. Shuning uchun
UIStackViewva zamonaviyUICollectionViewCompositionalLayoutorqali ierarxiyani tekislash tavsiya etiladi.
2. IntrinsicContentSize: Element qachon o'z o'lchamini biladi? {#intrinsic}
Ko'pgina view'larga (UIView, UIScrollView) eni va bo'yi uchun alohida width va height constraint berish shart. Aks holda Auto Layout ularning o'lchamini aniqlay olmaydi.
Ammo UILabel, UIButton, UIImageView kabi elementlarga width va height bermasangiz ham, ular ekranda to'g'ri chiziladi. Sababi — ularda **intrinsicContentSize** mavjud!
IntrinsicContentSize nima?
intrinsicContentSize — bu view'ning o'z ichidagi kontenti (matn uzunligi, font o'lchami yoki rasm hajmi) asosida tizimga: "Menga o'zimni to'liq ko'rsatishim uchun mana shuncha tabiiy o'lcham kerak!" deb xabar beruvchi xususiyatidir.
| View turi | Intrinsic Width | Intrinsic Height | Izoh |
|---|---|---|---|
**UIView** | noIntrinsicMetric (-1) | noIntrinsicMetric (-1) | Tabiiy o'lchamga ega emas, constraint shart |
**UILabel** | Matn eni bo'yicha | Matn bo'yi bo'yicha | Font va matn hajmiga bog'liq |
**UIImageView** | Rasmning piksel eni | Rasmning piksel bo'yi | Agar rasm nil bo'lsa, o'lcham bo'lmaydi |
**UITextField** | noIntrinsicMetric | Standart balandlik (odatda ~34 pt) | Eni cheksiz bo'lishi mumkin |
invalidateIntrinsicContentSize() qachon chaqiriladi?
Agar siz maxsus (custom) view yaratgan bo'lsangiz va uning ichidagi ma'lumot o'zgarsa, tizimga buni darhol xabar qilishingiz kerak:
import UIKit
final class BadgeView: UIView {
var count: Int = 0 {
didSet {
// Kontent o'zgardi, Auto Layout tabiiy o'lchamni qayta hisoblasin:
invalidateIntrinsicContentSize()
}
}
override var intrinsicContentSize: CGSize {
let textWidth = CGFloat(count > 99 ? 40 : 28)
return CGSize(width: textWidth, height: 20)
}
}
3. Content Hugging vs Content Compression Resistance {#hugging-compression}
Bu Senior intervyularda eng ko'p beriladigan va dasturchilarni eng ko'p chalg'itadigan mavzu!
Tasavvur qiling: Ekranda bitta gorizontal UIStackView ichida ikkita UILabel yonma-yon turibdi:
nameLabel: "Alisher Navoiy"roleLabel: "Developer"
Ekran kengligi 393 pt. Ikkala label'ning o'z tabiiy o'lchami bor, lekin ular butun qatorni to'ldirishi kerak. Ulardan qaysi biri cho'zilishi kerak va qaysi biri qisqarishi kerak?
Buni boshqarish uchun Apple ikkita tushunchani joriy qilgan:
[ KONTENT ]
<--- (Cho'zilishga qarshilik) --->
CONTENT HUGGING PRIORITY
(Standart: 250)
---> (Qisqarishga qarshilik) <---
CONTENT COMPRESSION RESISTANCE PRIORITY
(Standart: 750)
1. Content Hugging Priority (O'z kontentini quchoqlab turish / Cho'zilishga qarshilik):
- Falsafasi: "Meni keragidan ortiq cho'zib yuborma!"
- Qoida: Content Hugging qiymati KATTAROQ bo'lgan view o'zining tabiiy o'lchamini saqlab qoladi. Content Hugging qiymati KICHIKROQ bo'lgan view esa bo'sh joyni to'ldirish uchun CHO'ZILADI.
- Standart qiymat: 250 (Low).
2. Content Compression Resistance Priority (Siqilishga qarshilik):
- Falsafasi: "Mening kontentimni qisqartirib, uchta nuqta (...) qilib qo'yma!"
- Qoida: Compression Resistance qiymati KATTAROQ bo'lgan view siqilishdan himoyalanadi. Compression Resistance qiymati KICHIKROQ bo'lgan view esa joy yetmaganda SIQILADI (TRUNCATE BO'LADI).
- Standart qiymat: 750 (High).
Vizual Misol:
import UIKit
@MainActor
func configureContentPriorities(nameLabel: UILabel, roleLabel: UILabel) {
// nameLabel kengayishi kerak, roleLabel esa o'z holicha ixcham turishi kerak:
nameLabel.setContentHuggingPriority(.defaultLow, for: .horizontal) // 250 — cho'ziladi
roleLabel.setContentHuggingPriority(.defaultHigh, for: .horizontal) // 750 — cho'zilmaydi
// Joy kam bo'lib qolsa, roleLabel to'liq ko'rinsin, nameLabel esa qisqarsin:
roleLabel.setContentCompressionResistancePriority(.required, for: .horizontal) // 1000 — aslo qisqarmaydi
nameLabel.setContentCompressionResistancePriority(.defaultLow, for: .horizontal) // 250 — agar sig'masa qisqaradi
}
4. Constraint Prioritylar va ziddiyatlarni hal qilish {#priorities}
Har bir constraint 1 dan 1000 gacha bo'lgan ustuvorlik darajasiga (UILayoutPriority) ega:
| Qiymat | Nomi | Izoh |
|---|---|---|
| 1000 | .required | Majburiy constraint. Agar bajarilmasa, Auto Layout ziddiyat (crash/conflict) beradi |
| 750 | .defaultHigh | Muhim constraint, lekin kerak bo'lganda buzilishi mumkin |
| 250 | .defaultLow | Yengil talab, odatda bo'sh joyni taqsimlashda ishlatiladi |
| 50 | Custom | Eng past ustuvorlik |
Dinamik animatsiyalarda Priority bilan ishlash:
Ko'pincha view'ni ko'rsatish va yashirishda constraint'ni o'chirish (deactivate) o'rniga, uning priority'sini o'zgartirish orqali mukammal animatsiya qilinadi:
import UIKit
@MainActor
func configureCollapsibleView(customView: UIView) {
// Balandlikni 0 ga tushirish uchun ikkita constraint qo'yiladi:
let expandedConstraint = customView.heightAnchor.constraint(equalToConstant: 200)
expandedConstraint.priority = UILayoutPriority(999) // 1000 emas!
let collapsedConstraint = customView.heightAnchor.constraint(equalToConstant: 0)
collapsedConstraint.priority = UILayoutPriority(998)
}
5. Ambiguous Layout va Unsatisfiable Constraints diagnostikasi {#ambiguity}
Auto Layout'da ikkita asosiy xatolik yuz beradi:
1. Unsatisfiable Constraints (Qanoatlantirib bo'lmaydigan ziddiyat):
Cassowary matematik jihatdan bir-birini inkor qiluvchi ikkita majburiy (priority 1000) tenglamaga duch kelsa yuz beradi. Misol: View.width == 100 va View.width == 200 (Ikkalasi ham priority 1000). Tizim konsolga ulkan xatolik xabarini chiqaradi va constraint'lardan birini majburan uzib yuboradi:
Unable to simultaneously satisfy constraints.
Probably at least one of the constraints in the following list is one you don't want...
Davosi: Constraint'lardan birining priority'sini 999 ga tushirish.
2. Ambiguous Layout (Noaniq joylashuv):
Constraint'lar yetarli emas. Masalan, view'ning x va y nuqtasi bor, lekin width yoki height yo'q. Natijada Auto Layout elementni qayerga qo'yishni bilmaydi va turli qurilmalarda turli joyga sakrab qoladi.
Debug paytida buni aniqlash usuli:
#if DEBUG
print(customView.hasAmbiguousLayout) // true bo'lsa, demak constraint yetarli emas!
// LLDB konsolida butun view daraxtini tekshirish:
// po [UIWindow keyWindow] _autolayoutTrace
#endif
6. Amaliy kod: Dinamik va xavfsiz Auto Layout arxitekturasi {#example}
Quyida iOS 16–26 uchun Swift 6 qoidalariga to'liq mos, Content Hugging va Compression Resistance to'g'ri sozlangan profil kartasi keltirilgan:
import UIKit
public final class ProfileHeaderCard: UIView {
private let avatarImageView: UIImageView = {
let iv = UIImageView()
iv.translatesAutoresizingMaskIntoConstraints = false
iv.contentMode = .scaleAspectFill
iv.layer.cornerRadius = 24
iv.clipsToBounds = true
iv.backgroundColor = .systemGray5
return iv
}()
private let titleLabel: UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
label.font = .systemFont(ofSize: 16, weight: .bold)
label.textColor = .label
return label
}()
private let badgeLabel: UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
label.font = .systemFont(ofSize: 12, weight: .semibold)
label.textColor = .systemBlue
label.backgroundColor = .systemBlue.withAlphaComponent(0.12)
label.layer.cornerRadius = 6
label.clipsToBounds = true
return label
}()
public override init(frame: CGRect) {
super.init(frame: frame)
setupViews()
}
public required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
private func setupViews() {
addSubview(avatarImageView)
addSubview(titleLabel)
addSubview(badgeLabel)
// 1. Content Hugging & Compression Resistance strategiyasi:
// badgeLabel qisqarmasligi va cho'zilmasligi kerak:
badgeLabel.setContentHuggingPriority(.defaultHigh, for: .horizontal) // 750
badgeLabel.setContentCompressionResistancePriority(.required, for: .horizontal) // 1000
// titleLabel bo'sh joyni to'ldirib cho'zilishi mumkin, tor bo'lsa qisqaradi:
titleLabel.setContentHuggingPriority(.defaultLow, for: .horizontal) // 250
titleLabel.setContentCompressionResistancePriority(.defaultLow, for: .horizontal) // 250
NSLayoutConstraint.activate([
// Avatar joylashuvi
avatarImageView.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 16),
avatarImageView.centerYAnchor.constraint(equalTo: centerYAnchor),
avatarImageView.widthAnchor.constraint(equalToConstant: 48),
avatarImageView.heightAnchor.constraint(equalToConstant: 48),
// Title Label joylashuvi
titleLabel.leadingAnchor.constraint(equalTo: avatarImageView.trailingAnchor, constant: 12),
titleLabel.centerYAnchor.constraint(equalTo: centerYAnchor),
// Badge Label joylashuvi
badgeLabel.leadingAnchor.constraint(equalTo: titleLabel.trailingAnchor, constant: 8),
badgeLabel.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -16),
badgeLabel.centerYAnchor.constraint(equalTo: centerYAnchor),
// Kartaning minimal balandligi
heightAnchor.constraint(greaterThanOrEqualToConstant: 72)
])
}
public func configure(name: String, badge: String) {
titleLabel.text = name
badgeLabel.text = " \(badge) "
}
}
7. Xulosa {#xulosa}
- Auto Layout — bu Cassowary chiziqli tenglamalar tizimi asosida ishlovchi matematik yechuvchi (Solver).
intrinsicContentSizekomponentning o'z ichki kontentidan kelib chiqadigan tabiiy o'lchamidir (UILabel,UIButton).- Content Hugging (Standart 250) — view'ning keraksiz cho'zilishiga qarshilik ko'rsatadi. Yuqori qiymatli view o'z o'lchamida qoladi, past qiymatli view esa bo'shliqni to'ldirib cho'ziladi.
- Content Compression Resistance (Standart 750) — view'ning siqilib, matni qisqarishiga qarshilik qiladi. Yuqori qiymatli view siqilmaydi.
- Required priority (
1000) ziddiyatga uchrasa crash/warning beradi; dinamik holatlarda999yoki750priority ishlatish xavfsizdir.