Chào mừng các bạn quay trở lại với chuỗi bài viết “Lộ trình học Lập trình viên iOS 2025“. Sau khi đã tìm hiểu về những kiến thức nền tảng như cơ bản về Swift, OOP và Lập trình Hàm, Quản Lý Bộ Nhớ (ARC), cũng như cách xây dựng giao diện cơ bản với UIKit và vòng đời của ViewController, hôm nay chúng ta sẽ đi sâu vào một chủ đề cốt lõi: Điều hướng (Navigation) trong ứng dụng UIKit.
Một ứng dụng iOS không chỉ có một màn hình. Người dùng cần di chuyển giữa các màn hình khác nhau để truy cập các tính năng, xem thông tin chi tiết, hoặc hoàn thành tác vụ. Việc điều hướng mượt mà, trực quan là yếu tố quan trọng tạo nên trải nghiệm người dùng tốt. Trong UIKit, điều này chủ yếu được quản lý bởi các View Controller, các phương thức trình bày (như modal) và Segues.
Mục lục
ViewController – Nền Tảng Của Mọi Màn Hình
Như chúng ta đã thảo luận trước đây, UIViewController
là trái tim của một màn hình (hoặc một phần của màn hình) trong ứng dụng UIKit. Nó quản lý một View và xử lý các tương tác của người dùng với View đó. Điều hướng thực chất là quá trình chuyển đổi từ View Controller này sang View Controller khác.
Để điều hướng, bạn cần tạo ra các instance của UIViewController
hoặc các lớp con của nó (ví dụ: UITableViewController
, UICollectionViewController
, hoặc các lớp custom của bạn). Mỗi instance này đại diện cho trạng thái và giao diện của một màn hình cụ thể.
Khi một View Controller mới được hiển thị, nó được đưa vào “ngăn xếp” (stack) điều hướng hoặc được trình bày “phủ” lên View Controller hiện tại. Quá trình này kích hoạt các phương thức vòng đời của View Controller như viewDidLoad()
, viewWillAppear()
, viewDidAppear()
, v.v., cho phép bạn thực hiện các tác vụ khởi tạo, tải dữ liệu hoặc cập nhật giao diện tại các thời điểm thích hợp.
Các Phương Thức Điều Hướng Chính Trong UIKit
UIKit cung cấp hai phương thức chính để điều hướng giữa các View Controller:
- Điều hướng theo hệ thống phân cấp (Hierarchical Navigation): Sử dụng
UINavigationController
để quản lý một ngăn xếp các View Controller. Phương thức này thường được dùng cho các luồng ứng dụng chính, nơi người dùng di chuyển từ danh sách đến chi tiết và có thể quay lại. - Trình bày Modal (Modal Presentation): Hiển thị một View Controller mới đè lên View Controller hiện tại, thường được dùng cho các tác vụ độc lập, thu thập dữ liệu hoặc hiển thị thông tin tạm thời.
Ngoài ra, UIKit còn cung cấp Segues, một cách để định nghĩa và thực hiện điều hướng một cách trực quan trong Storyboards.
UINavigationController – Quản Lý Ngăn Xếp Màn Hình
UINavigationController
là một container View Controller quản lý một ngăn xếp (stack) các View Controller con. Nó cung cấp thanh điều hướng (Navigation Bar) ở phía trên, nơi thường chứa tiêu đề của màn hình và nút “Back” để quay lại màn hình trước đó trong ngăn xếp.
Khi bạn “đẩy” (push) một View Controller mới lên navigation stack, nó sẽ trở thành View Controller đang hiển thị và được thêm vào đầu ngăn xếp. Khi bạn “bật” (pop) một View Controller khỏi ngăn xếp, View Controller ở dưới cùng sẽ trở lại và View Controller hiện tại bị xóa khỏi ngăn xếp.
Thực Hiện Điều Hướng Với UINavigationController (Lập Trình)
Để sử dụng UINavigationController
, bạn thường bắt đầu bằng cách nhúng View Controller gốc (Root View Controller) vào một UINavigationController
. Điều này thường được thiết lập trong SceneDelegate hoặc AppDelegate (tùy thuộc vào cấu trúc ứng dụng và phiên bản iOS) hoặc trực tiếp trong Storyboard.
Giả sử bạn đang ở trong một View Controller đã được nhúng trong UINavigationController
, bạn có thể đẩy View Controller mới lên stack như sau:
let detailVC = DetailViewController() // Khởi tạo View Controller đích
navigationController?.pushViewController(detailVC, animated: true)
Trong đó, DetailViewController
là lớp con của UIViewController
mà bạn muốn chuyển đến. Thuộc tính animated: true
sẽ tạo hiệu ứng chuyển cảnh mượt mà.
Để quay lại màn hình trước đó (bật khỏi ngăn xếp), bạn sử dụng phương thức popViewControllerAnimated:
:
navigationController?.popViewController(animated: true)
Bạn cũng có thể bật về View Controller gốc của navigation stack:
navigationController?.popToRootViewController(animated: true)
Hoặc bật về một View Controller cụ thể trong ngăn xếp (nếu bạn có tham chiếu đến nó):
// Giả sử 'specificVC' là một View Controller tồn tại trong ngăn xếp
if let specificVC = navigationController?.viewControllers.first(where: { $0 is SpecificViewController }) {
navigationController?.popToViewController(specificVC, animated: true)
}
Sử Dụng UINavigationController Với Storyboard
Trong Storyboard, bạn có thể dễ dàng nhúng một View Controller vào Navigation Controller bằng cách chọn View Controller đó, vào Editor -> Embed In -> Navigation Controller. Bạn cũng có thể kéo thả một Navigation Controller mới và thiết lập Root View Controller cho nó.
Trình bày Modal – Hiển Thị Tạm Thời
Trình bày Modal là khi một View Controller được hiển thị đè lên View Controller hiện tại. View Controller được trình bày (presented view controller) thường che phủ toàn bộ hoặc một phần màn hình và ngăn người dùng tương tác với View Controller gốc (presenting view controller) cho đến khi nó bị loại bỏ (dismissed).
Phương thức này thường được sử dụng cho các tác vụ như:
- Hiển thị màn hình cài đặt (Settings).
- Màn hình nhập dữ liệu (Form).
- Hiển thị cảnh báo hoặc thông tin quan trọng.
- Màn hình đăng nhập/đăng ký.
Thực Hiện Trình Bày Modal (Lập Trình)
Để trình bày một View Controller mới theo kiểu modal, bạn gọi phương thức presentViewController:animated:completion:
trên View Controller hiện tại (View Controller sẽ trình bày View Controller mới).
let settingsVC = SettingsViewController() // Khởi tạo View Controller đích
// Tùy chọn: Thiết lập kiểu trình bày (presentation style)
// settingsVC.modalPresentationStyle = .fullScreen // Mặc định trên iPhone
// settingsVC.modalPresentationStyle = .pageSheet // Thường dùng trên iPad, hoặc iOS 13+ trên iPhone
// Tùy chọn: Nhúng vào Navigation Controller nếu bạn muốn thanh điều hướng trên màn hình modal
let navController = UINavigationController(rootViewController: settingsVC)
// Thực hiện trình bày
present(navController, animated: true) {
print("Settings screen presented!")
}
Trong ví dụ trên, chúng ta nhúng SettingsViewController
vào một UINavigationController
để nó có thanh điều hướng và cho phép quản lý các màn hình con bên trong luồng modal đó (ví dụ: đi từ màn hình Settings chung đến màn hình Account settings). Nếu màn hình modal đơn giản, bạn có thể trình bày trực tiếp settingsVC
.
Để loại bỏ (dismiss) một View Controller đã được trình bày theo kiểu modal, bạn gọi phương thức dismissViewControllerAnimated:completion:
trên View Controller đã được trình bày (hoặc trên View Controller đã trình bày nó):
// Từ bên trong SettingsViewController:
dismiss(animated: true) {
print("Settings screen dismissed!")
}
// Hoặc từ View Controller đã trình bày SettingsViewController (nếu bạn giữ tham chiếu):
// presentedViewController?.dismiss(animated: true, completion: nil)
Thường thì bạn sẽ gọi dismiss
từ bên trong View Controller được trình bày, ví dụ khi người dùng nhấn nút “Done” hoặc “Cancel”.
Segues – Điều Hướng Trực Quan Với Storyboard
Segues là một tính năng mạnh mẽ trong Storyboards cho phép bạn định nghĩa các chuyển đổi từ một View Controller sang View Controller khác một cách trực quan. Thay vì viết code để khởi tạo và trình bày/đẩy View Controller mới, bạn vẽ một kết nối (segue) giữa các Scene trong Storyboard.
Segues có nhiều loại, tương ứng với các phương thức điều hướng khác nhau:
- Show (or Push): Đây là loại segue phổ biến nhất khi sử dụng
UINavigationController
. Nó đẩy View Controller đích lên navigation stack. Nếu View Controller gốc không nằm trong Navigation Controller, segue này sẽ trình bày View Controller đích theo kiểu modal (kiểu mặc định cũ là `.fullScreen`). - Present Modally: Trình bày View Controller đích theo kiểu modal. Bạn có thể tùy chỉnh Presentation Style và Transition Style trong thuộc tính của segue.
- Present As Popover: Thường dùng trên iPad để hiển thị nội dung tạm thời trong một ô nhỏ (popover).
- Custom: Cho phép bạn tạo hiệu ứng chuyển cảnh tùy chỉnh.
Để tạo một segue trong Storyboard, bạn giữ phím Control và kéo từ một UI element (như nút, cell trong Table View) hoặc từ chính View Controller (nếu bạn muốn kích hoạt segue bằng code) đến View Controller đích. Sau đó, chọn loại segue mong muốn.
Mỗi segue cần có một Identifier duy nhất. Identifier này cực kỳ quan trọng vì nó cho phép bạn tham chiếu đến segue đó trong code.
Để kích hoạt một segue được kết nối từ View Controller (chứ không phải từ UI element), bạn sử dụng:
performSegue(withIdentifier: "ShowDetailSegue", sender: self)
Chuẩn Bị Trước Khi Segue Xảy Ra (Passing Data)
Một trong những nhu cầu phổ biến khi điều hướng là truyền dữ liệu từ View Controller gốc sang View Controller đích. Khi sử dụng segues, bạn thực hiện điều này trong phương thức prepare(for:sender:)
của View Controller gốc.
Hệ thống sẽ gọi phương thức này ngay trước khi segue được thực hiện. Tại đây, bạn có thể truy cập segue, View Controller đích và dữ liệu (nếu có) được gửi bởi sender.
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
// Kiểm tra identifier của segue để biết segue nào đang được kích hoạt
if segue.identifier == "ShowDetailSegue" {
// Lấy instance của View Controller đích
if let detailVC = segue.destination as? DetailViewController {
// Lấy dữ liệu cần truyền (ví dụ: từ một biến instance, từ sender, etc.)
let selectedData = self.dataToSend
// Truyền dữ liệu sang View Controller đích
detailVC.itemData = selectedData
}
}
// Có thể thêm các if khác cho các segues khác
}
Trong ví dụ này, chúng ta kiểm tra xem segue có identifier là “ShowDetailSegue” không. Nếu đúng, chúng ta lấy View Controller đích (kiểu UIViewController
) và downcast nó thành kiểu cụ thể của mình là DetailViewController
. Sau đó, chúng ta truy cập vào thuộc tính itemData
của detailVC
và gán dữ liệu cần truyền. Đây là cách phổ biến nhất để truyền dữ liệu khi sử dụng segues.
Unwind Segues (Exit Segues)
Unwind segues là một cách để điều hướng ngược trở lại qua một hoặc nhiều segues trước đó. Chúng thường được sử dụng để đóng màn hình modal hoặc thoát khỏi một luồng điều hướng phức tạp và truyền dữ liệu ngược trở lại View Controller ban đầu.
Để sử dụng unwind segue, bạn cần định nghĩa một phương thức action trong View Controller đích mà bạn muốn “unwind” về. Phương thức này có chữ ký cụ thể:
@IBAction func unwindToThisViewController(segue: UIStoryboardSegue) {
// Optional: Access the source view controller and retrieve data if needed
// if let sourceVC = segue.source as? SourceViewController {
// let returnedData = sourceVC.dataToReturn
// // Process the returned data
// }
print("Unwound back to this View Controller!")
}
Sau đó, trong Storyboard, bạn có thể kéo từ một UI element (như nút “Save” hoặc “Cancel”) trong View Controller đang hiển thị đến biểu tượng “Exit” của Scene đó. Chọn unwind segue mà bạn vừa tạo. Khi UI element đó được nhấn, segue sẽ được thực hiện và phương thức unwindToThisViewController
sẽ được gọi trên View Controller đích.
Khi Nào Sử Dụng Phương Thức Nào?
Việc lựa chọn giữa Navigation Controller, Modal Presentation và Segues phụ thuộc vào ngữ cảnh và luồng ứng dụng bạn muốn xây dựng. Dưới đây là bảng tóm tắt giúp bạn đưa ra quyết định:
Đặc Điểm | UINavigationController (Push/Pop) | Trình bày Modal (Present/Dismiss) | Segues (Sử dụng với Push/Modal) |
---|---|---|---|
Mục Đích Chính | Điều hướng theo hệ thống phân cấp (list -> detail). Quản lý luồng chính. | Hiển thị tạm thời, độc lập. Thu thập dữ liệu, cảnh báo. | Định nghĩa chuyển cảnh trực quan trong Storyboard. |
Kiểu Giao Diện | Thường chiếm toàn bộ màn hình, có Navigation Bar. | Có thể che phủ toàn bộ hoặc một phần màn hình. Không nhất thiết có Navigation Bar (trừ khi được nhúng). | Không phải là một kiểu giao diện riêng, mà là cách thực hiện chuyển cảnh. |
Cách Thực Hiện | Thường bằng code (pushViewController , popViewController ) hoặc qua Segue loại “Show”. |
Thường bằng code (present , dismiss ) hoặc qua Segue loại “Present Modally”. |
Được thiết lập trong Storyboard, có thể kích hoạt bằng code (performSegue ) hoặc từ UI element. |
Quản Lý Quay Lại | Nút Back trên Navigation Bar hoặc popViewController . |
Gọi dismiss từ View Controller được trình bày hoặc sử dụng Unwind Segue. |
Unwind Segue là cơ chế quay lại chính khi sử dụng Storyboard. |
Truyền Dữ Liệu | Gán thuộc tính trước khi gọi pushViewController /present . |
Gán thuộc tính trước khi gọi present . |
Trong phương thức prepare(for:sender:) . |
Độ Phức Tạp | Cơ bản, dễ hiểu. | Cơ bản, dễ hiểu. | Thêm một lớp trừu tượng; cần hiểu cách Storyboard hoạt động và phương thức prepareForSegue . |
Ứng Dụng Điển Hình | Settings, danh sách sản phẩm, luồng nhập liệu nhiều bước có thể quay lại. | Form thêm item mới, màn hình xác nhận, popup thông tin. | Phù hợp khi sử dụng Storyboards để thiết kế luồng ứng dụng. |
Truyền Dữ Liệu Khi Điều Hướng (Chi Tiết Hơn)
Việc truyền dữ liệu là một phần không thể thiếu của điều hướng. Ngoài cách sử dụng prepareForSegue:
đã nêu, khi điều hướng bằng code, bạn chỉ đơn giản là khởi tạo View Controller đích và gán các thuộc tính cần thiết trước khi trình bày hoặc đẩy nó:
// Khi Push
let detailVC = DetailViewController()
detailVC.itemData = "Dữ liệu từ màn hình trước"
navigationController?.pushViewController(detailVC, animated: true)
// Khi Present Modal
let settingsVC = SettingsViewController()
settingsVC.initialSetting = "Cài đặt ban đầu"
present(settingsVC, animated: true, completion: nil)
Điều quan trọng là View Controller đích phải có các thuộc tính công khai hoặc internal để nhận dữ liệu này. Đảm bảo quản lý bộ nhớ cẩn thận khi truyền dữ liệu, đặc biệt tránh tạo ra các retain cycle không cần thiết.
Các Kỹ Thuật Nâng Cao Hơn (Sơ Lược)
Đối với các ứng dụng phức tạp với nhiều luồng điều hướng đan xen, việc quản lý điều hướng chỉ với các phương thức cơ bản có thể trở nên khó khăn. Các pattern thiết kế như Coordinator pattern ra đời để giải quyết vấn đề này. Coordinator là các đối tượng riêng biệt chịu trách nhiệm hoàn toàn về logic điều hướng, tách nó ra khỏi View Controller. Điều này giúp View Controller trở nên “nhẹ” hơn (slim) và dễ tái sử dụng hơn. Tuy nhiên, đây là một chủ đề nâng cao hơn mà chúng ta có thể khám phá chi tiết trong các bài viết sau.
Best Practices Khi Làm Việc Với Điều Hướng
- Sử Dụng Identifier Rõ Ràng: Khi dùng Storyboard Segues, luôn đặt Identifier có ý nghĩa (ví dụ: “ShowProductDetail”, “PresentLoginModal”).
- Tránh Logic Phức Tạp Trong
prepareForSegue:
: Phương thức này chỉ nên dùng để lấy tham chiếu đến View Controller đích và truyền dữ liệu. Mọi logic khởi tạo phức tạp khác nên nằm trong View Controller đích. - Kiểm Tra Kiểu (Type Casting): Luôn sử dụng optional casting (
as?
) khi truy cập View Controller đích từ segue để tránh crash nếu kiểu không đúng. - Hiểu Vòng Đời: Nắm vững vòng đời của View Controller là rất quan trọng để biết khi nào nên thực hiện các tác vụ liên quan đến điều hướng hoặc tải dữ liệu.
- Cân Nhắc Giữa Storyboard và Code: Chọn phương pháp phù hợp với dự án và nhóm của bạn. Storyboard tốt cho các luồng đơn giản, trong khi điều hướng bằng code linh hoạt hơn cho các luồng phức tạp hoặc dynamic. Đọc thêm về so sánh Storyboards, XIBs và SwiftUI.
- Đảm Bảo Truyền Dữ Liệu Một Chiều (Hầu hết): Thông thường dữ liệu truyền từ View Controller gốc sang đích. Để truyền dữ liệu ngược lại, bạn có thể sử dụng delegates, completion handlers, hoặc unwind segues.
Kết Luận
Điều hướng là một khía cạnh không thể thiếu của phát triển ứng dụng iOS bằng UIKit. Hiểu rõ cách hoạt động của View Controllers, UINavigationController, Modal Presentation và Segues là kỹ năng cơ bản mà mọi lập trình viên iOS cần nắm vững.
Trong bài viết này, chúng ta đã đi qua các phương thức điều hướng chính, cách thực hiện chúng bằng cả code và Storyboard, và làm thế mạnh để truyền dữ liệu giữa các màn hình. Bằng cách kết hợp linh hoạt các kỹ thuật này, bạn có thể xây dựng các luồng ứng dụng phức tạp và trực quan.
Hãy dành thời gian thực hành tạo các ứng dụng demo nhỏ sử dụng Navigation Controller và Modal Presentation, thử nghiệm với Segues trong Storyboard, và làm quen với việc truyền dữ liệu qua prepareForSegue:
. Đây là nền tảng vững chắc để bạn tiếp tục khám phá các chủ đề nâng cao hơn trong lộ trình học Lập trình viên iOS 2025 của mình.
Hẹn gặp lại trong bài viết tiếp theo!