# 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](#view-to-view)
2. [Constraint Chaining (Zanjir)](#chaining)
3. [ConstraintMaker Ichki Arxitekturasi](#constraintmaker)
4. [ConstraintItem vs ConstraintView](#item-vs-view)
5. [Spacing Pattern'lari](#spacing)
6. [Amaliy Loyiha: Profil Kartasi](#amaliy-1)
7. [Amaliy Loyiha 2: Settings Row](#amaliy-2)
8. [Xulosa](#xulosa)
9. [Manbalar](#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.

```swift
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.

```swift
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.

```swift
// 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.

```swift
// 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.

```text
[ Vertikal Stack Pattern ]
+------------------+
|  View 1          |
+------------------+
        | offset(16)
+------------------+
|  View 2          |
+------------------+
```

```swift
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.

```swift
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.

```swift
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}
- [SnapKit Rasmiy Dokumentatsiyasi - Spacing](https://github.com/SnapKit/SnapKit)
- "iOS Auto Layout Demystified" by Erica Sadun
- [Apple Auto Layout Guide - Constraints](https://developer.apple.com/library/archive/documentation/UserExperience/Conceptual/AutolayoutPG/AnatomyofaConstraint.html)
