.onDisappear คือตัวปรับแต่ง SwiftUI ที่ดำเนินการ closure เมื่อ View ถูกลบออกจากลำดับชั้นของอินเทอร์เฟซ การเรียกเกิดขึ้นเมื่อปิดหน้าจอ เปลี่ยนแท็บ ปิดหน้าต่างโมดอล หรือเลื่อนองค์ประกอบออกนอกการมองเห็น ตาม เอกสารสำหรับนักพัฒนา Apple (2026) onDisappear ไม่รับประกันการเรียกในสถานการณ์ขัดข้องหรือเมื่อแอปถูกยุติโดย watch dog ของระบบ เรียนรู้เพิ่มเติมเกี่ยวกับ SwiftUI ได้ใน บทความเกี่ยวกับ SwiftUI
ประเด็นสำคัญ
.onDisappear คือตัวปรับแต่ง View ใน SwiftUI ที่รับ closure แบบ Void และดำเนินการเมื่อ View ถูกลบออกจากลำดับชั้น เมื่อรวมกับ .onAppear จะสร้างวงจรชีวิตที่สมบูรณ์ของหน้าจอ: การปรากฏ — การทำงาน — การหายไป Apple เปิดตัว onDisappear พร้อมกับ SwiftUI ใน iOS 13 ในฐานะอะนาล็อกของ viewDidDisappear จาก UIKit
ในทางไวยากรณ์ .onDisappear เหมือนกับ onAppear: มันปรับแต่ง View ใด ๆ และแนบ closure ที่ SwiftUI เรนเดอร์เรอร์เรียกเมื่อ view ถูกลบ แตกต่างจาก UIKit ที่ viewDidDisappear จะทำงานหลังจากแอนิเมชันการเปลี่ยนผ่านเสร็จสมบูรณ์เท่านั้น onDisappear ใน SwiftUI สามารถถูกเรียกก่อนที่แอนิเมชันจะสิ้นสุด — ในขณะที่ View ถูกทำเครื่องหมายให้ลบ
ไวยากรณ์พื้นฐาน ของ onDisappear นั้นกระชับเท่ากับ onAppear ตัวปรับแต่งไม่รับพารามิเตอร์เพิ่มเติม — เพียงแค่ closure ซึ่งดำเนินการแบบซิงโครนัสบนเธรดหลัก
struct DetailView: View {
var body: some View {
Text("Detail Screen")
.onDisappear {
print("DetailView หายไปจากหน้าจอ")
}
}
}
กลไกการเรียก ของ onDisappear ตรงกันข้ามกับ onAppear: องค์ประกอบลูกได้รับ onDisappear ก่อน จากนั้นจึงเป็นพ่อแม่ กฎ child-first นี้รับประกันว่าทรัพยากรลูกจะถูกปลดปล่อยก่อนที่ทรัพยากรพ่อแม่จะถูกปลดปล่อย หากองค์ประกอบลูกขึ้นอยู่กับข้อมูลของพ่อแม่ จะต้องสามารถยุติได้อย่างถูกต้องโดยไม่มีบริบทของพ่อแม่
SwiftUI เรียก onDisappear เมื่อ View ถูกลบออกจากกราฟการเรนเดอร์ ทริกเกอร์ได้แก่: pop จาก NavigationStack, การเปลี่ยน TabView, การปิดโมดอล (sheet/fullScreenCover), หรือ View ที่เรนเดอร์แบบมีเงื่อนไข (if/switch) ใน List และ ScrollView onDisappear จะถูกเรียกเมื่อเซลล์เลื่อนออกนอกบัฟเฟอร์โหลดล่วงหน้า
ลำดับ child-first หมายความว่าหาก VStack มีสาม View ลูก onDisappear จะถูกเรียกสำหรับลูกแต่ละตัวในลำดับย้อนกลับก่อน จากนั้นจึงเรียกสำหรับพ่อแม่ ซึ่งสำคัญต่อการล้างข้อมูลที่ถูกต้อง: ตัวจับเวลาของลูกจะถูกยกเลิกก่อนที่ ViewModel ของพ่อแม่จะปลดปล่อยทรัพยากรที่ใช้ร่วมกัน
struct ParentView: View {
var body: some View {
VStack {
ChildView(id: "A")
ChildView(id: "B")
}
.onDisappear {
print("Parent onDisappear — สุดท้าย")
}
}
}
struct ChildView: View {
let id: String
var body: some View {
Text("Child \(id)")
.onDisappear {
print("Child \(id) onDisappear")
}
}
}
ผลลัพธ์คอนโซล: Child B onDisappear, Child A onDisappear, Parent onDisappear — สุดท้าย ลำดับย้อนกลับเมื่อเทียบกับ onAppear รับประกันห่วงโซ่การล้างข้อมูลที่ถูกต้อง
สถานการณ์การเรียก ของ onDisappear ขึ้นอยู่กับชนิดของคอนเทนเนอร์ ใน NavigationStack onDisappear จะทำงานเมื่อ pop-to-root, pop-back ปกติ หรือการปิดหน้าจอด้วยการปัด (interactivePopGestureRecognizer) ใน TabView การเปลี่ยนแท็บจะเรียก onDisappear สำหรับแท็บที่ซ่อนและ onAppear สำหรับแท็บที่แสดง — ตัวปรับแต่งทั้งสองทำงานเกือบพร้อมกัน
ใน Sheet และ fullScreenCover onDisappear จะถูกเรียกเมื่อปิดโดยโปรแกรม (ผ่าน @Environment(\.dismiss)) หรือท่าทางปัดลง รายละเอียดสำคัญ: หากเปิด sheet ไว้แต่ผู้ใช้เปลี่ยนแอป onDisappear จะไม่ถูกเรียกจนกว่าจะปิดจริง
| สถานการณ์ | onDisappear ถูกเรียก | หมายเหตุ |
|---|---|---|
| Pop ใน NavigationStack | ใช่ | ทันทีหลังจากแอนิเมชัน |
| เปลี่ยนแท็บ TabView | ใช่ | แท็บปัจจุบัน |
| ปิด Sheet | ใช่ | ก่อนแอนิเมชันเสร็จสมบูรณ์ |
| เลื่อนใน List | ใช่ | เซลล์ออกจากโซนโหลดล่วงหน้า |
| ย่อแอป | ไม่ | ไม่รับประกันการเรียก |
| ขัดข้อง/watchdog kill | ไม่ | ไม่ถูกเรียก |
กรณีการใช้งานหลัก ของ onDisappear คือการล้างทรัพยากร การบันทึกสถานะ และการติดตาม แตกต่างจาก onAppear งานของ onDisappear จะดำเนินการเมื่อออกและไม่ต้องการการตรวจสอบซ้ำเนื่องจาก View หายไปครั้งเดียว
การบันทึกสถานะ ใน onDisappear มีประโยชน์อย่างยิ่งสำหรับฟอร์มที่ต้องบันทึกข้อมูลเมื่อออกจากหน้าจอ ตัวจับเวลาและการสมัครสมาชิก Combine จะถูกยกเลิกใน onDisappear เพื่อป้องกันการรั่วไหลเมื่อกลับมาที่หน้าจอ
struct FormView: View {
@State private var draftText = ""
@State private var timer: Timer?
var body: some View {
TextField("Enter text", text: $draftText)
.onAppear {
timer = Timer.scheduledTimer(withTimeInterval: 60, repeats: true) { _ in
saveDraft()
}
}
.onDisappear {
timer?.invalidate()
timer = nil
saveDraft()
}
}
private func saveDraft() {
UserDefaults.standard.set(draftText, forKey: "draft")
}
}
การยกเลิกตัวจับเวลา ใน onDisappear ป้องกันการดำเนินการโค้ดหลังจากปิดหน้าจอแล้ว หากไม่ยกเลิก ตัวจับเวลาอาจพยายามอัปเดต @State ที่ไม่ได้เป็นของ View ปัจจุบันอีกต่อไป ทำให้เกิดคำเตือนขณะรันไทม์
เวลาค้างอยู่ บนหน้าจอเป็นสถานการณ์การติดตามแบบคลาสสิก บันทึกเวลาใน onAppear คำนวณความแตกต่างใน onDisappear และส่งเหตุการณ์วิเคราะห์พร้อมระยะเวลาเซสชัน
struct TrackedView: View {
@State private var appearTime: Date?
var body: some View {
Text("Tracked Screen")
.onAppear {
appearTime = Date()
Analytics.shared.logEvent("screen_view", params: ["screen": "TrackedView"])
}
.onDisappear {
if let start = appearTime {
let duration = Date().timeIntervalSince(start)
Analytics.shared.logEvent("screen_close", params: [
"screen": "TrackedView",
"duration_ms": Int(duration * 1000)
])
}
}
}
}
ความแตกต่างหลัก ระหว่าง onDisappear และ onAppear คือลำดับการเรียกและการรับประกันการดำเนินการ onAppear ถูกเรียกเมื่อ View ถูกเพิ่มในลำดับชั้นและสามารถถูกเรียกอีกครั้งเมื่อสร้างใหม่ onDisappear ถูกเรียกเมื่อลบและรับประกันว่าจะทำงานเฉพาะเมื่อปิดปกติเท่านั้น ไม่ใช่ในสถานการณ์ขัดข้อง
ตาม WWDC 2024 Apple แนะนำให้ถือว่า onDisappear เป็นจุดล้างข้อมูล ไม่ใช่จุดยืนยันข้อมูล ข้อมูลที่สำคัญอย่างยิ่ง (การชำระเงิน การลงทะเบียน) ควรบันทึกแบบเรียลไทม์ ไม่ใช่ในขณะที่ View หายไป เนื่องจาก onDisappear ไม่รับประกันเมื่อแอปถูกย่อ
| คุณลักษณะ | .onAppear | .onDisappear |
|---|---|---|
| ช่วงเวลาที่เรียก | View ถูกเพิ่มในลำดับชั้น | View ถูกลบจากลำดับชั้น |
| ลำดับ | Parent-first | Child-first |
| การรับประกัน | สูง | ไม่เมื่อขัดข้อง |
| งานหลัก | การเริ่มต้น | การล้าง |
| การเรียกซ้ำ | เมื่อสร้าง View ใหม่ | ครั้งเดียวต่อการหายไป |
คำแนะนำ: ใช้ onDisappear สำหรับการล้างข้อมูลที่ไม่สำคัญและการติดตามเท่านั้น สำหรับการคงอยู่ของข้อมูล ให้ใช้ scenePhase หรือการแจ้งเตือน applicationWillTerminate ใน AppDelegate
สถานการณ์ที่ 1: การเล่นแบบ YouTube. บนหน้าจอรายละเอียดวิดีโอ onDisappear บันทึกตำแหน่งการเล่นใน UserDefaults เมื่อเปิดอีกครั้ง onAppear จะกู้คืนตำแหน่งจาก UserDefaults สร้างประสบการณ์การรับชมที่ต่อเนื่อง
สถานการณ์ที่ 2: ยกเลิกการสมัครสมาชิก Combine. หาก ViewModel ใช้ Combine publishers onDisappear จะยกเลิกการสมัครสมาชิกผ่าน cancellable?.cancel() ซึ่งป้องกันการอัปเดต UI หลังจากออกจากหน้าจอ ซึ่งสำคัญอย่างยิ่งสำหรับรายการที่มีการแบ่งหน้าและคำค้นหา
สถานการณ์ที่ 3: ปิด WebSocket. ในแอปที่มีการเชื่อมต่อแบบเรียลไทม์ ( messengers, ฟีดราคา) onDisappear จะปิดการเชื่อมต่อ WebSocket เพื่อประหยัดแบตเตอรี่และข้อมูล การเชื่อมต่อใหม่เกิดขึ้นใน onAppear เมื่อกลับมาที่หน้าจอ
WebSocket เป็นทรัพยากรทั่วไปที่ต้องปิดเมื่อออกจากหน้าจอ ในตัวอย่างด้านล่าง onDisappear ตัดการเชื่อมต่อซ็อกเก็ตและ onAppear เชื่อมต่อใหม่ ซึ่งช่วยประหยัดข้อมูลอย่างมากสำหรับแอปที่มีหลายหน้าจอ
struct ChatView: View {
@StateObject private var socket = WebSocketManager()
var body: some View {
ChatListView(messages: socket.messages)
.onAppear {
socket.connect()
}
.onDisappear {
socket.disconnect()
}
}
}
สำคัญ: เมื่อสลับระหว่างแท็บ TabView onDisappear ของแท็บปัจจุบันและ onAppear ของแท็บถัดไปจะทำงานเกือบพร้อมกัน สำหรับ WebSocket สิ่งนี้อาจทำให้เกิดวงจรตัดการเชื่อมต่อ-เชื่อมต่อใหม่ที่สร้างภาระโดยไม่จำเป็น วิธีแก้คือใช้การหน่วงเวลาหรือตรวจสอบว่าจำเป็นต้องใช้ซ็อกเก็ตบนหน้าจอถัดไปหรือไม่
คำถามที่พบบ่อย
ใช่ .onDisappear ไม่รับประกันการเรียกเมื่อแอปขัดข้อง (crash, watchdog kill) การย่อแอปโดยไม่ปิดหน้าจอ หรือสถานการณ์ระบบที่แอปถูกยุติในพื้นหลัง สำหรับข้อมูลสำคัญ ให้ใช้ scenePhase หรือ applicationWillTerminate
.onDisappear ทำงานเมื่อ View ถูกลบจากลำดับชั้น — callback เฉพาะที่สำหรับหน้าจอหนึ่ง .scenePhase (ผ่าน @Environment(\.scenePhase)) ทำงานเมื่อสถานะทั้งแอปเปลี่ยน: active, inactive, background ใช้ onAppear+onDisappear สำหรับการติดตามเวลาบนหน้าจอและ scenePhase สำหรับการคงอยู่ของสถานะทั่วโลก
สาเหตุคือการเปลี่ยนแท็บอย่างรวดเร็วหรือการ push/pop ซ้ำของหน้าจอเดียวกัน SwiftUI อาจสร้างอินสแตนซ์ View ใหม่ ลบอันเก่า สร้างอีกครั้ง — ทุกครั้งเรียก onDisappear และ onAppear ตรวจสอบว่าคุณใช้ .id(), .equatable() หรือสร้าง View ใหม่ใน body ของพ่อแม่หรือไม่
ใช่ .onDisappear รองรับอย่างสมบูรณ์บน macOS (10.15+) ด้วยพฤติกรรมเดียวกัน: ถูกเรียกเมื่อปิดหน้าต่าง ลบแผง split view หรือปิดโมดอล บน macOS onDisappear ยังถูกเรียกเมื่อซ่อนหน้าต่าง (ไม่ใช่แค่เมื่อปิด) ซึ่งสำคัญที่ต้องพิจารณาสำหรับแอป macOS
เก็บการอ้างอิงถึง URLSessionTask ใน @State และเรียก task.cancel() ใน onDisappear อีกทางเลือกหนึ่ง ใช้ตัวปรับแต่ง .task ซึ่งยกเลิกการดำเนินการ async โดยอัตโนมัติเมื่อ View หายไป .task เป็นที่นิยมสำหรับการดำเนินการ async ทั้งหมด รวมถึง URLSession
สรุป
เราจะพัฒนาแอปพลิเคชันบนมือถือแบบครบวงจร
IT Sectr สร้างแอปพลิเคชัน iOS และ Android สำหรับสตาร์ทอัพและธุรกิจตั้งแต่ปี 2017 เราจะให้คำแนะนำและเสนอวิธีแก้ปัญหาที่ดีที่สุดแก่คุณ
อ่านเพิ่มเติม