SnapKit 7: Constraint Hayot Sikli — makeConstraints, updateConstraints, remakeConstraints va Constraint Reference
🧩 SnapKit — Zero to Hero
SnapKit 7: Constraint Hayot Sikli — makeConstraints, updateConstraints, remakeConstraints va Constraint Reference
Holat: 2026-yil oktabr. Ushbu dars SnapKit’ning eng muhim mavzularidan biri — constraint’larni dinamik ravishda boshqarish va ularning hayot sikli (lifecycle) haqida. Qachon qaysi metodni ishlatishni bilish iOS dasturlashda muvaffaqiyatli UI yaratishning kalitidir.
Ushbu darsda biz SnapKit’da constraint’larni yaratish, yangilash va o'chirish usullarini, shuningdek, constraint referenslarini saqlab, UI'ni qanday jonlantirish mumkinligini ko'rib chiqamiz.
Mundarija
- makeConstraints — yaratish (bir marta!)
- updateConstraints — faqat constant'ni yangilash
- remakeConstraints — hammasini o'chirib qayta yozish
- Uch usulni taqqoslash jadvali
- Constraint Reference saqlash (Eng muhim pattern!)
- UIView.updateConstraints() lifecycle metodi
- Amaliy loyiha: Expandable Card
- Amaliy loyiha 2: Keyboard-aware input
- Xulosa
- Manbalar
1. makeConstraints — yaratish (bir marta!) {#make-constraints}
makeConstraints bloki orqali biz view'ning yangi constraint'larini yaratamiz va avtomatik ravishda activate qilamiz (ishga tushiramiz).
Muhim:
makeConstraintsfunksiyasini faqat view o'z superview'iga qo'shilgandan (addSubview) keyin chaqirish kerak, aks holda crash (xato) yuz beradi!
Yana bir juda muhim narsa: makeConstraints ni ayni bir view uchun qayta-qayta chaqiravermang! Har safar chaqirilganda, u yangi constraint yaratadi. Agar eski constraint'lar mavjud bo'lsa, sizda duplikat constraint'lar (conflict) yuzaga keladi.
import UIKit
import SnapKit
@MainActor
class MakeConstraintsView: UIView {
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 = .systemRed
// 1. Dastlab view qo'shilishi kerak!
addSubview(boxView)
}
private func setupConstraints() {
// 2. Keyin constraint yoziladi (bir marta!)
boxView.snp.makeConstraints { make in
make.center.equalToSuperview()
make.size.equalTo(100)
}
}
func changeSizeIncorrectly() {
// XATO YONDASHUV! Bu yerda makeConstraints qayta chaqirilmoqda.
// Natijada, size ham 100, ham 200 bo'lishga harakat qiladi va auto-layout conflict kelib chiqadi.
boxView.snp.makeConstraints { make in
make.size.equalTo(200)
}
}
}
2. updateConstraints — faqat constant'ni yangilash {#update-constraints}
updateConstraints avval yaratilgan (masalan, makeConstraints orqali) constraint'ning faqat constant qiymatini (offset, inset, size) yangilash uchun ishlatiladi.
Cheklovlar va xavflar:
- U orqali mutlaqo YANGI constraint qo'sha olmaysiz (masalan,
leftbormidi, faqat shuni update qila olasiz. Endirightni qo'shaman desangiz — CRASH!). - Agar munosabatni (relation) o'zgartirmoqchi bo'lsangiz (masalan,
equalToo'rnigagreaterThanOrEqualTo) — BU ISHLAMAYDI! Faqat aniq qat'iy (constant) son o'zgaradi.
Qachon ishlatiladi: Tugma bosilganda view'ning offset'ini qisqa ravishda o'zgartirish kerak bo'lganda.
import UIKit
import SnapKit
@MainActor
class UpdateConstraintsView: UIView {
let movingBox = UIView()
var isMoved = false
override init(frame: CGRect) {
super.init(frame: frame)
setupSubviews()
setupConstraints()
}
required init?(coder: NSCoder) { fatalError() }
private func setupSubviews() {
movingBox.backgroundColor = .systemBlue
addSubview(movingBox)
}
private func setupConstraints() {
movingBox.snp.makeConstraints { make in
make.top.equalToSuperview().offset(50)
make.centerX.equalToSuperview()
make.size.equalTo(100)
}
}
func togglePosition() {
isMoved.toggle()
let newOffset = isMoved ? 200 : 50
UIView.animate(withDuration: 0.3) {
// FAQAT avval yozilgan top constraint'ning constant qiymati yangilanadi
self.movingBox.snp.updateConstraints { make in
make.top.equalToSuperview().offset(newOffset)
}
self.layoutIfNeeded() // Animatsiya bo'lishi uchun kerak
}
}
}
3. remakeConstraints — hammasini o'chirib qayta yozish {#remake-constraints}
remakeConstraints — ayni view'dagi barcha SnapKit constraint'larini o'chirib, bloki ichidagilarini noldan yangidan yaratadi.
Bu eng xavfsiz usul (chunki eski constraint'lar qolib ketishi yuzasidan xavotir yo'q), biroq perfomance (ishlash tezligi) jihatdan biroz qimmat — chunki barcha munosabatlar uziladi va qayta quriladi.
Qachon ishlatiladi: View'ning layout'i to'liq o'zgarib ketganda. Masalan, telefonni landscape'dan portrait'ga burganingizda yoki ko'rinish mutlaqo boshqa dizaynga o'tganda.
import UIKit
import SnapKit
@MainActor
class RemakeConstraintsView: UIView {
let dynamicBox = UIView()
var isExpandedMode = false
// ... init va setupSubviews ...
func updateLayoutMode() {
isExpandedMode.toggle()
UIView.animate(withDuration: 0.4) {
self.dynamicBox.snp.remakeConstraints { make in
if self.isExpandedMode {
// Kengaytirilgan holat: butun ekranni egallaydi
make.edges.equalToSuperview().inset(20)
} else {
// Kichik holat: faqat markazda bo'ladi
make.center.equalToSuperview()
make.size.equalTo(100)
}
}
self.layoutIfNeeded()
}
}
}
4. Uch usulni taqqoslash jadvali {#comparison-table}
| Xususiyat | makeConstraints | updateConstraints | remakeConstraints |
|---|---|---|---|
| Nima qiladi? | Yangi constraint yaratadi va activate qiladi. | Mavjud constraint'ning faqat constant (offset/inset/size) qiymatini o'zgartiradi. | Barcha eski constraint'larni olib tashlaydi va o'rniga yangilarini yaratadi. |
| Qachon ishlatiladi? | View birinchi marta chizilayotganda (UI qurish bosqichida). | Qiymatni ozgina o'zgartirish kerak bo'lganda (masalan, +20px pastga siljitish). | Layout to'liq o'zgarib, tamoman boshqa dizayn/holatga o'tganda. |
| Xavfi | Qayta-qayta chaqirilsa, constraint duplikatlari (conflict) vujudga keladi. | Yangi relation (anchor) qo'shib bo'lmaydi. Faqat raqam o'zgaradi, aks holda dastur qulaydi (crash). | Deyarli xavfsiz. Biroq keraksiz joyda ko'p ishlatilsa, ishlash tezligini tushirishi mumkin. |
| Performance | Yaxshi (faqat 1 marta yaratiladi). | Eng tez (Optimal) (Auto Layout faqat constant'ni qayta hisoblaydi). | Birmuncha sekin (hamma narsa o'chib-yonganligi sababli). |
5. Constraint Reference saqlash (Eng muhim pattern!) {#constraint-reference}
SnapKit bilan dinamik layout qilinganda ko'p hollarda updateConstraints yoki remakeConstraints ham kerak bo'lmaydi. Buning eng "pro" usuli — bitta aniq constraint'ning reference'ini saqlab qolish va faqat shuni boshqarish.
Senior eslatmasi: Siz o'zgartirmoqchi bo'lgan qismlarning constraint'larini
var ... Constraint?tarzida saqlab oling. BuupdateConstraints'ga qaraganda o'qilishi oson va har xil xatolarning oldini oladi.
import UIKit
import SnapKit
@MainActor
class ConstraintReferenceView: UIView {
let circleView = UIView()
// O'zgaradigan constraint'lar reference'larini saqlash
var topConstraint: Constraint?
var sizeConstraint: Constraint?
override init(frame: CGRect) {
super.init(frame: frame)
setupSubviews()
setupConstraints()
}
required init?(coder: NSCoder) { fatalError() }
private func setupSubviews() {
circleView.backgroundColor = .systemYellow
circleView.layer.cornerRadius = 50
addSubview(circleView)
}
private func setupConstraints() {
circleView.snp.makeConstraints { make in
make.centerX.equalToSuperview()
// Reference saqlash
self.topConstraint = make.top.equalToSuperview().offset(50).constraint
self.sizeConstraint = make.width.height.equalTo(100).constraint
}
}
func animateToBottom() {
UIView.animate(withDuration: 0.5) {
// Reference orqali to'g'ridan-to'g'ri yangilash
self.topConstraint?.update(offset: 400)
self.sizeConstraint?.update(offset: 150)
// Xohlasangiz constraint'ni vaqtincha deactive qilishingiz ham mumkin:
// self.topConstraint?.deactivate()
// self.topConstraint?.activate()
self.layoutIfNeeded()
}
}
}
Nima uchun bu pattern kuchli?
- O'zgartirmoqchi bo'lgan narsangizga to'g'ridan-to'g'ri bog'lanasiz.
update(offset:),update(inset:)vaupdate(priority:)qilish imkoniyati bor.- Kerak bo'lmaganda vaqtincha
deactivate()qilib turishingiz mumkin.
6. UIView.updateConstraints() lifecycle metodi {#uiview-lifecycle}
Apple'ning Auto Layout tizimida o'zgarishlar maxsus tsikl asosida boradi: setNeedsUpdateConstraints() -> tizim vaqt topganda updateConstraints() ni chaqiradi -> Auto Layout Pass o'tadi.
Ba'zan layout mantiqi murakkab bo'lsa, constraintlarni saqlab olish o'rniga shu metodni override qilish tavsiya etiladi.
import UIKit
import SnapKit
@MainActor
class CustomLifecycleView: UIView {
var isRedState: Bool = true {
didSet {
// UIView'ga constraint'larni yangilash kerakligini aytamiz
setNeedsUpdateConstraints()
}
}
let colorView = UIView()
// ... init ...
override func updateConstraints() {
// SnapKit bilan bu yerda remake yoki update ishlatamiz
colorView.snp.remakeConstraints { make in
make.center.equalToSuperview()
if isRedState {
make.size.equalTo(100)
} else {
make.size.equalTo(200)
}
}
// Super class method'ni chaqirish shart!
super.updateConstraints()
}
}
7. Amaliy loyiha: Expandable Card {#expandable-card}
Ushbu amaliyotda foydalanuvchi bosganda kengayadigan karta (card) yaratamiz. Balandlik constraint'i saqlab olinib, animatsiya bilan o'zgartiriladi.
import UIKit
import SnapKit
@MainActor
class ExpandableCardView: UIView {
private let titleLabel = UILabel()
private let descriptionLabel = UILabel()
private var heightConstraint: Constraint?
private var isExpanded = false
override init(frame: CGRect) {
super.init(frame: frame)
setupView()
setupConstraints()
// Bosish uchun gesture
let tap = UITapGestureRecognizer(target: self, action: #selector(toggleCard))
addGestureRecognizer(tap)
isUserInteractionEnabled = true
}
required init?(coder: NSCoder) { fatalError() }
private func setupView() {
backgroundColor = .systemBackground
layer.cornerRadius = 12
layer.shadowColor = UIColor.black.cgColor
layer.shadowOpacity = 0.1
layer.shadowOffset = CGSize(width: 0, height: 4)
titleLabel.text = "SnapKit sirlari"
titleLabel.font = .boldSystemFont(ofSize: 18)
descriptionLabel.text = "Constraint reference bilan height'ni o'zgartirish juda zo'r pattern! Karta ochilganda bu text ko'rinadi."
descriptionLabel.numberOfLines = 0
descriptionLabel.alpha = 0 // Boshida yashirin
addSubview(titleLabel)
addSubview(descriptionLabel)
}
private func setupConstraints() {
self.snp.makeConstraints { make in
make.width.equalTo(300)
// Karta boshida 80 px balandlikka ega
self.heightConstraint = make.height.equalTo(80).constraint
}
titleLabel.snp.makeConstraints { make in
make.top.leading.equalToSuperview().offset(16)
}
descriptionLabel.snp.makeConstraints { make in
make.top.equalTo(titleLabel.snp.bottom).offset(12)
make.leading.trailing.equalToSuperview().inset(16)
}
}
@objc private func toggleCard() {
isExpanded.toggle()
UIView.animate(withDuration: 0.4,
delay: 0,
usingSpringWithDamping: 0.7,
initialSpringVelocity: 0.5) {
// Balandlik 200 yoki 80 ga o'zgaradi
self.heightConstraint?.update(offset: self.isExpanded ? 200 : 80)
self.descriptionLabel.alpha = self.isExpanded ? 1 : 0
// Superview'dan ham layout yangilanishini so'rashimiz mumkin
self.superview?.layoutIfNeeded()
}
}
}
8. Amaliy loyiha 2: Keyboard-aware input {#keyboard-aware}
TextField ekranning eng pastida turganda, klaviatura ochilsa u yopilib qolmasligi uchun klaviatura balandligi qadar uni tepaga ko'tarish kerak.
+-----------------------+
| |
| |
| |
| |
| [____TextField____] | <--- Keyboard yo'q paytida bottom offset 0
+-----------------------+
+-----------------------+
| |
| [____TextField____] | <--- Keyboard ochilganda bottom offset -(keyboardHeight)
|#######################|
|####### KEYBOARD ######|
|#######################|
+-----------------------+
import UIKit
import SnapKit
@MainActor
class ChatInputViewController: UIViewController {
private let containerView = UIView()
private let textField = UITextField()
private var bottomConstraint: Constraint?
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .systemGray6
setupSubviews()
setupConstraints()
setupKeyboardObservers()
}
private func setupSubviews() {
containerView.backgroundColor = .white
textField.borderStyle = .roundedRect
textField.placeholder = "Xabar yozing..."
view.addSubview(containerView)
containerView.addSubview(textField)
let tap = UITapGestureRecognizer(target: self, action: #selector(dismissKeyboard))
view.addGestureRecognizer(tap)
}
private func setupConstraints() {
containerView.snp.makeConstraints { make in
make.leading.trailing.equalToSuperview()
// Saqlab olinayotgan constraint (safe area'ga nisbatan)
self.bottomConstraint = make.bottom.equalTo(view.safeAreaLayoutGuide).constraint
make.height.equalTo(60)
}
textField.snp.makeConstraints { make in
make.edges.equalToSuperview().inset(10)
}
}
private func setupKeyboardObservers() {
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillShow), name: UIResponder.keyboardWillShowNotification, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillHide), name: UIResponder.keyboardWillHideNotification, object: nil)
}
@objc private func keyboardWillShow(notification: NSNotification) {
guard let keyboardSize = (notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue,
let duration = notification.userInfo?[UIResponder.keyboardAnimationDurationUserInfoKey] as? Double else { return }
let safeAreaBottom = view.safeAreaInsets.bottom
let offset = keyboardSize.height - safeAreaBottom
UIView.animate(withDuration: duration) {
// Negative qiymat, chunki tepaga ko'tarilyapmiz
self.bottomConstraint?.update(offset: -offset)
self.view.layoutIfNeeded()
}
}
@objc private func keyboardWillHide(notification: NSNotification) {
guard let duration = notification.userInfo?[UIResponder.keyboardAnimationDurationUserInfoKey] as? Double else { return }
UIView.animate(withDuration: duration) {
// Joyiga qaytaramiz
self.bottomConstraint?.update(offset: 0)
self.view.layoutIfNeeded()
}
}
@objc private func dismissKeyboard() {
view.endEditing(true)
}
deinit {
NotificationCenter.default.removeObserver(self)
}
}
Xulosa {#xulosa}
- makeConstraints – faqat 1 marta ishlatiladigan eng asosiy usul.
- updateConstraints – eng optimal, lekin cheklangan metod (faqat son/offset o'zgaradi).
- remakeConstraints – to'liq qayta chizish uchun ishlatiladigan xavfsiz metod.
- **Constraint Reference (
var const: Constraint?)** – arxitekturada eng toza, kerak bo'lganda faqat o'zini o'zgartirish, yoqish/o'chirish (activate/deactivate) uchun xizmat qiladigan eng kuchli pattern hisoblanadi.
Bu bilimlar sizning dasturingizni xatolar va crashlardan asrab, turli xil animatsiyalarni ishonch bilan bajarishingizga zamin yaratadi.