Как вы запускаете блок после задержки, например -performSelector: withObject: afterDelay :? - PullRequest
718 голосов
/ 10 ноября 2010

Есть ли способ вызвать блок с параметром примитива после задержки, например, используя performSelector:withObject:afterDelay:, но с аргументом, подобным int / double / float?

Ответы [ 18 ]

5 голосов
/ 09 июня 2016

Вот удобный помощник для предотвращения повторного выполнения раздражающего вызова 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
}

Если вы предпочитаете Framework , который также имеет некоторые более удобные функции, тогда извлекайте HandySwift . Вы можете добавить его в свой проект через Карфаген , а затем использовать его точно так же, как в примерах выше:

import HandySwift    

delay(bySeconds: 1.5) { 
    // delayed code
}
4 голосов
/ 15 сентября 2012

В инфраструктуре BlocksKit есть один хороший элемент.

BlocksKit

(и класс)

BBlocksKit.m

4 голосов
/ 17 мая 2017

В быстром 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

}
1 голос
/ 02 декабря 2015

Вот как вы можете запустить блок после задержки в 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 голос
/ 10 ноября 2010

Вы можете заключить аргумент в свой собственный класс или обернуть вызов метода в метод, который не нужно передавать в примитивном типе.Затем вызовите этот метод после вашей задержки, и в пределах этого метода выполните селектор, который вы хотите выполнить.

1 голос
/ 30 мая 2017

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 голосов
/ 25 апреля 2019

Xcode 10.2 и Swift 5 и выше

DispatchQueue.main.asyncAfter(deadline: .now() + 2, execute: {
   // code to execute                 
})
0 голосов
/ 10 апреля 2017

Я полагаю, что автор не спрашивает, как ждать дробное время (задержка), а вместо этого, как передать скаляр в качестве аргумента селектора (withObject :), и самый быстрый путь в современной цели C:

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

ваш селектор должен изменить свой параметр на NSNumber и извлечь значение с помощью селектора, такого как floatValue или doubleValue

...