如何在延迟后触发一个块,类似于-performSelector:withObject:afterDelay:?

774
有没有一种方法可以在延迟后使用一个原始参数调用块,就像使用 performSelector:withObject:afterDelay: 一样,但参数是像 int/double/float 这样的类型?
20个回答

6
按下 Cmd + Shift + L 显示 Xcode 内置代码片段库:

enter image description here

寻找 dispatch,然后将其拖放到您的代码中。

5

这里有一个方便的助手,可以避免反复进行烦人的GCD调用:

public func delay(bySeconds seconds: Double, dispatchLevel: DispatchLevel = .main, closure: @escaping () -> Void) {
    let dispatchTime = DispatchTime.now() + seconds
    dispatchLevel.dispatchQueue.asyncAfter(deadline: dispatchTime, execute: closure)
}

public enum DispatchLevel {
    case main, userInteractive, userInitiated, utility, background
    var dispatchQueue: DispatchQueue {
        switch self {
        case .main:                 return DispatchQueue.main
        case .userInteractive:      return DispatchQueue.global(qos: .userInteractive)
        case .userInitiated:        return DispatchQueue.global(qos: .userInitiated)
        case .utility:              return DispatchQueue.global(qos: .utility)
        case .background:           return DispatchQueue.global(qos: .background)
        }
    }
}

现在您可以像下面这样简单地在主线程上延迟您的代码:
delay(bySeconds: 1.5) { 
    // delayed code
}

如果你想要将代码延迟到不同的线程中执行:
delay(bySeconds: 1.5, dispatchLevel: .background) { 
    // delayed code that will run on background thread
}

如果你希望使用一个还有一些更方便的功能的框架,请查看HandySwift。你可以通过Carthage把它添加到你的项目中,然后像上面的例子一样使用它。
import HandySwift    

delay(bySeconds: 1.5) { 
    // delayed code
}

这意味着你的 delay 函数会在后台线程中执行代码。如果有人在 // delayed code 部分放置任何与 UI 相关的代码,他们使用你的示例可能会遇到非常困难的调试应用程序崩溃的情况。 - nalexn
默认情况下,我的方法使用主线程,所以不应该发生这种情况。看到dispatchLevel默认为.Main了吗? - Jeehut

5

这是Swift 3中延迟后排队工作的方法。

DispatchQueue.main.asyncAfter(
  DispatchTime.now() + DispatchTimeInterval.seconds(2)) {
    // do work
}

4

4
在Swift 3中,我们可以使用DispatchQueue.main.asyncAfter函数在延迟'n'秒后触发任何函数或动作。 在代码中,我们设置了1秒的延迟。您可以在该函数体内调用任何函数,在1秒延迟后触发该函数。
let when = DispatchTime.now() + 1
DispatchQueue.main.asyncAfter(deadline: when) {

    // Trigger the function/action after the delay of 1Sec

}

3
2021年4月更新,适用于Xcode 12.4。这仍然有效,除了现在+按钮会呈现更多图标,包括Views库和Modifiers库(见下文),并且Views可能是默认选项。 With View and Modifiers library icons 所以请确保选择Snippets库,如下图所示。

(2020年6月在Xcode 11.3.1上验证)

Xcode提供了一个代码片段来实现此操作。您只需输入延迟值和延迟后要运行的代码即可。

  1. 在编辑某些代码时,点击Xcode右上角的+按钮(而不是例如项目导航器中呈现的其他库,如Capabilities)
  2. 确保选择了代码段库(见屏幕截图,图标为{ }中的内容),而非其他可用的图标。
  3. 搜索after
  4. 它将仅返回1个搜索结果,即所需的代码段(见屏幕截图)。双击它即可开始使用。

screenshot illustrating how to get the snippet from within Xcode itself


1

你可以将参数包装在自己的类中,或者将方法调用包装在不需要传递原始类型的方法中。然后在延迟之后调用该方法,在该方法内执行您希望执行的选择器。


1

以下是如何在Swift中延迟触发代码块的方法:

runThisAfterDelay(seconds: 2) { () -> () in
    print("Prints this 2 seconds later in main queue")
}

/// EZSwiftExtensions
func runThisAfterDelay(seconds seconds: Double, after: () -> ()) {
    let time = dispatch_time(DISPATCH_TIME_NOW, Int64(seconds * Double(NSEC_PER_SEC)))
    dispatch_after(time, dispatch_get_main_queue(), after)
}

这是一个标准函数,已经包含在我的存储库中。


1

Swift 3 & Xcode 8.3.2

这段代码会帮助你,我也添加了解释。

// Create custom class, this will make your life easier
class CustomDelay {

    static let cd = CustomDelay()

    // This is your custom delay function
    func runAfterDelay(_ delay:Double, closure:@escaping ()->()) {
        let when = DispatchTime.now() + delay
        DispatchQueue.main.asyncAfter(deadline: when, execute: closure)
    }
}


// here how to use it (Example 1)
class YourViewController: UIViewController {

    // example delay time 2 second
    let delayTime = 2.0

    override func viewDidLoad() {
        super.viewDidLoad()

        CustomDelay.cd.runAfterDelay(delayTime) {
            // This func will run after 2 second
            // Update your UI here, u don't need to worry to bring this to the main thread because your CustomDelay already make this to main thread automatically :)
            self.runFunc()
        }
    }

    // example function 1
    func runFunc() {
        // do your method 1 here
    }
}

// here how to use it (Example 2)
class YourSecondViewController: UIViewController {

    // let say you want to user run function shoot after 3 second they tap a button

    // Create a button (This is programatically, you can create with storyboard too)
    let shootButton: UIButton = {
        let button = UIButton(type: .system)
        button.frame = CGRect(x: 15, y: 15, width: 40, height: 40) // Customize where do you want to put your button inside your ui
        button.setTitle("Shoot", for: .normal)
        button.translatesAutoresizingMaskIntoConstraints = false
        return button
    }()

    override func viewDidLoad() {
        super.viewDidLoad()

        // create an action selector when user tap shoot button
        shootButton.addTarget(self, action: #selector(shoot), for: .touchUpInside)   
    }

    // example shoot function
    func shoot() {
        // example delay time 3 second then shoot
        let delayTime = 3.0

        // delay a shoot after 3 second
        CustomDelay.cd.runAfterDelay(delayTime) {
            // your shoot method here
            // Update your UI here, u don't need to worry to bring this to the main thread because your CustomDelay already make this to main thread automatically :)
        }
    }   
}

0

我相信作者不是在询问如何等待一段时间(延迟),而是如何将标量作为选择器(withObject:)的参数传递,而现代Objective-C中最快的方法是:

[obj performSelector:...  withObject:@(0.123123123) afterDelay:10]

你的选择器必须将其参数更改为NSNumber,并使用类似floatValue或doubleValue的选择器检索值


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接