net - swift payment
¿Cancelar un evento cronometrado en Swift? (7)
Esto debería funcionar:
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()
}
Quiero ejecutar un bloque de código en 10 segundos desde un evento, pero quiero poder cancelarlo para que si algo sucede antes de esos 10 segundos, el código no se ejecutará después de que hayan pasado 10 segundos.
He estado usando esto, pero no se puede cancelar:
static func delay(delay:Double, closure:()->()) {
dispatch_after(
dispatch_time(
DISPATCH_TIME_NOW,
Int64(delay * Double(NSEC_PER_SEC))
),
dispatch_get_main_queue(), closure
)
}
¿Cómo puedo lograr esto?
Por alguna razón,
NSObject.cancelPreviousPerformRequests(withTarget: self)
no funcionaba para mí.
Un trabajo en el que pensé estaba dando con la cantidad máxima de bucles que permitiría y luego usando ese Int para controlar si incluso se llamó a la función.
Entonces puedo establecer el valor de currentLoop desde cualquier otro lugar en mi código y detiene el bucle.
//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()
}
}
y luego en otra parte de tu código puedes
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.
}
Es realmente bastante dinámico en realidad. Por ejemplo, puede ver si currentLoop == 123456789, y se ejecutará infinitamente (más o menos) hasta que lo establezca en ese valor en otro lugar de su código. O puede establecerlo en String () o Bool () incluso, si sus necesidades no se basan en el tiempo como las mías.
Pruebe esto (Swift 2.x, consulte la respuesta de David a continuación para 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)
}
Del comentario de Waam aquí: dispatch_after - GCD en rápido?
Swift 3 tiene
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()
Usted necesita hacer ésto:
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()
}
}
Referencias
Utilizo el método de @sas en algunos proyectos, de alguna manera esto ya no funciona, tal vez algo cambió después de Swift 2.1.1. valor de copia en lugar de puntero?
El método más sencillo para mí es:
var canceled = false
delay(0.25) {
if !canceled {
doSomething()
}
}
Actualización para Swift 3.0
Establecer selector de ejecución
perform(#selector(foo), with: nil, afterDelay: 2)
el método foo llamará después de 2 segundos
func foo()
{
//do something
}
Para cancelar el método pendiente llame
NSObject.cancelPreviousPerformRequests(withTarget: self)