UIScrollView blocks run loop? - iphone

I implemented a NSTimer(repeats) and UITableView on the same viewController.
Somehow, when I scroll through the tableView, the run loop seems to stop firing the NSTimer.
The same goes for UITextView, which is also a subclass of UIScrollView.
May I know what is happening here?

The reason that the timer stops firing is that the run loop switches to UITrackingRunLoopMode during scrolling and the timer is not added by default to that mode. You can do that manually when you start the timer:
NSTimer *timer = [NSTimer timerWithTimeInterval:1.0 target:self selector:#selector(timerFired:) userInfo:nil repeats:YES];
NSRunLoop *runloop = [NSRunLoop currentRunLoop];
[runloop addTimer:timer forMode:NSRunLoopCommonModes];
[runloop addTimer:timer forMode:UITrackingRunLoopMode];

Related

I want to invalidate all [self performSelector:#selector(showLyrics) withObject:nil afterDelay:2];

I am adding
[self performSelector:#selector(showLyrics) withObject:nil afterDelay:20];
but if user restart the song then the this selector should not get performed. So I just want to
know how I can cancel that. Because after 20 second it will get invoked but I don't want that, and reschedule
[self performSelector:#selector(showLyrics) withObject:nil afterDelay:20];
I'v so many
[self performSelector:#selector(showLyrics) withObject:nil afterDelay:2];
I want to cancel all those, which I've scheduled before.
[[NSRunLoop currentRunLoop] cancelPerformSelector:#selector(showLyrics)
target:self
argument:nil];
You can use NSTimer instead of performSelector:withObject:afterDelay:
NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:20 target:self selector:#selector(showLyrics) userInfo:nil repeats:NO];
To cancel:
[timer invalidate];
But you may want to invalidate before you start each time or keep timers in an array and iterate through them to cancel all of them.
Use an NSTimer and save a reference to it instead of performSelector. Afaik performSelector can't be cancelled. Edit: Apparently it can be cancelled, see omz's answer...
self.showLyricsTimer = [NSTimer scheduledTimerWithTimeInterval:20.0
target:self
selector:#selector(showLyrics)
userInfo:nil
repeats:NO];
To cancel the timer you use:
[self.showLyricsTimer invalidate];
But be careful to also invalidate the timer when your view disappears f.e. in the viewWillDisappear callback, since NSTimer retains it's target.
Cancels perform requests previously registered with performSelector:withObject:afterDelay:
+ (void)cancelPreviousPerformRequestsWithTarget:(id)aTarget selector:(SEL)aSelector object:(id)anArgument
It seems this is what you are looking for or what you were looking for~~

NSTimer and UITabBar

I have three tabbar items in one View. There is one NSTimer Schedule on Start Button Action in first tab item.When i press another tab item and come to the recent tab item(i.e tab item with NSTimer), it increases the seconds with 2. If i do the same for second time it increases the second with 3 and so on.I want seconds to be increased by 1.
This is the code i am using to schedule NSTimer
timer = [NSTimer scheduledTimerWithTimeInterval:1.0f target:self selector:#selector(timerTick:) userInfo:nil repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];
I have tried to solve it using [timer invalidate]; but it is not giving me a proper result.
Thanks in advance.
in the - (void)viewWillAppear:(BOOL)animated
method make timer invalidate by [timer invalidate];
and again start it by :
timer = [NSTimer scheduledTimerWithTimeInterval:1.0f target:self selector:#selector(timerTick:) userInfo:nil repeats:YES];

How update a label periodically on iOS (every second)? [duplicate]

This question already has answers here:
NSTimer not firing when runloop is blocked
(2 answers)
Closed 8 years ago.
I use a NSTimer which fires every second and updates a label which displays the remaining time until an event.
I works fine so far. The problem is while I am scrolling the TableView my Label does not update, because the MainThread is blocked by the touch/scroll event.
I thought about creating a second thread for the Timer but I couldn't update the label from a background thread anyways. I had to queue it with performSelector... on the MainThread where it would stuck like before.
Is there any way to update the label while scrolling?
The problem is that a scheduledTimer will not get called while the main thread is tracking touches. You need to schedule the timer in the main run loop.
So instead of doing
[NSTimer scheduledTimerWithTimeInterval:1.0f target:self selector:#selector(updateLabel:) userInfo:nil repeats:YES];
use
NSTimer* timer = [NSTimer timerWithTimeInterval:1.0f target:self selector:#selector(updateLabel:) userInfo:nil repeats:YES];
[[NSRunLoop mainRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
try this:
self.timer = [NSTimer timerWithTimeInterval:1.0 target:self selector:#selector(updateClock) userInfo:nil repeats:YES];
[[NSRunLoop mainRunLoop] addTimer:self.timer forMode:NSRunLoopCommonModes];
You could also use GCD. So run
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0ul);
dispatch_async(queue, ^{
[NSTimer scheduledTimerWithTimeInterval:1.0f target:self selector:#selector(updateLabel:) userInfo:nil repeats:YES];
});
and now in your updateLabel method
- (void) updateLabel:(id) sender {
NSString *text = #"some text";
dispatch_sync(dispatch_get_main_queue(), ^{
label.text = text;
});
}
This will update the label in the main thread.

Stopping a 'performSelector afterDelay' before it fires

I start a repeating NSTimer after a 4 second delay using the following code:
- (void)viewDidLoad {
[self performSelector:#selector(startTimer) withObject:self afterDelay:4];
}
- (void)startTimer {
NSTimer *mytimer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:#selector(doSomething) userInfo:nil repeats:YES];
}
- (void)doSomething {
NSLog(#"What up!");
}
Problem is I may need to cancel startTimer from being called before the 4 seconds is up. Is there a way of doing this? I'd actually prefer to not use the performSelector in the first place (seems messy). If only NSTimer had something along the lines of this…
NSTimer *mytimer = [NSTimer
scheduledTimerWithTimeInterval:1.0
afterDelay:4.0 target:self
selector:#selector(doSomething)
userInfo:nil repeats:YES];
…then that would be perfect as I could just call the following:
[myTimer invalidate];
Any help or tips are much appreciated =)
P.S. I've found something called cancelPreviousPerformRequestsWithTarget in the NSObject class reference. Doesn't seem to be a method I can call from where this code runs however. If that's getting back on the right track your feedback is welcome!
Plz go through the SP post link
Stopping a performSelector: from being performed
[NSObject cancelPreviousPerformRequestsWithTarget:self
selector:#selector(sr)
object:nil];
The documentation for -performSelector:withObject:afterDelay: points you to the methods for canceling a queued perform request.
[myTimer invalidate] doesn't work?
Just keep a track of the object in your class, or in a centralized store for example.
If you do so, you could access your timer from everywhere you want, and invalidate it whenever it is needed
Use the NSTimer to fix issue.
self.autoTimer = [NSTimer timerWithTimeInterval:3.0 target:self
selector:#selector(connectionTimeout:) userInfo:nil repeats:NO];
[[NSRunLoop currentRunLoop] addTimer:autoTimer
forMode:NSDefaultRunLoopMode];
and call when you want to stop timer
[self.autoTimer invalidate];
self.autoTimer = nil;

Problem with NSTimer called inside my -parserDidEndDocument

I have a NSTimer object.
timer = [NSTimer scheduledTimerWithTimeInterval:0.02 target:self selector:#selector(timerAction) userInfo:nil repeats:YES];
[timer fire];
The method 'timerAction' repeats perfectly when call the timer from viewDidLoad method, but when I call the timer from parserDidEndDocument, the method 'timerAction' runs only once. Why is this?
you can try running the timer on the main thread.
Try this
create a new method that includes the code, to start timer, like :-
-(void)createTimer{
timer = [NSTimer scheduledTimerWithTimeInterval:0.02 target:self selector:#selector(timerAction) userInfo:nil repeats:YES];
[timer fire];
}
In Your parserDidEndDocument delegate, try this:
[self performSelectorOnMainThread:#selector(createTimer) withObject:[nil waitUntilDone:YES]