How do you trigger a block after a delay, like -performSelector:withObject:afterDelay:?
IosObjective CGrand Central-DispatchObjective C-BlocksIos Problem Overview
Is there a way to call a block with a primitive parameter after a delay, like using performSelector:withObject:afterDelay:
but with an argument like int
/double
/float
?
Ios Solutions
Solution 1 - Ios
I think you're looking for dispatch_after()
. It requires your block to accept no parameters, but you can just let the block capture those variables from your local scope instead.
int parameter1 = 12;
float parameter2 = 144.1;
// Delay execution of my block for 10 seconds.
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 10 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{
NSLog(@"parameter1: %d parameter2: %f", parameter1, parameter2);
});
More: https://developer.apple.com/documentation/dispatch/1452876-dispatch_after
Solution 2 - Ios
You can use dispatch_after
to call a block later. In Xcode, start typing dispatch_after
and hit Enter
to autocomplete to the following:
Here's an example with two floats as "arguments." You don't have to rely on any type of macro, and the intent of the code is quite clear:
Swift 3, Swift 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)")
}
Swift 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)")
}
Objective 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);
});
Solution 3 - Ios
How about using Xcode built-in code snippet library?
Update for Swift:
Many up votes inspired me to update this answer.
The build-in Xcode code snippet library has dispatch_after
for only objective-c
language. People can also create their own Custom Code Snippet for Swift
.
Write this in 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#>
})
Drag this code and drop it in the code snippet library area.
Bottom of the code snippet list, there will be a new entity named My Code Snippet
. Edit this for a title. For suggestion as you type in the Xcode fill in the Completion Shortcut
.
For more info see CreatingaCustomCodeSnippet.
Update Swift 3
Drag this code and drop it in the code snippet library area.
DispatchQueue.main.asyncAfter(deadline: .now() + .seconds(<#delayInSeconds#>)) {
<#code to be executed after a specified delay#>
}
Solution 4 - Ios
Expanding on Jaime Cham's answer I created a NSObject+Blocks category as below. I felt these methods better matched the existing performSelector:
NSObject methods
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
and use like so:
[anyObject performBlock:^{
[anotherObject doYourThings:stuff];
} afterDelay:0.15];
Solution 5 - Ios
Perhaps simpler than going thru GCD, in a class somewhere (e.g. "Util"), or a Category on Object:
+ (void)runBlock:(void (^)())block
{
block();
}
+ (void)runAfterDelay:(CGFloat)delay block:(void (^)())block
{
void (^block_)() = [[block copy] autorelease];
[self performSelector:@selector(runBlock:) withObject:block_ afterDelay:delay];
}
So to use:
[Util runAfterDelay:2 block:^{
NSLog(@"two seconds later!");
}];
Solution 6 - Ios
For Swift I've created a global function, nothing special, using the dispatch_after
method. I like this more as it's readable and easy to use:
func performBlock(block:() -> Void, afterDelay delay:NSTimeInterval){
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, Int64(delay * Double(NSEC_PER_SEC))), dispatch_get_main_queue(), block)
}
Which you can use as followed:
performBlock({ () -> Void in
// Perform actions
}, afterDelay: 0.3)
Solution 7 - Ios
Here are my 2 cents = 5 methods ;)
I like encapsulate these details and have AppCode tell me how to finish my sentences.
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);
}
Solution 8 - Ios
PerformSelector:WithObject always takes an object, so in order to pass arguments like int/double/float etc..... You can use something like this.
//NSNumber is an object..
[self performSelector:@selector(setUserAlphaNumber:) withObject: [NSNumber numberWithFloat: 1.0f]
afterDelay:1.5];
-(void) setUserAlphaNumber: (NSNumber*) number{
[txtUsername setAlpha: [number floatValue] ];
}
Same way you can use [NSNumber numberWithInt:] etc.... and in the receiving method you can convert the number into your format as [number int] or [number double].
Solution 9 - Ios
The dispatch_after function dispatches a block object to a dispatch queue after a given period of time. Use below code to perform some UI related taks after 2.0 seconds.
let delay = 2.0
let delayInNanoSeconds = dispatch_time(DISPATCH_TIME_NOW, Int64(delay * Double(NSEC_PER_SEC)))
let mainQueue = dispatch_get_main_queue()
dispatch_after(delayInNanoSeconds, mainQueue, {
print("Some UI related task after delay")
})
In swift 3.0 :
let dispatchTime: DispatchTime = DispatchTime.now() + Double(Int64(2.0 * Double(NSEC_PER_SEC))) / Double(NSEC_PER_SEC)
DispatchQueue.main.asyncAfter(deadline: dispatchTime, execute: {
})
Solution 10 - Ios
Xcode 10.2 and Swift 5 and above
DispatchQueue.main.asyncAfter(deadline: .now() + 2, execute: {
// code to execute
})
Solution 11 - Ios
Here's a handy helper to prevent making the annoying GCD call over and over again:
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)
}
}
}
Now you simply delay your code on the Main thread like this:
delay(bySeconds: 1.5) {
// delayed code
}
If you want to delay your code to different thread:
delay(bySeconds: 1.5, dispatchLevel: .background) {
// delayed code that will run on background thread
}
If you prefer a Framework that also has some more handy features then checkout HandySwift. You can add it to your project via Carthage then use it exactly like in the examples above:
import HandySwift
delay(bySeconds: 1.5) {
// delayed code
}
Solution 12 - Ios
Here's the Swift 3 way to queue work after a delay.
DispatchQueue.main.asyncAfter(
DispatchTime.now() + DispatchTimeInterval.seconds(2)) {
// do work
}
Solution 13 - Ios
Solution 14 - Ios
In swift 3, We can simply use DispatchQueue.main.asyncAfter function to trigger any function or action after the delay of 'n' seconds. Here in code we have set delay after 1 second. You call any function inside the body of this function which will trigger after the delay of 1 second.
let when = DispatchTime.now() + 1
DispatchQueue.main.asyncAfter(deadline: when) {
// Trigger the function/action after the delay of 1Sec
}
Solution 15 - Ios
Press Cmd + Shift + L to show the Xcode built-in code snippet library:
Look for dispatch after, then, just drag and drop to your code.
Solution 16 - Ios
Updated in April 2021 with Xcode 12.4. This still works, except now the +
button presents more icons, including Views library and Modifiers library (see below), and Views may be the default.
So be sure to select the Snippets library, as in the image below.
(verified on Xcode 11.3.1 in June 2020)
Xcode provides a code snippet to do this. You just have to enter the delay value and the code you wish to run after the delay.
- click on the
+
button at the top right of Xcode, while you are editing some code (rather than, say, in the Project Navigator, where other libraries like Capabilities would be presented) - Make sure the Snippets library (see screenshot, the icon with { }) is selected, out of the icons available.
- search for
after
- It will return only 1 search result, which is the desired snippet (see screenshot). Double click it and you're good to go.
Solution 17 - Ios
You can either wrap the argument in your own class, or wrap the method call in a method that doesn't need to be passed in the primitive type. Then call that method after your delay, and within that method perform the selector you wish to perform.
Solution 18 - Ios
Here is how you can trigger a block after a delay in 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)
}
Its included as a standard function in my repo.
Solution 19 - Ios
Swift 3 & Xcode 8.3.2
This code will help you, i add an explanation too
// 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 :)
}
}
}
Solution 20 - Ios
I believe the author is not asking how to wait for a fractional time (delay), but instead how to pass a scalar as argument of the selector (withObject:) and the fastest way in modern objective C is:
[obj performSelector:... withObject:@(0.123123123) afterDelay:10]
your selector have to change its parameter to NSNumber, and retrieve the value using a selector like floatValue or doubleValue