Подтвердить что ты не робот

Отменить синхронизированное событие в Swift?

Я хочу запустить блок кода за 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
  )
}

Как я могу это сделать?

4b9b3361

Ответ 1

Попробуйте это (Swift 2.x, см. ниже ответ Дэвида для 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 в быстром?

Ответ 2

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()

Ответ 3

Обновление для Swift 3.0

Установить селектор настроек

perform(#selector(foo), with: nil, afterDelay: 2)

Метод foo вызовет через 2 секунды

 func foo()
 {
         //do something
 }

Отменить вызов ожидающего метода

 NSObject.cancelPreviousPerformRequests(withTarget: self)

Ответ 4

Это должно работать:

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()
}

Ответ 5

Вам нужно сделать это:

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()
    }
}

Рекомендации

Ссылка swiftbysundell

Ссылка гит

Ответ 6

Я использую метод @sas в некоторых проектах, так как это уже не работает, возможно, что-то изменилось после Swift 2.1.1. значение вместо указателя?

Самый простой метод работы для меня:

var canceled = false
delay(0.25) {
  if !canceled {
    doSomething()
  }
}

Ответ 7

По какой-то причине 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() даже, если ваши потребности не основаны на времени, как мои.