Question

I've got a somewhat complicated iOS user interface, where I need to reload a particular cell of a UICollectionView every second to show time (kind of like a complicated stopwatch), I also need to do a few other things every second in this call.

Part 1 of question


My question is what is the best practice for doing this type of problem?

Option 1 (use recursive call)

- (void)viewDidLoad
{
   [self runUpdateDisplayLoop];
}
- (void)runUpdateDisplayLoop
{
   //do stuff (reload cell, etc)
       
   //Recurissively Call Every Second
    dispatch_async(dispatch_get_main_queue(), ^{
        
        [self performSelector:@selector(runUpdateDisplayLoop) withObject:nil afterDelay:1.0];
        
    });
}

Option 2 (use NSTimer)

- (void)viewDidLoad
{
   NSTimer *updateDisplayTimer = [NSTimer scheduledTimerWithTimeInterval:1.0
                                              target:self 
                                            selector:@selector(runUpdateDisplayLoop:) 
                                            userInfo:nil 
                                             repeats:YES];
}

- (void)updateDisplayLoop:(NSTimer *) theTimer
{
 //do stuff (reload cell, etc)
}

Part 2 of Question


In either case, what is the best place to initiate and terminate these loops?

If I put them in ViewDidLoad they will continue being called even when I've navigated away from the relevant view, i'd rather only have the loop running when it needs to be. On the other hand, the loop must be running every time the view is displayed (even when the view has been dismissed in all variety of ways - app dismissed and reopened, phone call interruption, etc). In regards to app lifecycle, i'm targeting iOS 5 and iOS 6.

I'm guessing there is a solution involving "viewWillAppear" and "viewWillDisappear", but i'm leery of getting into situations where one or the other isn't called. I also don't want to spawn multiple loops by making any false assumptions.

Was it helpful?

Solution

For Part 1

I recommend using a timer. The main reason is so you can stop the process by invalidating the timer. Using performSelector you can achieve something similar but I wouldn't go that route. To each their own.

For Part 2

You do want to use viewWillAppear (or viewDidAppear) and viewDidDisappear (or viewWillDisappear). Setup the timer on appear and tear it down on disappear.

Check out RespondingtoDisplay-Notifications

Hope that sheds a light on things and addresses your "but i'm leery of getting into situations where one or the other isn't called." concern

OTHER TIPS

While NSTimer will kind of work, it's not really made for creating stop watches or the like. You can't rely on it firing every second, so you'll still need to use a NSDate object and something like timeIntervalSinceDate to calculate the value to display for the timer each time it fires. That way, your stop watch can catch up if it should happen to not fire for one or more seconds.

NSTimer events are processed by the main run loop, and if the main run loop is busy, the events aren't fired.

I like the background thread idea better. However, you don't need the recursion to make it work.

Simply create a custom Timer class with a start, calcTime, (and perhaps stop, reset, and resume). When you call start, the object should simply get the current date/time (NSDate) and store it. When you call calcTime, simply get the current date/time again, calculate the time elapsed using timeIntervalSinceDate, and either return the value or set properties that you can read.

In a background thread, simply keep calling the calcTime method and update the UI via dispatch_async(dispatch_get_main_que)(), then sleep for maybe 100 milliseconds between calls. You can check a Boolean global variable to see if you should keep running.

A background thread still may get bogged down if the processor is pegged, however, that's less likely to happen than the main run loop being busy, which is responsible for so much.

I've created a stop watch app, and this is the method I used, after deciding NSTimer wasn't viable.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top