How to Cancel Dispatchqueue.Main.Asyncafter(Deadline: Time) in Swift3

how to stop a dispatchQueue in swift

I'm not sure if there are best practices here, but I would consider doing what you are doing with a Timer rather than the DispatchQueue.

class GifClass: UIViewController {

@IBOutlet weak var gifImage: UIImageView!
@IBOutlet weak var skipButton: UIButton!

var timer = Timer()

override func viewDidLoad() {
super.viewDidLoad()

gifImage.loadGif(name: "promed")

timer = Timer.scheduledTimer(timeInterval: 11, target: self, selector: #selector(timerAction), userInfo: nil, repeats: false)
}

@objc func timerAction() {
performSegue(withIdentifier: "introLogin", sender: self)
}

@IBAction func skip(_ sender: Any) {
timer.invalidate()
performSegue(withIdentifier: "introLogin", sender: self)
}
}

Cancel a timed event in Swift?

Try this (Swift 2.x, see David's answer below for 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)
}

From Waam's comment here: dispatch_after - GCD in swift?

How does DispatchQueue.main.asyncAfter(deadline: .now() + 5.0) work in Swift 3?

OK, I found the answer to my own question in this thread:

How do I write dispatch_after GCD in Swift 3?

Apparently there is an override of the + operator that takes a DispatchTime and a double, treats the double as decimal seconds, and returns a resulting DispatchTime.

Create several DispatchQueue.main.asyncAfter methods at once in Swift 3

You can fix this by creating local variables:

@IBAction func playButtonPressed(_ sender: Any) {
var index = 0.0
var i = 0
var j = 0
while i < sites.count {
while j < sites[i].count {
let day = i
let site = j
DispatchQueue.main.asyncAfter(deadline: .now() + 1.0 * index) {
self.plot(day: day, site: site)
}
j += 1
index += 1
}
j = 0
i += 1
}
}

Or, as pointed out by Martin R in Pass value to closure?, you can "capture" these variables:

@IBAction func playButtonPressed(_ sender: Any) {
var index = 0.0
var i = 0
var j = 0
while i < sites.count {
while j < sites[i].count {
DispatchQueue.main.asyncAfter(deadline: .now() + 1.0 * index) { [i, j] in
self.plot(day: i, site: j)
}
j += 1
index += 1
}
j = 0
i += 1
}
}

Or, personally, I'd probably use for loops to clean this up a bit:

@IBAction func playButtonPressed(_ sender: Any) {
var delay = 0.0
for i in 0 ..< sites.count {
for j in 0 ..< sites[i].count {
DispatchQueue.main.asyncAfter(deadline: .now() + delay) { [i, j] in
self.plot(day: i, site: j)
}
delay += 1
}
}
}

Resetting DispatchQueue each time a tap occurs

It would be simpler to use a Timer, because that is something that is easy to cancel (invalidate) and start the timer again (Timer.scheduledTimer) if the user taps before the Timer fires at the end of 5 seconds.

For example, that is how my LinkSame app works. There is a 10-second "move" timer. If the user doesn't make a valid move within 10 seconds of the previous move, the user loses 10 points and the timer starts over. If the user does make a valid move within 10 seconds of the previous move, the user gets a score based on where we are in the timer and the timer starts over. That is all accomplished with a Timer.



Related Topics



Leave a reply



Submit