ViewController — bản chất của bộ điều khiển màn hình trong iOS và Lifecycle của nó

Tác giả: IT Sectr Đã đăng: 2026-02-22 Thời gian đọc: 7 phút

UIViewController là lớp trung tâm của các ứng dụng iOS, quản lý màn hình và nội dung của nó. Mỗi màn hình iPhone hoặc iPad được quản lý bởi một ViewController, lớp này điều phối việc hiển thị, vòng đời và điều hướng. Tìm hiểu thêm về kiến trúc UIKit tại tài liệu chính thức của Apple.

Những Điểm Chính

  • UIViewController — lớp cơ sở để quản lý màn hình trong UIKit với vòng đời riêng
  • viewDidLoad — được gọi một lần, điểm khởi tạo UI và đăng ký dữ liệu
  • viewWillAppear — màn hình sắp hiển thị, cập nhật dữ liệu trước khi hiển thị
  • Lifecycle bao gồm năm phương thức: viewDidLoad, viewWillAppear, viewDidAppear, viewWillDisappear, viewDidDisappear
  • Massive View Controller — phản mẫu chính của iOS, được giải quyết qua MVVM hoặc Coordinator

ViewController là gì?

UIViewController là một lớp từ framework UIKit quản lý hệ thống phân cấp UIView và điều phối việc hiển thị dữ liệu trên màn hình. Mỗi ứng dụng iOS chứa ít nhất một ViewController — bộ điều khiển gốc của cửa sổ. Bộ điều khiển xử lý xoay màn hình, chuyển tiếp giữa các màn hình và các sự kiện vòng đời.

Kiến trúc MVC (Model-View-Controller) trong iOS được triển khai chính xác thông qua UIViewController: bộ điều khiển nhận dữ liệu từ mô hình và cập nhật chế độ xem. ViewController không phải là một phần tử trực quan — nó quản lý thuộc tính view, chứa hệ thống phân cấp của các subview. Theo Apple (2026), UIKit chứa hơn 40 lớp con tích hợp sẵn của UIViewController.

iPhone SDK đầu tiên (2008) bao gồm UIViewController với ba phương thức vòng đời. Trong 18 năm, Apple đã thêm hỗ trợ cho Container View Controller, trình bày thích ứng, UIViewControllerTransitioningDelegate cho hoạt ảnh tùy chỉnh và chế độ màn hình chia đôi trên iPad. UIViewController vẫn là một thành phần bắt buộc cho các ứng dụng UIKit.

Vòng đời của UIViewController

Vòng đời của UIViewController là một chuỗi các phương thức được hệ thống gọi khi tạo, hiển thị và ẩn màn hình. Hiểu được Lifecycle là cực kỳ quan trọng: đặt mã sai vị trí dẫn đến rò rỉ bộ nhớ, yêu cầu mạng không cần thiết và giao diện nhấp nháy.

Phương thứcThời điểm gọiMục đích
viewDidLoadMột lần, sau khi view được tải vào bộ nhớThiết lập UI ban đầu, đăng ký Combine
viewWillAppearTrước khi màn hình xuất hiệnCập nhật dữ liệu, ẩn/hiện thanh điều hướng
viewDidAppearSau khi màn hình xuất hiệnBắt đầu hoạt ảnh, phân tích, cập nhật máy ảnh
viewWillDisappearTrước khi rời khỏi màn hìnhLưu bản nháp, hủy đăng ký thông báo
viewDidDisappearSau khi rời khỏi màn hìnhDừng các tiến trình nặng, giải phóng tài nguyên

Thứ tự gọi khi màn hình xuất hiện

Khi màn hình hiển thị lần đầu, thứ tự là: init → loadView → viewDidLoad → viewWillAppear → viewDidAppear. Khi xuất hiện lại (quay lại từ màn hình khác): viewWillAppear → viewDidAppear. viewDidLoad chỉ được gọi một lần trong suốt vòng đời của bộ điều khiển.

viewDidLoad, init và thiết lập UI

Phương thức viewDidLoad là điểm chính để cấu hình giao diện người dùng. Nó được gọi sau khi view được tải vào bộ nhớ, khi tất cả các kết nối IBOutlet đã được thiết lập. Tại đây, các phần tử UI được tạo theo chương trình, các ràng buộc được cấu hình và dữ liệu ban đầu được tải.

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)
    }
}

Trong SwiftUI, mã này tương đương với phần thân của View. Nhưng UIViewController cung cấp toàn quyền kiểm soát vòng đời và tối ưu hóa. bindViewModel sử dụng Combine để đăng ký phản ứng — dữ liệu tự động cập nhật khi mô hình thay đổi.

viewWillAppear và cập nhật dữ liệu

viewWillAppear được gọi mỗi lần trước khi màn hình xuất hiện, ngay cả khi nó đã có trong bộ nhớ. Đây là nơi cập nhật dữ liệu có thể đã thay đổi trên màn hình khác: tải lại danh sách, cập nhật huy hiệu thông báo, cấu hình thanh điều hướng cho một màn hình cụ thể.

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

    // Ẩn thanh điều hướng trên màn hình này
    navigationController?.setNavigationBarHidden(true, animated: animated)

    // Cập nhật dữ liệu khi quay lại từ màn hình khác
    tableView.reloadData()
    badgeLabel.text = "\(CartManager.shared.itemCount)"
}

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

    // Phân tích: chỉ sau khi người dùng đã nhìn thấy màn hình
    AnalyticsService.shared.logScreenView("Profile")
}

Sự khác biệt giữa viewDidLoad và viewWillAppear là rất quan trọng: viewDidLoad chạy một lần và phù hợp cho thiết lập tĩnh, viewWillAppear chạy mỗi khi màn hình được hiển thị và phù hợp cho cập nhật động. Đặt yêu cầu mạng trong viewDidLoad sẽ dẫn đến hiển thị dữ liệu cũ khi quay lại màn hình.

Container View Controller: UINavigationController và UITabBarController

Container View Controller là một ViewController quản lý một hoặc nhiều ViewController con. Apple cung cấp ba vùng chứa tích hợp: UINavigationController (ngăn xếp màn hình), UITabBarController (tab) và UISplitViewController (master-detail cho iPad).

UINavigationController tổ chức các chuyển tiếp trong một ngăn xếp — push thêm màn hình, pop xóa màn hình. UITabBarController chuyển đổi giữa các phần độc lập của ứng dụng. UISplitViewController hiển thị hai bộ điều khiển cạnh nhau trên iPad và một trên iPhone. Nhà phát triển có thể tạo vùng chứa tùy chỉnh thông qua addChild.

swift
// Container View Controller tùy chỉnh
final class ContainerViewController: UIViewController {

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

    override func viewDidLoad() {
        super.viewDidLoad()

        // Thêm bộ điều khiển con
        addChild(sidebarVC)
        view.addSubview(sidebarVC.view)
        sidebarVC.didMove(toParent: self)

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

Làm việc đúng cách với Container View Controller yêu cầu gọi addChild, thêm view và didMove(toParent:) theo thứ tự này. Khi xóa: willMove(toParent: nil), removeFromSuperview, removeFromParent. Vi phạm thứ tự dẫn đến rò rỉ bộ nhớ.

Giải quyết Massive View Controller với MVVM và Coordinator

Vấn đề Massive View Controller xảy ra khi một UIViewController chứa hàng trăm dòng mã với logic kinh doanh, yêu cầu mạng, điều hướng và mã UI. Apple nhận thức được vấn đề và khuyến nghị MVVM (Model-View-ViewModel) cùng với Coordinator để tách điều hướng.

MVVM di chuyển logic kinh doanh từ bộ điều khiển sang ViewModel. Controller chỉ liên kết ViewModel với View thông qua Combine hoặc ủy quyền. Coordinator tách logic điều hướng — tạo và chuyển tiếp giữa các bộ điều khiển — thành một lớp riêng biệt. Cách tiếp cận này đã được áp dụng trong các phương pháp hay nhất của Apple từ năm 2024.

swift
// Coordinator — quản lý điều hướng
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: khi nào chọn cái gì

Sự lựa chọn giữa UIViewController và SwiftUI View phụ thuộc vào năm bắt đầu dự án, yêu cầu tùy chỉnh và phiên bản iOS tối thiểu được hỗ trợ. UIKit với UIViewController vẫn là nền tảng cho các dự án bắt đầu trước năm 2020 và cho các ứng dụng có tùy chỉnh giao diện sâu.

SwiftUI phù hợp cho các dự án mới với iOS 17+, giao diện tiêu chuẩn và nguyên mẫu. Tuy nhiên, các chuyển tiếp tùy chỉnh, làm việc với máy ảnh, MapKit, hoạt ảnh CALayer phức tạp yêu cầu UIViewController. Apple khuyến nghị kết hợp các cách tiếp cận thông qua UIHostingController (SwiftUI trong UIKit) và UIViewRepresentable (UIKit trong SwiftUI).

Tình huốngUIKit (UIViewController)SwiftUI (View)
Hoạt ảnh tùy chỉnhKiểm soát hoàn toàn qua UIViewPropertyAnimatorGiới hạn qua Animation
Làm việc với máy ảnhAVCaptureSession + UIViewPreviewQua UIViewControllerRepresentable
CollectionViewUICollectionView + UICollectionViewLayoutLazyVGrid/LazyHGrid
Thích ứng iPadUISplitViewController + UITraitCollectionNavigationSplitView + sizeClass
Tốc độ phát triểnChậm hơn (bố cục thủ công)Nhanh hơn (khai báo)

Câu Hỏi Thường Gặp

UIViewController khác với UIView như thế nào?

UIViewController là bộ điều khiển quản lý màn hình và vòng đời của nó. UIView là chế độ xem hiển thị nội dung. ViewController chứa hệ thống phân cấp UIViews nhưng bản thân nó không phải là phần tử trực quan. Một bộ điều khiển quản lý nhiều chế độ xem.

Massive View Controller là gì?

Massive View Controller là phản mẫu khi UIViewController chứa quá nhiều logic: dữ liệu, điều hướng, yêu cầu mạng, hoạt ảnh. Giải pháp là tách mã vào các dịch vụ riêng biệt, bộ điều phối và ViewModel (MVVM).

Làm thế nào để truyền dữ liệu giữa các ViewControllers?

Bốn cách: thông qua thuộc tính trong prepare(for:sender:) (Segue), thông qua ủy quyền (Delegate), thông qua bao đóng (Closure), thông qua dịch vụ chia sẻ. Để kết nối lỏng lẻo, hãy sử dụng Coordinator + Delegate hoặc Combine.

Container View Controller là gì?

Container View Controller là bộ điều khiển quản lý các ViewController con. Ví dụ: UINavigationController, UITabBarController, UISplitViewController. Bộ điều khiển cha thêm con qua addChild, chuyển đổi giữa chúng và quản lý bố cục của chúng.

Khi nào sử dụng UIViewController thay vì SwiftUI View?

UIViewController — cho hoạt ảnh tùy chỉnh phức tạp, làm việc với máy ảnh, bản đồ, video, UICollectionView với bố cục tùy chỉnh. SwiftUI View — cho giao diện tiêu chuẩn trên iOS 13+. Kết hợp qua UIHostingController là chấp nhận được.

Tóm Tắt

  • UIViewController — lớp UIKit trung tâm để quản lý màn hình, hệ thống phân cấp UIView và vòng đời
  • Lifecycle bao gồm năm phương thức: viewDidLoad, viewWillAppear, viewDidAppear, viewWillDisappear, viewDidDisappear
  • viewDidLoad — điểm thiết lập UI ban đầu, được gọi một lần trong suốt vòng đời của bộ điều khiển
  • viewWillAppear — được gọi mỗi lần trước khi hiển thị, phù hợp để cập nhật dữ liệu và cấu hình thanh điều hướng
  • Container View Controller (UINavigationController, UITabBarController) quản lý hệ thống phân cấp các bộ điều khiển con
  • Massive View Controller được giải quyết qua MVVM (tách logic vào ViewModel) và Coordinator (tách điều hướng)
  • UIViewController và SwiftUI có thể được kết hợp qua UIHostingController và UIViewRepresentable cho các ứng dụng kết hợp

Chúng tôi sẽ phát triển ứng dụng di động chìa khóa trao tay

IT Sectr tạo các ứng dụng iOS và Android cho các công ty khởi nghiệp và doanh nghiệp từ năm 2017. Chúng tôi sẽ tư vấn và đề xuất giải pháp tốt nhất cho bạn.

Thảo luận dự án

Đọc thêm