Azimjon Abdurasulov

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

  1. Auto Layout arxitekturasi va Cassowary algoritmi
  2. IntrinsicContentSize: Element qachon o'z o'lchamini biladi?
  3. Content Hugging vs Content Compression Resistance
  4. Constraint Prioritylar va ziddiyatlarni hal qilish
  5. Ambiguous Layout va Unsatisfiable Constraints diagnostikasi
  6. Amaliy kod: Dinamik va xavfsiz Auto Layout arxitekturasi
  7. Xulosa
  8. 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:

  1. Siz kodda yoki Storyboard'da constraint'lar berasiz.
  2. Auto Layout Engine bu talablarni bitta katta matematik matritsaga joylaydi.
  3. Layout Pass boshlanganda, Cassowary algoritmi barcha tenglamalarni bir vaqtda qanoatlantiruvchi yagona yechimni (frame: x, y, width, height) topadi.
  4. Topilgan qiymatlar view'larning center va bounds xususiyatlariga 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 UIStackView va zamonaviy UICollectionViewCompositionalLayout orqali 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 turiIntrinsic WidthIntrinsic HeightIzoh
**UIView**noIntrinsicMetric (-1)noIntrinsicMetric (-1)Tabiiy o'lchamga ega emas, constraint shart
**UILabel**Matn eni bo'yichaMatn bo'yi bo'yichaFont va matn hajmiga bog'liq
**UIImageView**Rasmning piksel eniRasmning piksel bo'yiAgar rasm nil bo'lsa, o'lcham bo'lmaydi
**UITextField**noIntrinsicMetricStandart 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:

  1. nameLabel: "Alisher Navoiy"
  2. 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:

QiymatNomiIzoh
1000.requiredMajburiy constraint. Agar bajarilmasa, Auto Layout ziddiyat (crash/conflict) beradi
750.defaultHighMuhim constraint, lekin kerak bo'lganda buzilishi mumkin
250.defaultLowYengil talab, odatda bo'sh joyni taqsimlashda ishlatiladi
50CustomEng 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).
  • intrinsicContentSize komponentning 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 holatlarda 999 yoki 750 priority ishlatish xavfsizdir.

8. Manbalar {#manbalar}

  1. Understanding Auto Layout — Apple Developer
  2. Cassowary Linear Arithmetic Constraint Solving Algorithm — Greg J. Badros
  3. Anatomy of a Constraint — Apple Documentation
  4. Auto Layout Guide: Intrinsic Content Size — Apple

Markdown manba

iOS Lessons