I have UIView with button Start, UILabel with my NSTimer time and UITableView with time record.
Problem is when I scroll my tableview - NSTimer freeze, why I can do to they work async?
My code:
- (IBAction)startTimer {
_startButton.hidden = YES;
_stopButton.hidden = NO;
isRun = YES;
UIBackgroundTaskIdentifier bgTask = UIBackgroundTaskInvalid;
UIApplication *app = [UIApplication sharedApplication];
bgTask = [app beginBackgroundTaskWithExpirationHandler:^{
[app endBackgroundTask:bgTask];
}];
myTimer = [NSTimer scheduledTimerWithTimeInterval:0.01f
target:self
selector:#selector(tick)
userInfo:nil
repeats:YES];
}
- (void)tick {
_timeSot++;
sot++;
/*some code*/
myTime = [NSString stringWithFormat:#"%#:%#:%#", minStr, secStr, sotStr];
[_timeLabel setText:myTime];
}
Add myTimer to NSRunLoopCommonModes:
myTimer = [NSTimer scheduledTimerWithTimeInterval:0.01f
target:self
selector:#selector(tick)
userInfo:nil
repeats:YES];
[[NSRunLoop mainRunLoop] addTimer:myTimer forMode:NSRunLoopCommonModes];
Related
I call a method from background thread as follows.
- (void)applicationDidEnterBackground:(UIApplication *)application
{
UIApplication *app = [UIApplication sharedApplication];
//create new uiBackgroundTask
__block UIBackgroundTaskIdentifier bgTask = [app beginBackgroundTaskWithExpirationHandler:^{
[app endBackgroundTask:bgTask];
bgTask = UIBackgroundTaskInvalid;
}];
//and create new timer with async call:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
//run function methodRunAfterBackground
NSTimer* t = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:#selector(methodRunAfterBackground) userInfo:nil repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:t forMode:NSDefaultRunLoopMode];
[[NSRunLoop currentRunLoop] run];
});
}
After 15 seconds, I have to make a call to a number. I use [[UIApplication sharedApplication] openURL:[NSURL URLWithString:[NSString stringWithFormat:#"tel://%#",_number]]];but unable to make a call while application is in background state. Can we make a call from background state? If yes, how?
I create a NSTimer:
timer = [NSTimer scheduledTimerWithTimeInterval:0.1f target:self selector:#selector(sendImage) userInfo:nil repeats:YES];
In the method sendImage, I create another NSTimer timer2. The code following:
- (void)sendImage
{
for(int i = 0;i < 50 ; i++)
{
NSMutableDictionary *dict = [[NSMutableDictionary alloc] init];
[dict setObject:socket forKey:#"socket"];//parameter which deliver to the method`sendPieceOfImage`
[dict setObject:pData forKey:#"pData"];
timer2 = [NSTimer scheduledTimerWithTimeInterval:0.002f target:self selector:#selector(sendPieceOfImage:) userInfo:dict repeats:NO];
}
}
But it didn't work. I want to know can NSTimer apply mechanically? If it's infeasible, what can I do in the sendImage. I hope every cycle in the for() can run with interval.
The answer to your question is YES. That's possible to schedule one timer in the firing callback of another. Consider this:
dispatch_async(dispatch_get_main_queue(), ^{
[NSTimer scheduledTimerWithTimeInterval:1 target:self selector:#selector(timer1Event:) userInfo:nil repeats:YES];
});
- (void)timer1Event:(NSTimer*)timer {
NSLog(#"timer1Event");
[NSTimer scheduledTimerWithTimeInterval:1 target:self selector:#selector(timer2Event:) userInfo:nil repeats:NO];
}
- (void)timer2Event:(NSTimer*)timer {
NSLog(#"timer2Event");
}
Even the problem is not fully described, I'll try to guess that the rootcause is in how very first timer is scheduled.
You need thread with aproprietaly setted up RunLoop. Main thread is suitable.
dispatch_async(dispatch_get_main_queue(), ^{
self.timer = [NSTimer scheduledTimerWithTimeInterval:0.1f target:self selector:#selector(sendImage) userInfo:nil repeats:YES];
});
If you don't want to load the main thread, consider this:
You need your own thread:
self.workerThread = [[NSThread alloc] initWithTarget:self selector:#selector(startRunLoop) object:nil];
[self.workerThread start];
Which starts the runloop:
- (void)startRunLoop
{
NSRunLoop *runLoop = [NSRunLoop currentRunLoop];
[runLoop addPort:[NSMachPort port] forMode:NSDefaultRunLoopMode];
do {
#autoreleasepool
{
[runLoop runUntilDate:[NSDate dateWithTimeIntervalSinceNow:2.0]];
}
} while (![NSThread currentThread].isCancelled);
}
Now, for starting the timer on the worker thread you need this:
- (void)startTimer
{
self.timer = [NSTimer scheduledTimerWithTimeInterval:10 target:self selector:#selector(timerEvent:) userInfo:nil repeats:YES];
}
How to call:
[self performSelector:#selector(startTimer) onThread:self.workerThread withObject:nil waitUntilDone:NO];
Hope it helps.
I'm making a game and would like to use a timer to countdown an event, just like what's seen on Bejeweled. I know that I've to put NSTimer in a NSRunLoop to make it work, since NSTimer is inaccurate. Have tried the following but it still don't work. Please help!
#import ...
NSTimer *_gameTimer;
int secondsLeft;
//some code
//called countdownTimer using [self countdownTimer];
- (void)countdownTimer
{
_gameTimer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:#selector(updateTime:) userInfo:nil repeats:YES];
NSRunLoop *gameRun = [NSRunLoop currentRunLoop];
[gameRun addTimer:_gameTimer forMode:NSDefaultRunLoopMode];
}
- (void)updateTime:(NSTimer *)timer
{
if (secondsLeft>0 && !_gameOver) {
_timerLabel.text = [NSString stringWithFormat:#"Time left: %ds", secondsLeft];
secondsLeft--;
} else if (secondsLeft==0 && !_gameOver) {
// Invalidate timer
[timer invalidate];
[self timerExpire];
}
}
- (void)timerExpire
{
// Gameover
[self gameOver];
[_gameTimer invalidate];
_gameTimer = nil;
}
NSTimer needs to be a local variable so there can only be one instance of the object running through the loop. Here's the code.
- (void)countdownTimer
{
NSTimer *_gameTimer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:#selector(updateTime:) userInfo:nil repeats:YES];
NSRunLoop *gameRun = [NSRunLoop currentRunLoop];
[gameRun addTimer:_gameTimer forMode:NSDefaultRunLoopMode];
if (secondsLeft==0 || _gameOver) {
[_gameTimer invalidate];
_gameTimer = nil;
}
}
- (void)updateTime:(NSTimer *)timer
{
if (secondsLeft>0 && !_gameOver) {
_timerLabel.text = [NSString stringWithFormat:#"Time left: %ds", secondsLeft];
secondsLeft--;
} else if (secondsLeft==0 || _gameOver) {
// Invalidate timer
[timer invalidate];
[self timerExpire];
}
- (void)timerExpire
{
// Gameover
[self gameOver];
}
timer = [NSTimer bk_timerWithTimeInterval:60 block:^(NSTimer *timer) { ....} repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];
+ (id)bk_timerWithTimeInterval:(NSTimeInterval)inTimeInterval block:(void (^)(NSTimer *timer))block repeats:(BOOL)inRepeats
{
NSParameterAssert(block != nil);
return [self timerWithTimeInterval:inTimeInterval target:self selector:#selector(bk_executeBlockFromTimer:) userInfo:[block copy] repeats:inRepeats];
}
+ (void)bk_executeBlockFromTimer:(NSTimer *)aTimer {
void (^block)(NSTimer *) = [aTimer userInfo];
if (block) block(aTimer);
}
If I fold and unfold application after a few minutes, almost always the timer is firing immediately and sometimes it fire with delay.
Why the timer does not always work immediately?
try to add this in "didFinishLaunchingWithOptions" in your Appdelegate
__block UIBackgroundTaskIdentifier locationUpdater =[[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{
[[UIApplication sharedApplication] endBackgroundTask:locationUpdater];
locationUpdater=UIBackgroundTaskInvalid;
} ];
When i upload to app store, i have this problems
non-public classes: NSCalendarDate, NSTask
But i only use NSTimer to make my own stopwatch
This i my source code
self.timer = [NSTimer scheduledTimerWithTimeInterval:1
target:self
selector:#selector(stopWatch)
userInfo:nil
repeats:YES] ;
My "Pause" method
pauseStart = [NSDate dateWithTimeIntervalSinceNow:0];
previousFireDate = [self.timer fireDate];
[self.timer setFireDate:[NSDate distantFuture]];
My "Resume" method
float pauseTime = -1*[pauseStart timeIntervalSinceNow];
[self.timer setFireDate:[NSDate dateWithTimeInterval:pauseTime sinceDate:previousFireDate]];
i also have NSTimer in my AppDelegate
AppDelegate* appDelegate = (AppDelegate*)[[UIApplication sharedApplication] delegate];
appDelegate.delayTimer = [NSTimer scheduledTimerWithTimeInterval:1
target:self
selector:#selector(stopWatch)
userInfo:nil
repeats:YES];
I run in ios6
Could you please show me the root cause?
Thanks you so much
I must to remove mogenerator (https://github.com/rentzsch/mogenerator) library to resolve this problems
I don't know why this project use those private APIs