Azimjon Abdurasulov

SnapKit 3: View'lar Orasidagi Munosabatlar — Spacing, Chaining va ConstraintMaker sirlari

🧩 SnapKit — Zero to Hero

SnapKit 3: View'lar Orasidagi Munosabatlar — Spacing, Chaining va ConstraintMaker sirlari

Holat: 2026-yil oktabr holatiga ko'ra, murakkab UI qismlarini yaratishda UIStackView o'rniga ba'zan SnapKit yordamida view'larni bevosita bir-biriga ulash (view-to-view constraints) moslashuvchanlik nuqtai nazaridan afzalroq bo'ladi.

Mundarija

  1. View-to-View Constraints
  2. Constraint Chaining (Zanjir)
  3. ConstraintMaker Ichki Arxitekturasi
  4. ConstraintItem vs ConstraintView
  5. Spacing Pattern'lari
  6. Amaliy Loyiha: Profil Kartasi
  7. Amaliy Loyiha 2: Settings Row
  8. Xulosa
  9. Manbalar

1. View-to-View Constraints {#view-to-view}

View'larni har doim ham superview'ga bog'lash shart emas. Ko'pincha biz ularni bir-biriga nisbatan joylashtiramiz. Bu UIStackView'ning ishlashiga o'xshaydi, lekin to'liq nazorat sizda bo'ladi.

import UIKit
import SnapKit

class RelationView: UIView {
    
    let avatarView = UIView()
    let titleLabel = UILabel()
    
    override init(frame: CGRect) {
        super.init(frame: frame)
        setupSubviews()
        setupConstraints()
    }
    
    required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") }
    
    private func setupSubviews() {
        avatarView.backgroundColor = .blue
        titleLabel.text = "Azimjon"
        titleLabel.backgroundColor = .yellow
        
        addSubview(avatarView)
        addSubview(titleLabel)
    }
    
    private func setupConstraints() {
        avatarView.snp.makeConstraints { make in
            make.top.leading.equalToSuperview().inset(16)
            make.size.equalTo(CGSize(width: 50, height: 50))
        }
        
        titleLabel.snp.makeConstraints { make in
            // titleLabel'ning chap tomoni avatarView'ning o'ng tomonidan 12 pt uzoqda
            make.leading.equalTo(avatarView.snp.trailing).offset(12)
            
            // titleLabel'ning markazi avatarView'ning markaziga teng (gorizontaliga bir xil balandlikda)
            make.centerY.equalTo(avatarView.snp.centerY)
            
            // O'ng tomoni superview chetidan chiqib ketmasligi uchun
            make.trailing.lessThanOrEqualToSuperview().inset(16)
        }
    }
}

Senior eslatmasi: Agar bir view ikkinchisiga equalTo qilinsa, har ikkalasi ham bir xil parent view ierarxiyasida (masalan, bitta superview ichida yoki biri ikkinchisining siblingi) bo'lishi kerak.

2. Constraint Chaining (Zanjir) {#chaining}

SnapKit kodni qisqartirish imkonini beradi. Bitta satrda bir nechta anchor'larni "zanjir" qilib yozish mumkin.

import UIKit
import SnapKit

class ChainingExampleView: UIView {
    
    let boxView = UIView()
    let innerBox = UIView()
    
    private func setupConstraints() {
        addSubview(boxView)
        boxView.addSubview(innerBox)
        
        boxView.snp.makeConstraints { make in
            // 4 ta qator o'rniga 1 ta qator!
            make.top.bottom.leading.trailing.equalToSuperview().inset(20)
            
            // Bu bilan bir xil:
            // make.edges.equalToSuperview().inset(20)
        }
        
        innerBox.snp.makeConstraints { make in
            // Zanjir faqat bir xil o'lchov o'qlari bo'yicha ishlashi eng xavfsizidir
            make.top.leading.equalToSuperview().inset(10)
            make.width.height.equalTo(100) // Kenglik va balandlikni bittada berish
        }
    }
}

Qachon chain qilish mumkin?

  • Barcha zanjirlangan anchor'lar bitta narsaga (equalTo) tenglashayotgan bo'lsa.
  • Ular barchasi bitta offset yoki inset qabul qilsa.

3. ConstraintMaker Ichki Arxitekturasi {#constraintmaker}

SnapKit orqasida nima sodir bo'ladi?

  • make o'zgaruvchisi aslida ConstraintMaker turidagi obyektdir.
  • Siz make.top deb yozganingizda, u ConstraintMakerRelatable obyekti qaytaradi.
  • .equalTo(...) chaqirilganda, u orqada ConstraintDescription hosil qiladi va xotirada saqlaydi.
  • Closure tugashi bilan, SnapKit yig'ilgan barcha ConstraintDescription'larni olib, xaqiqiy NSLayoutConstraint'larni yaratadi va barchasini birdaniga aktivlashtiradi (batch activation).

Muhim: Bu arxitektura "Builder Pattern" ga misoldir. Closure ichida bevosita constraintlar yasalmaydi, faqatgina qoidalar yig'iladi, closure tugagach esa ular amalda qo'llaniladi.

4. ConstraintItem vs ConstraintView {#item-vs-view}

Ba'zida aniq anchor ko'rsatamiz, ba'zida faqat view'ning o'zini yozamiz.

// ConstraintView - view'ning o'zini berish
make.top.equalTo(otherView) 

Bu holda SnapKit aqlli harakat qiladi va siz qaysi anchor'dan (chap tomonda) kelayotganingizga qarab, o'ng tomondagi view'ning o'sha anchor'ini tanlaydi. Yuqoridagi kod aslida: make.top.equalTo(otherView.snp.top) bilan bir xil.

// ConstraintItem - aniq anchor'ni ko'rsatish
make.top.equalTo(otherView.snp.bottom).offset(10)

Bu yerda .top ni .bottom ga ulayapmiz. Shu sababli aniq otherView.snp.bottom deb yozish shart. Aks holda SnapKit top'ni top'ga ulab qo'yadi.

5. Spacing Pattern'lari {#spacing}

View'larni bir-biriga nisbatan joylashtirishning asosiy pattern'lari.

[ Vertikal Stack Pattern ]
+------------------+
|  View 1          |
+------------------+
        | offset(16)
+------------------+
|  View 2          |
+------------------+
view1.snp.makeConstraints { make in
    make.top.leading.trailing.equalToSuperview().inset(16)
    make.height.equalTo(100)
}

view2.snp.makeConstraints { make in
    make.top.equalTo(view1.snp.bottom).offset(16)
    make.leading.trailing.equalToSuperview().inset(16)
    make.height.equalTo(100)
}

Teng bo'shliqlar saqlash (Equal Spacing) uchun ko'pincha UILayoutGuide ishlatiladi, ammo SnapKit'da buni oddiygina "spacer view"lar bilan yoki faqat matematika yordamida qilsa ham bo'ladi: make.width.equalTo(superview).multipliedBy(0.5).offset(-20) (yarmi minus padding).

6. Amaliy Loyiha: Profil Kartasi {#amaliy-1}

Avatar chapda, o'ngroqda ustma-ust 2 ta yozuv va eng o'ng chekkada ikonka.

import UIKit
import SnapKit

class ProfileCardView: UIView {
    
    let avatarImageView = UIImageView()
    let titleLabel = UILabel()
    let subtitleLabel = UILabel()
    let chevronImageView = UIImageView()
    
    override init(frame: CGRect) {
        super.init(frame: frame)
        setupSubviews()
        setupConstraints()
    }
    
    required init?(coder: NSCoder) { fatalError() }
    
    private func setupSubviews() {
        backgroundColor = .systemBackground
        layer.cornerRadius = 12
        
        avatarImageView.backgroundColor = .systemGray
        avatarImageView.layer.cornerRadius = 25
        avatarImageView.clipsToBounds = true
        
        titleLabel.font = .boldSystemFont(ofSize: 16)
        titleLabel.text = "Azimjon"
        
        subtitleLabel.font = .systemFont(ofSize: 14)
        subtitleLabel.textColor = .secondaryLabel
        subtitleLabel.text = "iOS Developer"
        
        chevronImageView.image = UIImage(systemName: "chevron.right")
        chevronImageView.tintColor = .systemGray
        
        addSubview(avatarImageView)
        addSubview(titleLabel)
        addSubview(subtitleLabel)
        addSubview(chevronImageView)
    }
    
    private func setupConstraints() {
        avatarImageView.snp.makeConstraints { make in
            make.leading.equalToSuperview().offset(16)
            make.centerY.equalToSuperview()
            make.size.equalTo(50)
            make.top.bottom.equalToSuperview().inset(16).priority(.high)
        }
        
        titleLabel.snp.makeConstraints { make in
            make.leading.equalTo(avatarImageView.snp.trailing).offset(12)
            make.bottom.equalTo(avatarImageView.snp.centerY).offset(-2)
            make.trailing.lessThanOrEqualTo(chevronImageView.snp.leading).offset(-8)
        }
        
        subtitleLabel.snp.makeConstraints { make in
            make.leading.equalTo(titleLabel) // titleLabel bilan bir xil alignment
            make.top.equalTo(avatarImageView.snp.centerY).offset(2)
            make.trailing.lessThanOrEqualTo(chevronImageView.snp.leading).offset(-8)
        }
        
        chevronImageView.snp.makeConstraints { make in
            make.trailing.equalToSuperview().offset(-16)
            make.centerY.equalToSuperview()
            make.size.equalTo(20)
        }
    }
}

7. Amaliy Loyiha 2: Settings Row {#amaliy-2}

Sozlamalar qatori — iconka, yozuv, qoshimcha yozuv (o'ngroqda) va Switch.

import UIKit
import SnapKit

class SettingsRowView: UIView {
    
    let iconView = UIImageView()
    let titleLabel = UILabel()
    let detailLabel = UILabel()
    let toggleSwitch = UISwitch()
    
    override init(frame: CGRect) {
        super.init(frame: frame)
        setupSubviews()
        setupConstraints()
    }
    
    required init?(coder: NSCoder) { fatalError() }
    
    private func setupSubviews() {
        iconView.backgroundColor = .systemBlue
        iconView.layer.cornerRadius = 8
        
        titleLabel.text = "Notifications"
        
        detailLabel.text = "On"
        detailLabel.textColor = .secondaryLabel
        
        addSubview(iconView)
        addSubview(titleLabel)
        addSubview(detailLabel)
        addSubview(toggleSwitch)
    }
    
    private func setupConstraints() {
        iconView.snp.makeConstraints { make in
            make.leading.equalToSuperview().offset(16)
            make.centerY.equalToSuperview()
            make.size.equalTo(30)
            make.top.bottom.equalToSuperview().inset(12)
        }
        
        titleLabel.snp.makeConstraints { make in
            make.leading.equalTo(iconView.snp.trailing).offset(16)
            make.centerY.equalToSuperview()
        }
        
        toggleSwitch.snp.makeConstraints { make in
            make.trailing.equalToSuperview().offset(-16)
            make.centerY.equalToSuperview()
        }
        
        detailLabel.snp.makeConstraints { make in
            make.trailing.equalTo(toggleSwitch.snp.leading).offset(-12)
            make.centerY.equalToSuperview()
            
            // Detail label juda uzun bo'lib ketsa, titleLabel ni siqib qo'ymasligi uchun:
            make.leading.greaterThanOrEqualTo(titleLabel.snp.trailing).offset(16)
        }
    }
}

Xulosa {#xulosa}

SnapKit'ning eng kuchli tomoni bu view'larni bir-biriga bog'lashdagi sintaksisning soddaligidadir. equalTo, offset, inset kabi metodlar va zanjir(chain) yozish uslubi orqali murakkab interfeyslarni UIStackView'siz ham tez qurish mumkin. Bunda albatta SnapKit'ning tagidagi ConstraintMaker va Builder pattern arxitekturasi katta rol o'ynaydi.

Manbalar {#manbalar}

Markdown manba

iOS Lessons