i want to have a background thread in my app that changes an image every 5 seconds for as long as the app is being run. Can someone point me in the direction of how this works? I am new to threads.
If you are using UIImageView and want an animated change between images you do not even need a timer. UIImageView can animate between images all by itself:
NSArray *images = [NSArray arrayWithObjects: [UIImage imageNamed: #"foo.png"],
[UIImage imageNamed: #"bar.png"],
nil];
yourImageView.animationImages = images;
yourImageView.animationDuration = 5.0s;
[yourImageView startAnimating];
The details are documented in the UIImageView docs.
An NSTimer will probably do what you are looking to do, however, calls from NSTimer will normally block the main thread, if the process is involved, or you need to getting something from the internets to switch out the picture, you will need to create a new thread to do this.
For information on threading, I highly recommend the CS193P Lecture on performance, They go into detail on NSThread, NSOperations, ect.
Also, from Apple, Threading programing Guide.
You can use an NSTimer for this. No need to spawn off e new thread:
[NSTimer scheduledTimerWithTimeInterval:5.0s target:self selector:#selector(updateImage) userInfo:nil repeats:YES];
You do not need a thread for that. You can do it with a timer which is simpler than a thread. See the timer programming guide.
Related
i would like to explain my issue from the beginning. I am creating a app with UIWebView. Also I am capturing the screen at the moment user using the app and create a video using that image array.
I am using NSTimer to get screen shots of the screen. I worked well but i got one issue. That was when user start scrolling the web page or when user hold his touch on the screen NSTimer got pause and after he releases the touch NSTimer continue.
Here is my NSTimer used to get Screen shots of the screen.
assetWriterTimer = [NSTimer scheduledTimerWithTimeInterval:1.0f/24.0f
target:self
selector:#selector (writeSample)
userInfo:nil
repeats:YES];
I googled this issue and most of the people got same issue as i got. So they found a answer for that and that is using NSRunLoop. They said it stops pausing the NSTimer.
So I used NSRunLoop and add NSTimer into the NSRunLoop and here is the code after I did that.
NSRunLoop* runLoop = [NSRunLoop currentRunLoop];
assetWriterTimer = [NSTimer scheduledTimerWithTimeInterval:1.0f/24.0f
target:self
selector:#selector (writeSample)
userInfo:nil
repeats:YES];
[runLoop addTimer:assetWriterTimer forMode:NSRunLoopCommonModes];
[runLoop run];
This code worked well and it didnt stops pausing NSTimer. But after i used NSRunLoop My app got really really slow. Also it took lots of time to load the webpage into the WebView and scrolling also very slow.
So I started to search in google again and i found the issue. That was all those things are doing in main thread and iphone cant process every thing in same time. So people suggest to use NSThread to over come the problem. So what i did was create a NSThread and attach the process to the custom thread.
Here is my code after i apply NSThread to it.
[NSThread detachNewThreadSelector:#selector(launchTimer) toTarget:self withObject:nil];
-(void)launchTimer{
NSRunLoop* runLoop = [NSRunLoop currentRunLoop];
assetWriterTimer = [NSTimer scheduledTimerWithTimeInterval:1.0f/24.0f
target:self
selector:#selector (writeSample)
userInfo:nil
repeats:YES];
[runLoop addTimer:assetWriterTimer forMode:NSRunLoopCommonModes];
[runLoop run];
}
-(void) writeSample{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
//did some coding here to take screen shot
[pool release];
}
This solves the slowing the app issue forever and my app is running again very smoothly but it brings up serious problem now. That is happening because of using threads i guess. My custom thread and main thread getting conflict now and causes the app to crash. I searched the google for the reason. I found some questions related to this kind of problem and some people answered those and said Any UI changes need to be done in Main thread. Also UIWebView WebThread must run in main thread.
Those are some error reports i am getting,
Those errors are like some thread conflicting. But I have no idea how to solve this problem. I used my code to some native app that is not having any Web Views or Maps. So it works well with out crashing. But when i use this to app that is having WebView or Map its crashing some times and give me this errors.
I am in soo much trouble in here and If any one can give any idea or sample code to handle threads , handle thread priorities , or using NSTimer and NSRunLoop with out using NSThreads , or Overcome the app slowness without using NSThread , Any answer would be very very very helpful and I am looking forward to hear from you soon.
Thanks.
My guess is the code that you wrote to capture screen shots is causing the problem. A simple test would be to comment out all the code in the writeSample method and see if the app still crashes. I say this because you are right that all UI related tasks should work in the Main Thread at all times.
I would suggest to use a separate thread for timer so that your timer isn't paused and then from the writeSample method force the screen capture code to run on the main thread.
Hope this helps
First off:
As soon as you are using timers, you are working with run loops — as you can easily see from the reference:
Timers work in conjunction with run loops.
(That’s the first sentence of the second paragraph)
With that out of the way, to your syptoms:
Blocking a thread by capturing images, causes it to stutter.
This shouldn’t surprise you!
Moving the work off of that thread, releases the strain on the first one.
Shocking, I know!
Well, given that you didn’t show us any of the code that causes your synchronization issues, (read: crashes) this sarcastic answer is all the help I can offer you.
That and the tip to read and internalize the document I linked to above…
Edit
I just forgot to mention, this:
NSRunLoop* runLoop = [NSRunLoop currentRunLoop];
assetWriterTimer = [NSTimer scheduledTimerWithTimeInterval:blablabla];
[runLoop addTimer:assetWriterTimer forMode:NSRunLoopCommonModes];
[runLoop run];
doesn’t make much sense because…
-scheduledTimerWithTimeInterval:target:selector:userInfo:repeats:
Creates and returns a new NSTimer object and schedules it on the current run loop in the default mode.
(Which is explained in the magnificent documentation, as well.)
In essence: when you’re on your own private thread, the default mode is sufficient. You have to make sure that the run loop is actually running, though.
This is actually accomplished by the last line of that code snippet, but it effectively causes your method to not return until the timer is cancelled…
I am starting an animation via a button press. Here is the code for the button:
-(IBAction)startAnimation:(id)sender{
stick.animationImages = [NSArray arrayWithObjects:
[UIImage imageNamed:#"photo 1.JPG"],
[UIImage imageNamed:#"photo 2.JPG"],
[UIImage imageNamed:#"photo 3.JPG"],
[UIImage imageNamed:#"photo 4.JPG"],
[UIImage imageNamed:#"photo 5.JPG"],nil];
[stick setAnimationRepeatCount:200];
stick.animationDuration = 0.5;
[stick startAnimating];
}
and when the animation is done, i want to have a button appear, to play another animation on the screen. How can i test or see when my animation is done playing? Thanks in advance!
You haven't told us what stick is, but it looks like a UIImageView. You can only call isAnimating to check whether the animation is still running but you don't get any notification and there's no delegate either. You can calculate the stop time (200 * 0.5) and thus set up a timer (add a little safety margin). This won't be 100% correct but it might be "good enough".
If you called your animation using blocks, you could use a completion block.
Here is a tutorial you can refer to which shows a couple different ways to call animation routines on iOS. This tutorial also shows what you can do for completion if you don't feel like using blocks (namely UIView's setAnimationDidStopSelector method).
when my app starts it loads 60 images at a time in UIImageView
and it also loads a background music.
in simulator it works fine but in IPAD it crashes..
-(void)viewDidLoad
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
//img.animationImages = [[NSArray arrayWithObjects:
MyImages = [NSArray arrayWithObjects:
//[UIImage imageNamed: #"BookOpeningB001.jpg"],...... B099,nil];
//[NSTimer scheduledTimerWithTimeInterval: 8.0 target:self selector:#selector(onTimer) userInfo:nil repeats:NO];
img.animationImages = MyImages;
img.animationDuration = 8.0; // seconds
img.animationRepeatCount = 1; // 0 = loops forever
[img startAnimating];
[self.view addSubview:img];
[img release];
[pool release];
//[img performSelector:#selector(displayImage:) ];
//[self performSelector:#selector(displayImage:) withObject:nil afterDelay:10.0];
[self performSelector: #selector(displayImage) withObject: nil afterDelay: 8.0];
}
-(void)displayImage {
SelectOption *NextView = [[SelectOption alloc] initWithNibName:nil bundle:nil];
[self presentModalViewController:NextView animated:NO];
[NextView release];
}
Am i doing anything wrong ?
Is there any other alternative to load the local images faster in
UIImageView !
I was doing something similar. Even if you quit all of the other apps on your iPad and manage to get it to run it will crash on you randomly.
I fixed this by preload very small (like 5 or 6 k) gif images. Then when my user entered an area where the big image needed to be seen I loaded the full res pic as a jpg and released the object as soon as they were done with it.
Now my app no longer has an issue on the simulator or the device and I do not have to have the user close everything.
If something crashes on the device but not on the simulator 9/10 times is your app has received a few memory warnings and forced to quit.
This Theory can be tested easily in two ways. First is to Log something to console in the appDelegates Memory Warning event method and secondly, when you run the app in the simulator, open the activity monitor and view the memory usage of the app by name.
The golden rule is that you should use as low a res of an image that you can get away with.
Rohit, is right in saying that pre-loading smaller images is one way to solving this problem provided that your not leaking, but this solution is only suitable for certain apps.
Lazyily loading media is generally how you should do this. There is no reason why you should have to load any more than 3 full res (full screen) images at any one time in an iPad app if you implement a decent memory efficient design.
In the case where you are doing something like a slide show for exmaple, you should be only be loading the focused image + 1 image each side into memory, destroying out of scope images as you go. Using a UIScrollView makes this easy with events that fire when scrolling has started, occurring and ended.
Speed improvements can be made by using CATitledLayer's, mutithreading any image tasks you can (GCD and blocks) (getting UIKit contexts are now thread safe too).
Good luck there are plenty of examples of all of these things accessible via the apple documentation or a quick search.
I have problems updating the image of an UIImageView from within a method that is called from a NSTimer.
It works within the viewDidLoad method where I do something like this
[imageView setImage:[self getNextImage]];
later on I do
NSTimer* readTimer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:#selector(readSource:) userInfo:nil repeats:NO];
[readTimer fire];
that works and the readSource method gets called.
There I put this code:
UIImageView* tempImageView = (UIImageView*)[containerView viewWithTag:nextDefragSourcePosition ];
That returns exactly the imageView I want to have -
but the following line does not change the image of the UIImageView
[tempImageView setImage:dataBeingReadImage];
Any ideas ? Thanks in advance.
Heiko
You are aware that you are updating the GUI from a different thread than where the UIImageView is created? You can execute selectors on the main thread to work around this problem.
Just take a look at the performSelectorOnMainThread method. Just call that from the method that gets called when your NSTimer triggers (readSource:) and supply it with the image you want to display. Then just update the UIImageView from THAT selector (not the readSource one) and it SHOULD work.
There really isn't that much code for me to go on.
I am new to iPhone development. I used [NSTimer scheduledTimerWithTimeInterval:0.01] for game loop. The game consists drawscreen function in which I use CGContextClipToRect to clip the large images for animation.
But the speed 0.01 seconds is working in simulator only not on device. How can I overcome this problem?
The timer code is in view controller as
(void)viewDidLoad {
GameView *main = [[GameView alloc]
initWithFrame:[[UIScreen mainScreen] applicationFrame]];
main.backgroundColor = [UIColor blackColor];
self.view = main;
[main release];
self.tim = [NSTimer scheduledTimerWithTimeInterval: 0.01
target: self selector: #selector (gameloop:) userInfo: nil repeats: YES];
[super viewDidLoad];
}
Anyone can help me?
NSTimer is the wrong tool for this job. It's not meant to be a real-time timer. It has no guarantees on when it will fire, and you can miss frames easily.
There are a lot of good recommendations for how to develop this kind of program on this thread. Note particularly the references to Apple's sample code.
As U62 said, 100fps is not a realistic expectation, try running it at 30, maybe 60 updates a second and see how it runs. Also if your writing a game that need to run at a high frame rate you should look into writing it in OpenGL.
One option may be to process logic every tick, but only draw to the screen every few ticks.
i mean ,for example when i animate frames ,frames are shown in correct speed in simulator.
but in device it shows very slow(FPS)in other words a boy runs fastly in simulator.but in
device he walks.