# SnapKit 2: Asosiy Constraint'lar — edges, center, size, offset va inset

> **Holat:** Ushbu qo'llanma 2026-yil oktabr holatiga ko'ra SnapKit 5.7+ va Swift 6 talablari asosida tayyorlangan. Barcha kodlar zamonaviy `@MainActor` va xavfsiz concurrency qoidalariga mos.

SnapKit yordamida UI yaratishda eng ko'p ishlatiladigan constraint'lar (chegaraviy bog'lamalar) asosan 4 ta guruhga bo'linadi: chetlar (edges), markaz (center), o'lchamlar (size) va oraliq masofalar (offset/inset). Ushbu darsda biz bu tushunchalarni chuqur o'rganamiz va SnapKit'dagi eng chalkash mavzu — **offset va inset** farqini ko'rib chiqamiz.

## Mundarija

1. [Edge Constraints (Chetlar)](#edges)
2. [Center Constraints (Markaz)](#center)
3. [Size Constraints (O'lchamlar)](#size)
4. [Offset vs Inset (Eng chalkash mavzu)](#offset-inset)
5. [EqualToSuperview() vs EqualTo()](#equal-to)
6. [Amaliy loyiha: Login ekrani layouti](#amaliy-loyiha)
7. [Xulosa](#xulosa)
8. [Manbalar](#manbalar)

---

## 1. Edge Constraints (Chetlar) {#edges}

Elementni ekranning yoki boshqa view'ning chetlariga yopishtirish eng keng tarqalgan holatdir. SnapKit buning uchun juda qulay sintaksisni taqdim etadi.

### Alohida chetlar
Siz har bir chetni alohida belgilashingiz mumkin:

```swift
import UIKit
import SnapKit

@MainActor
final class EdgeExampleView: UIView {
    private let boxView = UIView()
    
    override init(frame: CGRect) {
        super.init(frame: frame)
        setupSubviews()
        setupConstraints()
    }
    
    required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") }
    
    private func setupSubviews() {
        boxView.backgroundColor = .systemBlue
        addSubview(boxView)
    }
    
    private func setupConstraints() {
        boxView.snp.makeConstraints { make in
            make.top.equalToSuperview().offset(20)
            make.leading.equalToSuperview().offset(16)
            make.trailing.equalToSuperview().offset(-16)
            make.bottom.equalToSuperview().offset(-20)
        }
    }
}
```

### Barcha 4 ta chetni bir vaqtda belgilash (edges)
Yuqoridagi kodni `make.edges` yordamida ancha qisqartirish mumkin:

```swift
// 4 chetni ota view'ga yopishtirish
make.edges.equalToSuperview()

// Hamma tomondan bir xil 16pt masofa tashlash (padding)
make.edges.equalToSuperview().inset(16)

// Har bir tomon uchun turlicha masofa tashlash
make.edges.equalToSuperview().inset(UIEdgeInsets(top: 20, left: 16, bottom: 20, right: 16))
```

> **Senior eslatmasi:** `left/right` va `leading/trailing` o'rtasidagi farqni bilish juda muhim.
> * `left/right` — Har doim ekranning chap va o'ng tomoni (absolyut).
> * `leading/trailing` — Matn o'qilish yo'nalishiga qarab o'zgaradi. Masalan, RTL (o'ngdan chapga o'qiladigan Arab, Ibroniy) tillarida `leading` o'ng tomonga, `trailing` esa chap tomonga aylanadi. Doimo `leading/trailing` ishlatish tavsiya etiladi! 
> SnapKit'da `directionalEdges` orqali RTL-safe layout'larni bir qatorda yozish mumkin:
> `make.directionalEdges.equalToSuperview().inset(16)` (leading, trailing, top, bottom bo'yicha ishlaydi).

---

## 2. Center Constraints (Markaz) {#center}

UI elementlarni ekranning yoki biror konteynerning o'rtasiga joylashtirish:

```swift
import UIKit
import SnapKit

@MainActor
final class CenterExampleView: UIView {
    private let logoImageView = UIImageView()
    
    // ... setupSubviews ...
    
    private func setupConstraints() {
        logoImageView.snp.makeConstraints { make in
            // Ham gorizontal, ham vertikal markaz
            make.center.equalToSuperview()
            
            // YOKI faqat gorizontal markaz (X o'qi):
            // make.centerX.equalToSuperview()
            
            // YOKI faqat vertikal markaz (Y o'qi) va uni 50pt pastroqqa surish:
            // make.centerY.equalToSuperview().offset(50)
            
            // YOKI Boshqa view'ning markaziga bog'lash:
            // make.center.equalTo(otherView)
        }
    }
}
```

---

## 3. Size Constraints (O'lchamlar) {#size}

View o'lchamlarini berish uchun `width`, `height` yoki `size` dan foydalaniladi.

```swift
boxView.snp.makeConstraints { make in
    // Aniq o'lchamlar (Constant)
    make.width.equalTo(200)
    make.height.equalTo(100)
    
    // Yoki ikkalasini bitta qilib yozish (CGSize)
    make.size.equalTo(CGSize(width: 200, height: 100))
    
    // Kvadrat (width = 50, height = 50) qilish uchun bitta raqam berish kifoya
    make.size.equalTo(50)
    
    // Boshqa view'ga nisbatan o'lcham (ota view'ning eni bilan bir xil)
    make.width.equalToSuperview()
    
    // Boshqa view'ning o'lchami bilan tenglashish
    // make.width.equalTo(otherView)
}
```

---

## 4. Offset vs Inset — eng chalkash mavzu! {#offset-inset}

Yangi o'rganayotganlarning 90% qismi `offset` va `inset` ni adashtiradi. Sababi ularning ishlash logikasi biroz farq qiladi.

* **Offset** — Elementni ekranning koordinatalar o'qi (X va Y) bo'ylab qayerga **siljitishni** belgilaydi. 
  * Musbat (Positive) qiymat: O'ngga va Pastga siljitadi.
  * Manfiy (Negative) qiymat: Chapga va Tepaga siljitadi.
* **Inset** — Elementni o'zining chegaralaridan qanchaga **ichkariga** tortishni belgilaydi. U asosan padding (ichki bo'shliq) kabi ishlaydi. Musbat qiymat doimo "ichkariga" qarab harakatlantiradi.

### Vizual tushuntirish

```text
    (0,0) Ota View koordinata boshi
      +-------------------------------------------+
      |                ^ offset(20)               |
      |                | inset(20)                |
      |          +-----------+                    |
      |          |           |                    |
offset(20)  <--  |   Obyekt  |  -->  offset(-20)  |
inset(20)        |           |       inset(20)    |
      |          +-----------+                    |
      |                | inset(20)                |
      |                v offset(-20)              |
      +-------------------------------------------+
```

Ko'rib turganingizdek:
* `top` uchun `offset(20)` va `inset(20)` **bir xil** ishlaydi (ikkalasi ham pastga, ichkariga suradi).
* `bottom` uchun `offset(-20)` ishlatish kerak, lekin `inset` bilan yozsangiz musbat `inset(20)` deb yozasiz (ichkariga qarab tortadi).

### Farqlar jadvali

| Tomon (Edge) | Offset (20pt ichkariga surish) | Inset (20pt ichkariga surish) | Qaysi biri qulayroq? |
|--------------|---------------------------------|-------------------------------|----------------------|
| **Top** | `make.top...offset(20)` | `make.top...inset(20)` | Farqi yo'q (offset mashhur) |
| **Leading** | `make.leading...offset(20)` | `make.leading...inset(20)` | Farqi yo'q (offset mashhur) |
| **Trailing** | `make.trailing...offset(-20)` | `make.trailing...inset(20)` | **Inset (manfiy belgi kerak emas)** |
| **Bottom** | `make.bottom...offset(-20)` | `make.bottom...inset(20)` | **Inset (manfiy belgi kerak emas)** |
| **Edges** | *ishlatish qiyin* | `make.edges...inset(16)` | **Inset** |

> **Muhim:** Agarda faqat `bottom` yoki `trailing` constraint berayotgan bo'lsangiz va musbat raqamlar bilan fikrlash qulay bo'lsa `inset(N)` foydalaning. Manfiy raqam yoqsa `offset(-N)` foydalaning. Ammo barcha tomonlarga birdaniga masofa berish uchun (`edges`) doimo `inset` foydalaning.

---

## 5. EqualToSuperview() vs EqualTo() {#equal-to}

SnapKit'da bog'lamalarni (constraint) ikki xil usulda ulash mumkin:

**1. `equalToSuperview()`** — Elementni to'g'ridan-to'g'ri o'zi joylashgan ota (super) view'ga ulaydi. Siz ota view nomini yozishingiz shart emas.

```swift
make.centerX.equalToSuperview() // Ota view'ning markaziga
make.width.equalToSuperview()   // Ota view bilan bir xil eni
```

**2. `equalTo()`** — Boshqa har qanday ob'ektga yoki raqamga (constant) ulash.

```swift
// 1. Raqamga (Constant) ulash
make.width.equalTo(100)

// 2. Boshqa View'ga ulash (avtomatik o'sha view'ning bir xil anchoriga ulanadi)
// Masalan, pastdagi kod: o'zining markazini boshqa view'ning markaziga ulaydi
make.center.equalTo(otherView)

// 3. Boshqa View'ning aniq anchoriga ulash
// Masalan, bu elementning tepa qismini, boshqa elementning PASTKI qismiga ulash:
make.top.equalTo(otherView.snp.bottom).offset(16)
```

---

## 6. Amaliy loyiha: Login ekrani layouti {#amaliy-loyiha}

Barcha o'rganganlarimizni jamlab, haqiqiy iOS dasturlaridagi kabi Login ekranini chizamiz.

```swift
import UIKit
import SnapKit

@MainActor
final class LoginView: UIView {
    
    // MARK: - UI Elements
    private let logoImageView: UIImageView = {
        let iv = UIImageView()
        iv.backgroundColor = .systemGray5
        iv.layer.cornerRadius = 16
        return iv
    }()
    
    private let emailTextField: UITextField = {
        let tf = UITextField()
        tf.placeholder = "Email"
        tf.borderStyle = .roundedRect
        return tf
    }()
    
    private let passwordTextField: UITextField = {
        let tf = UITextField()
        tf.placeholder = "Password"
        tf.isSecureTextEntry = true
        tf.borderStyle = .roundedRect
        return tf
    }()
    
    private let loginButton: UIButton = {
        let btn = UIButton(type: .system)
        btn.setTitle("Login", for: .normal)
        btn.backgroundColor = .systemBlue
        btn.setTitleColor(.white, for: .normal)
        btn.layer.cornerRadius = 8
        return btn
    }()
    
    private let forgotPasswordLabel: UILabel = {
        let lbl = UILabel()
        lbl.text = "Forgot password?"
        lbl.textColor = .systemBlue
        lbl.font = .systemFont(ofSize: 14)
        return lbl
    }()
    
    // MARK: - Initialization
    override init(frame: CGRect) {
        super.init(frame: frame)
        backgroundColor = .systemBackground
        setupSubviews()
        setupConstraints()
    }
    
    required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") }
    
    // MARK: - Setup
    private func setupSubviews() {
        addSubview(logoImageView)
        addSubview(emailTextField)
        addSubview(passwordTextField)
        addSubview(loginButton)
        addSubview(forgotPasswordLabel)
    }
    
    private func setupConstraints() {
        // Logo markazda (tepaga yaqinroq) va aniq o'lchamda
        logoImageView.snp.makeConstraints { make in
            make.centerX.equalToSuperview()
            make.top.equalTo(safeAreaLayoutGuide.snp.top).offset(60)
            make.size.equalTo(100) // 100x100 kvadrat
        }
        
        // Email maydoni: logonning pastida, chap/o'ngdan 24pt masofa
        emailTextField.snp.makeConstraints { make in
            make.top.equalTo(logoImageView.snp.bottom).offset(40)
            make.leading.trailing.equalToSuperview().inset(24) // offset ishlatganda trailing -24 bo'lardi!
            make.height.equalTo(50)
        }
        
        // Parol maydoni: email'ning tagida, o'lchamlari email bilan bir xil
        passwordTextField.snp.makeConstraints { make in
            make.top.equalTo(emailTextField.snp.bottom).offset(16)
            make.leading.trailing.height.equalTo(emailTextField) // Kod qisqarishi!
        }
        
        // Parolni unutdingizmi: parol maydoni ostida, o'ng tomonga yopishtirilgan
        forgotPasswordLabel.snp.makeConstraints { make in
            make.top.equalTo(passwordTextField.snp.bottom).offset(12)
            make.trailing.equalTo(passwordTextField.snp.trailing)
            // height / width berish shart emas, UILabel o'z contentSize'iga ega
        }
        
        // Login tugmasi: parolni unutdingizmi label'i tagida
        loginButton.snp.makeConstraints { make in
            make.top.equalTo(forgotPasswordLabel.snp.bottom).offset(30)
            make.leading.trailing.equalToSuperview().inset(24)
            make.height.equalTo(50)
        }
    }
}
```

Bu kodda SnapKit'ning kuchi yaqqol ko'rinadi: `make.leading.trailing.height.equalTo(emailTextField)` kabi zanjirli (chained) yozish usuli bilan juda ko'p qator kodlarni bitta qatorga qisqartirdik.

---

## Xulosa {#xulosa}
Ushbu darsda biz SnapKit'ning asosiy ustunlari bo'lgan chetlar, markazlar va o'lchamlarni belgilashni ko'rib chiqdik. Shuningdek, ko'pchilik xato qiladigan `offset` va `inset` orasidagi farqni vizual ravishda tushundik. Ushbu vositalar yordamida siz deyarli istalgan murakkablikdagi dizaynni aniq va tushunarli qilib yozishingiz mumkin.

## Manbalar {#manbalar}
- [SnapKit Rasmiy Dokumentatsiyasi (GitHub)](https://github.com/SnapKit/SnapKit)
- [Apple Auto Layout Guide](https://developer.apple.com/library/archive/documentation/UserExperience/Conceptual/AutolayoutPG/)
