开发者

NSTextField waits until the end of a loop to update

Here's the problem: I have some code that goes like this

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

where otherWinController is a subclass of NSWindowController that I am using to update my window as changes happen in code, and the alloc init method simply opens up the nib and shows the window. showMessage is a method that changes an NSTextView to display whatever text is in the parameter.

in the NSLog, the text changes every second and just counts to ten. However for the showMe开发者_开发问答ssage method, the text is blank for a full ten seconds and then just displays the number 10. any thoughts??

FTR, the showMessage method is simply

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

not that it should matter, that's pretty basic.


You can probably achieve the desired effect right inside your loop, if you explicitly give the run loop some time to run:

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


The problem is that you’re blocking the main thread in your for loop and user interface updates happen on the main thread. The main thread run loop will only spin (and consequently user interface updates will take place) after the method containing that for loop finishes executing.

If you want to update that text field every second, you should use a timer. For instance, considering otherWinController is an instance variable, declare a counter property in your class and:

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];

In the same class, implement the method that’s called whenever the time has been fired:

- (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;
    }
}


Views don't get updated until the end of the run loop; your for loop doesn't let the run loop continue, so all the view updates you make are just done after your for loop exits.

You should either use an NSTimer or performSelector:withObject:afterDelay: to change the display in a loop-like fashion.

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

Then your timer's action will change the view's image:

- (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++;
}

You also generally don't want to use sleep unless you're on a background thread, because it will lock up the rest of the UI; your user won't be able to do anything, and if you sleep long enough, you'll get the spinning beach ball.


I think that by calling sleep(1) you block the main thread, which must draw your changes. So the display is not updated. The task manager will not interrupt your function. You shouldn't use sleep in this case. Please take a look at NSTimer class. It has a static method scheduledTimerWithTimeInterval, which you should use.

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;
    }
}
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜