NSTextField 等待循环结束才更新

发布于 2024-11-04 12:29:01 字数 648 浏览 2 评论 0原文

问题是:我有一些代码是这样的

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 方法只是打开笔尖并显示窗口。 showMessage 是一种更改 NSTextView 以显示参数中的任何文本的方法。

在 NSLog 中,文本每秒都会变化,并且只数到十。然而,对于 showMessage 方法,文本整整十秒都是空白,然后只显示数字 10。有什么想法吗?

FTR,showMessage 方法根本

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

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

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 showMessage 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.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

十秒萌定你 2024-11-11 12:30:54

我认为通过调用 sleep(1) 你会阻塞主线程,它必须绘制你的更改。所以显示没有更新。任务管理器不会中断您的功能。在这种情况下,您不应该使用睡眠。请看一下 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;
    }
}

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;
    }
}
晨光如昨 2024-11-11 12:30:40

视图未更新直到运行循环结束;您的 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++;
}

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

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.

香橙ぽ 2024-11-11 12:30:28

问题是您在 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;
    }
}

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;
    }
}
平安喜乐 2024-11-11 12:30:14

如果您明确地给运行循环一些运行时间,您可能可以在循环内实现所需的效果:

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

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]];
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文