Problem: I need to fire values to an object 10 times per second. I must be able to start firing and stop firing.
I've discovered that the delegate of an UIScrollView gets notified in about the same time intervals, no matter how fast the scrolling is. You can easily see that if you NSLog the deltas from the offset changes. There must be a way to start firing a message 10 times per second until something says "stop". But how?
I would look into the NSTimer class. That should allow you to set up a timer with an arbitrary time span (and stop it when desired).
I'm not much of a Cocoa developer, but I'm pretty sure you want NSTimer.
Look into [NSTimer timerWithTimeInterval:invocation:repeats:], [NSTimer invalidate], and NSRunLoop.
Related
I have a simple UIView that draws itself using drawRect:. In order for the view to animate, the drawRect method needs to be called every say 0.05 seconds, so I use a repeating timer:
timer = [NSTimer scheduledTimerWithTimeInterval:0.05 target:self
selector:#selector(setNeedsDisplay) userInfo:nil repeats:YES];
I don't know too much about run loops, and threads, and all that system stuff, so I want to know if this is the correct way to run an animation? This timer repeats itself endlessly. Is this something I should be worried about? Will this block the main thread? What can I do to minimize overall impact on performance?
The approach is not bad, but there are other ways to do it.
The timer's target method must take the timer as an argument, so instead of setting it for setNeedsDisplay directly, you should set up a method like this:
- (void)animationTimerDidFire:(NSTimer *)timer
{
[myView setNeedsDisplay];
}
If your view will always be visible, then you can just set-and-forget the timer. On the other hand, if it may go away because you switch to a different view, you will need to invalidate and recreate the timer as needed.
The main thread of your app uses a run loop and calls out to various methods in response to events, like user taps, system notifications (e.g., memory warning), and I/O arriving. If anything the run loop calls takes a long time to return, it will hold up everything in the queue. When you set up a timer, it is added to a list and that the run loop checks it each time through; if one of the timers is ready, it calls your method.
The end result is that timers are not exact: they might not fire as often as you like, might be called late, etc. Again, if your app is pretty simple, the main run loop won't be very busy and so a timer will probably be good enough. Just make sure your animation is based on actual time elapsed between calls, rather than assuming each call happens exactly 0.05 seconds apart.
Alternatives
If your animation simply involves flipping through some static images, UIImageView has some support for this.
If creating each frame of animation takes a noticeable amount of time (and you don't want to block the main thread), you could use a background queue to draw into an image (see CGBitmapContextCreate and CGBitmapContextCreateImage), then signal the main thread when a new image is ready to display. Anything that touches a view MUST happen on the main thread, but you can do the drawing on the background.
You also might want to read up on CALayer in the QuartzCore framework. This is what the UIView objects actually manipulate to draw on the screen. You may find that, instead of drawing, you can get the effects you want by manipulating some CALayer objects and letting Core Animation do the heavy lifting (e.g., you change the color from red to blue, Core Animation takes care of fading from one to the other).
Well, if you are using an overridden or custom method, you should use recursion and a completion block for calling the animation. I find it works much better than a timer, since timers aren't always exact and can cause some animation issues if you have the animations timed for cycling.
EDIT: I don't know much about using drawRect: and calling [self setNeedsDisplay] to update it, so I can't help you in that regard. Sorry.
I'm working on a multiview app. One of the views is a table view. Each cell has a stopwatch. I'm planning to use NSTimer for the stopwatches. Do I need to implement multithreading for the timers to work properly even when the user switches the view and then comes back later?
I did my research but most of the tutorials cover one NSTimer in a single view. I want to make sure the user can do other things while the timers are running, like use the interface, navigation, etc. In another post Placing an NSTimer in a separate thread someone said you need a different runloop for the timer. Would I need one runloop for each timer in my case? Is it advisable? Any performance drawbacks?
Thanks a lot!
One run loop should be just fine. Your interface will still be responsive.
Keep in mind that timers are never guaranteed to be accurate. They are affected by how much other stuff is on the same loop. Its ok to use the timer to update the display but not to actually measure time. Set an NSDate when you start a stop watch then compare the current date with that start date each time your display timer updates the display.
Since you should only use the NSTimer to update the display, could you just use one generic display update timer that updates all running stopwatches, instead of having one for each stopwatch?
I have created an animated game app, In which I am using NSTimer to move an image after a particular interval of time.
timer = [NSTimer scheduledTimerWithTImerInterval:0.2 target:self selector:#selector(MoveVirus) userinfo:nil repeats:YES];
this timer calls up the function MoveVirus, MoverVirus moves virus (an Image on screen).
It works fine in the beginning but the speed of motion automatically increases.
The increased speed of motion destroys every further logic.
I don't know what is the problem with it?
Please help to solve this problem.
NSTimer isn't necessarily meant for this sort of use...From the docs on NSTimer:
A timer is not a real-time mechanism; it fires only when one of the
run loop modes to which the timer has been added is running and able
to check if the timer’s firing time has passed. Because of the various
input sources a typical run loop manages, the effective resolution of
the time interval for a timer is limited to on the order of 50-100
milliseconds. If a timer’s firing time occurs while the run loop is in
a mode that is not monitoring the timer or during a long callout, the
timer does not fire until the next time the run loop checks the timer.
Therefore, the actual time at which the timer fires potentially can be
a significant period of time after the scheduled firing time.
A better approach, if you are moving UIImages that are contained in a UIImageView would be to use the class animation methods on UIView. You can still get the same result of moving the image after a particular time if you use the animation method that contains a delay. The method is:
+ (void)animateWithDuration:(NSTimeInterval)duration delay:(NSTimeInterval)delay options:(UIViewAnimationOptions)options animations:(void (^)(void))animations completion:(void (^)(BOOL finished))completion
Using this method, you can specify the time of the animation (how long it takes to animate the move of your UIImages), how long to wait before starting this animation, a set on animation options, a block of animation code, and a block that executes when the animation is complete.
I have been tasked to write an app that allows a user to schedule emails to be sent out in future.
The user selects a date time from a date picker, composes the message and recipient and then schedules the event. When the date/time occurs the message is sent out.
Can someone guide me to how to get about scheduling lets say a text message. I know how to send a text message. Just was not sure on the scheduling aspect of things.
Any pointers will be much appreciated.
The first response will technically allow you to establish a timer that will fire every 2.5 seconds, however the original poster asked for a solution that would fire at a specific time. For that you need to use the following method of NSTimer:
- (id)initWithFireDate:(NSDate *)date interval:(NSTimeInterval)seconds target:(id)target selector:(SEL)aSelector userInfo:(id)userInfo repeats:(BOOL)repeats
The first argument is an NSDate indicating when the timer should fire.
The original poster did not specify, but if this is an iOS app then it is important to understand that timers scheduled to fire at a distant date/time will not fire if your app is not the foreground app. In fact there is no way to schedule such an event to occur when your app is in the background on iOS, so you must take that into account.
Here's a snippet of code which sets a one use timer to call self's imageSavedLabelOff: selector with itself (the timer) as the object parameter to the method. The timer schedules the call to be made in 2.5 seconds.
NSTimer *quickie = [NSTimer scheduledTimerWithTimeInterval:2.5 target:self selector:#selector(imageSavedLabelOff:) userInfo:nil repeats:NO];
You may have already found the answer by now but for future visiters like me I would like to suggest an answer- i.e. EventKit :
https://developer.apple.com/library/ios/documentation/DataManagement/Conceptual/EventKitProgGuide/ReadingAndWritingEvents.html
You can schedule/fetch events for any time and do your stuff accordingly. Hope this helps somebody.
You should be able to achieve this using NSRunLoop. Check out the Threading Programming Guide.
Apart from the use of NSTimer, you should be aware that sending of the E-Mail can fail for several reasons (no network available and others). Then you need to reschedule the request, maybe give up after 3 retries and notify the user about this.
You can use -
[self performSelector:#selector(myFunc:) withObject:nil afterDelay:5.0];
I schedule a timer with NSTimer's function in viewWillAppear as follows:
minutesTimer = nil;
minutesTimer = [NSTimer scheduledTimerWithTimeInterval:60 target:self selector:#selector(updateScrollViewItems) userInfo:NULL repeats:YES];
With this function call, I expect it to call the selector updateScrollViewItems every minute, but it does not, it update items faster than expected (around some seconds).
What is the cause of this unexpected behavior?
It might not be the direct cause of the issue you're seeing, but I can already spot one major error.
The fact that this timer is set up each time on viewWillAppear means that whenever your view appears, you're creating a new timer (and leaking the old one) which will fire 60 seconds after creation.
If your view disappears and reappears multiple times, you're going to have multiple timers all firing the same method at completely random intervals.
You need to manage the timer properly. If you want it to start when the view is first created and keep ticking/firing even when the view is not shown, then you need to create it during init, or viewDidLoad, and then be sure to stop it when you dealloc or viewDidUnload.
If you want your timer to only tick/fire when the view is the current view, then you need to ensure you're managing stopping and starting the timer appropriately on viewDidAppear and viewWillDisappear.
Also, as Williham Totland said in his answer, NSTimer shouldn't be relied upon for exact timing. This is also stated in the documentation:
A timer is not a real-time mechanism; it fires only when one of the run loop modes to which the timer has been added is running and able to check if the timer’s firing time has passed. Because of the various input sources a typical run loop manages, the effective resolution of the time interval for a timer is limited to on the order of 50-100 milliseconds. If a timer’s firing time occurs while the run loop is in a mode that is not monitoring the timer or during a long callout, the timer does not fire until the next time the run loop checks the timer. Therefore, the actual time at which the timer fires potentially can be a significant period of time after the scheduled firing time.
In this case, with a time span of 60 seconds, it shouldn't be a problem that the timer is not exact, I think the issues you're seeing are because the timer isn't being managed properly.
The iPhone is a lot of things; and a neat device, however; just like any other computer, a precision timepiece it is not. However, it seems as if the method in question takes an NSTimeInterval, which is a double. You might want to change that to 60.0.
If you want your timer to only
tick/fire when the view is the current
view, then you need to ensure you're
managing stopping and starting the
timer appropriately on viewDidAppear
and viewWillDisappear.
Thanks for this info, even though I'm not the OP of this thread I was looking for an answer for a similar issue. I was scratching my head for few days, searched all over the internet but I couldn't find the answer. Finally I found this thread. This is a valuable info for me.