In my project I am calling NSTimer 1000 times in a second. and with it I am allocating 5 UIImage objects per tap in some image view at same time with UIViewanimation. when I am doing that work the NSTimer freezes for some seconds or delaying. I know why it is happing because I am making more then 100-120 UIImageView objects in a seconds.The timer is dong fine when I am not calling UIAnimation. i Used [performSelectorInBackground:withObject:] to perform UIViewAnimation in the background.but in it the UIAnimation is not working .
Both NSTimer and UIViewAnimation is working on UI.so they are working on main thread. I can not make a different thread for them . what can I do to stop freezing of the NSTimer and calling UIViewAnimation in same time.These is because of the heavy computing. and I can not make sepererate thread for that what can I Do to workthat.
Thanks
That can be happening because of PNG images you are using for animation inside UIImageView.
Why not try using JPEG representation
UIImage *newImage=[UIImage imageWithData:UIImageJPEGRepresentation(myImage, 0.5)];
then use newImage as it will have less size than png one.
Related
There are a few similar questions out there on SO (links at end), but none of them has allowed me to fix my problem, so here goes:
I'm using OpenGL rendering to make an image tiling and caching library for use in a game project, and I want to hijack the physics of the UIScrollView to allow the user to navigate around the images (since it has nice bounce behaviour, might as well use it). So I have a UIScrollView which I'm using to get the rendering view for my textures, but there's a problem - moving around on the scroll view prevents the CADisplayLink from firing until the user has finished scrolling (which looks horrible). One temporary fix has been to use NSRunLoopCommonModes instead of the default run mode, but unfortunately this breaks some aspects of scroll view behaviour on certain phones I'm testing on (the 3GS and simulator seem to work fine, while the iPhone4 and the 3G don't).
Does anyone know how I could get around this clash between the CADisplayLink and the UIScrollView, or know how to fix the UIScrollView working in other run modes? Thanks in advance :)
Promised links to similar questions:
UIScrollView broken and halts scrolling with OpenGL rendering (related CADisplayLink, NSRunLoop)
Animation in OpenGL ES view freezes when UIScrollView is dragged on iPhone
It's possible that slow updates on the main thread triggered by the CADisplayLink are what's breaking UIScrollView's scrolling behavior here. Your OpenGL ES rendering might be taking long enough for each frame to throw off the timing of a UIScrollView when using NSRunLoopCommonModes for the CADisplayLink.
One way around this is to perform your OpenGL ES rendering actions on a background thread by using a Grand Central Dispatch serial queue. I did this in my recent update to Molecules (source code for which can be found at that link), and in testing with using NSRunLoopCommonModes on my CADisplayLink, I don't see any interruption of the native scrolling behavior of a table view that's onscreen at the same time as the rendering.
For this, you can create a GCD serial dispatch queue and use it for all of your rendering updates to a particular OpenGL ES context to avoid two actions writing to the context at the same time. Then, within your CADisplayLink callback you can use code like the following:
if (dispatch_semaphore_wait(frameRenderingSemaphore, DISPATCH_TIME_NOW) != 0)
{
return;
}
dispatch_async(openGLESContextQueue, ^{
[EAGLContext setCurrentContext:context];
// Render here
dispatch_semaphore_signal(frameRenderingSemaphore);
});
where frameRenderingSemaphore is created earlier as follows:
frameRenderingSemaphore = dispatch_semaphore_create(1);
This code will only add a new frame rendering action onto the queue if one isn't in the middle of executing. That way, the CADisplayLink can fire continuously, but it won't overload the queue with pending rendering actions if a frame takes longer than 1/60th of a second to process.
Again, I tried this on my iPad here and found no disruption to the scrolling action of a table view, just a little slowdown as the OpenGL ES rendering consumed GPU cycles.
My simple solution is to halve the rendering rate when the run loop is in tracking mode. All my UIScrollViews now work smoothly.
Here is the code fragment:
- (void) drawView: (CADisplayLink*) displayLink
{
if (displayLink != nil)
{
self.tickCounter++;
if(( [[ NSRunLoop currentRunLoop ] currentMode ] == UITrackingRunLoopMode ) && ( self.tickCounter & 1 ))
{
return;
}
/*** Rendering code goes here ***/
}
}
The answer at the following post works very well for me (it appears to be quite similar to Till's answer):
UIScrollView pauses NSTimer until scrolling finishes
To summarize: disable the CADisplayLink or GLKViewController render loop when the UIScrollView appears and start a NSTimer to perform the update/render loop at the desired framerate. When the UIScrollView is dismissed/removed from the view hierarchy, re-enable the displayLink/GLKViewController loop.
In the GLKViewController subclass I use the following code
on appear of UIScrollView:
// disable GLKViewController update/render loop, it will be interrupted
// by the UIScrollView of the MPMediaPicker
self.paused = YES;
updateAndRenderTimer = [NSTimer timerWithTimeInterval:1.0f/60.0f target:self selector:#selector(updateAndRender) userInfo:nil repeats:YES];
[[NSRunLoop mainRunLoop] addTimer:updateAndRenderTimer forMode:NSRunLoopCommonModes];
on dismiss of UIScrollView:
// enable the GLKViewController update/render loop and cancel our own.
// UIScrollView wont interrupt us anymore
self.paused = NO;
[updateAndRenderTimer invalidate];
updateAndRenderTimer = nil;
Simple and effective. I'm not sure if this could cause artifacts/tearing of some sort since the rendering is decoupled from screen refreshes, but using CADisplayLink with NSRunLoopCommonModes totally breaks the UIScrollView in our case. Using NSTimer looks just fine for our app and definitely a whole lot better than no rendering.
Even though this is not the perfect solution, it still might work as a workaround;
You could ignore the display link availability and use NSTimer for updating your GL-layer instead.
I have a couple of UIViews with UIImageViews on them. When I display a view the first time, there is a noticeable lag (0.5 second or so) before it is shown. When shown again everything works.
I have managed to work around this by adding all views and then removing them. Like this:
[self performSelectorOnMainThread:#selector(addAndRemoveAllViews)
withObject:nil
waitUntilDone:NO];
The function loops over all views and [addSubview:view] followed by [view removeFromSuperview]. This seems to trigger the images to load while not being shown.
Questions:
This all feels like a workaround to me. Is there a better approach to handle these types of things?
Is it possible to get a callback or something when it's completed? I want to run an animation when done.
Update:
Images are created from NSData like this:
[[UIImageView alloc] initWithImage:[UIImage imageWithData:rawData]];
It seems very likely the image is larger than the image view. If possible shrink it down to the size the image view needs.
The initial delay is loading the image from storage, after that you are using a cached version which is why it is faster.
I'm trying to use a UIImage as a button which gives the impression of it being turned on then off again within about half a second. This works fine if I switch it on but if I want to switch it off again it doesn't switch on at all. I have a short loop in there to prevent it switching on and off so fast I can't see it but it doesn't switch on at all. I've tried it with and without the [flashingButton release]. Is there something I'm misunderstanding here? Can I addSubview and removeFromSuperView at the same time even with a short delay?
if ( some conditional statements in here .......) {
UIImage *estimateButton1 = [UIImage imageNamed:#"FlashingButton.png"];
flashingButton = [[UIImageView alloc] initWithImage:flashingButton1];
flashingButton.frame = CGRectMake (146,8,165,30);
[self.view addSubview:flashingButton];
// [flashingButton release];
// short loop in here to delay urning the button off
[self.flashingButton removeFromSuperview];
User interface drawing doesn't happen until later in the main run loop. Your call to addSubview adds flashingButton to self.view but doesn't draw it. Your short loop blocks the main run loop, so it still doesn't get to the drawing part. And then, you remove the button before the main run loop gets to draw it.
A solution is to let the main run loop continue after you've added the flashing button (so it will get drawn), but create a timer that will remove that button in the future. You can use performSelector:withObject:afterDelay: to do this.
[self.flashingButton performSelector:#selector(removeFromSuperview) withObject:nil afterDelay:0.5f];
You can read about run loops in "Threading Programming Guide" and about how drawing gets done in "View Programming Guide for iOS."
Looping within the main thread will just hang the program temporarily and prevent any drawing from taking place. Instead, use an NSTimer.
I draw some GIS data dynamically, based on user's control, into CGImageRef.
But how to play these frame like an animation efficiently in an UIImageView?
After searching and surveying, the way to play an animation in UIImageView is preloading all images you need,and invoking the startanimating method,like this:
myImageView.animationimages=[NSArray arrayWithObjects:1uiimage,2uiimage,....,nil];
[myImageView startAnimating];
But the waiting time is too long if i preload all UIImages, because i have more than 400 frames each time.
I want to play the animation like a stream,real time stream, how to do that?
Deeply appreciating if you could give me any idea or an example. :)
Use CADisplayLink to get notified on each screen refresh. Change myImageView.image in the callback.
(You might have to do something more clever if you want to limit the framerate.)
Title is quite self explanatory, but I have some animation being done in a loop triggered by CADisplayLink. However, as soon as I scroll a UIScrollView I have added to my view hierarchy, the animation stops immediately, only to return again when scrolling has completely stopped and come to a standstill....
Anyway to cancel this behaviour?
You can also mitigate the effects of this issue by using NSRunLoopCommonModes instead of NSDefaultRunLoopModes:
[displayLink addToRunLoop:[NSRunLoop currentRunLoop]
forMode:NSRunLoopCommonModes];
Run the display link (using -addToRunLoop:forMode:) on another thread with another run loop. So create a new thread, create a run loop on that thread, and run the CADisplayLink on that thread/run loop.
Use UITrackingRunLoopMode. It's specifically designed for scrolling stuffs.
Otherwise, just call render & present routine at -scrollViewDidScroll.
UIScrollView broken and halts scrolling with OpenGL rendering (related CADisplayLink, NSRunLoop)
Here you can find a better (and more complex) solution:
Animation in OpenGL ES view freezes when UIScrollView is dragged on iPhone
that allows you to use 'NSRunLoopCommonModes' and avoids OpenGL freezing when holding a finger without scrolling.
This is related to what Doug found (setting the frame interval of CADisplayLink to 2 instead of 1 fixing UIScrollView).
Actually CADisplayLink support multiple RunloopMode.
try this:
[displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:UITrackingRunLoopMode];
[displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
NSRunLoopCommonModes seems to mess with the bounciness and continuous nature of the uiscrollview.
I found that if I set the frame interval to 2 instead of 1 (so 30 frames a second) everything works fine. So what I'm doing is setting it to 2 when my popover comes up and resetting it to 1 when it dismisses.
We fixed this same issue by changing the frameInterval from 1, to 2. This essentially halves the rendering rate of your OpenGL scene, but it still may render sufficiently for your needs.
frameInterval
The number of frames that must pass before the display
link notifies the target again.
#property(nonatomic) NSInteger frameInterval Discussion The default
value is 1, which results in your application being notified at the
refresh rate of the display. If the value is set to a value larger
than 1, the display link notifies your application at a fraction of
the native refresh rate. For example, setting the interval to 2 causes
the display link to fire every other frame, providing half the frame
rate.
Setting this value to less than 1 results in undefined behavior and is
a programmer error.