UIViewController เป็นคลาสหลักของแอปพลิเคชัน iOS ที่จัดการหน้าจอและเนื้อหาของมัน ทุกหน้าจอของ iPhone หรือ iPad ถูกจัดการโดย ViewController หนึ่งตัว ซึ่งประสานงานการแสดงผล วงจรชีวิต และการนำทาง อ่านเพิ่มเติมเกี่ยวกับสถาปัตยกรรม UIKit ได้ใน เอกสารทางการของ Apple
ประเด็นสำคัญ
UIViewController เป็นคลาสจากเฟรมเวิร์ก UIKit ที่จัดการลำดับชั้นของ UIView และประสานงานการแสดงผลข้อมูลบนหน้าจอ ทุกแอปพลิเคชัน iOS มี ViewController อย่างน้อยหนึ่งตัว — ตัวควบคุมรากของหน้าต่าง ตัวควบคุมจัดการการหมุนหน้าจอ การเปลี่ยนระหว่างหน้าจอ และเหตุการณ์วงจรชีวิต
สถาปัตยกรรม MVC (Model-View-Controller) ใน iOS ถูกนำไปใช้ผ่าน UIViewController อย่างแม่นยำ: ตัวควบคุมรับข้อมูลจากโมเดลและอัปเดตมุมมอง ViewController ไม่ใช่องค์ประกอบที่มองเห็นได้ — มันจัดการคุณสมบัติ view ซึ่งมีลำดับชั้นของ subview ตามข้อมูลของ Apple (2026) UIKit มีคลาสย่อย UIViewController ในตัวมากกว่า 40 คลาส
iPhone SDK แรก (2008) มี UIViewController พร้อมเมธอดวงจรชีวิตสามเมธอด ในช่วง 18 ปี Apple ได้เพิ่มการสนับสนุน Container View Controller การนำเสนอแบบปรับได้ UIViewControllerTransitioningDelegate สำหรับแอนิเมชันแบบกำหนดเอง และโหมดหน้าจอแบ่งบน iPad UIViewController ยังคงเป็นส่วนประกอบบังคับสำหรับแอปพลิเคชัน UIKit
วงจรชีวิตของ UIViewController คือลำดับของเมธอดที่ถูกเรียกโดยระบบเมื่อสร้าง แสดง และซ่อนหน้าจอ การทำความเข้าใจ Lifecycle มีความสำคัญอย่างยิ่ง: การวางโค้ดผิดตำแหน่งนำไปสู่การรั่วไหลของหน่วยความจำ คำขอเครือข่ายที่ไม่จำเป็น และการกระพริบของอินเทอร์เฟซ
| เมธอด | ช่วงเวลาที่เรียก | วัตถุประสงค์ |
|---|---|---|
| viewDidLoad | หนึ่งครั้ง หลังจาก view ถูกโหลดเข้าสู่หน่วยความจำ | การตั้งค่า UI เริ่มต้น การสมัครรับ Combine |
| viewWillAppear | ก่อนที่หน้าจอจะปรากฏ | อัปเดตข้อมูล ซ่อน/แสดงแถบนำทาง |
| viewDidAppear | หลังจากหน้าจอปรากฏ | เริ่มแอนิเมชัน การวิเคราะห์ อัปเดตกล้อง |
| viewWillDisappear | ก่อนออกจากหน้าจอ | บันทึกร่าง ยกเลิกการสมัครรับการแจ้งเตือน |
| viewDidDisappear | หลังจากออกจากหน้าจอ | หยุดกระบวนการหนัก ปลดปล่อยทรัพยากร |
เมื่อหน้าจอแสดงครั้งแรก ลำดับคือ: init → loadView → viewDidLoad → viewWillAppear → viewDidAppear เมื่อปรากฏอีกครั้ง (กลับจากหน้าจออื่น): viewWillAppear → viewDidAppear viewDidLoad ถูกเรียกเพียงครั้งเดียวตลอดอายุของตัวควบคุม
เมธอด viewDidLoad เป็นจุดหลักในการกำหนดค่าส่วนติดต่อผู้ใช้ มันถูกเรียกหลังจาก view ถูกโหลดเข้าสู่หน่วยความจำ เมื่อการเชื่อมต่อ IBOutlet ทั้งหมดถูกสร้างขึ้นแล้ว ที่นี่องค์ประกอบ UI ถูกสร้างขึ้นโดยโปรแกรม ข้อจำกัดถูกกำหนดค่า และข้อมูลเริ่มต้นถูกโหลด
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)
}
}ใน SwiftUI โค้ดนี้เทียบเท่ากับ body ของ View แต่ UIViewController ให้การควบคุมวงจรชีวิตและการปรับแต่งอย่างเต็มที่ bindViewModel ใช้ Combine สำหรับการสมัครรับแบบรีแอกทีฟ — ข้อมูลจะถูกอัปเดตโดยอัตโนมัติเมื่อโมเดลเปลี่ยน
viewWillAppear ถูกเรียกทุกครั้งก่อนที่หน้าจอจะปรากฏ แม้ว่าจะอยู่ในหน่วยความจำแล้วก็ตาม นี่คือสถานที่สำหรับอัปเดตข้อมูลที่อาจเปลี่ยนแปลงบนหน้าจออื่น: โหลดรายการใหม่ อัปเดตป้ายการแจ้งเตือน กำหนดค่าแถบนำทางสำหรับหน้าจอเฉพาะ
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
// ซ่อนแถบนำทางบนหน้าจอนี้
navigationController?.setNavigationBarHidden(true, animated: animated)
// อัปเดตข้อมูลเมื่อกลับจากหน้าจออื่น
tableView.reloadData()
badgeLabel.text = "\(CartManager.shared.itemCount)"
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
// การวิเคราะห์: หลังจากที่ผู้ใช้เห็นหน้าจอแล้วเท่านั้น
AnalyticsService.shared.logScreenView("Profile")
}ความแตกต่างระหว่าง viewDidLoad และ viewWillAppear มีความสำคัญ: viewDidLoad ทำงานครั้งเดียวและเหมาะสมสำหรับการตั้งค่าแบบคงที่ viewWillAppear ทำงานทุกครั้งที่แสดงหน้าจอและเหมาะสมสำหรับการอัปเดตแบบไดนามิก การวางคำขอเครือข่ายใน viewDidLoad จะทำให้แสดงข้อมูลที่ล้าสมัยเมื่อกลับมาที่หน้าจอ
Container View Controller คือ ViewController ที่จัดการ ViewController ย่อยหนึ่งตัวหรือมากกว่า Apple มีคอนเทนเนอร์ในตัวสามตัว: UINavigationController (สแต็กหน้าจอ), UITabBarController (แท็บ) และ UISplitViewController (มาสเตอร์-ดีเทลสำหรับ iPad)
UINavigationController จัดระเบียบการเปลี่ยนในสแต็ก — push เพิ่มหน้าจอ pop ลบออก UITabBarController สลับระหว่างส่วนอิสระของแอปพลิเคชัน UISplitViewController แสดงตัวควบคุมสองตัวเคียงข้างกันบน iPad และหนึ่งตัวบน iPhone นักพัฒนาสามารถสร้างคอนเทนเนอร์แบบกำหนดเองผ่าน addChild
// Container View Controller แบบกำหนดเอง
final class ContainerViewController: UIViewController {
private let sidebarVC = SidebarViewController()
private let contentVC = ContentViewController()
override func viewDidLoad() {
super.viewDidLoad()
// การเพิ่มตัวควบคุมย่อย
addChild(sidebarVC)
view.addSubview(sidebarVC.view)
sidebarVC.didMove(toParent: self)
addChild(contentVC)
view.addSubview(contentVC.view)
contentVC.didMove(toParent: self)
}
}การทำงานที่ถูกต้องกับ Container View Controller ต้องการการเรียก addChild การเพิ่ม view และ didMove(toParent:) ตามลำดับนี้ เมื่อลบ: willMove(toParent: nil), removeFromSuperview, removeFromParent การละเมิดลำดับนำไปสู่การรั่วไหลของหน่วยความจำ
ปัญหา Massive View Controller เกิดขึ้นเมื่อ UIViewController มีโค้ดหลายร้อยบรรทัดพร้อมตรรกะทางธุรกิจ คำขอเครือข่าย การนำทาง และโค้ด UI Apple รับทราบปัญหาและแนะนำ MVVM (Model-View-ViewModel) พร้อมกับ Coordinator เพื่อแยกการนำทาง
MVVM ย้ายตรรกะทางธุรกิจจากตัวควบคุมไปยัง ViewModel Controller เพียงเชื่อม ViewModel กับ View ผ่าน Combine หรือผู้รับมอบอำนาจ Coordinator แยกตรรกะการนำทาง — การสร้างและการเปลี่ยนระหว่างตัวควบคุม — ออกเป็นคลาสแยกต่างหาก แนวทางนี้ถูกนำมาใช้ในแนวทางปฏิบัติที่ดีที่สุดของ Apple ตั้งแต่ปี 2024
// Coordinator — การจัดการการนำทาง
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 และ SwiftUI View ขึ้นอยู่กับปีเริ่มต้นโครงการ ข้อกำหนดการปรับแต่ง และเวอร์ชัน iOS ขั้นต่ำที่รองรับ UIKit กับ UIViewController ยังคงเป็นพื้นฐานสำหรับโครงการที่เริ่มก่อนปี 2020 และสำหรับแอปพลิเคชันที่มีการปรับแต่งส่วนต่อประสานอย่างลึกซึ้ง
SwiftUI เหมาะสำหรับโครงการใหม่ที่มี iOS 17+ ส่วนต่อประสานมาตรฐาน และต้นแบบ อย่างไรก็ตาม การเปลี่ยนแบบกำหนดเอง การทำงานกับกล้อง MapKit แอนิเมชัน CALayer ที่ซับซ้อนจำเป็นต้องใช้ UIViewController Apple แนะนำให้รวมแนวทางผ่าน UIHostingController (SwiftUI ภายใน UIKit) และ UIViewRepresentable (UIKit ภายใน SwiftUI)
| สถานการณ์ | UIKit (UIViewController) | SwiftUI (View) |
|---|---|---|
| แอนิเมชันแบบกำหนดเอง | ควบคุมเต็มที่ผ่าน UIViewPropertyAnimator | จำกัดผ่าน Animation |
| การทำงานกับกล้อง | AVCaptureSession + UIViewPreview | ผ่าน UIViewControllerRepresentable |
| CollectionView | UICollectionView + UICollectionViewLayout | LazyVGrid/LazyHGrid |
| การปรับ iPad | UISplitViewController + UITraitCollection | NavigationSplitView + sizeClass |
| ความเร็วในการพัฒนา | ช้ากว่า (เลย์เอาต์ด้วยตนเอง) | เร็วกว่า (declarative) |
คำถามที่พบบ่อย
UIViewController คือตัวควบคุมที่จัดการหน้าจอและวงจรชีวิตของมัน UIView คือมุมมองที่แสดงเนื้อหา ViewController มีลำดับชั้นของ UIViews แต่ตัวมันเองไม่ใช่องค์ประกอบที่มองเห็นได้ ตัวควบคุมหนึ่งตัวจัดการหลายมุมมอง
Massive View Controller คือแอนติแพทเทิร์นที่ UIViewController มีตรรกะมากเกินไป: ข้อมูล การนำทาง คำขอเครือข่าย แอนิเมชัน วิธีแก้ไข — แยกโค้ดออกเป็นบริการ ผู้ประสานงาน และ ViewModel (MVVM) แยกต่างหาก
สี่วิธี: ผ่านคุณสมบัติใน prepare(for:sender:) (Segue), ผ่านผู้รับมอบอำนาจ (Delegate), ผ่านคลosure (Closure), ผ่านบริการที่ใช้ร่วมกัน สำหรับการเชื่อมต่อแบบหลวมๆ ให้ใช้ Coordinator + Delegate หรือ Combine
Container View Controller คือตัวควบคุมที่จัดการ ViewController ย่อย ตัวอย่าง: UINavigationController, UITabBarController, UISplitViewController ตัวควบคุมหลักเพิ่มลูกผ่าน addChild สลับระหว่างพวกเขา และจัดการเลย์เอาต์ของพวกเขา
UIViewController — สำหรับแอนิเมชันแบบกำหนดเองที่ซับซ้อน การทำงานกับกล้อง แผนที่ วิดีโอ UICollectionView ที่มีเลย์เอาต์แบบกำหนดเอง SwiftUI View — สำหรับส่วนต่อประสานมาตรฐานบน iOS 13+ การรวมผ่าน UIHostingController เป็นที่ยอมรับได้
สรุป
เราจะพัฒนาแอปพลิเคชันบนมือถือแบบครบวงจร
IT Sectr สร้างแอปพลิเคชัน iOS และ Android สำหรับสตาร์ทอัพและธุรกิจตั้งแต่ปี 2017 เราจะให้คำแนะนำและเสนอวิธีแก้ปัญหาที่ดีที่สุดแก่คุณ