viewDidLoad เป็นเมธอดแรกที่ UIKit เรียกหลังจากโหลด View ของ UIViewController เข้าสู่หน่วยความจำ ตาม Apple Developer Documentation เมธอดนี้จะถูกเรียกเพียงครั้งเดียวตลอดอายุของคอนโทรลเลอร์ viewDidLoad เป็นสถานที่หลักสำหรับการตั้งค่าอินเทอร์เฟซเริ่มต้น การลงทะเบียนเซลล์ และการเริ่มต้นข้อมูล
ประเด็นสำคัญ
viewDidLoad เป็นเมธอดอินสแตนซ์ของ UIViewController ที่ UIKit เรียกทันทีหลังจาก View ของคอนโทรลเลอร์ถูกโหลดเข้าสู่หน่วยความจำ ณ จุดนี้ คุณสมบัติ IBOutlet ทั้งหมดเชื่อมต่อกับองค์ประกอบอินเทอร์เฟซแล้ว แต่ View ยังไม่ได้ถูกเพิ่มไปยังลำดับชั้นหน้าต่างและยังไม่ปรากฏให้ผู้ใช้เห็น นักพัฒนาจะโอเวอร์ไรด์เมธอดนี้เพื่อดำเนินการกำหนดค่าหน้าจอเริ่มต้น
เมธอดนี้เป็นส่วนหนึ่งของ ViewController Lifecycle และมาหลังจาก loadView ทันทีหาก View ถูกสร้างขึ้นโดยโปรแกรม หรือหลังจากโหลดจาก Storyboard ในโปรเจกต์ทั่วไป viewDidLoad เป็นเมธอดที่ถูกโอเวอร์ไรด์บ่อยที่สุดของ UIViewController เนื่องจากเป็นจุดที่ปลอดภัยสำหรับการทำงานกับ subview ที่มีอยู่แล้วและพร้อมสำหรับการกำหนดค่า
รายละเอียดสำคัญ: ในขณะที่ viewDidLoad ถูกเรียก ขนาดของ View ยังไม่ตรงกับขนาดสุดท้าย — Auto Layout ยังไม่เสร็จสิ้นการส่งผ่าน และเฟรมอาจแตกต่างจากที่คาดไว้ สำหรับการคำนวณที่ขึ้นอยู่กับขนาด ให้ใช้ viewDidLayoutSubviews
ช่วงเวลาของการเรียก viewDidLoad ขึ้นอยู่กับวิธีที่คอนโทรลเลอร์ถูกเริ่มต้น ในกรณีส่วนใหญ่ UIKit จะเรียกเมธอดนี้โดยอัตโนมัติเมื่อมีการเข้าถึงคุณสมบัติ view ของคอนโทรลเลอร์เป็นครั้งแรก — เรียกว่ากลไกการโหลดแบบขี้เกียจ (lazy-loading) ของ UIViewController
เมื่อ NavigationController หรือ TabBarController แสดงหน้าจอของคุณเป็นครั้งแรก UIKit จะตรวจสอบว่า View ถูกโหลดหรือไม่ หากไม่ — จะเรียก loadView (หรือโหลดจาก Storyboard) หลังจากนั้น viewDidLoad จะถูกเรียกทันที นี่คือ สถานการณ์มาตรฐาน และเกิดขึ้นหนึ่งครั้งต่ออินสแตนซ์ของคอนโทรลเลอร์
override func viewDidLoad() {
super.viewDidLoad()
print("View โหลดแล้ว — คุณสามารถกำหนดค่าอินเทอร์เฟซได้")
setupUI()
configureTableView()
}
viewDidLoad จะไม่ถูกเรียกอีกครั้ง เมื่อกลับไปยังหน้าจอผ่านปุ่มย้อนกลับหรือ dismiss หากลอจิกของคุณขึ้นอยู่กับหน้าจอที่ปรากฏอีกครั้ง — ให้วางไว้ใน viewWillAppear นี่เป็นหนึ่งในข้อผิดพลาดทางแนวคิดที่พบบ่อยที่สุด: นักพัฒนาคาดหวังว่า viewDidLoad จะทำงานทุกครั้งที่แสดง แต่ UIKit เรียกมันเพียงครั้งเดียว
บางครั้งนักพัฒนาเข้าถึง view ของคอนโทรลเลอร์โดยบังคับเพื่อเรียกการโหลดล่วงหน้า: let _ = controller.view สิ่งนี้บังคับให้เรียก loadView และ viewDidLoad ก่อนที่คอนโทรลเลอร์จะปรากฏบนหน้าจอ เทคนิคนี้ใช้เมื่อคุณต้องการเตรียม View ล่วงหน้าสำหรับการเปลี่ยนผ่านที่ราบรื่น
viewDidLoad ออกแบบมาสำหรับการดำเนินการตั้งค่าครั้งเดียวที่ไม่ขึ้นอยู่กับว่าหน้าจอมองเห็นได้หรือไม่ การใช้เมธอดนี้อย่างถูกต้องเป็นกุญแจสำคัญสู่สถาปัตยกรรมที่สะอาดและพฤติกรรมของคอนโทรลเลอร์ที่คาดเดาได้
ใน viewDidLoad คุณลงทะเบียนไฟล์ nib และคลาสสำหรับ UITableView และ UICollectionView กำหนดค่า delegate และตั้งค่าเริ่มต้นของคุณสมบัติขององค์ประกอบ UI เนื่องจาก IBOutlet ทั้งหมดเชื่อมต่อแล้ว ณ จุดนี้ คุณสามารถเข้าถึง label.text, imageView.image และคุณสมบัติ subview อื่น ๆ ได้อย่างปลอดภัย
override func viewDidLoad() {
super.viewDidLoad()
tableView.dataSource = self
tableView.delegate = self
tableView.register(
CustomCell.self,
forCellReuseIdentifier: CustomCell.identifier
)
title = "หน้าจอหลัก"
}
ที่นี่คุณสร้าง viewModel เริ่มต้น data source ด้วยอาร์เรย์ และสมัครรับการแจ้งเตือนที่ควรทำงานตลอดอายุของคอนโทรลเลอร์ ตัวอย่างเช่น การสมัครรับ UIApplication.willEnterForegroundNotification เพื่ออัปเดตข้อมูลเมื่อกลับจากพื้นหลังเป็นตัวเลือกที่ดีสำหรับ viewDidLoad viewModel ในสถาปัตยกรรม iOS สมัยใหม่ทำหน้าที่เป็นสะพานเชื่อมระหว่างคอนโทรลเลอร์และลอจิกทางธุรกิจ และการเริ่มต้นใน viewDidLoad รับประกันว่าข้อมูลพร้อมเมื่อหน้าจอปรากฏครั้งแรก
ให้ความสนใจเป็นพิเศษกับการกำหนดค่า data source สำหรับตารางและคอลเลกชัน หากตารางของคุณใช้ UIFetchedResultsController หรือ NSFetchedResultsController กับ Core Data ให้เริ่มต้น fetch request และ delegate ใน viewDidLoad สิ่งนี้รับประกันว่าเมื่อหน้าจอปรากฏครั้งแรก ตารางจะมีข้อมูลอยู่แล้วโดยไม่ต้องมีคำขอเพิ่มเติม
ใน viewDidLoad คุณกำหนดค่าปุ่ม NavigationBar ตั้งค่า large title เพิ่ม search controller และตั้งค่าปุ่ม edit/done องค์ประกอบเหล่านี้ไม่ค่อยเปลี่ยนแปลงเมื่อหน้าจอถูกแสดงอีกครั้ง ดังนั้นการเริ่มต้นที่นี่จึง เหมาะสมที่สุด
ไม่ใช่ทุกการดำเนินการ ที่เหมาะสมใน viewDidLoad การกระทำบางอย่างที่วางในเมธอดนี้นำไปสู่การใช้หน่วยความจำมากเกินไป พฤติกรรมที่ไม่ถูกต้อง หรือข้อผิดพลาดเมื่อหน้าจอถูกแสดงอีกครั้ง
หลีกเลี่ยง การเริ่มต้นคำขอเครือข่ายที่ผลลัพธ์มีผลกับ UI เท่านั้น หากคำขอเสร็จสมบูรณ์ก่อนที่หน้าจอจะปรากฏ ผู้ใช้จะไม่เห็นผลลัพธ์ และหากเสร็จสมบูรณ์หลังจากนั้น — ข้อมูลอาจล้าสมัย เริ่มการโหลดใน viewDidLoad แต่ให้อัปเดต UI ใน viewWillAppear
อย่าทำ การดำเนินการใน viewDidLoad ที่ขึ้นอยู่กับขนาดและตำแหน่งของ View ในขณะที่เรียก Auto Layout ยังไม่เสร็จสิ้นการส่งผ่าน และเฟรมอาจไม่ใช่ค่าสุดท้าย สำหรับการคำนวณ ให้ใช้ viewDidLayoutSubviews หรือโอเวอร์ไรด์ updateViewConstraints
อย่าสมัครรับ การแจ้งเตือนที่เกี่ยวข้องเฉพาะเมื่อหน้าจอมองเห็นได้ การแจ้งเตือนคีย์บอร์ด การแจ้งเตือนการเปลี่ยนแปลงเนื้อหาจากคอนโทรลเลอร์ย่อย — สมัครรับใน viewWillAppear และยกเลิกการสมัครใน viewDidDisappear เพื่อหลีกเลี่ยงการเรียกที่ไม่จำเป็นและการรั่วไหล
อย่าเรียก เมธอดที่ต้องการหน้าจอที่มองเห็นได้ ตัวอย่างเช่น การพยายามแสดง UIAlertController จาก viewDidLoad จะทำให้เกิดข้อผิดพลาดเนื่องจาก View ของคอนโทรลเลอร์ยังไม่ได้ถูกเพิ่มไปยังลำดับชั้นหน้าต่าง การดำเนินการ UI ใด ๆ ที่ขึ้นอยู่กับ window หรือ presentedViewController ควรดำเนินการหลังจากหน้าจอปรากฏแล้วเท่านั้น
อย่าเริ่มต้น ทรัพยากรหนักโดยไม่จำเป็น หากหน้าจอไม่ค่อยถูกแสดงหรือข้อมูลไม่แสดงทันที ให้เลื่อนการสร้างออบเจกต์ที่ใช้ทรัพยากรมากจนกว่าจะจำเป็นจริง ๆ การเริ่มต้นคุณสมบัติแบบขี้เกียจ (lazy) ใน Swift เป็นกลไกในตัวสำหรับแก้ไขปัญหานี้: คุณสมบัติที่มี lazy modifier จะถูกสร้างขึ้นเมื่อมีการเข้าถึงครั้งแรกเท่านั้น ช่วยประหยัดหน่วยความจำและเร่งการโหลดหน้าจอ
อย่าใช้ viewDidLoad สำหรับการดำเนินการที่ควรดำเนินการทุกครั้งที่หน้าจอปรากฏ นี่เป็นข้อผิดพลาดพื้นฐานที่สุด: นักพัฒนามือใหม่มักวางลอจิกอัปเดตข้อมูลใน viewDidLoad และแปลกใจที่เมื่อกลับจากหน้าจออื่น ตารางไม่โหลดซ้ำ หากการดำเนินการควรทำซ้ำทุกครั้งที่แสดง — ใช้ viewWillAppear หากควรดำเนินการครั้งเดียวต่ออายุ — ใช้ viewDidLoad จำกฎง่าย ๆ นี้เพื่อหลีกเลี่ยงปัญหาส่วนใหญ่เกี่ยวกับวงจรชีวิตของ UIViewController
มาดู สามตัวอย่างเชิงปฏิบัติ ที่สาธิตการใช้ viewDidLoad อย่างถูกต้องในโปรเจกต์จริง แต่ละตัวอย่างแก้ไขงานกำหนดค่าหน้าจอเฉพาะ
override func viewDidLoad() {
super.viewDidLoad()
collectionView.register(
PhotoCell.self,
forCellWithReuseIdentifier: PhotoCell.reuseId
)
collectionView.register(
HeaderView.self,
forSupplementaryViewOfKind: UICollectionView.elementKindSectionHeader,
withReuseIdentifier: HeaderView.reuseId
)
viewModel.delegate = self
viewModel.fetchInitialPage()
}
override func viewDidLoad() {
super.viewDidLoad()
let label = UILabel()
label.text = "สวัสดีชาวโลก!"
label.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(label)
NSLayoutConstraint.activate([
label.centerXAnchor.constraint(equalTo: view.centerXAnchor),
label.centerYAnchor.constraint(equalTo: view.centerYAnchor)
])
}
ใน viewDidLoad คุณยังกำหนดค่าองค์ประกอบที่แสดงเมื่อไม่มีข้อมูล: สถานะว่าง ตัวโหลด ตัวยึดตำแหน่ง องค์ประกอบเหล่านี้ถูกสร้างขึ้นครั้งเดียวและ นำกลับมาใช้ใหม่ ทุกครั้งที่หน้าจอปรากฏ การซ่อนหรือแสดงองค์ประกอบเหล่านี้ถูกจัดการใน viewWillAppear ขึ้นอยู่กับข้อมูลปัจจุบัน
override func viewDidLoad() {
super.viewDidLoad()
emptyStateLabel = UILabel()
emptyStateLabel.text = "ไม่มีข้อมูล"
emptyStateLabel.textAlignment = .center
emptyStateLabel.isHidden = true
view.addSubview(emptyStateLabel)
activityIndicator = UIActivityIndicatorView(style: .medium)
activityIndicator.hidesWhenStopped = true
view.addSubview(activityIndicator)
}
override func viewDidLoad() {
super.viewDidLoad()
NotificationCenter.default.addObserver(
self,
selector: #selector(handleEnterForeground),
name: UIApplication.willEnterForegroundNotification,
object: nil
)
}
@objc private func handleEnterForeground() {
refreshContent()
}
คำถามที่พบบ่อย
ภายใต้ สภาวะปกติ ไม่ — UIKit เรียก viewDidLoad หนึ่งครั้งหลังจากโหลด View เข้าสู่หน่วยความจำ หากคอนโทรลเลอร์ถูกทำลายและสร้างใหม่ viewDidLoad จะทำงานสำหรับอินสแตนซ์ใหม่
ใช่ จำเป็นอย่างยิ่ง การเรียก super.viewDidLoad รับประกันว่า UIKit ดำเนินการตั้งค่าภายในที่จำเป็นสำหรับ Lifecycle ในการทำงานอย่างถูกต้อง เรียก super ก่อนเสมอภายในเมธอด
viewDidLoad ถูกเรียกหนึ่งครั้งเมื่อโหลด View viewWillAppear ถูกเรียกทุกครั้งก่อนที่หน้าจอจะปรากฏ อันแรกสำหรับการตั้งค่าครั้งเดียว อันที่สองสำหรับการอัปเดตข้อมูลและสถานะ
การดำเนินการ แบบซิงโครนัส ที่หนักใน viewDidLoad จะบล็อกเธรดหลักและทำให้การแสดงหน้าจอล่าช้า การโหลดแบบอะซิงโครนัสเป็นที่ยอมรับได้ แต่เมื่ออัปเดต UI หลังจากเสร็จสิ้น ต้องพิจารณาว่าหน้าจออาจถูกซ่อนไว้แล้ว
คุณไม่สามารถเรียก viewDidLoad โดยตรง — UIKit เป็นผู้เรียก หากต้องการบังคับให้โหลด View ให้เข้าถึงคุณสมบัติ controller.view สิ่งนี้จะเรียก loadView และ viewDidLoad โดยอัตโนมัติ
สรุป
เราจะพัฒนาแอปพลิเคชันบนมือถือแบบครบวงจร
IT Sectr สร้างแอปพลิเคชัน iOS และ Android สำหรับสตาร์ทอัพและธุรกิจตั้งแต่ปี 2017 เราจะให้คำแนะนำและเสนอวิธีแก้ปัญหาที่ดีที่สุดแก่คุณ
อ่านเพิ่มเติม