53

I know that there are several ways of delaying an action in Objective-C like:

performSelector:withObject:afterDelay:

or using NSTimer.

But there is such a fancy thing called blocks where you can do something like this:

[UIView animateWithDuration:1.50 delay:0 options:(UIViewAnimationOptionCurveEaseOut|UIViewAnimationOptionBeginFromCurrentState) animations:^{

    }completion:^(BOOL finished){
}];

Unfortunately, this method applies only to animating things.

How can I create a delay with a block in one method so I don't have to use all those @selectors and without the need to create a new separate method? Thanks!

Sergey Grischyov
  • 11,995
  • 20
  • 81
  • 120

4 Answers4

142

use dispatch_after:

double delayInSeconds = 2.0;
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, delayInSeconds * NSEC_PER_SEC);
dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
    //code to be executed on the main queue after delay
    [self doSometingWithObject:obj1 andAnotherObject:obj2];
});
Martin Ullrich
  • 94,744
  • 25
  • 252
  • 217
  • well it improves readability a lot - especially when you use blocks as method parameters – Martin Ullrich Mar 14 '13 at 15:22
  • 6
    It would be nice to hide this (especially `popTime` calculation) in a function/method, e.g `[NSObject performBlock:afterDelay:];` – Sulthan Mar 14 '13 at 15:45
25

Expanding on the accepted answer, I created a Helper function for anyone who doesn't care to memorize the syntax each time they want to do this :) I simply have a Utils class with this:

Usage:

[Utils delayCallback:^{
     //--- code here
} forTotalSeconds:0.3];

Helper method:

+ (void) delayCallback: (void(^)(void))callback forTotalSeconds: (double)delayInSeconds{

     dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, delayInSeconds * NSEC_PER_SEC);
     dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
           if(callback){
                callback();
           }
      });
}
Meet Doshi
  • 4,241
  • 10
  • 40
  • 81
rckehoe
  • 1,198
  • 15
  • 16
3

Xcode 11.3.1 (at least, and also other versions of Xcode) provides a code snippet to do this where you just have to enter the delay value and the code you wish to run after the delay.

  1. click on the + button at the top right of Xcode.
  2. search for after
  3. It will return only 1 search result, which is the desired snippet (see screenshot). Double click it and you're good to go.

screenshot illustrating how to get the snippet from within Xcode itself

auspicious99
  • 3,902
  • 1
  • 44
  • 58
1

Here is how you can trigger a block after a delay in Swift:

runThisAfterDelay(seconds: 4) { () -> () in
    print("Prints this 4 seconds later in main queue")
    // Or just call animatedMyObject() right here
}

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

Its included as a standard function in my repo: https://github.com/goktugyil/EZSwiftExtensions

Esqarrouth
  • 38,543
  • 21
  • 161
  • 168