SwiftUI点击手势下取消Timer/DispatchQueue延迟执行任务的问题
解决SwiftUI中点击手势取消/激活延迟任务的问题
针对你遇到的每次点击创建新Timer无法取消旧任务的问题,这里提供两种可行的实现方案,核心是通过SwiftUI的状态包装器管理任务实例,确保每次操作都能追踪并控制之前的延迟任务:
方案一:使用DispatchWorkItem + @State
通过@State保存DispatchWorkItem的引用,每次点击时先取消之前的任务,再根据状态决定是否创建新的延迟执行任务:
// 结构体顶层声明状态变量 @State private var pendingDeleteTask: DispatchWorkItem? @State private var isDeleting = false // 点击手势处理 .onTapGesture { isDeleting.toggle() // 取消之前的任务(如果存在) pendingDeleteTask?.cancel() pendingDeleteTask = nil if isDeleting { // 创建新的延迟任务 let workItem = DispatchWorkItem { [weak self] in self?.completeTask() } pendingDeleteTask = workItem DispatchQueue.main.asyncAfter(deadline: .now() + 2, execute: workItem) } } // 视图销毁时清理任务 .onDisappear { pendingDeleteTask?.cancel() pendingDeleteTask = nil }
方案二:使用Timer + @State
同样用@State包装Timer实例,每次点击先销毁旧Timer,再根据状态创建新Timer:
// 结构体顶层声明状态变量 @State private var deleteTimer: Timer? @State private var isDeleting = false // 点击手势处理 .onTapGesture { isDeleting.toggle() // 销毁之前的定时器(如果存在) deleteTimer?.invalidate() deleteTimer = nil if isDeleting { deleteTimer = Timer.scheduledTimer(withTimeInterval: 2.0, repeats: false) { [weak self] _ in self?.completeTask() } } } // 视图销毁时清理定时器 .onDisappear { deleteTimer?.invalidate() deleteTimer = nil }
关键说明
- @State的作用:SwiftUI的
@State属性包装器会将变量存储在结构体外部的可变存储区,避开了结构体不可变的限制,允许我们在视图生命周期内追踪和修改任务实例。 - 弱引用self:闭包中使用
[weak self]避免循环引用(Timer/DispatchWorkItem会持有闭包,闭包若持有self会导致内存泄漏),同时self?.completeTask()可以正常访问结构体顶层声明的tasksViewModel、task、userViewModel等变量。 - 视图销毁清理:在
onDisappear中手动取消任务/销毁定时器,防止视图销毁后延迟任务仍执行导致的异常。
你的completeTask函数无需修改,只要确保它能访问到结构体顶层的变量即可:
private func completeTask() { tasksViewModel.deleteTask(task: task) guard let userID = userViewModel.id?.uuidString else { return } Task { // 执行异步操作 } }
内容的提问来源于stack exchange,提问作者Nathace
相关产品推荐
相关产品推荐

