ViewController — istota, kontroler ekranu w iOS i jego cykl życia

Autor: IT Sectr Opublikowano: 2026-02-22 Czas czytania: 7 min

UIViewController — centralna klasa aplikacji iOS zarządzająca ekranem i jego zawartością. Każdy ekran iPhone'a lub iPada jest zarządzany przez jeden ViewController, który koordynuje wyświetlanie, cykl życia i nawigację. Więcej o architekturze UIKit przeczytasz w oficjalnej dokumentacji Apple.

Najważniejsze

  • UIViewController — klasa bazowa do zarządzania ekranem w UIKit z własnym cyklem życia
  • viewDidLoad — wywoływana raz, punkt inicjalizacji UI i subskrypcji danych
  • viewWillAppear — ekran wkrótce stanie się widoczny, aktualizacja danych przed wyświetleniem
  • Cykl życia obejmuje pięć metod: viewDidLoad, viewWillAppear, viewDidAppear, viewWillDisappear, viewDidDisappear
  • Massive View Controller — główny antywzorzec iOS, rozwiązywany przez MVVM lub Coordinator

Czym jest ViewController?

UIViewController — klasa z frameworka UIKit, która zarządza hierarchią UIView i koordynuje wyświetlanie danych na ekranie. Każda aplikacja iOS zawiera co najmniej jeden ViewController — kontroler główny okna. Kontroler obsługuje obroty ekranu, przejścia między ekranami i zdarzenia cyklu życia.

Architektura MVC (Model-View-Controller) w iOS jest zaimplementowana właśnie przez UIViewController: kontroler otrzymuje dane z modelu i aktualizuje widok. ViewController nie jest elementem wizualnym — zarządza właściwością view, która zawiera hierarchię subview. Według danych Apple (2026), UIKit zawiera ponad 40 wbudowanych podklas UIViewController.

Pierwszy iPhone SDK (2008) zawierał UIViewController z trzema metodami cyklu życia. Przez 18 lat Apple dodała obsługę Container View Controller, adaptacyjne prezentacje, UIViewControllerTransitioningDelegate dla niestandardowych animacji i tryb podzielonego ekranu na iPadzie. UIViewController pozostaje obowiązkowym komponentem dla aplikacji na UIKit.

Cykl życia UIViewController

Cykl życia UIViewController — sekwencja metod wywoływanych przez system przy tworzeniu, wyświetlaniu i ukrywaniu ekranu. Zrozumienie cyklu życia jest krytyczne: nieprawidłowe umieszczenie kodu prowadzi do wycieków pamięci, zbędnych zapytań sieciowych i migotania interfejsu.

MetodaMoment wywołaniaPrzeznaczenie
viewDidLoadRaz, po załadowaniu view do pamięciPoczątkowa konfiguracja UI, subskrypcja Combine
viewWillAppearPrzed pojawieniem się ekranuAktualizacja danych, ukrycie/pokazanie navigation bar
viewDidAppearPo pojawieniu się ekranuUruchomienie animacji, analityka, aktualizacja kamery
viewWillDisappearPrzed opuszczeniem ekranuZapisanie szkiców, wypisanie z powiadomień
viewDidDisappearPo opuszczeniu ekranuZatrzymanie ciężkich procesów, zwolnienie zasobów

Kolejność wywołania przy pojawieniu się ekranu

Przy pierwszym wyświetleniu ekranu sekwencja: init → loadView → viewDidLoad → viewWillAppear → viewDidAppear. Przy ponownym pojawieniu się (powrót z innego ekranu): viewWillAppear → viewDidAppear. viewDidLoad wywoływana jest tylko raz w ciągu życia kontrolera.

viewDidLoad, init i konfiguracja UI

Metoda viewDidLoad — główny punkt konfiguracji interfejsu użytkownika. Wywoływana po załadowaniu view do pamięci, gdy wszystkie IBOutlet są już połączone. Tutaj tworzone są elementy UI programowo, konfigurowane są constraints, ładowane są początkowe dane.

swift
final class ProfileViewController: UIViewController {

    private let tableView = UITableView()
    private let viewModel = ProfileViewModel()

    override func viewDidLoad() {
        super.viewDidLoad()
        setupUI()
        bindViewModel()
    }

    private func setupUI() {
        view.addSubview(tableView)
        tableView.translatesAutoresizingMaskIntoConstraints = false
        NSLayoutConstraint.activate([
            tableView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor),
            tableView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
            tableView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
            tableView.bottomAnchor.constraint(equalTo: view.bottomAnchor)
        ])
        tableView.register(ProfileCell.self,
                         forCellReuseIdentifier: ProfileCell.reuseId)
    }

    private func bindViewModel() {
        viewModel.$user
            .receive(on: DispatchQueue.main)
            .sink { [weak self] user in
                self?.title = user.name
            }
            .store(in: &cancellables)
    }
}

W SwiftUI kod ten jest odpowiednikiem ciała View. Ale UIViewController daje pełną kontrolę nad cyklem życia i optymalizacją. bindViewModel używa Combine do reaktywnej subskrypcji — dane aktualizują się automatycznie przy zmianie modelu.

viewWillAppear i aktualizacja danych

viewWillAppear wywoływana jest każdorazowo przed pojawieniem się ekranu, nawet jeśli był już w pamięci. To miejsce do aktualizacji danych, które mogły się zmienić na innym ekranie: przeładowanie listy, aktualizacja licznika powiadomień, konfiguracja navigation bar dla konkretnego ekranu.

swift
override func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(animated)

    // Ukryj pasek nawigacji na tym ekranie
    navigationController?.setNavigationBarHidden(true, animated: animated)

    // Aktualizuj dane przy powrocie z innego ekranu
    tableView.reloadData()
    badgeLabel.text = "\(CartManager.shared.itemCount)"
}

override func viewDidAppear(_ animated: Bool) {
    super.viewDidAppear(animated)

    // Analityka: dopiero gdy użytkownik zobaczył ekran
    AnalyticsService.shared.logScreenView("Profile")
}

Różnica między viewDidLoad a viewWillAppear jest krytyczna: viewDidLoad wykonuje się raz i nadaje się do statycznej konfiguracji, viewWillAppear — za każdym razem przy wyświetleniu, nadaje się do dynamicznych aktualizacji. Umieszczenie zapytań sieciowych w viewDidLoad spowoduje pokazanie nieaktualnych danych przy powrocie na ekran.

Container View Controller: UINavigationController i UITabBarController

Container View Controller — ViewController, który zarządza jednym lub wieloma podrzędnymi ViewController. Apple udostępnia trzy wbudowane kontenery: UINavigationController (stos ekranów), UITabBarController (zakładki) i UISplitViewController (master-detail dla iPada).

UINavigationController organizuje przejścia w stos — push dodaje ekran, pop usuwa. UITabBarController przełącza między niezależnymi sekcjami aplikacji. UISplitViewController pokazuje dwa kontrolery obok siebie na iPadzie i jeden na iPhonie. Deweloper może stworzyć własny kontener przez addChild.

swift
// Niestandardowy Container View Controller
final class ContainerViewController: UIViewController {

    private let sidebarVC = SidebarViewController()
    private let contentVC = ContentViewController()

    override func viewDidLoad() {
        super.viewDidLoad()

        // Dodawanie kontrolera podrzędnego
        addChild(sidebarVC)
        view.addSubview(sidebarVC.view)
        sidebarVC.didMove(toParent: self)

        addChild(contentVC)
        view.addSubview(contentVC.view)
        contentVC.didMove(toParent: self)
    }
}

Prawidłowa praca z Container View Controller wymaga wywołania addChild, dodania view i didMove(toParent:) w tej kolejności. Przy usuwaniu — willMove(toParent: nil), removeFromSuperview, removeFromParent. Naruszenie kolejności prowadzi do wycieków pamięci.

Rozwiązanie Massive View Controller przez MVVM i Coordinator

Problem Massive View Controller — gdy UIViewController zawiera setki linii kodu z logiką biznesową, zapytaniami sieciowymi, nawigacją i kodem UI. Apple zdaje sobie sprawę z problemu i zaleca MVVM (Model-View-ViewModel) wraz z Coordinator do wyodrębnienia nawigacji.

MVVM przenosi logikę biznesową z kontrolera do ViewModel. Controller tylko łączy ViewModel z View przez Combine lub delegat. Coordinator wyodrębnia logikę nawigacyjną — tworzenie i przejścia między kontrolerami — do osobnej klasy. To podejście zostało wdrożone do najlepszych praktyk Apple od 2024 roku.

swift
// Coordinator — zarządzanie nawigacją
protocol Coordinator {
    var childCoordinators: [Coordinator] { get set }
    func start()
}

final class MainCoordinator: Coordinator {

    var childCoordinators = [Coordinator]()
    private let navigationController: UINavigationController

    init(navigationController: UINavigationController) {
        self.navigationController = navigationController
    }

    func start() {
        let vc = ListViewController()
        vc.didSelectItem = { [weak self] item in
            self?.showDetail(item)
        }
        navigationController.pushViewController(vc, animated: false)
    }

    private func showDetail(_ item: Item) {
        let vc = DetailViewController(item: item)
        navigationController.pushViewController(vc, animated: true)
    }
}

UIViewController vs SwiftUI: kiedy co wybrać

Wybór między UIViewController a SwiftUI View zależy od roku rozpoczęcia projektu, wymagań dotyczących dostosowania i minimalnej obsługiwanej wersji iOS. UIKit z UIViewController pozostaje podstawą dla projektów rozpoczętych przed 2020 rokiem oraz dla aplikacji z głęboką personalizacją interfejsu.

SwiftUI nadaje się do nowych projektów z iOS 17+, standardowych interfejsów i prototypów. Jednak do niestandardowych przejść, pracy z kamerą, MapKit, złożonych animacji CALayer wymagany jest UIViewController. Apple zaleca łączenie podejść przez UIHostingController (SwiftUI w UIKit) i UIViewRepresentable (UIKit w SwiftUI).

ScenariuszUIKit (UIViewController)SwiftUI (View)
Niestandardowa animacjaPełna kontrola przez UIViewPropertyAnimatorOgraniczona przez Animation
Praca z kamerąAVCaptureSession + UIViewPreviewPrzez UIViewControllerRepresentable
CollectionViewUICollectionView + UICollectionViewLayoutLazyVGrid/LazyHGrid
Adaptacja iPadUISplitViewController + UITraitCollectionNavigationSplitView + sizeClass
Szybkość rozwojuWolniejsze (ręczny layout)Szybsze (deklaratywne)

Często zadawane pytania

Czym różni się UIViewController od UIView?

UIViewController — kontroler zarządzający ekranem i jego cyklem życia. UIView — widok wyświetlający treść. ViewController zawiera hierarchię UIView, ale sam nie jest elementem wizualnym. Jeden kontroler zarządza wieloma widokami.

Czym jest Massive View Controller?

Massive View Controller — antywzorzec, gdy UIViewController zawiera zbyt dużo logiki: dane, nawigację, zapytania sieciowe, animacje. Rozwiązanie — wydzielenie kodu do osobnych serwisów, koordynatorów i ViewModel (MVVM).

Jak przekazać dane między ViewController?

Cztery sposoby: przez właściwość przy prepare(for:sender:) (Segue), przez delegata (Delegate), przez domknięcie (Closure), przez wspólny serwis. Dla słabego powiązania używa się Coordinator + Delegate lub Combine.

Czym jest Container View Controller?

Container View Controller — kontroler zarządzający podrzędnymi ViewController. Przykłady: UINavigationController, UITabBarController, UISplitViewController. Kontroler rodzicielski dodaje podrzędne przez addChild, przełącza między nimi i zarządza ich layoutem.

Kiedy używać UIViewController zamiast SwiftUI View?

UIViewController — do złożonych niestandardowych animacji, pracy z kamerą, mapą, wideo, UICollectionView z niestandardowym layoutem. SwiftUI View — do standardowych interfejsów iOS 13+. Dopuszczalne jest łączenie przez UIHostingController.

Podsumowanie

  • UIViewController — centralna klasa UIKit do zarządzania ekranem, hierarchią UIView i cyklem życia
  • Cykl życia składa się z pięciu metod: viewDidLoad, viewWillAppear, viewDidAppear, viewWillDisappear, viewDidDisappear
  • viewDidLoad — punkt początkowej konfiguracji UI, wywoływana raz w ciągu życia kontrolera
  • viewWillAppear — wywoływana każdorazowo przed wyświetleniem, nadaje się do aktualizacji danych i konfiguracji navigation bar
  • Container View Controller (UINavigationController, UITabBarController) zarządza hierarchią podrzędnych kontrolerów
  • Massive View Controller rozwiązuje się przez MVVM (wydzielenie logiki do ViewModel) i Coordinator (wydzielenie nawigacji)
  • UIViewController i SwiftUI można łączyć przez UIHostingController i UIViewRepresentable dla aplikacji hybrydowych

Opracujemy aplikację mobilną pod klucz

IT Sectr tworzy aplikacje na iOS i Androida dla startupów i firm od 2017 roku. Doradzimy Ci i zaproponujemy najlepsze rozwiązanie.

Omów projekt

Przeczytaj również