I have a problem with the animation. At start, everything is smooth. However, when the application runs for a certain period of time (around 10 min) then the animation lags.
Here is the code in viewDidLoad:
[NSTimer scheduledTimerWithTimeInterval:3.0 target:self selector:#selector(updateImage) userInfo:nil repeats:YES];
And in -(void)updateImage: //image fade in
[aSubview release];
[aSubview removeFromSuperview];
aSubview = [[UIImageView alloc] initWithFrame:CGRectMake(28, 64, 265, 284)];
aSubview.image = [slideShowArray objectAtIndex:randNum];
[aSubview setAlpha:0.0];
[UIImageView beginAnimations:NULL context:nil];
[UIImageView setAnimationDuration:2.0];
[aSubview setAlpha:1.0];
[UIImageView commitAnimations];
[self.view addSubview:aSubview];
UIImageView *film = [[UIImageView alloc] initWithFrame:CGRectMake(20, 34, 280, 344)];
film.image = [UIImage imageNamed:#"film5.png"];
[film setAlpha:1.0];
[self.view addSubview:film];
[film release];
Can anyone explain this? Is it due to memory issue or something else? Thanks in advance.
[self.view addSubview:aSubview];
You are adding a subview in every 3 seconds. So after 10 mins there are 200 subviews which requires too much memory and thus slowing the app. Before adding a new subview, remove the previous ones if they are not needed. And if you need 200 subviews simultaneously then you should reconsider your design.
EDIT: After the edit of the question, why do you need to add a film every time? You are adding a new film object every time the method is called. Note that, super view retains subviews. So if the method is called 20 times then you have 20 film objects in memory.
Yes, your problem is with memory aggregating over time. To solve this problem use Instruments to check memory usage and find out what causes it. You might not release some object. In XCode go to Product -> Profile or press "cmd+i" choose "Allocations" and add "Leaks" manualy. Run Instruments and try to debug your problem.
[self.view addSubview:aSubview];
The above is not the issue at all, from the apple documentation for addSubView .
Views can have only one superview. If
view already has a superview and that
view is not the receiver, this method
removes the previous superview before
making the receiver its new superview.
Animation is always a heavy operation in term of memory that need to store many thing before and after animation, which are not visualable to as application developer. And in your case you are doing it more than 10 min.. in a loop that lead to reduce the availability of memory for next animation operation.
Related
After looking for some feedback on how to clean up animations once they've finished I implemented the following;
- (void)onAnimationComplete:(NSString *)animationID finished:(NSNumber *)finished context:(void *)context {
UIImageView *AnimationImageEnd = context;
[AnimationImageEnd removeFromSuperview];
[AnimationImageEnd release];
}
and to set them off (I've removed some of the frames and rotation calcs);
UIImageView * AnimationImageStart = [[UIImageView alloc] initWithImage: AnimationImage];
[self addSubview:AnimationImageStart];
[UIView beginAnimations:nil context:AnimationImageStart];
[UIView setAnimationDuration:iSpeed];
AnimationImageStart.transform = transform;
[UIView setAnimationDidStopSelector:#selector(onAnimationComplete:finished:context:)];
[UIView setAnimationDelegate:self];
[UIView commitAnimations];
This seems to be working fine and cleaning up all used memory once the animation completes.
Since implementing this approach I have now got issues with re-opening the App once it goes into the background if an animation had started before hand. The App is now crashing with the following error;
*** -[UIImage _isDecompressing]: message sent to deallocated instance 0x21083ad0
I've had a tinker and it appears to be all down to the release used. If I remove the [AnimationImageEnd release]; the crashes stop (but it obviously starts some memory leaks off..).
Is there something simple I am misunderstanding about the Animations and the App entering / re-opening from the background?
My guess is that iOS is automatically cleaning up any animations in progress once it enters the background and automatically fires the release to each. If this is the case, should I just simply expect them to have been cleaned up and hold a flag within the applicationDidBecomeActive to bypass the release?
Edit 1
I've read that the finished flag on onAnimationComplete will let me know if the animation completed it's full animation or not. Is it enough to just trust that if it did not finish it has already been cleaned up (removed from superview and released)?
Edit 2
As per David's response below I have updated this to a block;
[UIView animateWithDuration:5.0f
delay:0.0f
options:0
animations:^{
imgStart.frame = CGRectMake( 0, 0, 10.0f, 10.0f);
}
completion:^(BOOL finished){
[imgStart removeFromSuperview];
//This still crashes the App as soon as the App returns from being in the background
[imgStart release];
}];
The same behaviour as detailed above is still present. As per Edit 1, is it enough to just assume that whenever the finished BOOL is NO, it has already been cleaned up (removed from superview + released)?
Any information is greatly appreciated.
Ralph
First, please don't use an initial capital letter for an object - reserve those for classes (it makes reading code much harder for most of us).
Generally it's bad practice to release objects in callbacks. Use a dispatch block to the main queue to release the object or a performSelectorOnMainthread:, and see if that helps.
EDIT: didn't mean to imply you needed a complete block. Reading the docs again shows no indication of retain change if the animation does not complete. Since you have a release in your code, you are not using ARC.
Non-ARC: so imgStart is alloc/inited, no autorelease, so its retain count is 1. You add it to the subview, its now two. You (or the system) removes it from the subview, its now 1. The final release makes it 0 (I'm ignoring the retain/release by the block itself). I just cannot see how the system could release it, as its not really anywhere it can be seen (its not an ivar, so viewDidUnload cannot release it). Right now this is a mystery.
Suppose you converted to ARC: imgStart is released at the start of the animation, two things retain it, the subview array and the block. When its removed from the subview array (by whoever), retain count goes down by 1, and when the block completes it will release it as well, causing the retain count to go to 0 so it will be released.
If nothing above rings a bell or helps, you can at least try and find when/where that object is getting dealloced. Create a UIImageView subclass in your .m file:
#implementation MyIV : UIIMageView
#end
#interface MyIV
- (void)dealloc
{
[super dealloc];
NSLog(#"MYIV DEALLOC!!!!");
}
#end
Put a breakpoint on the log message, and you can see exactly where its getting dealloc'd. I've found this so helpful in the past I created an ARC class to do this even for class clusters on github, called Tracker.
when the following event gets executed, i initialize and call a UIImageView.
Later on i will remove this view. My code is as follows;
self.myView = [[UIImageView alloc] initWithFrame:CGRectMake(10, 10, 320, 460)];
[myView setBackgroundColor:[UIColor whiteColor]];
self.myView.backgroundColor = [[UIColor alloc] initWithPatternImage:[UIImage imageNamed:#"someImage.png"]];
[self.tableView addSubview:self.myView];
At a later instance i will remove the view, and my code for removing is as follows;
[self.tableView bringSubviewToFront:self.myView];
[self.myView setHidden:YES];
[self.myView removeFromSuperview];
The problem i am having is that, the view that i added to the tableview, is not getting removed. I need to know why this is hapenning, and a programatic solution to solve it.
note: i have debugged, and the remove part of the code gets executed but nothing gets removed. Help
A common issue that can cause behavior like this is modifying UIKit objects (like UIViews) from a background thread. UIKit is not generally threadsafe and calls from background threads will often not change the visible state of your app for a long time, if at all.
What is the method that executes your second block of code and how does it get called?
FWIW, bringSubviewToFront: and setHidden: should be unnecessary; removeFromSuperview should be all that is required to remove your view.
Was wondering if there is another controller on top of your tableview like a navigationcontroller. Would removeFromSuperview actually refer to it giving an unwanted result?
you should try only [self.myView removeFromSuperview];
Although I currently only have one app out in the App store, I have several in the works and was wondering how users are making their splash screens.
I have seen several very cool animated ones and was wondering if this was all done via code or is it just something you would make in possibly iMovie and just run it as a video.
Any idea how some of these are being created? Examples are anything from Time Warner Cables app to Bejeweled.
Thanks in advance for the info.
Geo...
See iPhone Animated Loading Screen <-- the answer in there seems to be that the "fancy" splash screens aren't actually loading screens.
So what you will have to do is to create an animation (maybe a movie clip, or an imageview animation or similar) that can be run when the app starts artifically, possibly with you loading your resources behind that, rather than using the default splash screen functionality (to speed up the start of your app).
Hope that helps
Try this in App delegate class ....
- (void)applicationDidFinishLaunching:(UIApplication *)application {
UIImage *splashImage = [UIImage imageNamed:#"Picture 2.png"];
splashImageView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 20, 320, 480)];
splashImageView.contentMode = UIViewContentModeScaleAspectFit;
splashImageView.image = splashImage;
[NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:#selector(removeSplashScreen) userInfo:nil repeats:NO];
[window addSubview:splashImageView];
}
-(void)removeSplashScreen{
[UIView beginAnimations: nil context:nil];
[UIView setAnimationDuration:2.0];
splashImageView.alpha = 0.0;
[UIView commitAnimations];
[NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:#selector(releaseSplashScreen) userInfo:nil repeats:NO];
}
-(void)releaseSplashScreen{
[splashImageView removeFromSuperview];
[splashImageView release];
//Load the rootviewController here
}
You can also include Default.png in the resource of the project
There's a couple chapters on custom splash screens in Drance & Warren's book 'iOS Recipies' from Pragmatic Bookshelf.
Perhaps a commercial plug isn't what you're seeking (I'm not affiliated the the title or publisher), I just remember reading through it and finding it interesting.
You can do a fancy splash screen if you made it on the first view controller, like your real splash screen is the first scene of your animation, and the first view controller is the complete animation then you dismiss the first controller or you push the main view controller after a delay from you first view controller.
I have a UIView that I'm sliding in using a UIScrollView. Code looks like this:
[view setObject:object]; // updates a bunch of labels, images, etc
[scrollView addSubview:view];
[scrollView setContentOffset:CGPointMake(320,0) animated:YES];
Problem is, the scrollView doesn't seem to wait for the view to be completely loaded before animating its contentOffset, and so the animation is kinda jerky (almost non-existent on the first slide-in and on older devices). Oddly enough, switching the 3rd line to this fixes it:
[UIView animateWithDuration:0.3 animations:^{
[scrollView setContentOffset:CGPointMake(320,0) animated:NO];
}];
No lag whatsoever, perfectly smooth slide-in. However, this doesn't trigger any of the UIScrollViewDelegate calls, on which I also depend (but which aren't responsible for the lag).
Any idea of how I could tell the UIScrollView to wait for the view to be completely loaded before animating its contentOffset? Or maybe there's something else that I'm missing?
EDIT: before anyone else suggests it: yes, I did try:
[self performSelector:#selector(slideIn) withObject:nil afterDelay:1];
just to see if it would fix it. And yes it does fix the lag, but this is not an actual solution. performSelector:afterDelay: is never a solution, it's only a superficial fix. Plus, you're making the user wait extra seconds every time (since the actual delay may be much shorter than 1 second depending on the device model).
Performing selector after delay 0.01 works because the invocation is scheduled on the runloop vs. calling the method immediately. For that matter, afterDelay:0.0 may also work. Did not try in this particular case but works in may similar situations.
have you tried starting the scrolling in the "viewDidAppear" function of the viewcontroller... maybe this is called when the loading is finished.
otherwise try afterdelay with a very short time like 0.01 .. so that the call is scheduled next after the current work is done.
Try this, set contentSize after some delay (here delay is 2 second).
[view setObject:object];
[scrollView addSubview:view];
[self performSelector:#selector(contentSize) withObject:nil afterDelay:2];
}
-(void) contentSize {
[scrollView setContentOffset:CGPointMake(320,0) animated:YES];
}
I am creating a basic animation for my iPhone app. I have a choice to make between 2 different types of animation. I can use this...
NSArray *myImages = [NSArray arrayWithObjects:
[UIImage imageNamed:#"myImage1.png"],
[UIImage imageNamed:#"myImage2.png"],
[UIImage imageNamed:#"myImage3.png"],
[UIImage imageNamed:#"myImage4.gif"], nil];
UIImageView *myAnimatedView = [UIImageView alloc];
[myAnimatedView initWithFrame:[self bounds]];
myAnimatedView.animationImages = myImages;
myAnimatedView.animationDuration = 0.25;
[self addSubview:myAnimatedView];
[myAnimatedView release];
or something like this...
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:1.5];
// other animations goes here
myImage.transform = CGAffineTransformMakeRotation(M_PI*0.5);
// other animations goes here
[UIView commitAnimations];
I have quite a few of these parts to animate so I want to choose an option which uses the least amount of memory and runds the quickest. Any advice would be great, thanks
There are a lot of variables to account for in your question. Much of it will depend on the size and complexity of the images and the frequency with which the animation will run. My hunch is using the API provided transform will be the cheapest but the only real way to tell will be to stress test both animations and see which holds up the best in your case.
I would go hybrid: Generate your still images from the animation code and save the layer off to CGImages.
So, the root issue that you need to consider is how many frames of animation you expect to be in memory at one time. It does not matter if you use the animationImagesArray or one single huge image with all the frames in one. Holding the decoded image data in memory takes up a lot of space, you might be able to get away with it for a small number of small images, but you could easily write some code that will crash you device due to using up all the memory. See my blog post about this specific issue video-and-memory-usage-on-ios-devices. A better way to write your code is to simply not hold all the decoded frames of video (UIImages or CGImageRefs) in memory at the same time.