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à 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 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ức | Thời điểm gọi | Mục đích |
|---|---|---|
| viewDidLoad | Một lần, sau khi view được tải vào bộ nhớ | Thiết lập UI ban đầu, đăng ký Combine |
| viewWillAppear | Trước khi màn hình xuất hiện | Cập nhật dữ liệu, ẩn/hiện thanh điều hướng |
| viewDidAppear | Sau khi màn hình xuất hiện | Bắt đầu hoạt ảnh, phân tích, cập nhật máy ảnh |
| viewWillDisappear | Trước khi rời khỏi màn hình | Lưu bản nháp, hủy đăng ký thông báo |
| viewDidDisappear | Sau khi rời khỏi màn hình | Dừng các tiến trình nặng, giải phóng tài nguyê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.
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.
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 đượ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ể.
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 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.
// 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ớ.
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.
// 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)
}
}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ống | UIKit (UIViewController) | SwiftUI (View) |
|---|---|---|
| Hoạt ảnh tùy chỉnh | Kiểm soát hoàn toàn qua UIViewPropertyAnimator | Giới hạn qua Animation |
| Làm việc với máy ảnh | AVCaptureSession + UIViewPreview | Qua UIViewControllerRepresentable |
| CollectionView | UICollectionView + UICollectionViewLayout | LazyVGrid/LazyHGrid |
| Thích ứng iPad | UISplitViewController + UITraitCollection | NavigationSplitView + sizeClass |
| Tốc độ phát triển | Chậm hơn (bố cục thủ công) | Nhanh hơn (khai báo) |
Câu Hỏi Thường Gặp
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à 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).
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à 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.
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
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.
Đọc thêm