Respuestas:
Creo que lo estás buscando dispatch_after()
. Requiere que su bloque no acepte parámetros, pero puede dejar que el bloque capture esas variables de su ámbito local.
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);
});
Más: https://developer.apple.com/documentation/dispatch/1452876-dispatch_after
dispatch_time(DISPATCH_TIME_NOW, 10ull * NSEC_PER_SEC)
fragmento es desagradable. ¿No hay una forma más limpia para esto?
dispatch_get_current_queue()
siempre devuelve la cola desde la que se ejecuta el código. Entonces, cuando este código se ejecuta desde el hilo principal, el bloque también se ejecutará en el hilo principal.
dispatch_get_current_queue()
ahora está en desuso
Puede usar dispatch_after
para llamar a un bloque más tarde. En Xcode, comience a escribir dispatch_after
y Enter
presione para autocompletar lo siguiente:
Aquí hay un ejemplo con dos flotantes como "argumentos". No tiene que confiar en ningún tipo de macro, y la intención del código es bastante clara:
let time1 = 8.23
let time2 = 3.42
// Delay 2 seconds
DispatchQueue.main.asyncAfter(deadline: .now() + 2.0) {
print("Sum of times: \(time1 + time2)")
}
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)")
}
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);
});
NSEC_PER_SEC * 0.5
funcionaría igual que NSEC_PER_MSEC * 500
. Si bien tiene razón al notar que dispatch_time
espera un número entero de 64 bits, el valor que espera está en nanosegundos. NSEC_PER_SEC
se define como 1000000000ull
, y multiplicar eso con una constante de punto flotante 0.5
implícitamente realizaría una aritmética de punto flotante, produciendo 500000000.0
, antes de que se convierta explícitamente en un entero de 64 bits. Por lo tanto, es perfectamente aceptable usar una fracción de NSEC_PER_SEC
.
¿Qué hay de usar la biblioteca de fragmentos de código incorporada de Xcode?
Actualización para Swift:
Muchos votos positivos me inspiraron para actualizar esta respuesta.
La biblioteca de fragmentos de código Xcode incorporada dispatch_after
solo tiene un objective-c
idioma. Las personas también pueden crear su propio fragmento de código personalizado para Swift
.
Escribe esto en 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#>
})
Arrastre este código y suéltelo en el área de la biblioteca de fragmentos de código.
Al final de la lista de fragmentos de código, habrá una nueva entidad llamada My Code Snippet
. Edite esto para un título. Para sugerencias mientras escribe el código X, complete el Completion Shortcut
.
Para obtener más información, consulte Creación de un código de código personalizado .
Arrastre este código y suéltelo en el área de la biblioteca de fragmentos de código.
DispatchQueue.main.asyncAfter(deadline: .now() + .seconds(<#delayInSeconds#>)) {
<#code to be executed after a specified delay#>
}
Ampliando la respuesta de Jaime Cham, creé una categoría NSObject + Blocks como se muestra a continuación. Sentí que estos métodos coincidían mejor con los performSelector:
métodos existentes de 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
y usar así:
[anyObject performBlock:^{
[anotherObject doYourThings:stuff];
} afterDelay:0.15];
delay
debería ser de NSTimeInterval
(que es a double
). #import <UIKit/UIKit.h>
no es necesario. Y, no veo por qué - (void)performBlock:(void (^)())block;
podría ser útil, por lo que se puede eliminar del encabezado.
Quizás más simple que ir a través de GCD, en una clase en algún lugar (por ejemplo, "Util"), o una Categoría en Objeto:
+ (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];
}
Para usar:
[Util runAfterDelay:2 block:^{
NSLog(@"two seconds later!");
}];
Para Swift, he creado una función global, nada especial, utilizando el dispatch_after
método. Me gusta más, ya que es legible y fácil de usar:
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)
}
Que puedes usar de la siguiente manera:
performBlock({ () -> Void in
// Perform actions
}, afterDelay: 0.3)
after
. Entonces puedes escribir:after(2.0){ print("do somthing") }
Aquí están mis 2 centavos = 5 métodos;)
Me gusta encapsular estos detalles y hacer que AppCode me diga cómo terminar mis oraciones.
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);
}
PerformSelector: WithObject siempre toma un objeto, por lo que para pasar argumentos como int / double / float, etc. ... puede usar algo como esto.
// NSNumber es un objeto ...
[self performSelector:@selector(setUserAlphaNumber:)
withObject: [NSNumber numberWithFloat: 1.0f]
afterDelay:1.5];
-(void) setUserAlphaNumber: (NSNumber*) number{
[txtUsername setAlpha: [number floatValue] ];
}
De la misma manera puede usar [NSNumber numberWithInt:] etc .... y en el método de recepción puede convertir el número a su formato como [número int] o [número doble].
La función dispatch_after despacha un objeto de bloque a una cola de despacho después de un período de tiempo determinado. Use el siguiente código para realizar algunas tareas relacionadas con la interfaz de usuario después de 2.0 segundos.
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")
})
En 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: {
})
mainQueue,
lugar demainQueue)
Aquí hay un útil ayudante para evitar hacer la molesta llamada GCD una y otra vez:
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)
}
}
}
Ahora simplemente retrasa su código en el hilo principal de esta manera:
delay(bySeconds: 1.5) {
// delayed code
}
Si desea retrasar su código a un hilo diferente :
delay(bySeconds: 1.5, dispatchLevel: .background) {
// delayed code that will run on background thread
}
Si prefiere un Framework que también tenga algunas características más útiles, entonces consulte HandySwift . Puede agregarlo a su proyecto a través de Carthage y luego usarlo exactamente como en los ejemplos anteriores:
import HandySwift
delay(bySeconds: 1.5) {
// delayed code
}
En swift 3, simplemente podemos usar la función DispatchQueue.main.asyncAfter para activar cualquier función o acción después del retraso de 'n' segundos. Aquí en el código hemos establecido el retraso después de 1 segundo. Llama a cualquier función dentro del cuerpo de esta función que se activará después del retraso de 1 segundo.
let when = DispatchTime.now() + 1
DispatchQueue.main.asyncAfter(deadline: when) {
// Trigger the function/action after the delay of 1Sec
}
Puede ajustar el argumento en su propia clase o ajustar la llamada al método en un método que no necesita pasarse en el tipo primitivo. Luego llame a ese método después de su retraso, y dentro de ese método realice el selector que desea realizar.
Así es como puede activar un bloqueo después de un retraso en 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)
}
Se incluye como una función estándar en mi repositorio .
Swift 3 y Xcode 8.3.2
Este código te ayudará, también agrego una explicación
// 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 :)
}
}
}
Creo que el autor no pregunta cómo esperar un tiempo fraccional (retraso), sino cómo pasar un escalar como argumento del selector (con Objeto :) y la forma más rápida en el objetivo moderno C es:
[obj performSelector:... withObject:@(0.123123123) afterDelay:10]
su selector tiene que cambiar su parámetro a NSNumber y recuperar el valor utilizando un selector como floatValue o doubleValue