Loading images into memory in iPhone App - ios

I have an UImageView and on click of an button i want to change the UImage in the UImageView. App has 22 UImages with each image exceeding 5 mb size , so the UImageView takes some time to load an image when the button is clicked , is there a way by which we can load these images into the memory so that image view takes lesser time to show the image?
I use the following code to set the image :
[ImageView setImage:[UIImage imageNamed:#"ImageName.jpg"]];
basically i want on swipe the images to be changed , e.g. if a user swipes from right to left the app must show the next image and on left to right click the back image be loaded . please suggest .

like #Gobot mentioned it is the best way du reduce the size of your pictures. For the swipe there is no need to use big pictures like this. You could just create two folders, one with small sized pictures and another with bigger one. If you swipe through, you just use the small pictures. If a user taps a picture you could get the name of the selected UIImage and load just this single picture. That way you also can provide high quality pictures for zooming in. And keep in mind, that your app package gets also bigger the more pictures you have included.
There is a good explanation in a WWDC Stream: iOS App Performance: Graphics and Animations
I also created a method inside a NSMutableArray category for loading all my UIImages once. You could use this method inside viewDidLoad:
- (NSMutableArray *)getImagesWithStartSuffix:(NSString *)start
andEndSuffix:(NSString *)end{
NSMutableArray *imageArray = [[NSMutableArray alloc] init];
for (int i = 1; i < 100 + 2; i++) {
NSString *fileName = [NSString stringWithFormat:#"%#%d%#.jpg",
start, i, end];
if([self fileExistsInProject:fileName]){
[imageArray addObject:[UIImage imageNamed:fileName]];
} else {
break;
}
}
return imageArray;
}
There is also a very easy way to change the size of your pictures by writing an Apple Script. You could run a loop over all pictures and resize them all this way. This is the easiest way and you don't have to use any tools like Gimp or PS:
do shell script "sips [path_to_file]/picture1.jpg -z 666 1000 --out [path]/changedSize/picture1_small.jpg"

From the Documentation:
This method looks in the system caches for an image object with the specified name and returns that object if it exists. If a matching image object is not already in the cache, this method loads the image data from the specified file, caches it, and then returns the resulting object.
if it takes too long, you may want to add a progress indicator in your app till the full view is generated/loaded then display it on your screen.

The first and simplest option would be to reduce your images under 5MB. Do they really need to be that big? Are they thumbnails? The more complex option would be to load/cache images in a background thread (asynchronously) using dispatch queues.

Related

UICollectionView & AVPlayer Performance

I have a UICollectionView whose cells each have an AVPlayerLayer, all playing a video at the same time. About 9 cells fit on the screen at a time and it's pretty laggy so I need ways to boost the performance to achieve smooth scroll. There's a few things I've tried already:
1) Each cell has only one instance of AVPlayer. It doesn't get created, instead, player.replaceCurrentItemWithPlayer is called when the video url changes.
2) Because I'm using ReactiveCocoa, it's trivial to skip repeat urls to avoid playing the same video twice.
What else can I do to speed up the scroll and performance?
First I want to say it's a bit crazy to see several players in action at the same time: it's a heavy task of rendering anyway.
As far as I know, simply scaling/re-framing the video to smaller size doesn't make it less content to render: if you have a 100x100 picture to render and you render it in a frame of 10x10, it still consumes the same amount of memory as the original picture would consume; same thing goes for videos. So, try to make your video assets having similar resolution as the frame where you would present them.
Store each cell in an NSCache or NSMapTable using the NSIndexPath as the key for each; then, whenever you need a cell, call one directly from the cache or map table.
Obviously, you'll have to create all the cells at once, but you'll get the same scrolling performance as you do with nothing in the cells at all.
Need sample code?
Here's my latest iteration of a perfectly smooth-scrolling collection view with real-time video previews (up to 16 at a time):
https://youtu.be/7QlaO7WxjGg
It even uses a cover flow custom layout and "reflection" view that mirrors the video preview perfectly. The source code is here:
http://www.mediafire.com/download/ivecygnlhqxwynr/VideoWallCollectionView.zip
Unfortunately, UIKit will hit bottlenecks and drop frames when put under pressure for this use case. If you can muster refactoring code to use Facebook's AsyncDisplayKit / then this would be your best bet.
For objective-c / this project here is a good reference.
https://github.com/TextureGroup/Texture/tree/master/examples/ASDKTube
- (ASCellNode *)tableNode:(ASTableNode *)tableNode nodeForRowAtIndexPath:(NSIndexPath *)indexPath
{
VideoModel *videoObject = [_videoFeedData objectAtIndex:indexPath.row];
VideoContentCell *cellNode = [[VideoContentCell alloc] initWithVideoObject:videoObject];
return cellNode;
}
there is swift code available if you dig deep enough in github.

How to load large amount of images in iOS?

I am developing an app which displays large number of images. when i'm loading images there's is no problem on loading of 1000 images when the image crosses the 1000 the app crashes and memory issues raised. How to solve it?
Is there any way. Kindly help me?
Thanks in Advance
Your app is crashing due to consuming too much memory. So you need a strategy to manage the memory your app uses.
You need to set up a pooling-type system where you only load those images that might be displayed "soon".
For example, if the user is looking at image 500, you could have 498, 499, 501, and 502 loaded into memory.
If they move to image 501, then you deallocate 498, and load 503.
What constitutes "soon" is dependent upon how your app's flow works, but you should be able to come up with something workable.
You need to come up with a tableview-like system.
If you look at them, all cells aren't loaded at the same time ; you can experience this if you load your app with a tableview, then put a breakpoint in cellforrow, and you'll see it being called when you start scrolling.
Because all cells aren't loaded at once.
You need a similar logic with your images ; you should NEVER load all of them at once. And if you have a reason to, then I strongly suggest loading a thumbnail version of them ( a really low quality version of them, for a grid-like view of all your pictures, for example ).
To achieve that you need to know what pictures need to be immidatly visible, and what pictures are likely to be visible soon. For small numbers let's say you have 10 pictures total (instead of 1000).
User is loading the app, and can see picture 1 and 2 on the screen. You'll obviously load them straight away, as well as picture 3 and most probably picture 4 (just to make sure). When he scrolls, you KNOW that he is scrolling and your need a method that reads that scroll to allocated the right pictures dynamically. You'll deallocate picture 1 first (because its the first to go away ) and start loading picture 5.
That way, instead of having 1000 pictures loaded, you have only up to 5 (depending on the size obviously).
A really good example is the 9gag app, which loads pictures right under the screen, which means by the time you read the first gag, the next one is already loaded. The "only" way to see it load is to scroll a bit faster or to have a poor connection.
This logic is, to me, the most reliable and effective way to load a big amount of data, wether it is pictures or anything else.
Using stuff like cache, arrays, scrollview(s) and maybe files (depending on your app really) is a good starting point.
Probably you're getting the image-date from some API, because it's ridiculous to save it into device.. :)
My solution is to work with UITableView or UICollectionView depends on what design flow you're are choosing, because those UIComponents will manage memory for you a.k.a will allocate the visible once + 5 down and + 5 up, and deallocate the others.
Also you will need to download the image-date asynchronically, so you
won't block the main UI - thread.
Now... if i were you, I'll definitely go for third party library such as AFNetworking in your case, its best for handling JSON data from server, also have very good error handling.
Here I'll post a method witch is for downloading image-data from server.
- (void)downloadImageAsynchronicallyByUrlString:(NSString *)url forIndexPath:(NSIndexPath *)indexPath {
// Perofrm request
AFHTTPRequestOperation *requestOperation = [[AFHTTPRequestOperation alloc]initWithRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:url]]];
[requestOperation setResponseSerializer:[AFImageResponseSerializer serializer]];
[requestOperation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
// IF SUCCSESS send dataSource SUCCSESS message
[dataSource downloadFinished:responseObject forIndexPath:indexPath];
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
// IF FAIL send dataSource FAIL message
[dataSource downloadFailAtIndexPath:indexPath];
}];
// Start request!!!
[requestOperation start];
}
Explanation:
You call every time this method in cellForRowAtIndexPath at your tableView or itemForRowAtIndexPath if you use collectionView and I'm using a protocol to handle when responseObject a.k.a theImage to send the responseObject via protocol method.
In your UIClass where your tableView pr collectionView lives just implement your protocol method
- (void)downloadFinished:(UIImage *)image forIndexPath:(NSIndexPath *)indexPath {
// Creating copy of cell
_lecturerCell = (LecturersCell *)[_lecturersTableView cellForRowAtIndexPath:indexPath];
// Set downloaded image
[_lecturerCell setLecturerImage:image];
}

Strange Image rotation after loading

In my app, user can make a profile pic by uploading it from phone or taking one from the phone.
Everything works fine, and the user is able to do any of these operations smoothly and the profile pic looks great, at the time of setting it up.
But, when the user starts the app again, I fetch the path of the image and try to show it.
And it is shown, but it is, for some reason, rotated for 90 or 180 degrees!
So, once again, at the moment when the user takes or uploads a pic from the phone, it is not rotated strangely like this, only on the next app life cycle.
Here is some code:
-(void)setAvatar
{
NSMutableArray* imagePathArray = [[Model shared] loadDataForKey:#"avatar"];
NSString *imagePath = [imagePathArray objectAtIndex:0];
UIImage *image = [UIImage imageWithContentsOfFile:imagePath];
if (image!=nil)
_avatar.image = image;
}
I suppose the problem is because of skipping embedded metadata information. When you use Media Library to get an image (?) it includes orientation transformation info. After that you start to UIImage, but this class truncates all image metadata. That's why you see your image rotated after you save and reload it.
Accessing Image Metadata in iOS

UICollectionView received memory warning while scrolling

I am developing an App with an UICollectionView in one ViewController.
This CollectionView it's a gallery with 3 images in each row.
I get the images from the server in groups of 30 pictures, first url and code, and when the cell is going to be displayed in cellForItemAtIndexPath I use SDWebImage Library to download those pictures asynchronously. Every 30 pictures I call again the web service and I get 30 pictures more sending the request with limit and offset. This could happen 100 times, there are profiles with 3000 pictures.
Well, my problem comes one I launch the app in my iPhone 4, after some fast scrolling I start getting Received Memory Warnings and after some warnings the app crashes. When I make the same test in the Simulator, nothing bad happens.
Each time I download 30 pictures I add the result array to the NSMutableArray *data property which handles the CollectionView data and reload the collectionView. I have tried to use Instruments with allocations, but it is very difficult for me to understand what is happening.
This is the code I use to create the cells
-(UICollectionViewCell *)collectionView:(LZProfileImagesCollectionView *)aCollectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
if(aCollectionView == self.imagesCollectionView){
if([self.data count] - 12 == indexPath.row){
self.photoOffset += 30;
[self loadUserData];
}
LZProfileCollectionViewCell * cell = (LZProfileCollectionViewCell *)[imagesCollectionView dequeueReusableCellWithReuseIdentifier:ImageCollectionCellIdentifier forIndexPath:indexPath];
Image *image =(self.data)[indexPath.row];
[cell configureCellWithImage:image];
return cell;
}
return nil;
}
And in the LZProfileCollectionViewCell cell I have this one:
-(void)configureCellWithImage:(Image *)image
{
[self setNeedsDisplay];
// Imagen
NSString *imageUrl = [kBaseURL stringByAppendingString:image.imageStringUrl];
[self.pictureImg setImageWithURL:[NSURL URLWithString:imageUrl] placeholderImage:[UIImage imageNamed:kDefaultLoadingImage]];
[self.pictureImg setContentMode:UIViewContentModeScaleAspectFit];
}
I have taken setImageWithURL from SDWebImage
After 3 minutes I get this snapshot in the Instruments(Statistics)
Thank you in advance
Well, you are caching every image in an array, so once you cache so many images you are eventually going to run out of memory. It doesn't happen on the simulator because the simulator has as much memory as the computer running it, so it is unlikely to ever run out of memory, a real device however will run out very fast. I'd handle the memory error yourself and empty the array, to free up new space for new images. Or better yet create your own caching mechanism to hold and clear images that haven't been in view for a while, either way you have to have some way to dump images after you hit a certain threshold.
In the method – didReceiveMemoryWarning you should free some memory of the array where you are stored the images. Maybe the first images that you are not displaying stored it a second array like an auxiliar array.
This is probably happening because a lot of UIImage objects end up in memory. You could instead cache this on disk. AFNetworking has a nice UIImageView category that does all of this automatically.
If you're not saving the Images in your dataSource array, then they're probably being retained somewhere else, which is a bug.
Also, make sure you're dequeuing cells and not creating new ones.
I've experienced similar situation so I'm replying on this issue though the reason seems a little different from yours.
I have a horizontal UICollectionView inside UITableViewCell. When I scroll UICollectionView right and left rapidly, the entire UI(even a home button) stops and after a while the app crashes with the message "Message from debugger: Terminated due to memory issue" on the console even without didReceiveMemoryWarning call.
I tested it with a UICollectionViewCell without any UI components but no changes. XCode Profile tool says it's actually keep allocating memory during the UI's stopped until it crashes even without any UI components in it.
The only suspicious point was that I was setting the width of a UICollectionViewCell's subview(custom UIView pinned to all superview edges) with AppDelegate.window.size.width * 0.35 to determine UICollectionViewCell's width and it was a float value like 130.25
When I hardcode this value to 130, all the crashes are gone. I think something was happening under the hood with this vague value of 130.25 and it's determining the width as 130 or 130.25 occasionally.
So I'm using CGFloat(Int(AppDelegate.window.size.width * 0.35)) now and everything's fine.

UIImageView not releasing memory (according to VM Tracker) - Low Memory Warnings & Terminate

I'm really struggling with running out of memory in my app. The are the basic operations that I'm performing, as specified below. Note that whenever a user takes a picture with the camera, I save two different versions of it--one is a preview (for fast loading), and one with high quality for zooming and marking up later.
Here is a list of my order of operations:
At app launch, show thumbnails of images that have been taken in the app (obviously, at first launch of the app, this list is empty).
Touch camera button to launch the UIImagePickerController
User takes a photo and saves it (again, a preview is saved and a full scale photo is saved)
The list of thumbnails refreshes and shows the new preview image. Again, the overall size of "Image IO" in the VM Tracker instrument is still very small (increased by maybe 0.5MB tops).
User touches one of the preview images, which launches a different view controller that does the following:
Create new UIImageView, load the image, add as a subview to View Controllers view
Suddenly, "Image IO" jumps by 30MB.
The wouldn't be a big deal, but when I go back (navigation controller pop), neither the "Image IO" type nor the "Dirty" type decreases in size. It's as if the UIImageView is not being destroyed. Every time I push this view controller and load an image, the size increases by 30MB, and popping never decreases it.
I'm using ARC, so I'm not calling release myself, but I would expect that it would be destroyed when I pop the view controller.
Just FYI, the settings I'm using for saving the preview and the full image are:
Preview: [UIImageJPEGRepresentation([UIImage imageWithImage:image scaledToSize:CGSizeMake(300, 400)], 0.4f) writeToFile:previewPath atomically:NO];
Full: [UIImageJPEGRepresentation(image , 0.8f) writeToFile:imagePath atomically:NO];
My main concern is (1) figuring out how to get rid of the memory used from the UIImage after I pop the containing view controller, but I'd also like to know (2) why the image is 30MB when loaded.
i'm worked on image editor app and this help me lot for reduce memory use.Try this
don't load images with [UIImage imageNamed:] for load image in ImageView,it's cause memory issue when no.of images are more,
use this,
NSString * imgpath= [ [ NSBundle mainBundle] pathForResource:#"imageName" ofType:#"png"];
ImageView.image=[UIImage imageWithContentsOfFile: imgpath];
As suggested by #Matic Oblak...
(1) I have already seen similar problem with image view, try setting its image to "nil" before popping the controller and see if the problem persists.
RESULT: I explicitly set the UIImage AND the UIImageView to nil (not sure if both are needed), and sure enough, it released the memory.

Resources