【问题标题】:Stop dispatch_after停止 dispatch_after
【发布时间】:2015-02-05 23:53:57
【问题描述】:

我使用动画来指定提示以帮助延迟交互:

 let delay = 1.8 * Double(NSEC_PER_SEC)
    let time = dispatch_time(DISPATCH_TIME_NOW, Int64(delay))
    dispatch_after(time, dispatch_get_main_queue()) {
        //call the method which have the steps after delay.

        self.rain.alpha = 0

        UIView.animateWithDuration(5, animations: {

            self.rain.alpha = 1

        })

        self.tip.startAnimating()

    }

但是,如果在动画开始之前,用户触摸了屏幕,我需要停止这个延迟过程。

【问题讨论】:

标签: swift animation dispatch


【解决方案1】:

iOS 8 和 OS X Yosemite 引入了dispatch_block_cancel,允许您在它们开始执行之前取消它们

你在类中声明一个变量如下:

var block: dispatch_block_t?

初始化block变量并在dispatch_after中提供:

block = dispatch_block_create(DISPATCH_BLOCK_INHERIT_QOS_CLASS) {
  print("I executed")
}
let time: dispatch_time_t = dispatch_time(DISPATCH_TIME_NOW, Int64(5 * NSEC_PER_SEC))
dispatch_after(time, dispatch_get_main_queue(), block!)

之后您可以按如下方式取消它:

dispatch_block_cancel(block!)

【讨论】:

  • 你真的应该得到更多的支持;没有足够的人知道这...
  • 在 Swift 4 代码上说:'dispatch_block_t' 在 Swift 中不可用
  • 这是一个很好的答案,但需要针对 Swift 进行更新。请改用以下 API:var block = DispatchWorkItem { //do stuff },然后是 DispatchQueue.global().async(execute: block),最后是 block.cancel()
  • 一旦调用了 dispatch_block_cancel(),随后对 dispatch_after() 的调用似乎会被忽略。有没有办法正常再次调用 dispatch_after() ? (我使用的是objective-C而不是swift)
  • 今天学到了新东西 :) 谢谢 :)
【解决方案2】:

Swift 3.0 示例 DispatchQueue 取消或停止

var dispatchQueue: DispatchQueue?
var dispatchWorkItem: DispatchWorkItem?

func someOnClickButtonStart() {
    self.dispatchQueue = DispatchQueue.global(qos: .background) // create queue
    self.dispatchWorkItem = DispatchWorkItem { // create work item
        // async code goes here
    }
    if self.dispatchWorkItem != nil {
        self.dispatchQueue?.asyncAfter(
            deadline: .now() + .seconds(1),
            execute: self.dispatchWorkItem!
        ) // schedule work item
    }
}

func someOnClickButtonCancel() {
   if let dispatchWorkItem = self.dispatchWorkItem {
        dispatchWorkItem.cancel() // cancel work item
    }
}

【讨论】:

  • 我要给你投票,因为这最终奏效了。但是这段代码需要一些工作。
  • 这个解决方案效果很好。使用DispatchWorkItem的原则是Swift中的正确解决方案。
【解决方案3】:

这是我为在 Swift 中取消 dispatch_after 编写的通用解决方案:

typealias cancellable_closure = (() -> ())?

func dispatch_after(#seconds:Double, queue: dispatch_queue_t = dispatch_get_main_queue(), closure:()->()) -> cancellable_closure {
    var cancelled = false
    let cancel_closure: cancellable_closure = {
        cancelled = true
    }

    dispatch_after(
        dispatch_time(DISPATCH_TIME_NOW, Int64(seconds * Double(NSEC_PER_SEC))), queue, {
            if !cancelled {
                closure()
            }
        }
    )

    return cancel_closure
}

func cancel_dispatch_after(cancel_closure: cancellable_closure) {
    cancel_closure?()
}

用法:

let doSomethingLater = dispatch_after(seconds: 3.0) {
    something()
}
....
if shouldCancelForSomeReason {
    cancel_dispatch_after(doSomethingLater)
}

默认情况下它在主队列上运行,但你可以传入一个参数让它在另一个队列上运行:

let doSomethingLater = dispatch_after(seconds: 3.0, queue: dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0)) {
    something()
}

【讨论】:

    【解决方案4】:

    只是分享,在 Swift 4.x 中,我这样做:

    var block: DispatchWorkItem?

    self.block = DispatchWorkItem { self.go(self) }
    
    // execute task in 2 seconds
    DispatchQueue.main.asyncAfter(deadline: .now() + .seconds(3), execute: self.block!)
    

    然后取消阻塞,self.block?.cancel()

    试试这个示例项目:

    import UIKit
    
    class ViewController: UIViewController {
    
        var block: DispatchWorkItem?
    
        @IBAction func go(_ sender: Any) {
            self.block?.cancel()
            let vc2 = VC2()
            self.navigationController?.pushViewController(vc2, animated: true)
        }
    
        override func viewDidLoad() {
            super.viewDidLoad()
    
            self.view.backgroundColor = .white
        }
    
        override func viewDidAppear(_ animated: Bool) {
            super.viewDidAppear(animated)
    
            self.block = DispatchWorkItem { self.go(self) }
    
            // execute task in 2 seconds
            DispatchQueue.main.asyncAfter(deadline: .now() + .seconds(3), execute: self.block!)
        }    
    }
    
    
    class VC2: UIViewController {
        override func viewDidLoad() {
            super.viewDidLoad()
    
            self.view.backgroundColor = .green
        }
    }
    

    【讨论】:

      【解决方案5】:

      您可以创建一个布尔变量shouldCancelAnimation 并在dispatch_after 块内对其进行测试,以防止您的动画执行。

      var shouldCancelAnimation = false // property of class
      
      func runAnimation()
      {
          let delay = 1.8 * Double(NSEC_PER_SEC)
          let time = dispatch_time(DISPATCH_TIME_NOW, Int64(delay))
          dispatch_after(time, dispatch_get_main_queue()) {
      
              if !self.shouldCancelAnimation
              {
                  self.rain.alpha = 0
                  UIView.animateWithDuration(5, animations: {
                      self.rain.alpha = 1
                  })
      
                  self.tip.startAnimating()
              }
              self.shouldCancelAnimation = false
          }
      }
      
      func viewWasTouched() // This could be touches began or taprecognizer event
      {
          shouldCancelAnimation = true
      }
      

      【讨论】:

      • 如果你在延迟时间之前再次调用runAnimation,这将不起作用。
      猜你喜欢
      • 2015-07-03
      • 2014-07-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-03-07
      相关资源
      最近更新 更多