Azimjon Abdurasulov

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

🧩 SnapKit — Zero to Hero

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)
  2. Center Constraints (Markaz)
  3. Size Constraints (O'lchamlar)
  4. Offset vs Inset (Eng chalkash mavzu)
  5. EqualToSuperview() vs EqualTo()
  6. Amaliy loyiha: Login ekrani layouti
  7. Xulosa
  8. 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:

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:

// 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:

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.

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

    (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?
Topmake.top...offset(20)make.top...inset(20)Farqi yo'q (offset mashhur)
Leadingmake.leading...offset(20)make.leading...inset(20)Farqi yo'q (offset mashhur)
Trailingmake.trailing...offset(-20)make.trailing...inset(20)Inset (manfiy belgi kerak emas)
Bottommake.bottom...offset(-20)make.bottom...inset(20)Inset (manfiy belgi kerak emas)
Edgesishlatish qiyinmake.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.

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.

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

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}

Markdown manba

iOS Lessons