Question

I have an IBAction with some simple code inside:

-(IBAction)change:(id)sender {
    [textfield setHidden:NO];
    [self dolengthyaction];
}

'textfield' is an NSTextField in a nib file, and -'dolengthyaction' is a function that takes about a minute to finish executing.

My question is: Why isn't the textfield shown until AFTER "dolengthyaction" is done executing? I want it to be revealed before the dolengthyaction starts taking place. Is this an inherent problem or is there something wrong with my code? (or in another part of my code?)

I'm still not very good at programming so I apologize if I worded something badly and formatted something wrong.

EDIT: There isn't much else other than this IBAction and -dolengthyaction...

-(void)doLengthyAction {
    sleep(10);
}
-(IBAction)change:(id)sender {
    [textfield setHidden:NO];
    [self doLengthyAction];
    [textfield setHidden:YES];
}

All I really want to do is display the label when the action is running and hide it when it's done.

Basically what this means is that it's not displaying at all right now.

In reality, in -doLengthyAction it's not sleep(10) but rather a NSFileManager operation that copies about 50 Mb of material. The code was rather long, but if you want me to post it I can. I tested it with sleep() but it doesn't work either.

Was it helpful?

Solution

All drawing operations (including hiding and showing views) are triggered from the run loop. The run loop cannot perform the next event until after your function returns.

If you have an operation that takes more than a second to run, you should perform it in a thread. Once it completes, use performSelectorOnMainThread to make UI changes on the main thread.

OTHER TIPS

As mentioned in some of the previous answers, the application must return to the main run loop before it will redraw (it's an optimization to avoid redrawing when you make many changes).

You really should handle things on a background thread if they're going to run for a long time. If you don't the UI will beach ball while your operation runs.

If you're targeting 10.6+, you can use GCD and blocks as follows to run things easily in the background (and not have to define multiple methods to get things done):

-(void)doLengthyAction {
    sleep(10);
}
-(IBAction)change:(id)sender {
    [textfield setHidden:NO];
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        [self doLengthyAction];
        dispatch_async(dispatch_get_main_queue(), ^{
            [textfield setHidden:YES];
        });
    });
}

Of course by using this, you'll need to make sure that what's going on in the lengthy action is thread safe, but you'll end up with a better user experience. For more info on this type of CGD code, you might want to read this.

I think there is something wrong with the rest of your code. This should not happen.

Post more?

Can't say what exactly is transpiring with out looking at rest of the code, but a hackish way would be to try:

-(IBAction)change:(id)sender {
   [textfield setHidden:NO];
   [self performSelector:@selector(doLenghtyAction) withObject:nil afterDelay:0.1]; --> or maybe even 0.0
 }
-(void)doLengthyAction {
     sleep(10);
    [textfield setHidden:YES];
} 

-(IBAction)change:(id)sender {
    [textfield setHidden:NO];
    [NSTimer scheduledTimerWithTimeInterval:0.1 target:self selector:@selector(doLengthyAction) userInfo:nil repeats:NO];
}

Try this:

-(IBAction)change:(id)sender {
    [textfield setHidden:NO];
    [[textfield window] display]; // force the window to update
    [self doLengthyAction];
    [textfield setHidden:YES];
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top