.onDisappear là bộ điều chỉnh SwiftUI thực thi một closure khi View bị xóa khỏi hệ thống phân cấp giao diện. Lệnh gọi xảy ra khi đóng màn hình, chuyển tab, loại bỏ cửa sổ phương thức hoặc cuộn phần tử ra khỏi tầm nhìn. Theo Tài liệu dành cho nhà phát triển Apple (2026), onDisappear không đảm bảo gọi trong các kịch bản sự cố hoặc khi ứng dụng bị kết thúc bởi watchdog hệ thống. Tìm hiểu thêm về SwiftUI trong bài viết về SwiftUI.
Những điểm chính
.onDisappear là bộ điều chỉnh View trong SwiftUI nhận một closure Void và thực thi nó khi View bị xóa khỏi hệ thống phân cấp. Cùng với .onAppear, nó tạo thành vòng đời hoàn chỉnh của màn hình: xuất hiện — làm việc — biến mất. Apple giới thiệu onDisappear cùng với SwiftUI trong iOS 13 như một phiên bản tương tự của viewDidDisappear từ UIKit.
Về mặt cú pháp, .onDisappear giống hệt onAppear: nó sửa đổi bất kỳ View nào và đính kèm một closure mà trình kết xuất SwiftUI gọi khi view bị xóa. Không giống như UIKit, nơi viewDidDisappear chỉ kích hoạt sau khi hoàn thành hoạt ảnh chuyển tiếp, onDisappear trong SwiftUI có thể được gọi trước khi hoạt ảnh kết thúc — tại thời điểm View được đánh dấu để xóa.
Cú pháp cơ bản của onDisappear cũng ngắn gọn như onAppear. Bộ điều chỉnh không nhận tham số bổ sung — chỉ có closure, thực thi đồng bộ trên luồng chính.
struct DetailView: View {
var body: some View {
Text("Detail Screen")
.onDisappear {
print("DetailView đã biến mất khỏi màn hình")
}
}
}
Cơ chế gọi của onDisappear ngược với onAppear: các phần tử con nhận onDisappear trước, sau đó đến cha. Quy tắc child-first này đảm bảo tài nguyên con được giải phóng trước khi tài nguyên cha được giải phóng. Nếu phần tử con phụ thuộc vào dữ liệu cha, nó phải có khả năng kết thúc chính xác mà không cần ngữ cảnh cha.
SwiftUI gọi onDisappear khi View bị xóa khỏi đồ thị kết xuất. Các trình kích hoạt bao gồm: pop từ NavigationStack, chuyển đổi TabView, loại bỏ phương thức (sheet/fullScreenCover) hoặc View được kết xuất có điều kiện (if/switch). Trong List và ScrollView, onDisappear được gọi khi một ô cuộn ra ngoài bộ đệm tải trước.
Thứ tự child-first có nghĩa là nếu VStack có ba View con, onDisappear được gọi trước cho mỗi con theo thứ tự ngược lại, sau đó cho cha. Điều này rất quan trọng để dọn dẹp đúng cách: bộ đếm thời gian con bị hủy trước khi ViewModel cha giải phóng tài nguyên dùng chung.
struct ParentView: View {
var body: some View {
VStack {
ChildView(id: "A")
ChildView(id: "B")
}
.onDisappear {
print("Parent onDisappear — cuối cùng")
}
}
}
struct ChildView: View {
let id: String
var body: some View {
Text("Child \(id)")
.onDisappear {
print("Child \(id) onDisappear")
}
}
}
Đầu ra bảng điều khiển: Child B onDisappear, Child A onDisappear, Parent onDisappear — cuối cùng. Thứ tự ngược lại so với onAppear đảm bảo chuỗi dọn dẹp chính xác.
Các kịch bản gọi của onDisappear phụ thuộc vào loại vùng chứa. Trong NavigationStack, onDisappear kích hoạt khi pop-to-root, pop-back thông thường hoặc loại bỏ màn hình bằng cách vuốt (interactivePopGestureRecognizer). Trong TabView, chuyển tab gọi onDisappear cho tab ẩn và onAppear cho tab hiển thị — cả hai bộ điều chỉnh kích hoạt gần như đồng thời.
Trong Sheet và fullScreenCover, onDisappear được gọi khi loại bỏ theo chương trình (qua @Environment(\.dismiss)) hoặc cử chỉ vuốt xuống. Một chi tiết quan trọng: nếu sheet đã được mở nhưng người dùng đã chuyển ứng dụng, onDisappear sẽ KHÔNG được gọi cho đến khi đóng thực tế.
| Kịch bản | onDisappear được gọi | Ghi chú |
|---|---|---|
| Pop trong NavigationStack | Có | Ngay sau hoạt ảnh |
| Chuyển tab TabView | Có | Tab hiện tại |
| Loại bỏ sheet | Có | Trước khi hoàn thành hoạt ảnh |
| Cuộn trong List | Có | Ô đã ra khỏi vùng tải trước |
| Thu nhỏ ứng dụng | Không | Không đảm bảo gọi |
| Sự cố/watchdog kill | Không | Không được gọi |
Các trường hợp sử dụng chính của onDisappear là dọn dẹp tài nguyên, lưu trạng thái và theo dõi. Không giống như onAppear, các tác vụ onDisappear thực thi khi thoát và không cần kiểm tra trùng lặp vì View biến mất một lần.
Lưu trạng thái trong onDisappear đặc biệt hữu ích cho các biểu mẫu nơi dữ liệu phải được lưu khi rời khỏi màn hình. Bộ đếm thời gian và đăng ký Combine bị hủy trong onDisappear để tránh rò rỉ khi quay lại màn hình.
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")
}
}
Hủy bộ đếm thời gian trong onDisappear ngăn thực thi mã sau khi màn hình đã đóng. Nếu không hủy, bộ đếm thời gian có thể cố gắng cập nhật @State không còn thuộc về View hiện tại, gây ra cảnh báo thời gian chạy.
Thời gian lưu lại trên màn hình là một kịch bản theo dõi cổ điển. Ghi lại thời gian trong onAppear, tính toán chênh lệch trong onDisappear và gửi sự kiện phân tích với thời lượng phiên.
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)
])
}
}
}
}
Sự khác biệt chính giữa onDisappear và onAppear là thứ tự gọi và đảm bảo thực thi. onAppear được gọi khi View được thêm vào hệ thống phân cấp và có thể được gọi lại khi tạo lại. onDisappear được gọi khi xóa và chỉ được đảm bảo kích hoạt khi đóng bình thường, không phải trong các kịch bản sự cố.
Theo WWDC 2024, Apple khuyến nghị coi onDisappear là điểm dọn dẹp, không phải điểm cam kết dữ liệu. Dữ liệu quan trọng (thanh toán, đăng ký) nên được lưu theo thời gian thực, không phải tại thời điểm View biến mất, vì onDisappear không được đảm bảo khi ứng dụng bị thu nhỏ.
| Đặc điểm | .onAppear | .onDisappear |
|---|---|---|
| Thời điểm gọi | View được thêm vào hệ thống phân cấp | View bị xóa khỏi hệ thống phân cấp |
| Thứ tự | Parent-first | Child-first |
| Đảm bảo | Cao | Không khi sự cố |
| Nhiệm vụ chính | Khởi tạo | Dọn dẹp |
| Gọi lại | Khi tạo lại View | Một lần mỗi lần biến mất |
Khuyến nghị: chỉ sử dụng onDisappear cho việc dọn dẹp không quan trọng và theo dõi. Để duy trì dữ liệu, hãy sử dụng scenePhase hoặc thông báo applicationWillTerminate trong AppDelegate.
Kịch bản 1: phát lại kiểu YouTube. Trên màn hình chi tiết video, onDisappear lưu vị trí phát lại vào UserDefaults. Khi mở lại, onAppear khôi phục vị trí từ UserDefaults, tạo trải nghiệm xem liên tục.
Kịch bản 2: hủy đăng ký Combine. Nếu ViewModel sử dụng nhà xuất bản Combine, onDisappear hủy đăng ký qua cancellable?.cancel(). Điều này ngăn cập nhật UI sau khi rời khỏi màn hình, đặc biệt quan trọng cho danh sách có phân trang và truy vấn tìm kiếm.
Kịch bản 3: đóng WebSocket. Trong các ứng dụng có kết nối thời gian thực (trình nhắn tin, nguồn cấp dữ liệu báo giá), onDisappear đóng kết nối WebSocket để tiết kiệm pin và dữ liệu. Kết nối lại xảy ra trong onAppear khi quay lại màn hình.
WebSocket là tài nguyên điển hình cần được đóng khi rời khỏi màn hình. Trong ví dụ dưới đây, onDisappear ngắt kết nối socket và onAppear kết nối lại, mang lại tiết kiệm dữ liệu đáng kể cho các ứng dụng có nhiều màn hình.
struct ChatView: View {
@StateObject private var socket = WebSocketManager()
var body: some View {
ChatListView(messages: socket.messages)
.onAppear {
socket.connect()
}
.onDisappear {
socket.disconnect()
}
}
}
Quan trọng: khi chuyển đổi giữa các tab TabView, onDisappear của tab hiện tại và onAppear của tab tiếp theo kích hoạt gần như đồng thời. Đối với WebSocket, điều này có thể gây ra chu kỳ ngắt kết nối-kết nối lại tạo ra tải không cần thiết. Giải pháp là sử dụng độ trễ hoặc kiểm tra xem socket có cần thiết trên màn hình tiếp theo không.
Các câu hỏi thường gặp
Có, .onDisappear không đảm bảo gọi khi ứng dụng gặp sự cố (crash, watchdog kill), thu nhỏ ứng dụng mà không đóng màn hình hoặc các kịch bản hệ thống nơi ứng dụng bị kết thúc trong nền. Đối với dữ liệu quan trọng, hãy sử dụng scenePhase hoặc applicationWillTerminate.
.onDisappear kích hoạt khi View bị xóa khỏi hệ thống phân cấp — một callback cục bộ cho một màn hình cụ thể. .scenePhase (qua @Environment(\.scenePhase)) kích hoạt khi trạng thái toàn bộ ứng dụng thay đổi: active, inactive, background. Sử dụng onAppear+onDisappear để theo dõi thời gian trên màn hình và scenePhase để duy trì trạng thái toàn cầu.
Nguyên nhân là chuyển đổi tab nhanh hoặc push/pop lặp lại cùng một màn hình. SwiftUI có thể tạo một phiên bản View mới, xóa cái cũ, tạo lại — mỗi lần gọi onDisappear và onAppear. Kiểm tra xem bạn có đang sử dụng .id(), .equatable() hoặc tạo lại View trong body cha không.
Có, .onDisappear được hỗ trợ đầy đủ trên macOS (10.15+) với cùng hành vi: được gọi khi đóng cửa sổ, xóa bảng split view hoặc loại bỏ phương thức. Trên macOS, onDisappear cũng được gọi khi ẩn cửa sổ (không chỉ khi đóng), điều quan trọng cần xem xét cho các ứng dụng macOS.
Lưu tham chiếu đến URLSessionTask trong @State và gọi task.cancel() trong onDisappear. Thay vào đó, hãy sử dụng bộ điều chỉnh .task tự động hủy hoạt động không đồng bộ khi View biến mất. .task được ưu tiên cho tất cả các hoạt động không đồng bộ, bao gồm URLSession.
Tóm tắt
Chúng tôi sẽ phát triển ứng dụng di động chìa khóa trao tay
IT Sectr tạo các ứng dụng iOS và Android cho các công ty khởi nghiệp và doanh nghiệp từ năm 2017. Chúng tôi sẽ tư vấn và đề xuất giải pháp tốt nhất cho bạn.
Đọc thêm