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.
Related
I have scoured the net for this question and have came up empty handed. I have an app that I'm making which I want the user to be able to view an image i have hooked up to the UIImageView to show fullscreen. Basically by tapping the UIImageView it would make the buttons and status bar disappear until image is tapped again. I know this is probably a simple animations block that i would throw in my viewdidload or where I call my image from. I'm just unsure where it goes.
here is where my image comes from if the helps.this is in my .m file
-(void)viewDidLoad {////Loads UIImageView from URL
todaysWallpaper.image = [UIImage imageWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:#"http://www.inkdryercreative.com/daily/archive/mondays/images/062-mondays-960x640-A.jpg"]]];
I have four buttons that appear on screen. i want it to resemble the feel you get in the native photos app when you look and a picture and everything else on screen dissolves. Any help or guidance would be great. I can send additional code if needed
CGRect *oldFrame = todaysWallpaper.frame; // store this somewhere for when you go back
CGRect *newFrame = [[UIScreen mainScreen] bounds];
[UIView animateWithDuration:0.5 animations:^{
todaysWallPaper.frame = newFrame;
}];
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.
What is the best or recommended technique for animating PNG Sequences.
Heres what I've learned:
Do it Manually
Using MutableArrays containing Strings, you can animate a UIImageView with a timer which increments an index number
UIImage - animation methods
This works, the only problem is to find if an image has completed its animation, you have to check the isAnimating BOOL, and to do that you need a timer.
What is the best and recommended?
Looking to do Oldschool game sprite animations,
ie:
Idle Animation
Attack Animation
Walk Animation
ect...
Let me know if any one has something.
#lessfame
Example to animate arrows
UIImage* img1 = [UIImage imageNamed:#"fleche1.png"];
UIImage* img2 = [UIImage imageNamed:#"fleche2.png"];
NSArray *images = [NSArray arrayWithObjects:img1,img2, nil];
UIImageView* imageView = [[UIImageView alloc] initWithFrame:CGRectMake(0.0, 0.0, 160.0, 160.0)];
[imageView setAnimationImages:images];
[imageView setAnimationRepeatCount:100];
[imageView setAnimationDuration:3.0];
imageView.center = myView.center;
[imageView startAnimating];
[myView addSubview:imageView];
[imageView release];
Easiest way is to use Core Animation layers to do sprite animation:
Make a 'multi-cell' image strip (could be PNG or JPG) of all the various moves for the sprite. Make each 'cell' be a fixed height or width.
Put the image in a UIImageView.
Take the CALayer of the view, and adjust the contentsRect property of the layer. This acts as a clipping rectangle for the sprite. To animate sprite all you have to do is move the contentsRect over to the next cell position in the image strip.
Something like this will do it (assuming you've already calculated the newSpritePosition.
[CATransaction begin];
[CATransaction setDisableActions:YES];
spriteLayer.contentsRect = newSpritePosition;
[CATransaction commit];
(If you don't do the second line, then you'll get a default slide animation instead of a 'jump' to the next state.)
With this technique you can rotate through all the frames of the sprite -- much like a flipbook. CALAyer transactions are optimized so you should get pretty fast frame rates. You can set an animationDidStop completion handler to move to the next state of the sprite or loop back to the beginning frame.
It depends on how you are going to use the sprites. If you just need a simple looping sprite then the UIImage method is great. If you want more granular control then you will be happier loading the images into an array and cycling them using a NSTimer to handle the timing. Personally I use the array method most often because it leaves me more options in the future (like detecting when animations have completed). One more suggestion would be to check out the cocos2d project. Its sprite is alot more powerful than either of these suggestions.
As I said in another answer, I found this to be a good way: PNG Animation method by Moses DeJong
From his words:
This example implements an animation oriented view controller that simply waits to read the PNG image data for a frame until it is needed. Instead of alllocating many megabytes, this class run in about a half a meg of memory with about a 5-10% CPU utilization on a 2nd gen iPhone.
I'm still unsure exactly how it's done. I believe it basically uses UIImageView to cache up AVAudioPlayer.
I fear this is evil code:
CGRect iRect = CGRectMake(0.0f, 0.0f, 320.0f, 400.0f);
UIImageView *imgView = [[UIImageView alloc] initWithFrame:iRect];
imgView.animationImages = [NSArray arrayWithObjects:
[UIImage imageNamed:#"b0001.png"],
[UIImage imageNamed:#"b0002.png"],
// 150 more
[UIImage imageNamed:#"b0152.png"],
nil];
I slightly remember that imageNamed: is evil. If I do it this way, and I have to provide UIImage objects, then those UIImage objects immediately load those image files into memory, right? And, besides that, all those 152 chunky UIImage objects take a big break in memory because they are autoreleased, isn't it?
So in conclusion, using this technique sucks. Does it? I don't know. On my age old first gen iPod touch this seems to run with no lag at 25 fps. Very smooth and nice. The only thing I fear about is that some other devices may think different about this. Although I have the weakest programmable ipod touch available.
Anyways, does anyone see any improvement possibility there? Or should I not use that and use setImage: of UIImageView in an own algorithm which would load and set those images with delayed selectors using that chunky imageWithContentsOfFile thing (may be named different), with no autorelease? Maybe some clever guy wrote a little lib for high performance thumb vids that consist of image sequences?
(no, video is no an option on the iphone; the apple frameworks for this just supports fullscreen, and I don't know of anything else that would do it)
You are right about imageNamed... you should not use it in many cases as it internally caches the image. Use:
[UIImage imageWithContentsOfFile:[[NSBundle mainBundle] pathForResource:#"image1" ofType:#"png"];
The only time you want to use imageNamed is when
1) You are reusing an icon over and over (tableviews etc...)
2) You have not implemented your own caching system
I'd stay away from trying to load so many images into a UIImageView array. A similar question was asked here. The buffering method I describe there should also apply to your situation.
I have an animation that I'm displaying using a UIImageView:
imageView.animationImages = myImages;
imageView.animationDuration = 3;
[imageView startAnimating];
I know I can stop it using stopAnimating, but what I want is to be able to pause it. The reason is that when you call stop, none of your animation images are displayed, whereas I would like the last one that is up at the time when I hit a button to stay up.
I have tried setting the duration to a much larger number, but that causes all the images to disappear as well. There must be a really basic way to do this?
This code will pause an animated object at its current position in the animation process. If you record other variables like the time or progress or whatever you need, it should be fairly trivial to resume the animation again.
UIView *viewBeingAnimated = //your view that is being animated
viewBeingAnimated.frame = [[viewBeingAnimated.layer presentationLayer] frame];
[viewBeingAnimated.layer removeAllAnimations];
//when user unpauses, create new animation from current position.
Hmmm...since no one seems to know I guess it's not possible.
I went ahead and wrote my own UIView, with a UIImageView subview, that uses an NSTimer to switch between images. The advantage of this is that I can pause and resume the timer at my leisure, and performance doesn't seem to be an issue.
#oddmeter just a little edit:
animatedView.animationImages = images; //images is your array
[animatedView startAnimating];
//Then when you need to pause;
[animatedView stopAnimating]; //Important!!
animatedView.image = [images objectAtIndex: 0];
This should do the trick: https://developer.apple.com/library/ios/#qa/qa2009/qa1673.html
It basically tells you what you need to do to pause/resume any CALayer based animation.
If you feel uncomfortable using CALayer methods on UIImageView controlled animation, you could always just make the UIImage array based animation yourself. The code needed is very short, you can take it from here: http://rssv2.blogspot.com/2011/04/animating-series-of-images-with-calayer.html
Another option is to set the image property as well as the animationImages property. Doing this will display the static image when the UIImageView has its animations stopped.
Assuming your class is a subclass of UIImageView and have an NSMutableArray of images, do the following:
self.animationImages = images;
//yes, I'm skipping the step to check and make sure you have at least one
//element in your array
self.image = [images objectAtIndex: 0];
Maybe you can take a screenshot of the last animated image and display that?