NSTextField一直等到循环结束才能更新

时间:2011-04-29 08:49:24

标签: objective-c cocoa nstextfield

问题在于:我有一些像这样的代码

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

其中,otherWinController是NSWindowController的子类,我用它来更新我的窗口,因为代码中发生了更改,而alloc init方法只是打开了nib并显示了窗口。 showMessage是一种更改NSTextView以显示参数中的任何文本的方法。

<>在NSLog中,文本每秒都会更改,只计为10。但是对于showMessage方法,文本为空整整十秒,然后只显示数字10.任何想法??

FTR,showMessage方法就是

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

并不重要,这是非常基本的。

4 个答案:

答案 0 :(得分:6)

如果明确地给运行循环一段时间运行,你可以在循环中实现所需的效果:

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

答案 1 :(得分:2)

问题是你阻塞了for循环中的主线程,并且主线程上发生了用户界面更新。在包含for循环的方法完成执行后,主线程运行循环将仅旋转(并因此将发生用户界面更新)。

如果要每秒更新该文本字段,则应使用计时器。例如,考虑otherWinController是一个实例变量,在类中声明一个counter属性并且:

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

在同一个类中,实现每当触发时间时调用的方法:

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

答案 2 :(得分:1)

视图无法更新until the end of the run loop;你的for循环不会让运行循环继续,所以你所做的所有视图更新都是在你的for循环退出后完成的。

您应该使用NSTimerperformSelector:withObject:afterDelay:以类似循环的方式更改显示。

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

然后你的计时器动作将改变视图的图像:

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

除非你在后台线程上,否则你通常也不想使用sleep,因为它会锁定UI的其余部分;你的用户将无法做任何事情,如果你睡得足够长,你就会得到旋转的沙滩球。

答案 3 :(得分:0)

我认为通过调用 sleep(1)来阻止主线程,它必须绘制你的更改。因此显示不会更新。任务管理器不会中断您的功能。在这种情况下,您不应该使用 sleep 。请查看 NSTimer 课程。它有一个静态方法 scheduledTimerWithTimeInterval ,您应该使用它。

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