I have some labels on my UI which show something like "2s ago", "1h ago"...
Now I want to update them periodically without user interaction. How could I achieve that?
Use NSTimer. When the screen opens and you display the label, decide when you want it updated next. Say, you just updated the labels saying "2s ago", and the next thing that you want your users to see is "1m ago". You schedule a timer task to fire in 58 seconds, and provide a callback selector that updates the label, and schedules the next firing of the timer.
Since you are likely to update using irregular intervals (60 seconds for the first hour, then 60 minutes for the next 23 hours; one day after that) you should set your timer up without repeating.
Do not forget to cancel the timer when the view disappears.
With NSTimer
[NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:#selector(updateLabel: userInfo:nil repeats:YES];
And update your label in updateLabel:
Related
I was recording a sample of CMDeviceMotion acceleration data for 30 seconds through NSTimer. The thing is, when the app is in foreground, everything is fine. The interval is set to 0.01 and in 30 seconds, 3000 readings are stored. But when app goes to background, I get 300 only readings.
self.deviceMotionTimer = [NSTimer scheduledTimerWithTimeInterval:0.01 target:self selector:#selector(recordUpdates) userInfo:nil repeats:YES];
Is this how NSTimer works?
NSTimer will never work in background. When your app goes to background, NSTimer also freezes. In short, You can't use NSTimer, when you want to do some repetitive task in background.
If you still want to run something in background, then use the answer given by #Robert in this thread:
Scheduled NSTimer when app is in background?
I'm working on an App where I need to start a timer (using NSTimer) when the Watch is activated. With the Timer I asks some information to the iPhone (about every 1 seconds and maximum for 5 seconds). I'm using this to start the timer
timer = [NSTimer scheduledTimerWithTimeInterval:2 target:self selector:#selector(myfunction) userInfo:nil repeats:NO];
In the "myfunction" function, I restart the timer for the next time.
- (void) myfunction
{
//Here I update a label text
// [...]
[timer invalidate];
timer = nil;
counter++;
if(counter<5)
{
timer = [NSTimer scheduledTimerWithTimeInterval:2 target:self selector:#selector(myfunction) userInfo:nil repeats:NO];
}
}
My problem is that in the simulator all works fine but in a real watch (Watch-OS2 GM) the timer sometimes doesn't start or sometimes it starts but only for one time and after seems freeze! I see this because i Update a label in the watch at every elapsed period that shows a counter and I'm sure all is initialized in the "will activate" function. I don't understand why. Someone with the same issue?
From documentation
Use your interface controller’s init and awakeWithContext: methods to
load any required data, set the values for any interface objects, and
prepare your interface to be displayed. Do not use the willActivate to
initialize your interface controller. The willActivate method is
called shortly before your interface is displayed onscreen, so you
should use that method only to make last-minute changes. For example,
you might also use that method to start animations or start other
tasks that should only happen while your interface is onscreen.
So, what method did you use to instantiate timer?
make sure you use willActivate method and also use the didDeactivate method to clean up your interface and put it into a quiescent state. For example, use this method to invalidate timers and stop animations.
Hope this helps
My problem is solved with Watch OS 2.1. The problem was related to a quickly movement of the wrist: with the latest update of WatchOS all the timer are correctly restore after a quick move and all works fine
I have an NSTimer that should be running all the time the app is active. It is intended to show a countdown that depends on certain user's actions. I fire this timer this way:
self.timer = [NSTimer scheduledTimerWithTimeInterval:1
target:self
selector:#selector(notifyTimerTick:)
userInfo:nil
repeats:YES];
Then, in the method notifyTimerTick:, I update the countdown label I show to users and, if the countdown is over, I invalidate the timer, I look for a new countdown, and I fire the timer again.
I'm not having troubles being the UI blocked doing this way, but on the other hand, I've found that sometimes the notifyTimerTick: selector call is significantly delayed: I have a view that takes a couple of seconds to be completely loaded, and I've seen that timer's selector is not called until the corresponding view controller's viewDidLoad delegate method is called.
I've read several posts dealing with timers blocking the UI, but I'm not sure how to deal with a timer getting blocked by the UI... what the best way to handle this should be?
Thanks in advance
You need to use a different run loop mode.
When you use the ScheduledtimerWithTimeInterval class method, the timer is scheduled on the current run loop.
Instead do something like this:
NSTimer *labelTick = [NSTimer timerWithTimeInterval:1 target:self selector:#selector(updateTime:) userInfo:nil repeats:YES];
[[NSRunLoop mainRunLoop] addTimer:labelTick forMode:NSRunLoopCommonModes];
I'm developing for ios and I'm in the making of a UITextField containing UIDatePicker as its inputView.
My problem is as follows: I have set the minimum time to now --> [NSDate date] and that's because I don't want the user to enter a time that is before now.
This works fine, but if the user stays in the ViewController for when a time passes in the phone, I get a wrong time (1 minute before now).
Is there a way to listen to the hardware clock, and sync with the phone's clock? like listen to the iPhone's (hopefully) notification for changing the minute?I don't want the user to see wrong time, I want the text field to change if the time passes.
I thought of an implementation and I'd like to hear another perspective since mine is not easy at all...
My implementation:
1. run a delayed GCD async for the seconds left in the current minute, and when that reaches, listen for every 60 seconds... but this doesn't seems like the right idea, since if the user leaves the ViewController I might get a leak or user puts the app in the background, it'll be frozen and continue from the wrong place (According to GCD documentation).
Any suggestion will be very appreciated.
Rather than using GDC, take a similar approach but with NSTimer. invalidate the timer when the view is hidden (viewDidDisappear:) and observe app notifications for going to the background and returning to the foreground to reset your timer.
You need to calculate the end of the next minute, this becomes the first fire date (fireDate). Once you have that:
NSTimer *timer = [[NSTimer alloc] initWithFireDate:fireDate interval:60 target:self selector:#selectior(...) userInfo:nil repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:timer forMode: NSDefaultRunLoopMode];
Configure your date picker behind a getter or a method of it's own and do something like this, in the UITextField delegate
-(BOOL)textFieldShouldBeginEditing:(UITextField *)textField
{
UIDatePicker *datePicker = self.datePicker;
datePicker.minimumDate = [NSDate date];
textField.inputView = datePicker;
return YES;
}
How can I prevent a NSTimer from being delayed by the user scrolling a table?
I found the answer:
I had a timer that repeated about 8 or 9 times with intervals of 0.4 to 0.8 seconds. I don't need much precision, but if the user scrolls a table the timer would stop working until the table finished scrolling (this could be a few seconds wait!). I thought I needed background threads, but timers on background threads were somewhat complicated to implement.
The answer to my problem was very very simple and easy. I just need to add a line after invoking the timer:
//////////// start the timer
self.playingTimer = [NSTimer scheduledTimerWithTimeInterval:tempo target:self selector:#selector(playSoundFromArray:) userInfo:nil repeats:YES];
//////////// the magic line:
[[NSRunLoop currentRunLoop] addTimer:self.playingTimer forMode:UITrackingRunLoopMode];
Now I can scroll the table as much as I want and my timers work OK!!!
Now I need to study a little more NSRunLoop...
You should add your timer for NSDefaultRunLoopMode mode. UITrackingRunLoopMode is used by tracking UI actions(in your case scrolling).