NSTextField espera hasta el final de un ciclo para actualizar

Aquí está el problema: tengo un código que funciona así

otherWinController = [[NotificationWindowController alloc] init]; for (int i = 0; i < 10; i++) { [otherWinController showMessage:[NSString stringWithFormat:@"%d", i]]; NSLog(@"%d", i); sleep(1); } 

donde otherWinController es una subclase de NSWindowController que estoy usando para actualizar mi ventana a medida que ocurren cambios en el código, y el método alloc init simplemente abre el plumín y muestra la ventana. showMessage es un método que cambia un NSTextView para mostrar cualquier texto que esté en el parámetro.

en NSLog, el texto cambia cada segundo y solo cuenta hasta diez. Sin embargo, para el método showMessage, el texto está en blanco durante diez segundos completos y luego solo muestra el número 10. ¿Alguna idea?

FTR, el método showMessage es simplemente

 - (void)showMessage:(NSString *)text { [[self message] setStringValue:text]; } 

No es que importe, es bastante básico.

Probablemente pueda lograr el efecto deseado dentro de su bucle, si le da al bucle de ejecución algo de tiempo para ejecutar:

 [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow: 0.1]]; 

El problema es que está bloqueando el hilo principal en su bucle for y las actualizaciones de la interfaz de usuario ocurren en el hilo principal. El ciclo principal de ejecución de subproceso solo girará (y consecuentemente las actualizaciones de la interfaz de usuario tendrán lugar) después de que el método que contiene ese ciclo finalice la ejecución.

Si desea actualizar ese campo de texto cada segundo, debe usar un temporizador. Por ejemplo, considerando otherWinController es una variable de instancia, declare una propiedad de counter en su clase y:

 otherWinController = [[NotificationWindowController alloc] init]; self.counter = 0; [otherWinController showMessage:[NSString stringWithFormat:@"%d", self.counter]]; [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(updateCounter:) userInfo:nil repeats:YES]; 

En la misma clase, implemente el método que se llama siempre que se haya disparado la hora:

 - (void)updateCounter:(NSTimer *)timer { self.counter = self.counter + 1; [otherWinController showMessage:[NSString stringWithFormat:@"%d", self.counter]]; if (self.counter == 9) { [timer invalidate]; // if you want to reset the counter, // self.counter = 0; } } 

Las vistas no se actualizan hasta el final del ciclo de ejecución ; su bucle for no permite que el ciclo de ejecución continúe, por lo que todas las actualizaciones de vista que acaba de realizar se terminan una vez que finaliza su bucle for.

Debería utilizar un NSTimer o performSelector:withObject:afterDelay: para cambiar la visualización en forma de bucle.

 [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(changeTextFieldsString:) userInfo:nil repeats:YES]; 

Entonces la acción de su temporizador cambiará la imagen de la vista:

 - (void)changeTextFieldsString:(NSTimer *)tim { // currStringIdx is an ivar keeping track of our position if( currStringIdx >= maxStringIdx ){ [tim invalidate]; return; } [otherWinController showMessage:[NSString stringWithFormat:@"%d", currStringIdx]] currStringIdx++; } 

En general, tampoco desea utilizar el modo de sleep menos que esté en una cadena de fondo, ya que bloqueará el rest de la interfaz de usuario; su usuario no podrá hacer nada, y si duerme lo suficiente, obtendrá la pelota de playa giratoria.

Creo que al llamar a sleep (1) bloquea el hilo principal, que debe dibujar los cambios. Entonces la pantalla no está actualizada. El administrador de tareas no interrumpirá tu función. No deberías usar sleep en este caso. Por favor, eche un vistazo a la clase NSTimer . Tiene un método estático scheduleTimerWithTimeInterval , que debe usar.

 static UIViewController *controller = nil; ..... { ..... otherWinController = [[NotificationWindowController alloc] init]; controller = otherWinController; [NSTimer scheduledTimerWithTimeInterval:1.0f target:self selector:@selector(timerTick:) userInfo:nil repeats:YES]; //set timer with one second interval ..... } - (void) timerTick:(NSTimer*)theTimer { static int i = 0; [controller showMessage:[NSString stringWithFormat:@"%d", i]]; NSLog(@"%d", i); if (++i == 10) { [theTimer invalidate]; i = 0; } }