I have an app that allows a user to go into their gallery and select photos from their photo library to add to the app, it is displayed in a tableview
This is then added to an array which after UIImagePickerController fetches the image, reloads the collection view with the new images.
My problem is that this uses large amounts of memory. I need a way to display the photos added exactly like the native photos app on the iPhone.
I've looked in lazy loading but I have no idea where to begin.
Could someone please tell how I would go about displaying images in a UICollectionView with lazy loading, or at least reducing the amount of memory used.
The app is generally at normal use using around 10mb of memory. This increases to 50mb+ when displaying a multitude of images in the collectionView.
Thanks.
Holding high-resolution images into an array will generally be problematic. Also, using the full resolution images for thumbnail sized image views in collection view is an extravagant use of memory.
So, when the user selects an image, capture a reference to that asset's URL. Then, as images are required by cellForItemAtIndexPath, retrieve the image, resize it to thumbnail dimensions (e.g. you could use something like this) and use that in your cell's image view.
If you want to be elegant about it, implement a NSCache in which you'll cache previously resized images, but make sure you have reasonable retention rules and have that purge itself upon memory pressure. That way, cellForItemAtIndexPath can see if the image exists in its cache, and if so, use that, otherwise go back to the assets library and resize that image. But by using cache, you can speed up the process of scrolling back through images that were previously resized.
But the key is to avoid holding high resolution images in memory. And if you're going to hold even the thumbnails in memory, you might want to capture that in something like a NSCache rather than an array.
Related
I'm creating an iOS app where I need to use many photos(in big resolution). There're almost 400 photos. I want to use them to show in UIImageView on the different pages.
But it's a big bunch of data. So I'm afraid about efficient(not so much) and size of the app itself. What's the best way to use these photos in my iOS app? Does it make sense to create New Image Set and create 1x,2x and 3x photos for each photo or do I have to use other features I don't now about?
UPD1:
The app is about sights. From the tableview user can chose any sight and will be shown screen with 1-3 photos( in CollectionView if there's more than 1 photo) and some text. All data(except photos) I'm getting from sqlite db using CoreData.
The photos are shown on the screen have width equal to screen width (the height 25:38 from width). Width is bigger:)
I want to use big resolution photos because I want show full-size
photos by tap.
Each photo can be 2000px or more on the biggest
side.
The app is an offline app so I want to store photos in the
app. I now just about image assets. But are there other ways to
store photos?
I have an idea to store them in high-resolution and resize to any device(3x, 2x, 1x) to display on the screen. And load high-resolution version of a photo just when user taps on it to show the photo on the full screen.
You cannot load all the photos from the app - this is btw. not possible because of memory issues during loading images.
You should at the beginning think about presenting photos and photos itself.
Do you really need to show photos in big resolution?
What big resolution means? Is that 1024x1024 or native resolution or what?
From where these photos are loaded? App includes it or you'll load them from system library? I see you're writing about image assets, probably you want to keep them in the app.
Do you need to show this images in constant almost 0 time? Maybe keeping it on the backend and fetching and cache when necessary will be better approach. With this you can decrease size of the app.
You can keep in assets only the biggest one - the #3x and resize it on devices that need this in runtime before displaying it.
To keep photos in device's memory you can use NSCache - Apple's Documentation and let system managing the photos. You'll load this and cache in the instance of NSCache and every time you want to access it and get photo you have to check if there is photo or nil and if nil you have to re-load it again from the disk. This will somehow keep your device to not get memory warnings and will automatically managing photos.
Another important thing to think is to how you plan to show the photos. Is is possible to show only 1 at a time or there is more to show? Do you want to present thumbnails of them and after user select some photo show it in the big resolution? Using thumbnails will have less memory footprint.
Update 1:
UPD1: The app is about sights. From the tableview user can chose any places and will be shown screen with 1-3 photos( in CollectionView if there's more than 1 photo) and some text. All data(except photos) I'm getting from sqlite db using CoreData.
The app is an offline app so I want to store photos in the app. I now just about image assets. But are there other ways to store photos?
You don't need assets catalog for this. Just keep it in some directory in the project and load them from /Documents directory when necessary. IMO this will be the best possible approach to store and use locally. Keeping them in the assets will cause that there will be #1x and #2x empty in xcassets file. And keeping them directly in project will be easier to maintain I think.
I want to use big resolution photos because I want show full-size photos by tap.
Each photo can be 2000px or more on the biggest side.
I have an idea to store them in high-resolution and resize to any device(3x, 2x, 1x) to display on the screen. And load high-resolution when user taps on the photo to show photo on the full screen.
Keeping such big photos in the app is poor idea, the app will be big. Even if one photo will be 1mb size which I think is not possible and there are 400 photos the app will be fat.
You should consider keeping them online, really.
You also know all the images in the app so you can create thumbnails in real time and cache them in /Cache directory for later use. The same with big photos for specified device screen resolution. Before displaying resize it and save in /Cache directory for future next time use.
I have simple application, who consumes data from local core data storage. I am displaying images in collectionview & it lags when i scroll. All images are from local bundle itself.
Can anyone help?
Thanx.
Store all the images in an array once and use that array instead of fetching images from core data everytime the cell loads.
A typical solution to this would be to use the proxy design pattern and to load images asynchronously on a background thread.
You could have a UIImageView subclass that shows a placeholder image or a blank rectangle, but that is automatically updated once the image has been loaded. Some apps even use this to make a fancy "polaroid fade" effect, i.e. fade the image from white or black when it goes on-screen.
To prevent placeholder images from ever being seen (although you'd want to test this on device, as local storage might be fast enough to make this unnoticeable), you could pre-cache images in advance (for example by anticipating a number of extra rows in your cellForRowAtIndexPath method)
In my app, I have a UITableView which displays fairly large images and loads a moderately designed Xib file to display it in. Each image is around 700KB to 1MB in size. The flow is virtually never ending, it loads more and more as we scroll down. So you can imagine that I am running into memory issues.
I have tried using SDImageCache and NSCache. The former used disk memory for caching images. In both cases, the caches somehow didn't clear images automatically. I had to manually clear them when I got a Received memory warning prompt. And each time I clear these caches, the memory freed seems to be lesser each subsequent time.
Now I confused as to which cache strategy I must use for such a long list of images. Might I be having some leaks somewhere? They certainly didn't show up when I profiled the app.
P.S.: I am loading the images from the web. Just to be clear.
From the docs:
UIImage
+(UIImage *)imageNamed:(NSString *)name
Discussion 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.
So I guess leaving this to the UIImage class is a good approach.
Hope this helps!
As we implemented it in both Android and iOS: once you can show on the screen only 2-3 images.
Load in memory 2 more for the downwards scroll and 2 more for the upwards one. So you have in memory 7 images. Display them. The other images must be stored in files (when you download them). If the user scrolls too fast, do not show all the sequence of the images, instead show some "loading" icons in place of the images. When the scrolling stops, show the appropriate image + the previous one + the next one + prepare 2 more (for upwards scroll) and 2 more for downwards scroll.
I am building my first project that is an interactive ebook app for the iPad
I started with the Single View App template from XCode
So far, the project is mostly a series of block animated transitions between UIImageViews and MPMovieController videos, very serial so far
Everything is coded within a single view under a single view - the image views fade in and out with alpha animations
I am beginning to run into memory issues. I've used memory instruments and see that most everything is loaded into memory at the beginning (images from the InterfaceBuilder) aside from some videos instantiated at runtime
My question is - how should I reorganize my code to better utilize memory? Should I separate into different views under one view controller, or have multiple view controllers?
And which might be the most straight forward to implement?
Images are very memory-intensive. So:
Do not load an image until you actually need it (for display). When you are done with it (the image view is no longer visible), release it (by setting that image view's image to nil). Do not maintain the images in an array or anything like that. Do not create the image views preloaded with images in advance in the nib.
When you actually need an image for display, load it in code using imageWithContentsOfFile:, not imageNamed:. Thus you prevent caching of the image.
It is a waste of memory to work with an image larger than the display size. If these images are large, you can save a lot of memory up front if you load the image at the actual size needed for display. This is easy to do with Image IO framework and CGImageSourceCreateThumbnailAtIndex.
I think you need to use multiple view controllers and separate your code in small separated views and objescts that controll you app flow..
On the one hand it's better to have multiple views and so on..but it's a pain to rewrite code you worked on (unless it's absolutely necessary).
In my opinion if you don't have anything unnecessary in memory (i.e. when you take a view off screen you release the memory it used) you don't need to do anything.
After all, even if you split the code as it should have been, if your memory management is good, it will take the exact same memory.
You should be able to do this with a single view controller:
Load your images lazily from code. Keep an array of the image names rather than the images themselves, and load them just before you need them.
Make sure your images aren't larger than they need to be.
Recycle your image views. If the user won't see more than two image views at a time (including both the from and to in a transition), you should only have two image views.
Don't worry about whether the images are cached; iOS's caches are designed to release their contents under stress. That said, do not implement your own caching system. You might not release images properly under stress. If you need caching, use NSCache.
Imagine you use a web service that delivers images via an API. Every image has an UID and you have a Core Data entity Image with attributes uid (int) and image (transformable).
Now a gallery of your app needs to show many images (the UIDs are known). You don't know which of the images have been downloaded and stored before. How can you lazily download images with core data in the background, so that the view may show an UIActivityIndicator during loading and automatically shows the image as soon as it is stored locally (e.g. by using the NSFetchedResultsControllerDelegate protocol)?
Is it useful to subclass UIImageView for that purpose?
Yes, you can use Core Data for this, but be forewarned that there is a performance hit when you use Core Data to store images. It's negligible if you're dealing with small (e.g. thumbnail) images, but for very large images, the performance hit is observable. For large images, I'd store the images in Documents folder (or, better, a subfolder), and use Core Data to keep track of what images have been downloaded, their filenames, etc. But if the images are smaller, keeping everything right there in Core Data is cleaner.
I probably would not want to use a subclassed UIImageView for this purpose, because you might want to decouple the presentation layer (the image view) from the caching of images. Also, for sophisticated user interfaces, UIImageView objects may be discarded or reused as the user scrolls through a big collection of images, so you might not want a hard link between the UIImageView and your caching logic. Also, depending upon your user interface, sometimes the images can dictate something broader than the UIImageView (e.g., if you're using a tableview, you might want to adjust the cell height based upon the image as it's downloaded). The particulars of the implementation of what the UI might do depend upon where the image is being used (a UITableView, a UIScrollView that is showing a grid of images, etc.).
So, bottom line, for simple user interfaces, perhaps you could subclass a UIImageView but I'd generally advise to have some custom image caching object that does the lazy loading with some delegate protocol to tell the UI when the image load is complete.
I wouldn't use Core Data to store images, but I'd store them on disk. I was trying to find in the documentation where Apple themselves warn against storing images larger than 100k or so in Core Data since you'd run into a performance issue.
However, I found this article that talks about Core Data Image Caching that may be of use.
Also, here's another Stack Overflow post with a good answer that tells you when to store images in a database and when to just use references to disk storage.
If you don't absolutely need Core Data, then may I recommend using MWPhotoBrowser?
https://github.com/mwaterfall/MWPhotoBrowser
It essential generates a gallery view controller for you which you can push onto your navigation controller. The gallery view controller has scrollable images with pinch zoom, pan, everything, even emailing the photo to someone.
It also does the lazy loading of the image with the activity indicator.
Short answer: everything you wanted to do without reinventing the wheel.