How do you trigger a block after a delay, like -performSelector:withObject:afterDelay:?
有没有办法在延迟后调用带有原始参数的块,比如使用
我想你正在寻找
1 2 3 4 5 6 7 | 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); }); |
更多:https://developer.apple.com/documentation/dispatch/1452876-dispatch_after
您可以稍后使用
这是一个以两个浮点数作为"参数"的示例。您不必依赖任何类型的宏,并且代码的意图非常明确:
Swift 3,Swift 4
1 2 3 4 5 6 7 | 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
1 2 3 4 5 6 7 | 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.
1 2 3 4 5 6 7 8 | 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); }); |
如何使用Xcode内置代码片段库?
Swift更新:
很多投票激励我更新这个答案。
内置的Xcode代码段库只有
在Xcode中写下这个。
1 2 3 | 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#> }) |
拖动此代码并将其放在代码段库区域中。
在代码片段列表的底部,会有一个名为
有关详细信息,请参阅CreatingaCustomCodeSnippet。
更新Swift 3
拖动此代码并将其放在代码段库区域中。
1 2 3 | DispatchQueue.main.asyncAfter(deadline: .now() + .seconds(<#delayInSeconds#>)) { <#code to be executed after a specified delay#> } |
扩展了Jaime Cham的答案,我创建了一个NSObject + Blocks类别,如下所示。我觉得这些方法更符合现有的
NSObject的+ Blocks.h
1 2 3 4 5 6 7 | #import <Foundation/Foundation.h> @interface NSObject (Blocks) - (void)performBlock:(void (^)())block afterDelay:(NSTimeInterval)delay; @end |
NSObject的+ Blocks.m
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | #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 |
并使用如下:
1 2 3 | [anyObject performBlock:^{ [anotherObject doYourThings:stuff]; } afterDelay:0.15]; |
也许比通过GCD,某个类(例如"Util")或者对象类别更简单:
1 2 3 4 5 6 7 8 9 | + (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]; } |
所以使用:
1 2 3 | [Util runAfterDelay:2 block:^{ NSLog(@"two seconds later!"); }]; |
对于Swift,我使用
1 2 3 | 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) } |
您可以使用以下内容:
1 2 3 | performBlock({ () -> Void in // Perform actions }, afterDelay: 0.3) |
这是我的2美分= 5种方法;)
我喜欢封装这些细节,并让AppCode告诉我如何完成我的句子。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | 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); } |
dispatch_after函数在给定的时间段之后将块对象调度到调度队列。使用以下代码在2.0秒后执行一些与UI相关的操作。
1 2 3 4 5 6 7 8 | 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") }) |
在swift 3.0中:
1 2 3 4 | let dispatchTime: DispatchTime = DispatchTime.now() + Double(Int64(2.0 * Double(NSEC_PER_SEC))) / Double(NSEC_PER_SEC) DispatchQueue.main.asyncAfter(deadline: dispatchTime, execute: { }) |
PerformSelector:WithObject总是接受一个对象,所以为了传递像int / double / float等参数.....你可以使用这样的东西。
// NSNumber是一个对象..
1 2 3 4 5 6 7 8 9 10 11 | [self performSelector:@selector(setUserAlphaNumber:) withObject: [NSNumber numberWithFloat: 1.0f] afterDelay:1.5]; -(void) setUserAlphaNumber: (NSNumber*) number{ [txtUsername setAlpha: [number floatValue] ]; } |
同样的方法你可以使用[NSNumber numberWithInt:]等....在接收方法中你可以将数字转换为格式为[number int]或[number double]。
这是一个方便的助手,可以防止反复拨打恼人的GCD电话:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | 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) } } } |
现在,您只需将代码延迟到主线程上,如下所示:
1 2 3 | delay(bySeconds: 1.5) { // delayed code } |
如果您想将代码延迟到不同的线程:
1 2 3 | delay(bySeconds: 1.5, dispatchLevel: .background) { // delayed code that will run on background thread } |
如果你更喜欢一个也有一些更方便功能的框架,那么请查看HandySwift。您可以通过Carthage将其添加到项目中,然后使用它与上面的示例完全相同:
1 2 3 4 5 | import HandySwift delay(bySeconds: 1.5) { // delayed code } |
这是Swift 3在延迟后排队工作的方法。
1 2 3 4 | DispatchQueue.main.asyncAfter( DispatchTime.now() + DispatchTimeInterval.seconds(2)) { // do work } |
BlocksKit框架中有一个很好的。
BlocksKit
(和班级)
BBlocksKit.m
在swift 3中,我们可以简单地使用DispatchQueue.main.asyncAfter函数在'n'秒延迟后触发任何函数或动作。 在代码中,我们在1秒后设置了延迟。 您可以调用此函数体内的任何函数,该函数将在延迟1秒后触发。
1 2 3 4 5 6 | let when = DispatchTime.now() + 1 DispatchQueue.main.asyncAfter(deadline: when) { // Trigger the function/action after the delay of 1Sec } |
以下是在Swift延迟后触发块的方法:
1 2 3 4 5 6 7 8 9 | 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和Xcode 8.3.2
这段代码对你有所帮助,我也加了解释
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 | // 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 :) } } } |
Xcode 10.2和Swift 5及以上版本
1 2 3 | DispatchQueue.main.asyncAfter(deadline: .now() + 2, execute: { // code to execute }) |
我相信作者不会问如何等待一个小数时间(延迟),而是如何传递一个标量作为选择器的参数(withObject :)和现代目标C中最快的方法是:
1 | [obj performSelector:... withObject:@(0.123123123) afterDelay:10] |
您的选择器必须将其参数更改为NSNumber,并使用floatValue或doubleValue等选择器检索该值