# 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](#lifecycle-map)
2. [loadView() qachon va qanday yoziladi?](#load-view)
3. [viewIsAppearing: viewWillAppear va viewDidAppear o'rtasidagi inqilob](#view-is-appearing)
4. [Custom Container View Controller: 4 qadamli protokol](#containment)
5. [Modal taqdimot va iOS 27 da Trait merosxo'rligi](#presentation-traits)
6. [UIScene migratsiyasi: iOS 26 deprecatsiyalari va iOS 27 talabi](#ui-scene)
7. [UIWindowScene bilan to'g'ri initsializatsiya](#window-scene)
8. [Xulosa](#xulosa)
9. [Manbalar](#manbalar)

---

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

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

```text
[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!**

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

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

```swift
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](https://developer.apple.com/documentation/ios-ipados-release-notes/ios-ipados-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](file:///Users/codingtech/Documents/GitHub/azimjondev.uz/lessons-handoff/research-uikit/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](https://developer.apple.com/documentation/uikit/transitioning-to-the-uikit-scene-based-life-cycle)).

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:

```swift
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](https://developer.apple.com/documentation/uikit/uiviewcontroller)
2. [Transitioning to the UIKit Scene-Based Life Cycle — Apple](https://developer.apple.com/documentation/uikit/transitioning-to-the-uikit-scene-based-life-cycle)
3. [iOS 27 Release Notes — Apple Developer](https://developer.apple.com/documentation/ios-ipados-release-notes/ios-ipados-27-release-notes)
4. [Creating a Custom Container View Controller — Apple](https://developer.apple.com/documentation/uikit/view_controllers/creating_a_custom_container_view_controller)
