模型应该如何更新其进度的 UI?

发布于 2024-08-07 18:08:52 字数 557 浏览 1 评论 0 原文

我正在尝试解决 Objective-C 中的问题,但我认为该问题不是特定于语言的。

我必须在没有 UI 概念的模型类中进行一些处理。然而,这个处理需要一些时间,我想通过进度条让用户知道状态。

我的第一次尝试是使用一些方法定义进度处理程序协议/接口的概念,这样

-startOperation;
-updateProgress:(double)currentValue ofMax:(double)maxValue
-endOperation;

我的 UI 就可以实现模型不需要知道除了有人想要进度更新之外发生的事情的详细信息。目前,我的用户界面取消隐藏进度条并更新它,然后在完成后隐藏它。到目前为止,一切都很好。

然而,事实证明,有时这个操作处理速度非常快。这样,UI 更新在执行时会导致相当令人不安的闪烁。事先不知道操作会快还是慢。

我的一个想法是强制操作至少需要一定的持续时间,以避免 UI 更改看起来如此刺眼,但这似乎将 UI 的知识放入模型类中,这肯定是错误的。

这似乎是(希望)某些已知模式的常见问题。

你会如何解决这个问题?

I am trying to solve a problem in Objective-C, but I don't think the question is language specific.

I have to do some processing down in a model class that has no notion of UI. However, this processing takes some time and I want to let the user know the status via a progress bar.

My first attempt at this was defining a notion of a progress handler protocol/interface with some methods like

-startOperation;
-updateProgress:(double)currentValue ofMax:(double)maxValue
-endOperation;

This way my UI can implement that the the model need not know details about what goes on other than someone wants progress updates. Currently my UI unhides a progress bar, and updates it, then hides it when done. So far so good.

However, it turns out that sometimes this operation processing is very fast. Such that the UI updates result in a pretty disconcerting flicker as they execute. I don't know if the operation will be fast or slow beforehand.

One idea I had was to force the operation to take at least a certain duration to avoid the UI changes being so jarring to the eye, but this seemed to put knowledge of the UI in the model class, which must be wrong.

This would seem to be a common issue with (hopefully) some known pattern.

How would you address this?

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

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

发布评论

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

评论(3

找回味觉 2024-08-14 18:08:52

乔纳森和达伦的答案涵盖了您的实际问题,但我想添加一些关于标题中的问题的内容:“模型应该如何更新其进度的 UI?”

答案当然是不应该。该模型不必了解有关显示数据的任何协议的任何。应该有一个统一的绑定层负责将信息从模型传播到界面。幸运的是,Cocoa 已经包含了这样的绑定机制:Key-Value Observing。

您应该做的是在任何模型类上定义一个属性,其中进度概念有意义,例如@property(分配)浮动进度。然后你 确保该类符合 KVO 规范。想要跟踪进度的控制器代码只需注册即可观察该值,例如:

[theObject addObserver:self forKeyPath:@"progress" options:0 context:NULL];

请务必阅读 NSKeyValueObserving (KVO) 非正式协议的文档。
另外,您可能想查看 Mike Ash 的 KVO 相关注释和代码:键值观察正确

Jonathan's and Darren's answers cover your actual problem, but I would add something regarding the question in the title: "How should the model update the UI of its progress?"

The answer, of course, is that it shouldn't. The model shouldn't have to know anything about any protocols for displaying data. There should be one uniform bindings layer taking care about propagating information from the model to the interface. Fortunately, Cocoa already includes such a bindings mechanism: Key-Value Observing.

What you should do is define a property on any model class where the concept of progress makes sense, something like @property (assign) float progress. Then you make sure the class is KVO compliant. Controller code that want to keep track of the progress simply registers to observe this value with something like:

[theObject addObserver:self forKeyPath:@"progress" options:0 context:NULL];

Make sure to read the documentation for the NSKeyValueObserving (KVO) informal protocol.
Also, you might want to have a look at Mike Ash's KVO-related notes and code: Key-Value Observing Done Right.

迎风吟唱 2024-08-14 18:08:52

您可以使用 NSTimer 延迟进度条的显示,直到您的操作运行了给定的时间(例如半秒):

-(void)startOperation {
    // Show the progress bar in 0.5 seconds
    if (!_timer) {
        _timer = [[NSTimer scheduledTimerWithTimeInterval:0.5 
                                                   target:self 
                                                 selector:@selector(showProgressBar:) 
                                                 userInfo:nil 
                                                  repeats:NO] retain];
    }
}

在 -endOperation 中,您取消计时器并隐藏进度条:

-(void)endOperation {
    [_timer invalidate]; // cancel the timer
    [_timer release];
    _timer = nil;

    [self hideProgressBar];
}

如果操作在不到 0.5 内完成秒,在显示进度条之前取消计时器。

You can use NSTimer to delay the display of your progress bar until your operation had run for a given amount of time, say half a second:

-(void)startOperation {
    // Show the progress bar in 0.5 seconds
    if (!_timer) {
        _timer = [[NSTimer scheduledTimerWithTimeInterval:0.5 
                                                   target:self 
                                                 selector:@selector(showProgressBar:) 
                                                 userInfo:nil 
                                                  repeats:NO] retain];
    }
}

In -endOperation, you cancel the timer and hide progress bar:

-(void)endOperation {
    [_timer invalidate]; // cancel the timer
    [_timer release];
    _timer = nil;

    [self hideProgressBar];
}

If the operation completes in less than 0.5 seconds, the timer is canceled before the progress bar is displayed.

云归处 2024-08-14 18:08:52

通常做的一件事是让进度条实现不立即显示自己,并根据前几次更新(或超时)应用一些启发式方法来确定它是否需要显示自己。这就是 Java ProgressMonitor 的行为方式,对于例子。 (ProgressMonitor 是一个很好的抽象,它将进度知识与其图形表示分开)。

显示进度小部件后,您可以在悠闲的计时器上重新绘制它,例如每秒 10 次,而不是通过重新绘制对每个进度更改事件做出反应。

One thing commonly done is to have your progress bar implementation not show itself right away, and apply some heuristic based on the first couple of updates (or a timeout) to determine whether it needs to show itself at all. That's how the Java ProgressMonitor behaves, for example. (The ProgressMonitor is a nice abstraction that separates the knowledge of progress from its graphical representation).

Once the progress widget is showing, you could repaint it on a leisurely timer, say 10 times per second, rather than reacting to every progress change event with a repaint.

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