ios - 如何在延遲後觸發塊,如-performSelector:withObject:afterDelay:?




objective-c grand-central-dispatch (12)

有沒有辦法在延遲後使用基本參數調用一個塊,如使用performSelector:withObject:afterDelay:但使用像int / double / float這樣的參數?



PerformSelector:WithObject總是需要一個對象,所以為了傳遞像int / double / float等參數.....你可以使用類似這樣的東西。

// NSNumber是一個對象..

[self performSelector:@selector(setUserAlphaNumber:)
     withObject: [NSNumber numberWithFloat: 1.0f]       
     afterDelay:1.5];



-(void) setUserAlphaNumber: (NSNumber*) number{

     [txtUsername setAlpha: [number floatValue] ];

}

您可以使用[NSNumber numberWithInt:] etc ....的方式相同,並且在接收方法中,您可以將數字轉換為[number int]或[number double]的格式。


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

下面是你如何在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)
}

它包含在我的回購中作為標準功能。


在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

}

如何使用Xcode內置代碼片段庫?

Swift更新:

許多投票激勵我更新這個答案。

內置的Xcode代碼片段庫僅針對objective-c語言具有dispatch_after 。 人們也可以為Swift創建自己的自定義代碼片段

在Xcode中寫這個。

dispatch_after(dispatch_time(DISPATCH_TIME_NOW, Int64(<#delayInSeconds#> * Double(NSEC_PER_SEC))), dispatch_get_main_queue(), {
        <#code to be executed after a specified delay#>
    })

將此代碼拖放到代碼段庫區域中。

在代碼片段列表的底部,將會有一個名為My Code Snippet的新實體。 編輯這個標題。 建議您在Xcode中輸入填寫Completion Shortcut

有關更多信息,請參閱CreatingaCustomCodeSnippet

更新Swift 3

將此代碼拖放到代碼段庫區域中。

DispatchQueue.main.asyncAfter(deadline: .now() + .seconds(<#delayInSeconds#>)) {
    <#code to be executed after a specified delay#>
}

您可以將參數包裝在自己的類中,也可以將方法調用包裝在不需要以原始類型傳遞的方法中。 然後在延遲後調用該方法,並在該方法內執行您希望執行的選擇器。


您可以稍後使用dispatch_after來調用塊。 在Xcode中,開始輸入dispatch_after並按Enter自動完成以下內容:

這裡有一個以兩個浮點數作為“參數”的例子。 你不必依賴任何類型的宏,代碼的意圖是非常明確的:

斯威夫特3,斯威夫特4

let time1 = 8.23
let time2 = 3.42

// Delay 2 seconds
DispatchQueue.main.asyncAfter(deadline: .now() + 2.0) {
    print("Sum of times: \(time1 + time2)")
}

斯威夫特2

let time1 = 8.23
let time2 = 3.42

// Delay 2 seconds
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, Int64(2.0 * Double(NSEC_PER_SEC))), dispatch_get_main_queue()) { () -> Void in
        println("Sum of times: \(time1 + time2)")
}

目標C

CGFloat time1 = 3.49;
CGFloat time2 = 8.13;

// Delay 2 seconds
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
    CGFloat newTime = time1 + time2;
    NSLog(@"New time: %f", newTime);
});

我相信作者並沒有要求如何等待分數時間(延遲),而是如何傳遞一個標量作為選擇器的參數(withObject :),而現代客觀的C中最快的方法是:

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

您的選擇器必須將其參數更改為NSNumber,並使用像floatValue或doubleValue這樣的選擇器來檢索該值


擴展Jaime Cham的答案,我創建了一個NSObject + Blocks類別如下。 我覺得這些方法更符合現有的performSelector: NSObject方法

NSObject的+ Blocks.h

#import <Foundation/Foundation.h>

@interface NSObject (Blocks)

- (void)performBlock:(void (^)())block afterDelay:(NSTimeInterval)delay;

@end

NSObject的+ Blocks.m

#import "NSObject+Blocks.h"

@implementation NSObject (Blocks)

- (void)performBlock:(void (^)())block
{
    block();
}

- (void)performBlock:(void (^)())block afterDelay:(NSTimeInterval)delay
{
    void (^block_)() = [block copy]; // autorelease this if you're not using ARC
    [self performSelector:@selector(performBlock:) withObject:block_ afterDelay:delay];
}

@end

並像這樣使用:

[anyObject performBlock:^{
    [anotherObject doYourThings:stuff];
} afterDelay:0.15];

這是一個方便的幫手,可以防止一次又一次地發出煩人的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
}

這裡是我的2美分= 5的方法;)

我喜歡封裝這些細節並讓AppCode告訴我如何完成我的句子。

void dispatch_after_delay(float delayInSeconds, dispatch_queue_t queue, dispatch_block_t block) {
    dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, delayInSeconds * NSEC_PER_SEC);
    dispatch_after(popTime, queue, block);
}

void dispatch_after_delay_on_main_queue(float delayInSeconds, dispatch_block_t block) {
    dispatch_queue_t queue = dispatch_get_main_queue();
    dispatch_after_delay(delayInSeconds, queue, block);
}

void dispatch_async_on_high_priority_queue(dispatch_block_t block) {
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), block);
}

void dispatch_async_on_background_queue(dispatch_block_t block) {
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), block);
}

void dispatch_async_on_main_queue(dispatch_block_t block) {
    dispatch_async(dispatch_get_main_queue(), block);
}






objective-c-blocks