19
votes

I have a spinning circle UI element that is updated by an NSTimer. I also have a UIScrollView that would 'block' the spinning circle while being scrolled. That was expected, because the timer and the scroll view where both in the same thread.

So I put the timer in a separate thread which basically works great! I can see it working because NSLogs keep coming even while scrolling.

But my problem is that my spinning circle is still stopping on scrolling! I suspect redrawing is halted until the next iteration of the main thread's run loop (?). So even if its angle is changed all the time, it might not be redrawn...

Any ideas what I can do? Thanks!

3

3 Answers

45
votes

While scrolling, the main thread's run loop is in UITrackingRunLoopMode. So what you need to do is schedule your timer in that mode (possibly in addition to the default mode). I believe NSRunLoopCommonModes includes both the default and event tracking modes, so you can just do this:

NSTimer *timer = [NSTimer timerWithTimeInterval:0.42 target:foo selector:@selector(doSomething) userInfo:nil repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];

By the way, you should do this all on the main thread. No need to spawn a background thread (unless each timer invocation is going to do some lengthy processing).

3
votes

UIScrollView doesn't just block NSTimers, it blocks the entire main thread. And UIKit objects should be accessed on the main thread only (and, often, are limited in unpredictable ways if you try to dodge round that restriction), which is probably why — even if you have a clock coming in from an external thread — you're unable to post updates.

It's a bit of a dodge, but is there any way your animation can be achieved with CoreAnimation? Things tied to that continue working irrespective of what's happening in the main thread.

0
votes

You are correct that UIScrollView does not update its content view during scrolling.

Probably your best bet -- and this is something of a hack -- is to display your animating view separately (perhaps within a frame-view, to get the free clipping) and use the scroll-view delegate callbacks to find out the current position of the scroll and position your animation accordingly.

NOTE: this suggestion is a hack! It's not best practices and it's certainly counter to simple, maintainable code without side effects. However, it's the only way I can think to get the display you want.

Another tack: re-think the importance of displaying animated contents of a scrolling view while it is scrolling.