I'm making a camera app in iOS. Everything is fine except from the memory usage. This is the flow of the "takeImageTask":
AVCaptureStillImageOutput -> NSData -> UIImage (which I crop and do other changes to) -> save to ALAssetsLibrary
I need to keep the UIImage in an array because I need to have them in a UICollectionView, and I have the possibility to show the images in a "big size" view.
This flow uses a lot of memory, so what I thought could be a better solution was to use the AssetURL I get from the AVCaptureStillImageOutput writeImageToSavedPhotosAlbum to fetch the images from the phone storage. My app also have the possibility to fetch images from the photo album by using UIImagePickerController, and I've noticed that this just uses a fraction of the memory compared to the "takeImageTask". I first made the app as an Android version, and there I just stored all the URLs, and used them to present images. Does anyone have any experience using asseturls to present images in iOS?
I need to keep the UIImage in an array because I need to have them in a UICollectionView, and I have the possibility to show the images in a "big size" view.
This is flawed logic. You might want to keep a minimal size thumbnail image in memory for the collection view, but you should even drop these from memory before the count gets too big. And the big images should be loaded from disk on demand because it is relatively infrequent and relatively expensive (using the asset URL is a good plan for this).
Related
I have an app which is photo-based, and had a ton of large-scaled resolution images on Parse. I currently have my app set up to grab these images from Parse with a query, and storing each photo into an UIImage array with a loop, and then displaying these photos in a UICollectionView.
It works great, if we are pulling less than 10 photos from Parse. However, if I am retrieving, say 20 photos, when I scroll down my UICollectonView after the photos have been loaded, around the 18th or so photo, my app will crash, and Xcodes console will output "Received memory warning".
What is the best practice for retrieving a large amount of large sized photos from Parse? (If you are displaying them in a UICollectionView)
I would download only the thumbnails. You can even do this using lazy loading (http://www.theappguruz.com/blog/ios-lazy-loading-images) if so inclined.
When the image is clicked on and you want to see it full size, then you download the full size image :)
Set a limit on how many images you want to retain in memory, and use a queue to decide when to store/get rid of the old images.
I know this is a stupid problem, but this is my first real app that I have to make, I have no one to ask and I looked up this problem and found no other similar problems.
My app crashes on real devices with no exception. I saw in the simulator that uses too much RAM and after a while I got to the conclusion that the pictures I am using are to blame.
The app is structured in this way: it has 8 viewControllers for different things: for example, it starts with one which lets the user select the avatar with which he/she will play and here I have two pictures, next is a viewController which shows the stats for that avatar and here it is another picture and so on. The problem is that each picture uses 40MB of RAM to be displayed and things add up so the app uses more than 300MB of RAM when the user gets to the gameviewCOntroller where the game is. Because of this, on devices like iPAD 2 or iphone 4 it crashes, but not on iphone 5.
I tried to set the images both from "images.xcassets" and from a ".atlas" folder, but the result is exactly the same. The pictures have a dimension of no more than 1500x1999px, they are in png format.
Also, I saw that if the app were to start directly into the gaveViewController it would use 180MB so the other viewController remain in memory or something like that. Should I "clear" them or something similar?
//-------update-------
This is what I got from Instruments:
Memory is a big deal on mobile devices, there is not a clear answer to you question, but I can give you some advices:
If your images are plain colors or have symmetric axes use resizable images. You can just use one line of pixel multiplied by with or height to cover the entire screen using a small amount of memory
Image compression doens't have effects when the image is decompressed. So if you have a png that is 600kb and you are thinking that converting in a 300kb will lower memory usage is only true for "disk space" when an image is decompressed in memory the size is widthXheightXNumber_of_channelXbit_for_channel
resize images: if are loading a 2000px square image into memory and you show it inside an image view of 800 px square, resize before adding it.You will have just a peak while resizing, but later it will use less memory
If you need to use big images, use tiling techniques such as CATiledLayer
If you don't need an image anymore get rid of it. It's ok to have an array of path to images, but not an array of full uncompressed images
Avoid -imageNamed it caches images and even if Apple says that this cache is released under memory pressure, you don't have a lot of control on it and it could be too late to avoid a crash
Those are general advices, it's up to you if they fit your requirements.
You should definitely follow Andrea's advices.
Additionally you should consider setting the image size to exactly what your need is. You're saying that you've tried to set them from xcassets so you have full control over the images you're loading, which is great (compared to downloading an image that you cannot modify).
I highly suggest you read some documentation on using Asset catalog files. This will allow you to have high-resolution image for bigger screens that also have more memory, and smaller ones for older devices, which is what you want here.
Also, note that 1500x1999px is still a very big size for most mobile devices.
More links about screen-size:
https://developer.apple.com/library/ios/documentation/UserExperience/Conceptual/MobileHIG/IconMatrix.html
http://www.paintcodeapp.com/news/iphone-6-screens-demystified
I use SDWebImage to download images asynchronously to my UIImageViews. Most images that received are of acceptable sizes, and can be easily downloaded, and set to the UIImageViews.
However there are times, when the source image at the URL is of insanely high resolution (relative to the size of my imageView - 60x60). In such cases, the image is never set. Sometimes my app crashes, sometimes, nothing happens (image stays nil), and in a very rare case I received an error something like : Unable to allocate 400000 bytes of memory (I am not entirely sure of the exact error log, I apologise).
For example, this image of the pinterest icon from Pinterest's site itself is enormous (10000 x 10000). My imageView can never plot this image. For the time being, I have hardcoded to replace the image with this, but I know this is bad practice. Also, this is just one case, there might be infinitely more images such as this that might screw with the user experience.
How can I handle such cases ?
Just put a check for size of image that you receive and when size much greater than what your imageview would accept then you could resize the image ,and here link for resizing the image link. After resizing then you could place it in imageview. Hope helpful for you.
I'd suggest you to use this AsyncImageView. I've used it and it work wonders. To call this API:
ASyncImage *img_EventImag = alloc with frame;
NSURL *url = yourPhotoPath;
[img_EventImage loadImageFromURL:photoPath];
[self.view addSubView:img_EventImage];
It's same as using UIImageView. Easy and it does most of the things for you. AsyncImageView includes both a simple category on UIImageView for loading and displaying images asynchronously on iOS so that they do not lock up the UI, and a UIImageView subclass for more advanced features. AsyncImageView works with URLs so it can be used with either local or remote files.
Loaded/downloaded images are cached in memory and are automatically cleaned up in the event of a memory warning. The AsyncImageView operates independently of the UIImage cache, but by default any images located in the root of the application bundle will be stored in the UIImage cache instead, avoiding any duplication of cached images.
The library can also be used to load and cache images independently of a UIImageView as it provides direct access to the underlying loading and caching classes.
SDWebImage is open-source library. Add sources to your project and append your size-check in SDWebImageDownloader.m
I'm building a camera application that saves the image data to a single JPEG file in the sandbox. The images average at about 2mb in size.
Problem : I cannot display the images in a photo viewer because having a few images in memory throws memory warnings and makes scrolling through the images very slow.
I cannot split the image into tiles and save them to disk because that's even more expensive than displaying the single image. I tried splitting the image up into tiles upon capture, but on the 5S it took, on average, 5 1/2 seconds to save all the tiles to disk. It will only get worse from there as the app is executed on older devices. This is very bad because what if the user exists the app in the middle of the save? I will have missing tiles and no uncompressed original file to find missing tiles later.
Question : what's the best way to show a full sized image without causing memory issues and keeping the scrolling fast? Tons of camera applications on the App Store do this and the Photos app does this, there has to be a good solution.
Note : I'm currently showing a thumbnail of the image and then loading the full size image from disk in another thread. Once the full size image loading has finished, I present the full size image on the main thread. This removes the memory issues because I only have one full size image in memory at once, with two thumbnails, but still causes lagging on the scrollview because drawing the full size image in the main thread is still pretty expensive.
I would greatly appreciate any input!
you could..
create a down sized thumb nail..
create a smaller image and save that in a different "sandbox" folder.. and read that for browsing.. then after that load the image if the user wants to look at it full size.
One way to deal with this is to tile the image.
You can save the large decompressed image to "disk" as a series of tiles, and as the user pans around pull out only the tiles you need to actually display. You only ever need 1 tile in memory at a time because you draw it to the screen, then throw it out and load the next tile. (You'll probably want to cache the visible tiles in memory, but that's an implementation detail. Even having the whole image as tiles may relieve memory pressure as you don't need one large contiguous block.)
This is how applications like Photoshop deal with this situation.
Second way which I suggest you is to
check the example from Apple for processing large images called PhotoScroller. The images have already been tiled. If you need an example of tiling an image in Cocoa check out cimgf.com
Hope this will helps you.
I am writing an app that relays an image saved on the iOS device to a larger screen. Since a fullRes image is too large and takes long to transfer (using an on-device CocoaHTTP server), I am trying to load thumbnail first.
In Windows, we have a thumbs.db, which means that if we access that, there is no image-resizing etc ... its a thumbnail version of the image pre-saved by the OS.
Does the [UIImage imageWithCGImage:asset.aspectRatioThumbnail] for the ALAsset class in iOS does the same action, or does it load the complete hi-res image and then scales it down before returning?
The documentation does not specify but from my experiments reading the thumbnail is 5 times faster than loading the image from disk (even without decoding or scaling it). I assume iOS stores the pre-made thumbnails somewhere for fast access.