iOS - UIImageView Animation receiving memory warning - ios

I am attempting to run two UImageViews that have multiple arrays of animations they load and animate based on a selection the user makes. For example, here is one method:
-(void)initiateAnimations {
nullAnimation = [NSArray arrayWithObjects:
[UIImage imageWithContentsOfFile:[[NSBundle mainBundle] pathForResource:#"null0002" ofType:#"png"]],
[UIImage imageWithContentsOfFile:[[NSBundle mainBundle] pathForResource:#"null0003" ofType:#"png"]],
[UIImage imageWithContentsOfFile:[[NSBundle mainBundle] pathForResource:#"null0004" ofType:#"png"]],
[UIImage imageWithContentsOfFile:[[NSBundle mainBundle] pathForResource:#"null0005" ofType:#"png"]],
[UIImage imageWithContentsOfFile:[[NSBundle mainBundle] pathForResource:#"null0006" ofType:#"png"]],
[UIImage imageWithContentsOfFile:[[NSBundle mainBundle] pathForResource:#"null0007" ofType:#"png"]],
[UIImage imageWithContentsOfFile:[[NSBundle mainBundle] pathForResource:#"null0008" ofType:#"png"]],
[UIImage imageWithContentsOfFile:[[NSBundle mainBundle] pathForResource:#"null0009" ofType:#"png"]],
[UIImage imageWithContentsOfFile:[[NSBundle mainBundle] pathForResource:#"null0010" ofType:#"png"]], nil];
}
And here is an IBAction calling the animation method:
-(IBAction)nullani {
player.animationImages = nullAnimation;
player.animationDuration = 0.50;
player.animationRepeatCount = 1;
[player startAnimating];
}
It all works well at first, but keep in mind i have about 5-6 other arrays of animations roughly with the same size images. They arent huge, ranging from 12-80k .png files. When i attempt to animate after a few times, I am recieving this error in the output:
2013-03-16 01:34:44.438 [3316:907] Received memory warning.
After i receive this message, any new animation loaded will crash the app.
I ran it through Instruments and was unable to find any leaks, and the output gives me nothing upon crash.
Is there any memory issues here? how can i get rid of this issue? thanks.

Some ideas:
1) Never have more than 1 animation loaded into an array. Once you stop one animation, and are ready to start another, delete the first animation and replace the image the viewer sees with the single image that should be showing. If you can determine the exact image that was showing (and am doubtful of this) then you can replace the array with a single image and the image will not flicker.
2) If you cannot do 1), then try using Core Animation directly (more work for you), and do the animation yourself. This gives you more control over memory usage (you could cache image bitmaps on the file system).
3) Using a Mac and AVFoundation or QTKit, make an animation from images, then create a "movie" that you start and stop instead of the image sequence. In this case you'd replace the static images in the bundle with movie objects.

Related

Is there any alternate for animating array of images using UIImageView?

UIImageView has animationImages for animating sequence of images. That works fine. But It holds the images object. So There is a spike in use of memory when this animation is happening. I tried NSTimer for setting image property of that image view, But it doesn't work.
Can we achieve this in any other approach?
Instead of looking for alternatives, just try to correct the existing code.
The best practice of allocating an image for animationImages should be using initWithContentsOfFile instead of imageNamed:
imageNamed: it cache’s your images and you lose control over the memory - there's no guarantee that releasing the object will actually release the image but does provide faster loading of images second time around as they are cached.
imageWithContentsOfFile: it does not cache images and is more memory friendly however as it does not cache images and the heavier images are loaded much slower.
When the animation does stop, just release the image collection array. If you are using ARC then make it the image collection array to nil.
Best Practice:
for(int itemIndex = 0; itemIndex < 20; itemIndex++) {
NSString *filePath = [[NSBundle mainBundle] pathForResource:#"myImage1" ofType:#"png"];
UIImage *image = [[UIImage alloc] initWithContentsOfFile:filePath];
[imageArray addObject:image];
}
After the Animation:
imageArray = nil;

Memory Warning & Crash ImageIO-jpeg-data

I have created an app and it is (well it was) ready to submit.
The app is basically a flash cards apps for kids where there are 12 cards per page.
It is currently iPad only and when the cards are touched a image view animates out of the card to full screen with a full resolution 2048 x 1536 and it plays a sound of the animal, you tap the full size image and it goes back in the card. Each card has 5 images assigned to it. The app has been working perfectly all though the design period and now that I have loaded all of the animals and I have been testing it and noticed it crashed.
Long story short the memory continually grows by 12MB per image (although each image is around 200-400KB) until it reaches 700MB of Memory and it crashes. This memory never reduces.
I have run the app through the xcode instruments and can see no leaks but have identified in the 'Allocations' that it is the 'ImageIO-jpeg-data' category that is the culprit.
I have spent all evening reading up about this as I have found some promising results however these results have not worked for me so I am assuming that I have misunderstood what has been advised or I have not understood so was hoping that someone could maybe help me out or explain in laymen terms how I can resolve this.
OK so to explain how I have the view contoller set up.
Originally I set up the 5 images for each card in an array but there was a delay so someone on here recommended that I pre load the images in viewDidLoad and then call them when the card/button was touched.
So for example I was using this format in viewDidLoad
domestic100 = [UIImage imageNamed:#"ferret-00"];
domestic101 = [UIImage imageNamed:#"ferret-01"];
domestic102 = [UIImage imageNamed:#"ferret-02"];
domestic103 = [UIImage imageNamed:#"ferret-03"];
domestic104 = [UIImage imageNamed:#"ferret-04"];
and then in the the button pressed I assigned the image to the image view by calling:
domestic01ImageContainer.image = domestic100;
inside a if else look checking if the image number was 1 to 4.
Anyway it worked perfectly (apart form this memeory issue)
I have read online that I would be better off declaring the uiimages using
[UIImage imageWithContentsOfFile:path]
as seen in this post:
UIImage memory not deallocating VM: ImageIO_JPEG_DATA?
So the alterations I have made in my code are:
in my viewDidLoad I now create uiimages as so:
domestic200 = [UIImage imageWithContentsOfFile: [[NSBundle mainBundle] pathForResource: #"dog-00" ofType: #"jpg"]];
domestic201 = [UIImage imageWithContentsOfFile: [[NSBundle mainBundle] pathForResource: #"dog-01" ofType: #"jpg"]];
domestic202 = [UIImage imageWithContentsOfFile: [[NSBundle mainBundle] pathForResource: #"dog-02" ofType: #"jpg"]];
domestic203 = [UIImage imageWithContentsOfFile: [[NSBundle mainBundle] pathForResource: #"dog-03" ofType: #"jpg"]];
domestic204 = [UIImage imageWithContentsOfFile: [[NSBundle mainBundle] pathForResource: #"dog-04" ofType: #"jpg"]];
and then in the button pressed call the image in to the imageview using
[domestic01ImageContainer setImage:domestic100];
I am getting the same results - the image are just building up in the same memory and not releasing or deallocating.
I'm no wiz when it comes of objective-c and this is my first real app.
If someone could be kind enough to spare a moment of their time to help me I would be really appreciative.
Thanks in advance.
****** Edit ******
So I have told you how I load my images above, I also in the viewDidLoad give the 12 imageviews a starting point and a hidden alpha.
domestic01ImageContainer = [[UIImageView alloc] initWithFrame:CGRectMake(30, 123, 152, 224)];
domestic01ImageContainer.Alpha = 0;
I then add the subview with:
[self.view addSubview:domestic01ImageContainer];
each imageview is allocated a tap gesture to hide the image at a later point:
UITapGestureRecognizer *domestic01Tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(imageTaped:)];
[domestic01ImageContainer addGestureRecognizer:domestic01Tap];
[domestic01ImageContainer setUserInteractionEnabled:YES];
When the thumb / card is pressed a streamlined version of the button would be:
- (IBAction)domestic01ButtonClicked:(id)sender {
static int imageNumber = 0;
if (imageNumber == 0) {
[domestic01ImageContainer setImage:domestic100];
imageNumber++;
}
else if (imageNumber == 1) {
[domestic01ImageContainer setImage:domestic101];
imageNumber++;
}
else if (imageNumber == 2) {
[domestic01ImageContainer setImage:domestic102];
imageNumber++;
}
else if (imageNumber == 3) {
[domestic01ImageContainer setImage:domestic103];
imageNumber++;
}
else if (imageNumber == 4) {
[domestic01ImageContainer setImage:domestic104];
imageNumber = 0;
}
domestic01ImageContainer.Alpha = 1;
[UIView animateWithDuration:0.2 delay:0.0 options:UIViewAnimationOptionCurveEaseIn animations:^{
domestic01ImageContainer.frame = CGRectMake(0, 0, 768, 1024);
} completion:^(BOOL finished) {
}];
}
The in my imageTapped recognizer to shrink the full size image back to the card I use:
[UIView animateWithDuration:0.2 delay:0.0 options:UIViewAnimationOptionCurveEaseIn animations:^{
domestic01ImageContainer.frame = CGRectMake(30, 123, 152, 224);
} completion:^(BOOL finished) {
domestic01ImageContainer.Alpha = 0;
}];
Which basically put the flash card back from full screen to the size of the card / thumbnail and the sets alpha again to 0.
Like I said it all works but as you have suggested the image is being retained somewhere.
Hope this sheds a bit more light on what could be causing the issue.
I had tried setting the image to nil in after the alpha was set to 0 in the imageTapped but this didn't clear the memory.
[domestic05ImageContainer setImage:nil];
Ok so if anyone else is suffering from this issue then here is the solution that I found that work perfectly.
http://xcodenoobies.blogspot.co.uk/2011/02/best-way-to-use-uiimageview-memory.html
Thanks Anna for your input, it pointed me in the right direct but his was the answer that did it for me.
I was having a similar issue. Memory was being held on abnormally. I had a UIImageView IBOutlet and I was setting its image property with my view controller's UIImage property, which I would set while initializing my view controller. The UIImageView would hold on to the UIImage property of my view controller wrongly even when I would go back to the root view controller.
I fixed the issue by changing the view controller's UIImage property to NSData and setting the UIImageView's image property as follows:
[_previewImageView setImage:[UIImage imageWithData:_previewImageData]];
This resolved the issue of ImageIO-jpeg-data being help on unnecessarily. Hope this helps someone.

ImageCaching on IOS

I've some trouble with memory on my app. I've checked Instruments to get more clue about this issue and i've found that 79% of my memory is used by this :
So i've searched on Google and some people said that is image caching which saved in memory all my images. Maybe it comes from my allocation ?
Here is how i call my images :
info = [InfoModel getInfo:[NSString stringWithFormat:#"%d", self.idEnigme]];
myImage = [UIImage imageNamed:[NSString stringWithFormat:#"res/img/%#", [info objectForKey:#"path1"]]];
myImageView = [[UIImageView alloc] initWithImage:myImage];
myImageView.frame = CGRectMake(0, 200, [[UIScreen mainScreen] bounds].size.width, 400);
[self.scrollView addSubview:myImageView];
Info is a class where i parse a Json file where are my path to images.
Thanks for helping, this drives me crazy.
iOS automatically caches your image for future use when you call imageNamed:
As discussed in a few places, including here:Does UIImageView cache images?
You can get around this caching if you know you are only going to create it once by using
[UIImage imageWithContentsOfFile:[[NSBundle mainBundle] pathForResource:fileName ofType:nil]]
instead
I wouldn't worry about that too much, the UIImage cache is cleared when the app receives a low memory warning. It's all handled automatically, so any images that are no longer in use will be flushed from memory at this point.
So if your app is crashing from running out of memory it is not likely because the OS is caching images that are no longer in use.
You can handle your own caching of images by using initWithData instead of imageNamed but I doubt this solution will help you.
I'm very worried about the following screen. When i launch "Instruments" in Allocations mode, i see every image of my app adding to "Living". I really don't understand how it could be possible...
I've found the problem and i solved it. I'll explain you what was the problem, maybe it could help someone.
The real problem was my UIImage init. Initially i allocated my UIImage in method like the following code :
NSString* fileName = [NSString stringWithFormat:#"%#%#%#", name, number, ext];
UIImage *myImageHeader = [UIImage imageWithContentsOfFile:[[NSBundle mainBundle] pathForResource:fileName ofType:nil]];
UIImageView *myImageViewHeader = [[UIImageView alloc] initWithImage:myImageHeader];
myImageViewHeader.frame = CGRectMake(0, 0, [[UIScreen mainScreen] bounds].size.width, 277);
[self.scrollView addSubview:myImageViewHeader];
Finally i've decided to create a UIImageview in my .h via a property and to set it in my method, like this :
#.h
#property (weak, nonatomic) IBOutlet UIImageView *headerImage;
#.m
[self.headerImage setImage:[UIImage imageWithContentsOfFile:[[NSBundle mainBundle] pathForResource:fileName ofType:nil inDirectory:nil]]];
By this way i saved a lot of memory but i still have leaks. So i decided to delete the image in my UIImageView after use :
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^(void){
self.headerImage.image = nil;
});
Now, my app only use 8mb of memory and manage memory correctly.
Thx for helping !

Image Optimization (iOS)

I'm working on a iPad Magazine and I'm using a lot of images (background, slideshow and animation) and the memory utilized is very high.
I've read the following method uses a lot of memory
UIImage *picture = [UIImage imageNamed:#"myFile.png"];
And they recommended using this one
NSString *fullpath = [[[NSBundle mainBundle] bundlePath] stringByAppendingString:#"/myFile.png"];
imageView.image = [UIImage imageWithContentsOfFile:fullpath];
But I've found another method as well
imageView.image = [[UIImage alloc] initWithContentsOfFile: [[NSBundle mainBundle] pathForResource:#"myFile" ofType:#"png"]];
In order to optimize my app, which method should I use? All my images are .jpg and were saved for web in Photoshop.
All 3 methods will use the same amount of memory. The differences are the following:
Using [UIImage imageNamed:#"myFile.png"] image is cached in memory for faster reuse. This is good for small images used several times in your application (image background, etc). Cache is removed for non used images when memory warning is received.
Using [[UIImage alloc] initWithContentsOfFile:path] image is not cached and you can "force" release of memory by calling [image release] or setting property to nil using ARC. You have a better management of when memory is released
Using [UIImage imageWithContentsOfFile:fullpath] is just equivalent to [[[UIImage alloc] initWithContentsOfFile:path]autorelease]

Fastest way to load images into a UIView?

i have a UIScrollView which holds different UIView container. Each container has to load six UIImages. When scrolling i need to adjust the contents within the "layoutSubviews" method.
Unfortunately loading these images affects in hiccups when the new container and the images will be created.
I´m loading the images async via "dispatch_async". I also used small 32x32px thumbnails which will be replaced with the full image 256x256px image. Unfortunately its stuttering..
Here´s the code of the "initWithFrame" method of such a UIView.
These UIViews will later be added as a subview to the UIView container.
// load ThumbnailImage and replace it later with the fullImage
NSString* thumbImageName = [self.data.imageName stringByAppendingString:#"_small"];
NSString* fileLocation = [[NSBundle mainBundle] pathForResource: thumbImageName ofType:#"png"];
UIImage* image = [UIImage imageWithData:[NSData dataWithContentsOfFile:fileLocation]];
[self setBackgroundImage:img forState:UIControlStateNormal];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
dispatch_async(dispatch_get_main_queue(), ^{
NSString* fileLocation = [[NSBundle mainBundle] pathForResource: self.data.imageName ofType:#"png"];
UIImage* image = [UIImage imageWithData:[NSData dataWithContentsOfFile:fileLocation]];
[self setBackgroundImage:img forState:UIControlStateNormal];
});
});
Is this the faster way to load images into a UIView?
How does Apple implemented the really fluid image view inside the "Photo.app" when scrolling between different fullscreen images?
I don´t know if it would make sense to use CoreImage?
Any advice would be great!
Thanks for any help and your time.
Have a nice day.

Resources