viewWillDisappear — เป็นเมธอดของ UIViewController ที่ UIKit เรียกโดยตรงก่อนที่หน้าจอจะเริ่มหายไปจากจอแสดงผลของผู้ใช้ ตามข้อมูลจาก Apple Developer Documentation เมธอดนี้รับพารามิเตอร์ animated และทำงานเมื่อ push, pop, present, dismiss และการเปลี่ยนแท็บ viewWillDisappear เป็นตำแหน่งหลักสำหรับการบันทึกสถานะและการล้างทรัพยากรอย่างถูกต้อง
ข้อสำคัญ
viewWillDisappear เป็นเมธอดของ UIViewController ที่ UIKit เรียกโดยตรงก่อนที่ View ของคอนโทรลเลอร์จะเริ่มหายไปจากหน้าจอ ในขณะนี้หน้าจอยังคงมองเห็นได้สำหรับผู้ใช้ แต่การเปลี่ยนผ่านได้เริ่มต้นแล้ว: NavigationController เริ่มแอนิเมชัน push/pop, หน้าต่างโมดอลเริ่มปิด หรือ TabBar เริ่มเปลี่ยนไปยังแท็บอื่น นักพัฒนาจะโอเวอร์ไรด์เมธอดนี้เพื่อดำเนินการที่จำเป็นต้องให้หน้าจอยังเข้าถึงได้ แต่กำลังเตรียมที่จะถูกซ่อน
แตกต่างจาก viewDidDisappear ที่ทำงานหลังจากที่หน้าจอถูกซ่อนแล้ว viewWillDisappear ให้ โอกาสสุดท้าย ในการบันทึกข้อมูลและปล่อยทรัพยากรในขณะที่ผู้ใช้ยังเห็นอินเทอร์เฟซอยู่ นี่เป็นสิ่งสำคัญอย่างยิ่งสำหรับ UX — การบันทึกร่างหรือหยุดตัวจับเวลาควรเกิดขึ้นก่อนที่ผู้ใช้จะเปลี่ยนไปยังหน้าจออื่น
เมธอดรับพารามิเตอร์ animated ซึ่งระบุว่าการหายไปเกิดขึ้นพร้อมแอนิเมชันหรือไม่ ค่า true หมายถึง UIKit ดำเนินการเปลี่ยนผ่านด้วยแอนิเมชัน ส่วน false หมายถึงหน้าจอหายไปทันที เช่นเมื่อ dismiss โดยไม่มีแอนิเมชันหรือการลบออกจากลำดับชั้นโดยทางโปรแกรม
viewWillDisappear ถูกเรียกในทุกสถานการณ์ที่หน้าจอปัจจุบันหยุดเป็นหน้าจอที่ใช้งาน เรามาดูกรณีหลักที่เฉพาะเจาะจงกับการพัฒนา iOS
เมื่อ UINavigationController ทำการ push คอนโทรลเลอร์ใหม่ viewWillDisappear ของคอนโทรลเลอร์ปัจจุบันจะถูกเรียกที่จุดเริ่มต้นของแอนิเมชันการเปลี่ยนผ่าน ในขณะนี้หน้าจอปัจจุบัน ยังคงมองเห็นได้ ใต้คอนโทรลเลอร์ใหม่ที่เลื่อนขึ้นมาด้านบน นี่เป็นสถานการณ์มาตรฐานที่ viewWillDisappear ทำงานด้วย animated = true
เมื่อผู้ใช้กดปุ่ม back หรือทำท่าทางปัดกลับแบบอินเทอร์แอคทีฟ viewWillDisappear ของคอนโทรลเลอร์ปัจจุบันจะถูกเรียก ในการปัดแบบอินเทอร์แอคทีฟ การเรียกนี้อาจ ถูกยกเลิก หากผู้ใช้เปลี่ยนใจและดึงหน้าจอกลับไปยังตำแหน่งเดิม นี่เป็นคุณสมบัติสำคัญที่ต้องพิจารณาเมื่อออกแบบการบันทึกสถานะ
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
saveDraftData()
NotificationCenter.default.removeObserver(self)
}
เมื่อปิดหน้าต่างโมดอล viewWillDisappear จะถูกเรียกบนคอนโทรลเลอร์ที่กำลังถูกปิดที่จุดเริ่มต้นของแอนิเมชัน dismiss ในขณะนี้สามารถส่งผลลัพธ์กลับผ่าน delegate หรือ closure เนื่องจากคอนโทรลเลอร์ที่แสดงหน้าต่างโมดอลยังไม่ได้รับการควบคุม
UITabBarController เรียก viewWillDisappear บนคอนโทรลเลอร์ของแท็บที่กำลังถูกออกทันทีหลังจากที่ผู้ใช้แตะแท็บอื่น หากแท็บปัจจุบันมี กระบวนการที่ทำงานอยู่ — การเล่นสื่อ การดาวน์โหลดไฟล์ ตัวจับเวลา — ควรหยุดหรือพักไว้ที่นี่
viewWillDisappear แก้ปัญหางานเฉพาะด้านการจัดการทรัพยากรและสถานะ มาดูสถานการณ์สำคัญพร้อมตัวอย่างโค้ด
งานที่สำคัญที่สุดของ viewWillDisappear คือ การบันทึกข้อมูลที่ผู้ใช้ป้อนหรือเปลี่ยนแปลงบนหน้าจอปัจจุบัน ร่างข้อความ ฟิลด์ฟอร์มที่ถูกแก้ไข การตั้งค่าที่เลือก — ทั้งหมดนี้ควรถูกบันทึกก่อนที่หน้าจอจะหายไป ใช้ Core Data, UserDefaults หรือพื้นที่จัดเก็บไฟล์เพื่อความคงทน
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
guard hasUnsavedChanges else { return }
draftStorage.save(currentDraft)
}
NotificationCenter, KVO และ Combine publishers ที่คุณสมัครรับใน viewWillAppear หรือ viewDidLoad ควรถูกยกเลิกใน viewWillDisappear หากไม่ทำเช่นนั้น การแจ้งเตือนจะยังคงมาถึงหน้าจอที่ถูกซ่อน ทำให้เกิดการอัปเดต UI ที่ผู้ใช้ไม่เห็น หรือที่แย่กว่านั้น — เกิดการขัดข้องจากการเข้าถึงออบเจกต์ที่ถูกปล่อยไปแล้ว
แอนิเมชัน UIView ที่เริ่มใน viewDidAppear และตัวจับเวลาที่ทำงานผ่าน Timer หรือ DispatchSource ควรถูกหยุดใน viewWillDisappear แอนิเมชันที่ดำเนินต่อไปบนหน้าจอที่ถูกซ่อน ใช้ทรัพยากร GPU และแบตเตอรี่โดยไม่มีประโยชน์ใด ๆ ต่อผู้ใช้ หยุด它们อย่างชัดเจน โดยเรียก invalidate บนตัวจับเวลาและ removeAllAnimations บนเลเยอร์
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
countdownTimer?.invalidate()
countdownTimer = nil
loadingIndicator.layer.removeAllAnimations()
}
หากคอนโทรลเลอร์ถูกเปิดเพื่อรับผลลัพธ์ — การเลือกรายการ การป้อนข้อความ การยืนยันการกระทำ — viewWillDisappear เป็นช่วงเวลาสุดท้ายที่คอนโทรลเลอร์ดั้งเดิมยังคงมีอยู่ในสแต็กและสามารถรับข้อมูลได้ เรียก delegate หรือ closure ก่อน ที่ deinit จะถูกเรียก
การบันทึกสถานะของหน้าจออย่างน่าเชื่อถือเป็นหนึ่งในงานที่ซับซ้อนที่สุดในการพัฒนา iOS viewWillDisappear เป็นองค์ประกอบสำคัญ แต่ไม่ใช่เพียงองค์ประกอบเดียวของกลยุทธ์ มาดูแนวทางแบบองค์รวม
ระดับที่ 1 — การบันทึกใน viewWillDisappear การบันทึกข้อมูลเบา ๆ อย่างรวดเร็วที่ควรพร้อมใช้งานทันทีหลังจากกลับมา เหมาะสำหรับสถานะ UI: ตำแหน่งการเลื่อน เซ็กเมนต์ที่เลือก ข้อความในฟิลด์ป้อนข้อมูล ปัญหา: เมื่อท่าทาง pop แบบอินเทอร์แอคทีฟถูกยกเลิก การบันทึกเกิดขึ้นแม้ว่าผู้ใช้จะยังคงอยู่บนหน้าจอ — ข้อมูลถูกเขียนทับโดยไม่จำเป็น
ระดับที่ 2 — การบันทึกใน viewDidDisappear ทำซ้ำการบันทึกจากระดับแรก แต่ทำงานหลังจากที่หน้าจอถูกซ่อนอย่างแน่นอนแล้วเท่านั้น นี่เป็นประกันสำหรับ ท่าทางที่ถูกยกเลิก อย่างไรก็ตาม หากใน viewWillDisappear คุณยกเลิกการสมัครรับการแจ้งเตือนแล้ว viewDidDisappear อาจไม่สามารถเข้าถึงข้อมูลบางส่วนได้
ระดับที่ 3 — การบันทึกผ่านการแจ้งเตือนของแอปพลิเคชัน UIApplication.willResignActiveNotification และ UIApplication.didEnterBackgroundNotification จะตรวจจับการย่อขนาดแอปพลิเคชัน หากผู้ใช้ย่อขนาดแอปพลิเคชัน viewWillDisappear อาจไม่ถูกเรียก — แต่การบันทึกผ่านการแจ้งเตือนเหล่านี้ รับประกันความสมบูรณ์ของข้อมูลเมื่อสิ้นสุดเซสชัน
| ระดับ | เมธอด/การแจ้งเตือน | ความน่าเชื่อถือ | การใช้งาน |
|---|---|---|---|
| 1 | viewWillDisappear | สูง | สถานะ UI, ร่าง |
| 2 | viewDidDisappear | สูงมาก | ข้อมูลสำคัญ |
| 3 | willResignActive | สูงสุด | เมื่อย่อขนาด |
คำแนะนำ: ใช้การผสมผสานทั้งสามระดับสำหรับข้อมูลผู้ใช้ที่สำคัญ สำหรับสถานะที่ไม่สำคัญ — ใช้แค่ระดับแรกก็เพียงพอ สิ่งสำคัญคือไม่บันทึกข้อมูลเดียวกันซ้ำหลายครั้ง — ใช้แฟล็ก dirty ที่ระบุว่าข้อมูลเปลี่ยนแปลงไปตั้งแต่การบันทึกครั้งล่าสุด
ควรให้ความสนใจเป็นพิเศษกับกลยุทธ์สำหรับหน้าจอ CRUD ที่ผู้ใช้ป้อนข้อมูล ในหน้าจอดังกล่าว ไม่แนะนำให้บันทึกทุกการกดแป้นพิมพ์ใน viewWillDisappear — ซึ่งมากเกินไป ใช้ auto-save แบบหน่วงเวลา (debounce) ผ่าน Timer และใช้ viewWillDisappear สำหรับการบันทึกแบบบังคับครั้งสุดท้ายเท่านั้น หากมีการเปลี่ยนแปลงที่ยังไม่ได้บันทึก วิธีการนี้จะสร้างสมดุลระหว่างประสิทธิภาพและความปลอดภัยของข้อมูล
สำหรับแอปพลิเคชันที่ใช้ Core Data มาตรการเพิ่มเติมคือการเรียก saveContext ใน viewWillDisappear เฉพาะเมื่อมีการเปลี่ยนแปลงจริงใน managed object context การตรวจสอบ context.hasChanges ก่อนบันทึกจะป้องกันการเขียนที่ไม่จำเป็นไปยัง persistent store และยืดอายุแบตเตอรี่ของอุปกรณ์ รวมการตรวจสอบนี้กับการบันทึกทั่วโลกใน applicationDidEnterBackground
การใช้งาน viewWillDisappear ที่ไม่ถูกต้อง อาจนำไปสู่การสูญหายของข้อมูล หน่วยความจำรั่วไหล และพฤติกรรมที่ไม่เสถียรของแอปพลิเคชัน มาดูข้อผิดพลาดที่พบบ่อยของนักพัฒนา iOS
ข้อผิดพลาดแรก — การบันทึกข้อมูลเฉพาะใน viewWillDisappear ตามที่กล่าวไว้ข้างต้น เมื่อใช้ท่าทาง pop แบบอินเทอร์แอคทีฟ เมธอดจะถูกเรียกแม้ว่าหน้าจอจะไม่หายไป หากการบันทึกมีผลข้างเคียง — การส่งข้อมูลไปยังเซิร์ฟเวอร์ การเปลี่ยนสถานะ — อาจนำไปสู่ การทำงานที่ผิดพลาด เพิ่มการตรวจสอบ isBeingDismissed หรือ isMovingFromParent
ข้อผิดพลาดที่สอง — การไม่ยกเลิกการสมัครรับจาก NotificationCenter นี่เป็นหนึ่งในสาเหตุที่พบบ่อยที่สุดของหน่วยความจำรั่วไหลใน iOS หากคุณสมัครรับใน viewWillAppear สำหรับ UIResponder.keyboardWillShowNotification แต่ไม่ยกเลิกใน viewWillDisappear closure จะยังคงถูกเรียก เมื่อ deinit ของคอนโทรลเลอร์ closure จะอ้างอิงถึงออบเจกต์ที่ถูกปล่อยไปแล้ว — การขัดข้องของแอปพลิเคชันจะเกิดขึ้นอย่างแน่นอน
ข้อผิดพลาดที่สาม — การดำเนินการซิงโครนัสที่หนักหน่วง การบันทึกข้อมูลปริมาณมาก การเขียนไปยัง Core Data หรือระบบไฟล์ใน viewWillDisappear จะบล็อก main thread หากการดำเนินการใช้เวลานานกว่าแอนิเมชันการเปลี่ยนผ่าน UIKit จะหยุดเธรดและอินเทอร์เฟซ ค้าง ย้ายการบันทึกที่หนักหน่วงไปยังคิวพื้นหลัง
ข้อผิดพลาดที่สี่ — ลืมเรียก super การไม่เรียก super.viewWillDisappear อาจทำให้การทำงานของ UINavigationController และ UITabBarController ผิดปกติ ซึ่งใช้เมธอดนี้สำหรับสถานะภายในของตน เรียก super ก่อนหรือหลังเสมอ ตามเอกสารประกอบของ Apple
ปัญหานี้รุนแรงขึ้นใน iOS ที่มีการทำงานหลายอย่างพร้อมกันและการสลับระหว่างแอปพลิเคชัน ข้อผิดพลาดที่ห้า — การใช้ DispatchQueue.main.async หลังจากบันทึกใน viewWillDisappear หากคุณส่งบล็อกแบบอะซิงโครนัสไปยังคิวหลักหลังจากเรียก super.viewWillDisappear ไม่มีการรับประกันว่าคอนโทรลเลอร์ยังคงมีอยู่ในขณะที่บล็อกทำงาน ใช้ weak references [weak self] ภายใน closures เสมอเพื่อป้องกันการเข้าถึงหน่วยความจำที่ถูกปล่อยไปแล้วและป้องกันการขัดข้องของแอปพลิเคชัน
คำถามที่พบบ่อย
viewWillDisappear ถูกเรียกเมื่อเริ่มการหายไป ในขณะที่หน้าจอยังมองเห็นได้ ส่วน viewDidDisappear ถูกเรียกหลังจากที่หน้าจอถูกซ่อนอย่างสมบูรณ์และแอนิเมชันเสร็จสิ้น
ใช้ viewDidDisappear เพื่อยืนยันการบันทึก หรือตรวจสอบคุณสมบัติ isMovingFromParent และ isBeingDismissed ภายใน viewWillDisappear เพื่อระบุว่าหน้าจอจะหายไปจริงหรือไม่
ใช่ จำเป็นอย่างยิ่ง หากคุณใช้บล็อกหรือ selectors กับ self ARC ไม่จัดการการสมัครรับของ NotificationCenter ใน iOS 9+ สำหรับบล็อกให้ใช้ weak reference และยกเลิกการสมัครใน viewWillDisappear
ไม่สามารถ — force quit ไม่เรียกเมธอด Lifecycle สำหรับการบันทึกที่รับประกันเมื่อสิ้นสุดแอปพลิเคชัน ให้ใช้ UIApplication.willTerminateNotification หรือบันทึกข้อมูลแบบเรียลไทม์เมื่อมีการเปลี่ยนแปลง
ได้ เมื่อใช้ท่าทาง pop แบบอินเทอร์แอคทีฟ UIKit จะเรียก viewWillDisappear ทันทีหลังจากเริ่มท่าทาง หากผู้ใช้ยกเลิกท่าทาง หน้าจอยังคงมองเห็นได้ แต่เมธอดทำงานแล้ว ตรวจสอบ isMovingFromParent เสมอ
สรุป
เราจะพัฒนาแอปพลิเคชันบนมือถือแบบครบวงจร
IT Sectr สร้างแอปพลิเคชัน iOS และ Android สำหรับสตาร์ทอัพและธุรกิจตั้งแต่ปี 2017 เราจะให้คำแนะนำและเสนอวิธีแก้ปัญหาที่ดีที่สุดแก่คุณ
อ่านเพิ่มเติม