Azimjon Abdurasulov

SnapKit 5: Priority Tizimi va Content Hugging — Qaysi view cho'ziladi, qaysi siqiladi?

🧩 SnapKit — Zero to Hero

SnapKit 5: Priority Tizimi va Content Hugging — Qaysi view cho'ziladi, qaysi siqiladi?

Holat: 2026-yil oktabr holatiga ko'ra, murakkab va o'zgaruvchan interfeyslarda (masalan, chat ekranlari, dinamik ro'yxatlar) constraint'lar ziddiyatini (conflict) oldini olish uchun Priority, Hugging va Compression Resistance tushunchalarini mukammal bilish shart.

Mundarija

  1. SnapKit Priority API
  2. Nima uchun 999 priority eng muhim?
  3. Content Hugging Priority (CHP)
  4. Content Compression Resistance (CCRP)
  5. Vizual diagrammalar
  6. Amaliy loyiha: Telegram uslubidagi Chat Row
  7. Amaliy loyiha 2: Price Tag (Narx belgisi)
  8. Xulosa
  9. Manbalar

1. SnapKit Priority API {#snapkit-priority-api}

Auto Layout tizimi har bir constraint uchun ma'lum bir muhimlik darajasini (priority) talab qiladi. Bir nechta ziddiyatli (conflict) constraint'lar yuzaga kelsa, tizim doim priority'si yuqori bo'lganini bajaradi.

SnapKit'da priority o'rnatish juda oson. Buning uchun constraint oxirida .priority() funksiyasidan foydalanamiz.

PrioritySon qiymatiSnapKit APIIzoh
Required1000.priority(.required)Majburiy constraint. Buzilsa crash yoki runtime warning beradi.
High750.priority(.high)Yuqori ustuvorlik, ko'pincha UI elementlarning tabiiy o'lchamini saqlash uchun.
Medium500.priority(.medium)O'rta ustuvorlik.
Low250.priority(.low)Eng past ustuvorlik. Qolgan hamma narsa bajarilgandan keyin ishlaydi.
Custom999 (va h.k).priority(999)Aynan o'zimiz kiritgan raqam.

Misol:

import UIKit
import SnapKit

let boxView = UIView()

func setupConstraints() {
    boxView.snp.makeConstraints { make in
        make.top.equalToSuperview().offset(20)
        make.width.equalTo(200).priority(.high) // 750
        make.height.equalTo(150).priority(250)  // custom yoki .low
    }
}

2. Nima uchun 999 priority eng muhim? {#999-priority}

Senior eslatmasi: Agar siz dinamik o'zgaradigan yoki animatsiya qilinadigan constraint yozsangiz, unga doim 1000 (.required) o'rniga 999 priority bering!

Sababi:

  • .required (1000) constraint'lar hech qachon buzilishi mumkin emas. Agar Auto Layout hisoblashda biroz xatoga yo'l qo'ysa yoki o'tish jarayonida (transition) 1-2 piksellik ziddiyat chiqsa, xunuk Xcode konsol warning'lari ("Unable to simultaneously satisfy constraints") ko'rasiz.
  • 999 priority eslatadi: bu constraint deyarli required, lekin agar biron kutilmagan vaziyat bo'lsa (masalan keyboard chiqib ekran qisilganda), tizim shunchaki bu constraint'ni e'tiborsiz qoldiradi va Crash bo'lmaydi, Warning ham chiqmaydi.
make.bottom.equalTo(view.safeAreaLayoutGuide.snp.bottom).priority(999)

3. Content Hugging Priority (CHP) — Cho'zilishga qarshilik {#chp}

Content Hugging Priority (CHP) — bu View'ning o'z "tabiiy o'lchami"dan (intrinsic content size) kattalashib, cho'zilib ketishiga qarshilik kuchi.

  • Yuqori CHP: View o'z o'lchamini mahkam ushlab turadi, cho'zilMAYDI.
  • Past CHP: View bo'sh joyni to'ldirish uchun cho'ziLADI.

Tasavvur qiling, 2 ta Label yonma-yon joylashgan va butun ekranni to'ldirishi kerak. Qaysi biri bo'sh joyni egallaydi? CHP'si past bo'lgani!

let leftLabel = UILabel()
let rightLabel = UILabel()

func setupConstraints() {
    // SnapKit usulida CHP o'rnatish
    leftLabel.setContentHuggingPriority(.defaultHigh, for: .horizontal) 
    // yoki
    // leftLabel.snp.contentHuggingHorizontalPriority = 750 (bu kamroq ishlatiladi)
    
    rightLabel.setContentHuggingPriority(.defaultLow, for: .horizontal)
    
    // leftLabel qisqa bo'lib qoladi, rightLabel esa qolgan joyni to'ldirib cho'ziladi.
}

4. Content Compression Resistance Priority (CCRP) — Siqilishga qarshilik {#ccrp}

Content Compression Resistance Priority (CCRP) — bu View'ning o'z "tabiiy o'lchami"dan kichiklashib, siqilib qolishiga qarshilik kuchi.

  • Yuqori CCRP: View siqilMAYDI, uning matni to'liq ko'rinadi.
  • Past CCRP: View siqiLADI, masalan UILabel matni oxiriga ... tushib qisqaradi.

Agar joy yetishmasa, tizim qaysi view'ni ezib qo'yishini CCRP orqali hal qiladi.

let titleLabel = UILabel()
let timeLabel = UILabel()

func setupConstraints() {
    // timeLabel'ni hech qachon siqma, u doim to'liq (masalan "14:25") ko'rinishi shart!
    timeLabel.setContentCompressionResistancePriority(.required, for: .horizontal)
    
    // titleLabel'ni esa joy yetmasa qisqartirib yuborsa bo'ladi (masalan "Assalomu ala...")
    titleLabel.setContentCompressionResistancePriority(.defaultLow, for: .horizontal)
}

5. Vizual diagrammalar {#diagrammalar}

[   CHO'ZILISH (Hugging) JARAYONI   ]

Bo'sh joy ko'p. Qaysi biri cho'ziladi?
|-[ Name (CHP: 750) ]-[ Message (CHP: 250) ]-----------------------|
Natija:
|-[ Name ]-[ Message                                              ]|
Sabab: Message'ning Hugging priority'si past, shuning uchun u bo'sh joyni o'rab oladi.


[   SIQILISH (Compression Resistance) JARAYONI   ]

Joy yetishmayapti. Qaysi biri eziladi?
|-[ Long Title Text (CCRP: 250) ]-[ 14:30 (CCRP: 1000) ]|
Natija:
|-[ Long Title T... ]-[ 14:30 ]|
Sabab: Time label'ning Resistance'i (1000), uni siqib bo'lmaydi. Shuning uchun Title qisqaradi.

6. Amaliy loyiha: Telegram uslubidagi Chat Row {#telegram-chat}

Telegram chatlar ro'yxatini ko'z oldingizga keltiring:

  • Avatar doim 50x50.
  • Vaqt (14:25) va Unread badge doim o'ng tomonda va aslo ezilmaydi (High CCRP).
  • Ism va Oxirgi xabar esa qolgan joyni egallaydi.
import UIKit
import SnapKit

class TelegramChatCell: UITableViewCell {
    
    let avatarImageView = UIImageView()
    let nameLabel = UILabel()
    let messageLabel = UILabel()
    let timeLabel = UILabel()
    let unreadBadge = UILabel()
    
    override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
        super.init(style: style, reuseIdentifier: reuseIdentifier)
        setupSubviews()
        setupConstraints()
    }
    
    required init?(coder: NSCoder) { fatalError() }
    
    @MainActor
    private func setupSubviews() {
        [avatarImageView, nameLabel, messageLabel, timeLabel, unreadBadge].forEach {
            contentView.addSubview($0)
        }
        
        nameLabel.font = .boldSystemFont(ofSize: 16)
        messageLabel.textColor = .gray
        timeLabel.textColor = .lightGray
        timeLabel.font = .systemFont(ofSize: 12)
        
        // --- Priority Sozlamalari ---
        
        // Time label va Badge aslo siqilmasligi kerak
        timeLabel.setContentCompressionResistancePriority(.required, for: .horizontal)
        unreadBadge.setContentCompressionResistancePriority(.required, for: .horizontal)
        
        // Name label siqilishiga qattiqroq qarshilik qiladi, xabarga nisbatan
        nameLabel.setContentCompressionResistancePriority(.defaultHigh, for: .horizontal)
        messageLabel.setContentCompressionResistancePriority(.defaultLow, for: .horizontal)
    }
    
    @MainActor
    private func setupConstraints() {
        avatarImageView.snp.makeConstraints { make in
            make.left.equalToSuperview().offset(16)
            make.centerY.equalToSuperview()
            make.size.equalTo(50) // Qat'iy o'lcham
        }
        
        timeLabel.snp.makeConstraints { make in
            make.top.equalToSuperview().offset(12)
            make.right.equalToSuperview().inset(16)
            // Kenglik berilmaydi, o'z contentiga qarab o'sadi, siqilmaydi
        }
        
        nameLabel.snp.makeConstraints { make in
            make.top.equalToSuperview().offset(12)
            make.left.equalTo(avatarImageView.snp.right).offset(12)
            make.right.lessThanOrEqualTo(timeLabel.snp.left).offset(-8)
        }
        
        unreadBadge.snp.makeConstraints { make in
            make.bottom.equalToSuperview().inset(12)
            make.right.equalToSuperview().inset(16)
            make.height.equalTo(20)
            make.width.greaterThanOrEqualTo(20)
        }
        
        messageLabel.snp.makeConstraints { make in
            make.bottom.equalToSuperview().inset(12)
            make.left.equalTo(avatarImageView.snp.right).offset(12)
            make.right.equalTo(unreadBadge.snp.left).offset(-8)
        }
    }
}

7. Amaliy loyiha 2: Price Tag (Narx belgisi) {#price-tag}

Mahsulot narxi kartochkasida ko'pincha "Asl narx" (ustidan chizilgan), "Chegirmali narx" va "Foiz" yonma-yon turadi.

import UIKit
import SnapKit

class PriceTagView: UIView {
    let discountPriceLabel = UILabel()
    let originalPriceLabel = UILabel()
    let percentBadge = UILabel()
    
    override init(frame: CGRect) {
        super.init(frame: frame)
        setupSubviews()
        setupConstraints()
    }
    
    required init?(coder: NSCoder) { fatalError() }
    
    @MainActor
    private func setupSubviews() {
        addSubview(discountPriceLabel)
        addSubview(originalPriceLabel)
        addSubview(percentBadge)
        
        discountPriceLabel.font = .boldSystemFont(ofSize: 24)
        
        originalPriceLabel.textColor = .gray
        // Ustidan chizilgan text funksiyasi bu yerda yozilardi
        
        percentBadge.backgroundColor = .red
        percentBadge.textColor = .white
        
        // Qoidalar:
        // 1. Foiz (badge) doim ko'rinishi shart (ezilmaydi)
        percentBadge.setContentCompressionResistancePriority(.required, for: .horizontal)
        
        // 2. Original narx kerak bo'lsa qisqarib ketishi mumkin
        originalPriceLabel.setContentCompressionResistancePriority(.defaultLow, for: .horizontal)
        
        // 3. Discount narx muhimroq, uni siqish qiyinroq
        discountPriceLabel.setContentCompressionResistancePriority(.defaultHigh, for: .horizontal)
    }
    
    @MainActor
    private func setupConstraints() {
        discountPriceLabel.snp.makeConstraints { make in
            make.left.top.bottom.equalToSuperview()
        }
        
        originalPriceLabel.snp.makeConstraints { make in
            make.left.equalTo(discountPriceLabel.snp.right).offset(8)
            make.lastBaseline.equalTo(discountPriceLabel.snp.lastBaseline)
        }
        
        percentBadge.snp.makeConstraints { make in
            make.left.equalTo(originalPriceLabel.snp.right).offset(8)
            make.right.lessThanOrEqualToSuperview()
            make.centerY.equalTo(discountPriceLabel)
        }
    }
}

Xulosa {#xulosa}

  • .priority(999) — Auto Layout kutilmagan constraint conflict'larida ilovani crash qilmasligi yoki warning bermasligi uchun qilingan eng yaxshi "tricky" (ayyorona) yechim.
  • Hugging (cho'zilishga qarshi) va Compression Resistance (siqilishga qarshi) xususiyatlari interfeysning har qanday ekran o'lchamlariga moslashuvchan (adaptive) bo'lishini ta'minlaydi.
  • SnapKit yordamida standart UIKit funksiyalari (masalan, setContentHuggingPriority) mukammal sinergiyada ishlaydi.

Manbalar {#manbalar}

Markdown manba

iOS Lessons