How should the model update its progress UI?

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

I have to do some processing on a model class that has no concept of UI. However, this processing takes a while and I want to inform the user of the status using a progress bar.

My first attempt is to define a protocol / interface concept of a progress handler with some methods like

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

      

This way my UI can realize that the model doesn't need details of what is going on, except for those who want to update the progress. Currently my UI displays a progress bar and updates it, then hides it when done. So far so good.

However, it turns out that sometimes this operation is very fast. So UI updates lead to some pretty nasty flickering when they are executed. I don't know if the operation will be fast or slow.

One of my ideas was to force the operation to take at least a certain length of time so that the UI changes are so harsh on the eye, but that seemed to know the UI in the model class, which should be wrong.

This would seem to be a common problem with (hopefully) some well-known pattern.

How would you touch on this?

+2


source to share


3 answers


You can use an NSTimer to delay the display of the progress bar until your operation has completed within 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 the 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 until a progress bar is displayed.

+3


source


Jonathan and Darren's answers address your actual problem, but I would add something relevant to the question in the title: "How should the UI model of its progress be updated?"

The answer, of course, is that he shouldn't. The model does not need to know anything about any protocols to display the data. There should be one homogeneous binding layer taking care of the propagation of information from the model to the interface. Fortunately, Cocoa already includes such a binding mechanism: Key-Value Observing.

What you need to do is define a property in any model class where the concept of progress makes sense, something like @property (assign) float progress

. Then you will verify that the class meets the KVO requirements . Controller code that wants to track progress is simply logging to observe this value with something like:



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

Be sure to read the documentation for the unofficial NSKeyValueObserving (KVO) protocol. Alternatively, you can take a look at the notes and code related to Mike Ash KVO: Key Value Compliance Note .

+7


source


One thing usually done is that the implementation of your progress bar is not immediately displayed, and apply some heuristic based on the first pair of updates (or timeout) to determine if you need to show yourself at all. This is for example the behavior of Java ProgressMonitor . (ProgressMonitor is a nice abstraction that separates the knowledge of progress from its graphical representation).

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

+2


source







All Articles