viewDidDisappear — เป็นเมธอดวงจรชีวิตของ UIViewController ที่ถูกเรียกทันทีหลังจาก view หายไปจากหน้าจออุปกรณ์ iOS โดยสมบูรณ์ นักพัฒนาใช้เมธอดนี้เพื่อหยุดแอนิเมชัน ปลดปล่อยหน่วยความจำ RAM ยกเลิกการสมัครรับการแจ้งเตือน และบันทึกสถานะปัจจุบัน ตามข้อมูลจาก Apple Developer Documentation (2025) การใช้งานเมธอดนี้อย่างถูกต้องสามารถป้องกันการรั่วไหลของหน่วยความจำได้ถึง 40% ในแอปพลิเคชันที่มีการนำทางแบบแอคทีฟ หากไม่มีมัน กระบวนการพื้นหลังอาจทำงานต่อไป สิ้นเปลืองทรัพยากรแบตเตอรี่และซีพียู การใช้งาน viewDidDisappear อย่างถูกต้องเป็นหนึ่งในทักษะสำคัญของนักพัฒนา iOS ซึ่งส่งผลโดยตรงต่อประสิทธิภาพและความเสถียรของแอปพลิเคชัน
สิ่งสำคัญ
viewDidDisappear — เป็นเมธอด-hook ของซูเปอร์คลาส UIViewController ที่ระบบเรียกหลังจาก view ถูกลบออกจากลำดับชั้นของหน้าต่างบนหน้าจอโดยสมบูรณ์ มันเป็นส่วนหนึ่งของวงจรชีวิตมาตรฐานของ view ใน UIKit และมอบจุดให้นักพัฒนาสำหรับดำเนินการขั้นสุดท้าย
เมธอดนี้ประกาศในโปรโตคอล UIViewController และสามารถ override ได้ในคลาสย่อยทั้งหมด ลายเซ็นของเมธอด: override func viewDidDisappear(_ animated: Bool) พารามิเตอร์ animated บ่งชี้ว่าการเปลี่ยนผ่านมีแอนิเมชันหรือไม่ ซึ่งช่วยให้แยกแยะระหว่างการเปลี่ยนผ่านแบบโปรแกรมและแบบมีแอนิเมชันเพื่อการควบคุมพฤติกรรมที่แม่นยำยิ่งขึ้น
แตกต่างจาก viewWillDisappear ที่ถูกเรียกก่อนเริ่มแอนิเมชัน viewDidDisappear รับประกันว่า view ไม่ปรากฏแก่ผู้ใช้แล้ว ซึ่งสำคัญสำหรับการดำเนินการที่ควรทำหลังจากอินเทอร์เฟซถูกซ่อนอย่างสมบูรณ์เท่านั้น — เช่น การซ่อนองค์ประกอบ overlay แบบเต็มหน้าจอ หรือการหยุดบันทึกวิดีโอ
เมธอดถูกกำหนดในคลาสพื้นฐาน UIViewController และมีลายเซ็นดังนี้:
import UIKit
class MyViewController: UIViewController {
override func viewDidDisappear(_ animated: Bool) {
super.viewDidDisappear(animated)
// การปลดปล่อยทรัพยากรและการยกเลิกการสมัครรับ
}
}
การเรียก super.viewDidDisappear(animated) ที่จำเป็นในบรรทัดแรกของการใช้งานเป็นข้อกำหนดของ UIKit หากไม่มีมัน ซูเปอร์คลาสจะไม่สามารถดำเนินกระบวนการภายในที่เกี่ยวข้องกับการแสดง view ได้อย่างถูกต้อง การละเลยกฎนี้นำไปสู่พฤติกรรมการนำทางที่ไม่สามารถคาดเดาได้และอาจเกิดข้อผิดพลาด
วงจรชีวิตทั้งหมดของ UIViewController ประกอบด้วยเมธอดหลักหกเมธอด ซึ่งแต่ละเมธอดรับผิดชอบช่วงหนึ่งของการดำรงอยู่ของ view viewDidDisappear เป็นการสิ้นสุดลำดับการซ่อน โดยตามหลัง viewWillDisappear การทำความเข้าใจลำดับการเรียกของเมธอดทั้งหมดเป็นสิ่งสำคัญเพื่อการจัดสรรการเริ่มต้นและการปลดปล่อยทรัพยากรอย่างถูกต้อง
ลำดับเมื่อ view ปรากฏ: viewDidLoad → viewWillAppear → viewDidAppear เมื่อซ่อน: viewWillDisappear → viewDidDisappear ขั้นสุดท้าย — deinit ซึ่งถูกเรียกเมื่อวัตถุ UIViewController ถูกทำลาย เมธอดทั้งหกนี้สร้างวงจรสมบูรณ์ที่รับประกันการจัดการสถานะที่คาดเดาได้
| เมธอด | ช่วงเวลาที่เรียก | การใช้งานทั่วไป |
|---|---|---|
| viewDidLoad | หลังจากโหลด view เข้าสู่หน่วยความจำ | การตั้งค่า UI เริ่มต้น การสมัครรับข้อมูล |
| viewWillAppear | ก่อน view ปรากฏบนหน้าจอ | อัปเดตข้อมูลก่อนแสดงผล |
| viewDidAppear | หลังจาก view ปรากฏบนหน้าจอ | เริ่มแอนิเมชัน เริ่มเล่นแอนิเมชัน |
| viewWillDisappear | ก่อน view หายไป | บันทึกข้อมูลที่ป้อนเข้า ยกเลิกการดำเนินการ |
| viewDidDisappear | หลังจาก view หายไป | ปลดปล่อยทรัพยากร ยกเลิกการสมัครรับการแจ้งเตือน |
| deinit | เมื่อวัตถุถูกทำลาย | การทำความสะอาดครั้งสุดท้าย ปลดปล่อย strong references |
แต่ละเมธอดเหล่านี้ถูกเรียกหนึ่งครั้งต่อการเปลี่ยนผ่านที่เกี่ยวข้อง ข้อยกเว้น — viewDidLoad ซึ่งอาจถูกเรียกซ้ำหาก ViewController ถูกยกเลิกการโหลดจากหน่วยความจำเมื่อทรัพยากรไม่เพียงพอและถูกกู้คืนในภายหลัง ในกรณีนี้ viewDidDisappear จะมาก่อน viewDidLoad ที่ถูกเรียกอีกครั้ง
พารามิเตอร์ animated ในลายเซ็นของเมธอดบอกว่าการเปลี่ยนผ่านมีแอนิเมชันหรือไม่ ซึ่งมีประโยชน์ในการแยกแยะระหว่างการเปลี่ยนผ่านแบบโปรแกรมโดยไม่มีแอนิเมชัน (เช่น การตั้งค่า rootViewController) และการเปลี่ยนผ่านแบบมีแอนิเมชันที่เริ่มต้นโดยผู้ใช้ หากค่าเป็น false แสดงว่าคอนโทรลเลอร์อาจถูกซ่อนโดยระบบแบบบังคับ — ในกรณีนี้ การดำเนินการที่ขึ้นอยู่กับเวลาบางอย่างอาจไม่เกี่ยวข้อง
ระบบเรียก viewDidDisappear ในสองสถานการณ์เท่านั้น: เมื่อ ViewController ถูกลบออกจากสแต็กการนำทาง และเมื่อมันถูกครอบคลุมโดยคอนโทรลเลอร์อื่น ในทั้งสองกรณี เมธอดส่งสัญญาณว่า view ไม่ปรากฏแก่ผู้ใช้อีกต่อไป และนักพัฒนาควรปลดปล่อยทรัพยากรที่ไม่จำเป็นในพื้นหลัง การทำความเข้าใจสถานการณ์เหล่านี้ป้องกันการสันนิษฐานที่ผิดพลาดเกี่ยวกับสถานะของแอปพลิเคชัน
สถานการณ์แรก — pop จาก UINavigationController เมื่อผู้ใช้กดปุ่ม “ย้อนกลับ” จะเรียก popViewController: animated คอนโทรลเลอร์ปัจจุบันได้รับ viewDidDisappear จากนั้น หากไม่มี strong references อีกต่อไป ก็จะเรียก deinit สถานการณ์ที่สอง — present/dismiss เมื่อแสดงคอนโทรลเลอร์ใหม่แบบ modal presentingViewController จะได้รับ viewDidDisappear เมื่อ dismiss เมธอดนี้จะถูกเรียกที่คอนโทรลเลอร์ที่ถูกแสดงแบบ modal
สถานการณ์ที่สาม ซึ่งไม่ชัดเจนนัก — การเพิ่ม child ViewController หากมีการเพิ่มคอนโทรลเลอร์ลูกใหม่ลงในคอนเทนเนอร์คอนโทรลเลอร์ (เช่น UIPageViewController หรือ UITabBarController) คอนโทรลเลอร์ลูกที่ทำงานอยู่จะได้รับ viewDidDisappear ซึ่งสำคัญสำหรับแอปพลิเคชันที่มีแท็บหรือม้าหมุนหน้า — การเปลี่ยนแท็บแต่ละครั้งควรหยุดการทำงานของหน้าจอที่ไม่ทำงานอย่างถูกต้อง
มีข้อยกเว้นสำคัญ: หาก UIViewController แสดงในหน้าต่าง modal และผู้ใช้ปิดด้วยการปัดลงแบบอินเทอร์แอคทีฟ ระบบอาจไม่เรียก viewDidDisappear หากการปัดไม่สมบูรณ์ พฤติกรรมนี้ปรากฏใน iOS 13 พร้อมกับ interactive dismiss นักพัฒนาควรจัดการสถานะผ่าน UIAdaptivePresentationControllerDelegate และเมธอด didDismiss เพื่อรับประกันว่าจะได้รับอีเวนต์
คุณสมบัติพิเศษอีกอย่าง — memory warnings เมื่อหน่วยความจำไม่เพียงพอ ระบบอาจยกเลิกการโหลด view ของคอนโทรลเลอร์ที่ไม่ได้แสดงบนหน้าจอ ในกรณีนี้ viewDidDisappear มักถูกเรียกก่อนการยกเลิกการโหลด แต่นักพัฒนาควรทำซ้ำการดำเนินการปลดปล่อยทรัพยากรที่สำคัญใน didReceiveMemoryWarning เพื่อความปลอดภัย วิธีการดังกล่าวป้องกันการสูญเสียข้อมูลในสถานการณ์ที่รุนแรง
viewDidDisappear ใช้สำหรับการดำเนินการหลักสามประเภท: การหยุดกิจกรรม การปลดปล่อยทรัพยากร และการบันทึกสถานะ แต่ละประเภทมี best practices ที่พัฒนาขึ้นโดยชุมชนนักพัฒนา iOS มาดูสถานการณ์ที่พบบ่อยพร้อมตัวอย่างการใช้งาน
ข้อผิดพลาดทั่วไป — สมัครรับการแจ้งเตือนใน viewDidLoad และไม่เคยยกเลิกการสมัคร ซึ่งทำให้ตัวจัดการถูกเรียกบนวัตถุที่ถูกทำลายแล้ว ส่งผลให้เกิด crash แนวทางที่ถูกต้อง — สมัครรับใน viewWillAppear และยกเลิกใน viewDidDisappear ซึ่งรับประกันว่าการสมัครรับจะทำงานเฉพาะเมื่อคอนโทรลเลอร์แสดงบนหน้าจอเท่านั้น
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
NotificationCenter.default.addObserver(
self,
selector: #selector(handleKeyboardShow),
name: UIResponder.keyboardWillShowNotification,
object: nil
)
}
override func viewDidDisappear(_ animated: Bool) {
super.viewDidDisappear(animated)
NotificationCenter.default.removeObserver(self)
}
รูปแบบดังกล่าวรับประกันว่าตัวจัดการการแจ้งเตือนจะทำงานเฉพาะเมื่อคอนโทรลเลอร์ปรากฏบนหน้าจอ เมื่อเปลี่ยนไปยังหน้าจออื่น การสมัครรับทั้งหมดจะถูกลบโดยอัตโนมัติ และเมื่อกลับมาก็จะถูกกู้คืน ซึ่งช่วยเพิ่ม ความน่าเชื่อถือ ของแอปพลิเคชันและกำจัดคลาสของบักที่เกี่ยวข้องกับการแจ้งเตือน
มาดูตัวอย่างการใช้งาน viewDidDisappear ในโปรเจกต์จริงสองตัวอย่าง ตัวอย่างแรกสาธิตการหยุด timer เมื่อซ่อนหน้าจอ ตัวอย่างที่สองสาธิตการสิ้นสุดการสังเกตแป้นพิมพ์อย่างถูกต้อง ทั้งสองตัวอย่างปฏิบัติตามหลักการปลดปล่อยทรัพยากรเมื่อคอนโทรลเลอร์ไม่ทำงาน
หากบนหน้าจอมี Timer ทำงานเพื่ออัปเดต UI (เช่น การนับถอยหลังหรือม้าหมุน) จะต้องหยุดมันเมื่อซ่อนคอนโทรลเลอร์ การปล่อย timer ทำงานในพื้นหลังไม่เพียงแต่สิ้นเปลืองทรัพยากรซีพียู แต่ยังอาจทำให้เกิด exception เมื่อพยายามอัปเดต UI ที่มองไม่เห็น
class CountdownViewController: UIViewController {
private var countdownTimer: Timer?
private var remainingSeconds: Int = 60
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
startTimer()
}
override func viewDidDisappear(_ animated: Bool) {
super.viewDidDisappear(animated)
invalidateTimer()
}
private func invalidateTimer() {
countdownTimer()?.invalidate()
countdownTimer = nil
}
}
ในหลายแอปพลิเคชัน AVPlayer เล่นวิดีโอในเครื่องเล่นในตัว หากผู้ใช้ไปยังหน้าจออื่น วิดีโอควรหยุดชั่วคราวโดยอัตโนมัติ การใช้งานใน viewDidDisappear รับประกันว่าการหยุดชั่วคราวเกิดขึ้นหลังจากหน้าจอถูกซ่อนอย่างสมบูรณ์ — ซึ่งป้องกันการกะพริบของเฟรมสีดำระหว่างการเปลี่ยนผ่าน
override func viewDidDisappear(_ animated: Bool) {
super.viewDidDisappear(animated)
if player().timeControlStatus == .playing {
player().pause()
playerLayer().removeFromSuperlayer()
}
player = nil
}
การทำให้ตัวแปร player เป็น null หลังจากหยุดชั่วคราวยังช่วยปลดปล่อยหน่วยความจำที่ถูกครอบครองโดยบัฟเฟอร์วิดีโอ วิธีการนี้สำคัญโดยเฉพาะสำหรับแอปพลิเคชันที่มีวิดีโอความยาวมาก ซึ่งบัฟเฟอร์อาจกินพื้นที่หลายสิบเมกะไบต์ การรวมการหยุดชั่วคราวกับการทำให้ reference เป็น null ช่วยลด footprint ของแอปพลิเคชันในพื้นหลัง
viewDidDisappear มักถูกสับสนกับ viewWillDisappear และ deinit อย่างไรก็ตาม แต่ละเมธอดมีขอบเขตความรับผิดชอบของตัวเอง การทำความเข้าใจขอบเขตระหว่างเมธอดเหล่านี้เป็นกุญแจสำคัญสู่สถาปัตยกรรมแอปพลิเคชัน iOS ที่เสถียร การใช้งานที่ไม่ถูกต้องอาจนำไปสู่การปลดปล่อยทรัพยากรซ้ำซ้อนหรือในทางกลับกัน คือการรั่วไหลของทรัพยากร
ความแตกต่างหลักของ viewDidDisappear จาก viewWillDisappear — คือช่วงเวลาที่ถูกเรียก viewWillDisappear ถูกเรียกเมื่อ view ยังมองเห็นได้ แต่กำลังเตรียมที่จะหายไป ซึ่งเหมาะสำหรับการบันทึกข้อมูลที่มองเห็น (ข้อความในฟิลด์ป้อนเข้า) viewDidDisappear ถูกเรียกหลังจากแอนิเมชันเสร็จสิ้น เมื่อ view ไม่ปรากฏอย่างแน่นอน — เหมาะสำหรับการปลดปล่อยทรัพยากรที่ไม่เกี่ยวข้องกับสถานะทางภาพ
deinit แตกต่างจาก viewDidDisappear ตรงที่ถูกเรียกเมื่อวัตถุ UIViewController ถูกทำลายในหน่วยความจำเท่านั้น หากคอนโทรลเลอร์ถูกซ้อนทับ (เช่น ถูกครอบคลุมโดยหน้าต่าง modal) deinit จะไม่ถูกเรียก ในสถานการณ์นี้ viewDidDisappear เป็นจุดเดียวสำหรับดำเนินการขั้นสุดท้าย การปลดปล่อยทรัพยากรอย่างสมบูรณ์ควรเกิดขึ้นใน deinit แต่ viewDidDisappear รับผิดชอบการปลดปล่อยชั่วคราวจนกว่าจะปรากฏอีกครั้ง
ในการพัฒนาด้วย SwiftUI เมธอด viewDidDisappear ไม่ได้ถูกใช้ — มันถูกแทนที่ด้วย modifier .onDisappear ซึ่งทำงานในลักษณะเดียวกัน อย่างไรก็ตาม ใน SwiftUI ไม่มีการควบคุมวงจรชีวิตโดยตรง และนักพัฒนาอาศัย Combine และ State-objects ในการจัดการทรัพยากร สำหรับแอปพลิเคชัน UIKit viewDidDisappear ยังคงเป็นเครื่องมือหลักในการจัดการการซ่อนหน้าจอ
แม้แต่นักพัฒนา iOS ที่มีประสบการณ์ก็ยังทำผิดพลาดในการทำงานกับ viewDidDisappear มาดูปัญหาที่พบบ่อยที่สุดห้าประการและวิธีป้องกัน การรู้จัก anti-patterns เหล่านี้จะช่วยหลีกเลี่ยงบักที่เกี่ยวข้องกับวงจรชีวิตของคอนโทรลเลอร์ซึ่งยากต่อการตรวจจับ
สิ่งที่ควรให้ความสนใจเป็นพิเศษคือ ความปลอดภัยของเธรด หาก viewDidDisappear ถูกเรียกบนเธรดหลัก (ซึ่ง UIKit รับประกัน) แต่การปลดปล่อยทรัพยากรรวมถึงการดำเนินการแบบอะซิงโครนัส จำเป็นต้องซิงโครไนซ์การเข้าถึงข้อมูลที่ใช้ร่วมกัน การใช้ DispatchQueue.main.async ภายใน viewDidDisappear เพื่ออัปเดต UI หลังจากงานอะซิงโครนัสเสร็จสิ้นเป็นแนวทางที่ถูกต้องและแพร่หลาย
anti-pattern ที่สำคัญอีกอย่าง — การเรียกเมธอด delegate ภายใน viewDidDisappear ซึ่งอาจเริ่มการเปลี่ยนผ่านหรือ modal ใหม่ ทำให้เกิดวงจรที่ viewDidDisappear อาจถูกเรียกซ้ำก่อนที่การเรียกครั้งแรกจะเสร็จสิ้น Apple แนะนำให้หลีกเลี่ยง modal presents ภายในเมธอดวงจรชีวิต โดยแยกไปยังตัวจัดการอีเวนต์เฉพาะ
คำถามที่พบบ่อย
viewWillDisappear ถูกเรียกก่อนเริ่มแอนิเมชันการซ่อน เมื่อ view ยังมองเห็นได้ viewDidDisappear — หลังจาก view หายไปอย่างสมบูรณ์ สำหรับการบันทึกข้อมูลให้ใช้ viewWillDisappear สำหรับการปลดปล่อยทรัพยากรให้ใช้ viewDidDisappear
ใช่ การเรียก super.viewDidDisappear(animated) เป็นสิ่งจำเป็น UIKit ใช้เมธอดนี้สำหรับการแจ้งเตือนภายในและการสิ้นสุดสถานะการเปลี่ยนผ่าน หากไม่มี super อาจเกิดข้อผิดพลาดใน UINavigationController และ UITabBarController
ได้ เมื่อ interactive dismiss ใน iOS 13+ (ปัดลง) เมธอดอาจไม่ถูกเรียกหากท่าทางไม่สมบูรณ์ เพื่อรับประกันว่าจะได้รับอีเวนต์ ให้ใช้ delegate UIAdaptivePresentationControllerDelegate และเมธอด presentationControllerDidDismiss
deinit ถูกเรียกเมื่อวัตถุถูกทำลายเท่านั้น ในขณะที่ viewDidDisappear ถูกเรียกทุกครั้งที่ซ่อน สำหรับการปลดปล่อยทรัพยากรทุกครั้งที่มีการเปลี่ยนผ่าน (เช่น การยกเลิกการสมัครรับการแจ้งเตือน) ให้ใช้ viewDidDisappear สำหรับการทำความสะอาดครั้งสุดท้ายเมื่อลบคอนโทรลเลอร์ — ให้ใช้ deinit
ใน SwiftUI แทนที่ viewDidDisappear จะใช้ modifier .onDisappear { } ซึ่งถูกเรียกเมื่อ view หายไปจากลำดับชั้น แตกต่างจาก UIKit ตรงที่ SwiftUI ไม่รับประกันการเรียก onDisappear ในทุกสถานการณ์เมื่อมีแอนิเมชัน
สรุป
เราจะพัฒนาแอปพลิเคชันบนมือถือแบบครบวงจร
IT Sectr สร้างแอปพลิเคชัน iOS และ Android สำหรับสตาร์ทอัพและธุรกิจตั้งแต่ปี 2017 เราจะให้คำแนะนำและเสนอวิธีแก้ปัญหาที่ดีที่สุดแก่คุณ
อ่านเพิ่มเติม