Swift中实现按钮按下后延迟执行且重置延迟的方法
解决方案:实现点击按钮后延迟n秒弹窗(支持重置倒计时)
下面提供两种在Swift中实现该需求的常用方案,都能确保每次点击重置延迟,且只会弹出一个弹窗:
方案一:使用Timer实现
核心思路是维护一个可选的Timer实例,每次点击按钮时先取消已有的Timer(如果存在),再重新创建一个新的Timer来延迟执行弹窗逻辑。
import UIKit class ViewController: UIViewController { // 存储Timer实例,可选类型方便判断是否存在未执行的延迟任务 private var delayTimer: Timer? // 自定义延迟时长,这里设为3秒 private let delaySeconds: TimeInterval = 3 @IBAction func buttonTapped(_ sender: UIButton) { // 先取消之前的Timer,避免旧的延迟任务触发 delayTimer?.invalidate() // 创建新Timer,延迟指定秒数后执行弹窗 delayTimer = Timer.scheduledTimer(withTimeInterval: delaySeconds, repeats: false) { [weak self] _ in self?.showAlert() // 执行完成后清空Timer引用,避免内存残留 self?.delayTimer = nil } } private func showAlert() { let alert = UIAlertController(title: "提示", message: "最后一次点击已过\(delaySeconds)秒", preferredStyle: .alert) alert.addAction(UIAlertAction(title: "确定", style: .default)) present(alert, animated: true) } // 页面销毁时取消Timer,防止内存泄漏 deinit { delayTimer?.invalidate() } }
注意事项:
- 闭包中使用
[weak self]避免循环引用,防止ViewController无法被正常释放 - 每次点击必须先调用
invalidate()取消旧Timer,确保只有最后一次点击的延迟任务生效 - 在
deinit中取消Timer,避免页面销毁后Timer仍持有引用导致内存泄漏
方案二:使用DispatchWorkItem实现
基于GCD的DispatchWorkItem可以更灵活地管理延迟任务,每次点击时取消未执行的WorkItem,再重新提交新的延迟任务。
import UIKit class ViewController: UIViewController { // 存储WorkItem实例,用于取消未执行的延迟任务 private var delayWorkItem: DispatchWorkItem? private let delaySeconds: TimeInterval = 3 @IBAction func buttonTapped(_ sender: UIButton) { // 取消之前的WorkItem(如果还没执行) delayWorkItem?.cancel() // 创建新的WorkItem,封装弹窗逻辑 let workItem = DispatchWorkItem { [weak self] in self?.showAlert() } delayWorkItem = workItem // 提交到主队列,延迟指定秒数执行 DispatchQueue.main.asyncAfter(deadline: .now() + delaySeconds, execute: workItem) } private func showAlert() { let alert = UIAlertController(title: "提示", message: "最后一次点击已过\(delaySeconds)秒", preferredStyle: .alert) alert.addAction(UIAlertAction(title: "确定", style: .default)) present(alert, animated: true) // 执行完成后清空WorkItem引用 delayWorkItem = nil } deinit { delayWorkItem?.cancel() } }
注意事项:
cancel()方法只能取消尚未执行的WorkItem,已经开始执行的任务无法终止,但这里因为是延迟执行,所以点击时旧任务肯定还没开始- 同样要使用
[weak self]避免循环引用 deinit中取消WorkItem,确保页面销毁后不会残留未执行的任务
方案对比
- Timer方案:逻辑直观,适合习惯Timer API的开发者,需要手动管理Timer的生命周期
- DispatchWorkItem方案:基于GCD,代码更简洁,取消操作更灵活,是Swift中处理这类延迟任务的常用方式
内容的提问来源于stack exchange,提问作者robotsquidward
相关产品推荐
相关产品推荐

