Azimjon Abdurasulov

UIKit 3: UIViewController hayot sikli — viewIsAppearing, containment, trait va UIScene migratsiyasi

📱 UIKit va SwiftUI — chuqur

UIKit 3: UIViewController hayot sikli — viewIsAppearing, containment, trait va UIScene migratsiyasi

Holat: 2026-yil sentabr. Ko'plab dasturchilar UIViewController hayot siklini hanuz eski iOS 12 qoidalari bo'yicha tushunadilar. Biroq Apple zamonaviy tizimlarda viewIsAppearing metodini standart qildi, AppDelegate ning asosiy hayot sikli metodlarini iOS 26 da eskirgan (deprecated) deb e'lon qildi va iOS 27 SDK da UIScene ni majburiy talabga aylantirdi. Ushbu darsda zamonaviy hayot sikli, Container View Controller va UIScene arxitekturasi tahlil qilinadi.

Mundarija

  1. UIViewController to'liq hayot sikli xaritasi
  2. loadView() qachon va qanday yoziladi?
  3. viewIsAppearing: viewWillAppear va viewDidAppear o'rtasidagi inqilob
  4. Custom Container View Controller: 4 qadamli protokol
  5. Modal taqdimot va iOS 27 da Trait merosxo'rligi
  6. UIScene migratsiyasi: iOS 26 deprecatsiyalari va iOS 27 talabi
  7. UIWindowScene bilan to'g'ri initsializatsiya
  8. Xulosa
  9. Manbalar

1. UIViewController to'liq hayot sikli xaritasi {#lifecycle-map}

Ekran yaratilishidan to xotiradan o'chirilgunigacha quyidagi tartibda bosqichlarni bosib o'tadi:

[init(nibName:bundle:) / init(coder:)]
        |
        v
[loadView()]  <--- View obyektini yaratish (faqat bir marta)
        |
        v
[viewDidLoad()]  <--- View yuklandi, bir martalik sozlashlar
        |
        v
[viewWillAppear(_:)]  <--- Ko'rinish arafasida (geometriya hali noaniq bo'lishi mumkin)
        |
        v
[viewIsAppearing(_:)]  <--- TAVSIYA ETILADI: View ierarxiyada, frame va trait tayyor!
        |
        v
[viewDidAppear(_:)]  <--- Foydalanuvchi ko'rib turibdi, animatsiyalar
        |
        v
[viewWillDisappear(_:)]  <--- Ekrandan ketish arafasida
        |
        v
[viewDidDisappear(_:)]  <--- Ekrandan butunlay ketdi
        |
        v
[deinit]  <--- Xotiradan to'liq bo'shatildi

2. loadView() qachon va qanday yoziladi? {#load-view}

loadView() metodining yagona vazifasi — self.view xususiyatiga boshlang'ich UIView nusxasini tayinlashdir.

  • Qachon yoziladi? Agar siz Storyboard/XIB ishlatmasdan, butun ekranni o'zingiz yaratgan maxsus view (masalan, ProfileRootView) bilan to'ldirmoqchi bo'lsangiz.
  • Qat'iy qoida: loadView() ichida **hech qachon super.loadView() chaqirilmasligi kerak!**
import UIKit

public final class ProfileViewController: UIViewController {
  public override func loadView() {
    // super.loadView() CHAQRILMAYDI!
    let customView = UIView()
    customView.backgroundColor = .systemBackground
    self.view = customView
  }

  public override func viewDidLoad() {
    super.viewDidLoad()
    // Elementlarni sozlash
  }
}

3. viewIsAppearing: viewWillAppear va viewDidAppear o'rtasidagi inqilob {#view-is-appearing}

Ilgari dasturchilar katta muammoga duch kelishar edi:

  • viewWillAppear da view hali oynaga qo'shilmagan bo'ladi, uning o'lchami (bounds) va traitCollection (Dark/Light mode, Compact/Regular) hali to'liq aniqlanmagan bo'lishi mumkin.
  • viewDidAppear da esa o'lcham aniq, lekin foydalanuvchi ekranni allaqachon ko'rib turgani uchun unda o'lcham o'zgarishi ko'zga tashlanadigan sakrash (glitch) keltirib chiqaradi.

Apple ushbu muammoni hal qilish uchun **viewIsAppearing(_:)** metodini kiritdi:

  • U viewWillAppear dan keyin, lekin viewDidAppear dan oldin bajariladi.
  • Bu vaqtda view oynaning ierarxiyasiga qo'shib bo'lingan va uning haqiqiy ekrandagi o'lchami (geometry) hamda trait parametrlari 100% to'g'ri o'rnatilgan bo'ladi!
  • Layoutga bog'liq boshlang'ich sozlashlar aynan shu yerda qilinishi eng to'g'ri amaliyotdir.
import UIKit

public final class AdaptiveViewController: UIViewController {
  public override func viewIsAppearing(_ animated: Bool) {
    super.viewIsAppearing(animated)

    // Bu yerda view.bounds va traitCollection to'liq haqiqiy qiymatga ega:
    if traitCollection.horizontalSizeClass == .compact {
      // iPhone portret rejimi uchun sozlash
    } else {
      // iPad yoki gorizontal rejim uchun sozlash
    }
  }
}

4. Custom Container View Controller: 4 qadamli protokol {#containment}

Ko'plab murakkab arxitekturalarda (masalan, TabBar, Navigation yoki maxsus BottomSheet) bitta View Controller boshqasini o'z ichiga oladi.

Agar child controllerni shunchaki view.addSubview(child.view) qilib qo'ysangiz, u etim (orphan) bo'lib qoladi va tizim unga viewWillAppear, traitCollectionDidChange kabi hayot sikli xabarlarini uzatmaydi!

Qo'shishning qat'iy protokoli:

  1. addChild(child) — ota-bola aloqasini o'rnatadi.
  2. view.addSubview(child.view) — view ierarxiyasini ulaydi.
  3. Frame yoki Auto Layout cheklovlarini o'rnatish.
  4. child.didMove(toParent: self) — qo'shilish yakunlanganini xabar qiladi.

Olib tashlashning qat'iy protokoli:

  1. child.willMove(toParent: nil) — ketishini ogohlantiradi.
  2. child.view.removeFromSuperview() — view'ni oladi.
  3. child.removeFromParent() — ota-bola aloqasini uzadi.
import UIKit

public final class ContainerViewController: UIViewController {
  private let contentVC = UIViewController()

  public override func viewDidLoad() {
    super.viewDidLoad()

    // 1. Qo'shish
    addChild(contentVC)
    contentVC.view.frame = view.bounds
    contentVC.view.autoresizingMask = [.flexibleWidth, .flexibleHeight]
    view.addSubview(contentVC.view)
    contentVC.didMove(toParent: self)
  }

  public func removeContent() {
    // 2. To'g'ri tozalash
    contentVC.willMove(toParent: nil)
    contentVC.view.removeFromSuperview()
    contentVC.removeFromParent()
  }
}

5. Modal taqdimot va iOS 27 da Trait merosxo'rligi {#presentation-traits}

Modallarni ko'rsatishda modalPresentationStyle tanlanadi:

  • .fullScreen: Butun ekranni egallaydi, ostidagi view ierarxiyadan vaqtincha chiqariladi (viewDidDisappear chaqiriladi).
  • .pageSheet: Karta ko'rinishida tepadan ochiladi, ostidagi view ko'rinib turadi.

iOS 27 da muhim o'zgarish:

Apple'ning iOS 27 Release Notes rasmiy hujjatiga ko'ra: UIPresentationController orqali taqdim etilgan view controller endi trait parametrlarini (masalan, Dark Mode, Size Classes) to'g'ridan-to'g'ri presentation controller'dan emas, balki superview zanjiri orqali meros oladi (iOS 27 Release Notes). Custom presentation controller yaratuvchi dasturchilar buni hisobga olishlari shart.


6. UIScene migratsiyasi: iOS 26 deprecatsiyalari va iOS 27 talabi {#ui-scene}

Ko'p yillar davomida ilovaning hayot sikli AppDelegate ichida boshqarib kelingan edi (applicationDidBecomeActive, applicationDidEnterBackground).

iOS 26 da rasman eskirgan metodlar (sdk_facts.md bo'yicha):

Apple iOS 26 SDK'da quyidagi metodlarni rasman API_DEPRECATED qildi (sdk_facts.md):

  • applicationDidBecomeActive:
  • applicationWillResignActive:
  • applicationDidEnterBackground:
  • applicationWillEnterForeground:
  • application:openURL:options:
  • application:performActionForShortcutItem:completionHandler:

Tizim ogohlantirishi: "Use UIScene lifecycle and sceneDidBecomeActive(_:) from UISceneDelegate instead."

iOS 27 qat'iy majburiyati:

Apple'ning rasmiy "Transitioning to the UIKit scene-based life cycle" hujjatida so'zma-so'z keltirilgan:

"apps built with the latest SDK must adopt the scene-based life cycle or they fail to launch" (Apple Docs — Transitioning to UIScene).

Agar sizning ilovangiz iOS 27 SDK bilan yig'ilsa va unda UIScene sozlanmagan bo'lsa, ilova ishga tushmasdan qulaydi (crash bo'ladi)!


7. UIWindowScene bilan to'g'ri initsializatsiya {#window-scene}

iOS 26 da eski UIWindow(frame: UIScreen.main.bounds) konstruktori ham eskirgan deb belgilandi: API_DEPRECATED("Use init(windowScene:) instead.").

Zamonaviy SceneDelegate orqali oynani to'g'ri ulash:

import UIKit

public final class SceneDelegate: UIResponder, UIWindowSceneDelegate {
  public var window: UIWindow?

  public func scene(
    _ scene: UIScene,
    willConnectTo session: UISceneSession,
    options connectionOptions: UIScene.ConnectionOptions
  ) {
    guard let windowScene = scene as? UIWindowScene else { return }

    // UIWindow(windowScene:) zamonaviy standartdir:
    let window = UIWindow(windowScene: windowScene)
    window.rootViewController = UIViewController()
    self.window = window
    window.makeKeyAndVisible()
  }
}

8. Xulosa {#xulosa}

  • loadView() faqat maxsus root view tayinlash uchun yoziladi va unda super.loadView() chaqirilmaydi.
  • viewIsAppearing geometriya va trait'larga bog'liq kodlar uchun eng to'g'ri joydir, chunki bu paytda view oynaga to'liq ulangan bo'ladi.
  • Custom Container View Controller'da 4 bosqichli qoidaga amal qilish shart, aks holda child controller hayot sikli hodisalaridan uzilib qoladi.
  • iOS 26 da AppDelegate ning asosiy hayot sikli metodlari va UIWindow(frame:) rasman deprecated qilindi.
  • iOS 27 SDK da yig'ilgan har qanday ilova UIScene arxitekturasiga o'tishi shart, aks holda ishga tushmaydi.

Manbalar {#manbalar}

  1. UIViewController Lifecycle — Apple Documentation
  2. Transitioning to the UIKit Scene-Based Life Cycle — Apple
  3. iOS 27 Release Notes — Apple Developer
  4. Creating a Custom Container View Controller — Apple

Markdown manba

iOS Lessons