我希望在10秒内从事件中运行一段代码,但我希望能够取消它,这样如果在10秒之前发生了某些事情,代码将在10秒后不再运行。
我一直在使用它,但它不可取消:
static func delay(delay:Double, closure:()->()) {
dispatch_after(
dispatch_time(
DISPATCH_TIME_NOW,
Int64(delay * Double(NSEC_PER_SEC))
),
dispatch_get_main_queue(), closure
)
}
我该如何做到这一点?
答案 0 :(得分:204)
Swift 3有DispatchWorkItem
:
let task = DispatchWorkItem { print("do something") }
// execute task in 2 seconds
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 2, execute: task)
// optional: cancel task
task.cancel()
答案 1 :(得分:20)
Swift 3.0更新
设置执行选择器
perform(#selector(foo), with: nil, afterDelay: 2)
foo方法将在2秒后调用
func foo()
{
//do something
}
取消待处理的方法调用
NSObject.cancelPreviousPerformRequests(withTarget: self)
答案 2 :(得分:12)
试试这个(Swift 2.x,请参阅David的以下Swift 3的答案):
typealias dispatch_cancelable_closure = (cancel : Bool) -> ()
func delay(time:NSTimeInterval, closure:()->()) -> dispatch_cancelable_closure? {
func dispatch_later(clsr:()->()) {
dispatch_after(
dispatch_time(
DISPATCH_TIME_NOW,
Int64(time * Double(NSEC_PER_SEC))
),
dispatch_get_main_queue(), clsr)
}
var closure:dispatch_block_t? = closure
var cancelableClosure:dispatch_cancelable_closure?
let delayedClosure:dispatch_cancelable_closure = { cancel in
if let clsr = closure {
if (cancel == false) {
dispatch_async(dispatch_get_main_queue(), clsr);
}
}
closure = nil
cancelableClosure = nil
}
cancelableClosure = delayedClosure
dispatch_later {
if let delayedClosure = cancelableClosure {
delayedClosure(cancel: false)
}
}
return cancelableClosure;
}
func cancel_delay(closure:dispatch_cancelable_closure?) {
if closure != nil {
closure!(cancel: true)
}
}
// usage
let retVal = delay(2.0) {
println("Later")
}
delay(1.0) {
cancel_delay(retVal)
}
来自Waam的评论:dispatch_after - GCD in swift?
答案 3 :(得分:1)
这应该有效:
var doIt = true
var timer = NSTimer.scheduledTimerWithTimeInterval(10, target: self, selector: Selector("doSomething"), userInfo: nil, repeats: false)
//you have now 10 seconds to change the doIt variable to false, to not run THE CODE
func doSomething()
{
if(doIt)
{
//THE CODE
}
timer.invalidate()
}
答案 4 :(得分:1)
您需要执行以下操作:
class WorkItem {
private var pendingRequestWorkItem: DispatchWorkItem?
func perform(after: TimeInterval, _ block: @escaping VoidBlock) {
// Cancel the currently pending item
pendingRequestWorkItem?.cancel()
// Wrap our request in a work item
let requestWorkItem = DispatchWorkItem(block: block)
pendingRequestWorkItem = requestWorkItem
DispatchQueue.main.asyncAfter(deadline: .now() + after, execute:
requestWorkItem)
}
}
// to use
lazy var workItem = WorkItem()
private func onMapIdle() {
workItem.perform(after: 1.0) {
self.handlePOIListingSearch()
}
}
参考
答案 5 :(得分:0)
我在某些项目中使用@sas的方法,不知怎的,它不再起作用,可能在Swift 2.1.1之后发生了变化。值复制而不是指针?
对我来说,最简单的解决方法是:var canceled = false
delay(0.25) {
if !canceled {
doSomething()
}
}
答案 6 :(得分:0)
出于某种原因,NSObject.cancelPreviousPerformRequests(withTarget: self)
对我不起作用。我想到的解决方法是提出我允许的最大循环量,然后使用该Int来控制函数是否被调用。
然后,我可以从代码中的任何其他位置设置currentLoop值,并停止循环。
//loopMax = 200
var currentLoop = 0
func loop() {
if currentLoop == 200 {
//do nothing.
} else {
//perform loop.
//keep track of current loop count.
self.currentLoop = self.currentLoop + 1
let deadline = DispatchTime.now() + .seconds(1)
DispatchQueue.main.asyncAfter(deadline: deadline) {
//enter custom loop parameters
print("i looped")
self.loop()
}
}
然后您可以在代码的其他地方
func stopLooping() {
currentLoop = 199
//setting it to 199 allows for one last loop to happen. You can adjust based on the amount of loops you want to be able to do before it just stops. For instance you can set currentLoop to 195 and then implement a fade animation while loop is still happening a bit.
}
实际上它非常有活力。例如,您可以看到currentLoop == 123456789,它将无限运行(几乎),直到您在代码中的其他位置将其设置为该值。或者你甚至可以将它设置为String()或Bool(),如果你的需求不像我的那样基于时间。