Есть ли способ вызвать блок с примитивным параметром после задержки, например, с помощью performSelector:withObject:afterDelay:
, но с аргументом типа int
/double
/float
?
Как вы запускаете блок после задержки, например -performSelector: withObject: afterDelay:?
Ответ 1
Я думаю, что вы ищете dispatch_after()
. Он требует, чтобы ваш блок не принимал никаких параметров, но вы можете просто позволить блоку захватить эти переменные из вашей локальной области.
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
Ответ 2
Вы можете использовать dispatch_after
для вызова блока позже. В Xcode начните вводить dispatch_after
и нажмите Enter
для автозаполнения следующим образом:
Вот пример с двумя поплавками как "аргументы". Вам не нужно полагаться на какой-либо макрос, и цель кода достаточно ясна:
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)")
}
Цель 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);
});
Ответ 3
Как насчет использования встроенной библиотеки фрагментов кода Xcode?
Обновление для Swift:
Множество голосов побудили меня обновить этот ответ.
Встроенная библиотека фрагментов кода Xcode имеет dispatch_after
только для языка objective-c
. Люди могут также создать свой собственный код фрагмента кода для 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#>
}
Ответ 4
Расширяясь в ответ Хайме Чама, я создал категорию 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];
Ответ 5
Для Swift я создал глобальную функцию, ничего особенного, используя метод dispatch_after
. Мне это больше нравится, поскольку он читается и прост в использовании:
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)
}
Что вы можете использовать следующим образом:
performBlock({ () -> Void in
// Perform actions
}, afterDelay: 0.3)
Ответ 6
Возможно, проще, чем идти через GCD, в каком-либо классе (например, "Util" ) или в категории по объекту:
+ (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];
}
Итак, чтобы использовать:
[Util runAfterDelay:2 block:^{
NSLog(@"two seconds later!");
}];
Ответ 7
Вот мои 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);
}
Ответ 8
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:] и т.д., а в методе приема вы можете преобразовать число в ваш формат как [число int] или [number double].
Ответ 9
Функция dispatch_after отправляет объект блока в очередь отправки по истечении заданного периода времени. Используйте нижеприведенный код для выполнения некоторых подключений, связанных с UI, через 2.0 секунды.
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")
})
В быстрой версии 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: {
})
Ответ 10
Здесь удобный помощник, чтобы не допустить повторного раздражающего вызова 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. Вы можете добавить его в свой проект через Carthage, а затем использовать его точно так же, как в приведенных выше примерах:
import HandySwift
delay(bySeconds: 1.5) {
// delayed code
}
Ответ 11
Здесь быстрый способ 3 очереди в очереди после задержки.
DispatchQueue.main.asyncAfter(
DispatchTime.now() + DispatchTimeInterval.seconds(2)) {
// do work
}
Ответ 12
В блоке BlocksKit есть хороший.
(и класс)
Ответ 13
В 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
}
Ответ 14
Вы можете либо обернуть аргумент в свой собственный класс, либо обернуть вызов метода в методе, который не нужно передавать в примитивном типе. Затем вызовите этот метод после задержки, и внутри этого метода выполните селектор, который вы хотите выполнить.
Ответ 15
Вот как вы можете запускать блок после задержки в 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)
}
Он включен как стандартная функция в мое репо.
Ответ 16
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 :)
}
}
}
Ответ 17
Я считаю, что автор не спрашивает, как ждать дробного времени (задержки), но вместо этого, как передать скаляр в качестве аргумента селектора (с помощью Object:) и самым быстрым способом в современной цели C:
[obj performSelector:... withObject:@(0.123123123) afterDelay:10]
ваш селектор должен изменить свой параметр на NSNumber и получить значение с помощью селектора типа floatValue или doubleValue
Ответ 18
[NSTimer scheduledTimerWithTimeInterval:2.5
target:self
selector:@selector(toDoSomething)
userInfo:nil
repeats:NO];