I have an app with 150 local images (about 500kb each). I have loaded them all into an array like this:
allPics = [[NSMutableArray alloc] init];
//NSString *imagePath;
NSArray *result = [database performQuery:#"SELECT image_path FROM validWords order by valid_word"];
for (NSArray *row in result) {
NSString *temp = [row objectAtIndex:0];
NSLog(#"%#", temp);
//imagePath = temp;
UIImage *newImage = [UIImage imageNamed:temp];
[allPics addObject:newImage];
}
When I set my UIImageView later to one of these pics, it hangs my interface up for a second, due to lazy loading from what I have read. I tried to prerender them, but that spiked my memory usage to over 3gb before it got a third of the way through my images. Should I be looking to use a background thread to render the image when I need it? When I reduced the image total to 4, once all 4 were rendered once, the transitions between them was seamless.
I appreciate any and all tips and solutions!
Yes, I would suggest a background thread and paging. If the user is looking at image 7, you should load images, say, 5,6,8 and 9. If the user then moves onto image 8, you can discard image 5 and lazy load image 10. This way the user sjhould be able to move through your images without a significant memory or performance overhead.
You can then also add heuristics such as 'if the user is paging through the images very quickly, don't load any images until they slow down',
Another tip is to store a very low resolution version of the image (say, a 50kb version) and store that at a different path. Then you can show the thumbnail images to the user and only lazy load in the high res image if the user stops on that image for a period of time.
Finally, be careful when you talk about image sizes. Is the 500KB compressed or uncompressed? If it is a 500KB compressed JPeg, the actual image on the device could be vastly bigger. A jpg with fairly uniform colour and a reasonably high level of compression can be very small on disk, but decompressed it could be a massive image. This could be another source of the lag you experience.
Related
Right now I'm loading images view a file url and its taking a long time. I've even put it on the main thread as a high priority but its still slow. This is actually in a loop for like 6 images. My question is:
Is there a faster way to load images to a view than this? Like an alternative to a file url?
//check the filetype
if ([fileType isEqual: #"image"])
{
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
//get image
NSURL *imageFileUrl = [[NSURL alloc] initWithString:file.url];
NSData *imageData = [NSData dataWithContentsOfURL:imageFileUrl];
dispatch_async(dispatch_get_main_queue(), ^{
imageView.image = [UIImage imageWithData:imageData];
});
});
}
My images are at a quality of this preset and the size is the size of the iphone screen whether that be a 5,6,or 6 plus
self.camera = [[LLSimpleCamera alloc] initWithQuality:AVCaptureSessionPreset1280x720
position:CameraPositionBack
videoEnabled:YES];
thanks,
While there isn't any shortcut to loading the images there are things you can do to help.
Initially only load the image that are visible, after that load any that may be visible soon.
If the size of the image is larger than the view create a smaller image, perhaps a couple for different screen resolution devices.
Create a low resolution image versions, load them first to get something up for the user and then load the full resolution images replacing the low resolution version..
Please, Add the sizes of the images and the sizes of the UIImageView and screen resolution to the question.
What can I suggest you that,
Save the duplicate copy of each image in lower resolution/Size on
server.
Load the duplicate image on first load.
When user particularly views an image, load original image then.
I am creating a Keyboard Extensions, which shows animated GIF images, at a time it shows only max 2MB of images distributed in 20 GIFs all together.
Whenever the GIFs are loaded, the memory consumption increases by more than 8MB, which is causing Memory Pressure, and the keyboard is getting terminated as soon as it is loaded.
I am using SDWebImage to show the GIFs.
Can anyone suggest me, how should I use GIFs, or is it good idea to show animated GIFs in keyboard extensions? Why is the extension taking up so much memory?
Thank You.
SDWebImage has a lot of features so the object would be very heavy. Try to use FLAnimatedImage. It is made specifically for showing GIFs
Try to use FLAnimatedImage with Nuke which automatically integrates FLAnimatedImage for you. FLAnimatedImage is the best in class animated GIF engine for iOS at the moment.
The problem with SDWebImage is that they have their own implementation of animated GIFs. They iterate thought all the frames and create bitmaps upfront:
for (size_t i = 0; i < count; i++) {
CGImageRef image = CGImageSourceCreateImageAtIndex(source, i, NULL);
duration += [self sd_frameDurationAtIndex:i source:source];
[images addObject:[UIImage imageWithCGImage:image scale:[UIScreen mainScreen].scale orientation:UIImageOrientationUp]];
CGImageRelease(image);
}
FLAnimatedImage on the other hand creates bitmaps on-demand and intelligently caches them.
When the GIFs are loaded they will be decompressed so they will take a lot more space in memory. This is because iOS needs the images in raw bitmap format in order to edit or display them.
The GIF format has a very good compression ratio with the limitation of having only a 8bpp color space. So a 4x compression in your case is very likely.
What is the most efficient way to iterate over the entire camera roll, open every single photo and resize it?
My naive attempts to iterate over the asset library and get the defaultRepresentation results took about 1 second per 4 images (iPhone 5). Is there a way to do better?
I need the resized images to do some kind of processing.
Resizing full resolution photos is rather expensive operation. But you can use images already resized to screen resolution:
ALAsset *result = // .. do not forget to initialize it
ALAssetRepresentation *rawImage = [result defaultRepresentation];
UIImage *image = [UIImage imageWithCGImage:rawImage.fullScreenImage];
If you need another resolution you can still use 'fullScreenImage' since it has smaller size than original photo.
(CGImageRef)fullScreenImage
Returns a CGImage of the representation that is appropriate for
displaying full screen. The dimensions of the image are dependent on
the device your application is running on; the dimensions may not,
however, exactly match the dimensions of the screen.
In iOS 5 and later, this method returns a fully cropped, rotated, and
adjusted image—exactly as a user would see in Photos or in the image
picker.
Returns a CGImage of the representation that is appropriate for
displaying full screen, or NULL if a CGImage representation could not
be generated.
I have the following issue:
I have a primary view object (that inherits from UIView) that displays a grid of 16 squares (each is a class I created that inherits from UIImageView), in a 4x4 layout.
Each of these 16 squares is 160x160, and contains an image (a different image for each square) that is no bigger than 30kb. The image, however, is 500x500 (because it is used elsewhere in the program, in its full size), so it gets resized in the "square" class to 160x160, by the setFrame method.
By looking at the memory management feature of Xcode when the app is running, I've noticed a few things:
each of these squares, when added to the primary view object, increase the memory usage of the app by 1MB. This doesn't happen at instantiation, but only when they are added by [self addSubview:square] at the primary view object.
if I use the same image for all the squares, the memory increase is
minimal. If I initialize the square objects without any images, then
the increase is basically zero.
the same app, when running in the simulator, uses 1/6 of the memory
it does on an actual device.
The whole point here is: why is each of the squares using up 1MB of memory when loading a 30kb image? Is there a way to reduce this? I've tried creating the images in a number of different ways: [UIImage imageNamed:img], [UIImage imageWithContentsFromFile:path], [UIImage imageWithData:imgData scale:scale], as well as not resizing the frame.
When you use a 500x500 image in a smaller UIImageView, it's still loading the larger image into memory. You can solve this by resizing the UIImage, itself (not just adjusting the frame of the UIImageView), making a 160x160 image, and use that image in your view. See this answer for some code to resize the image, which can then be invoked as follows:
UIImage *smallImage = [image scaleImageToSizeAspectFill:CGSizeMake(160, 160)];
You might even want to save the resized image, so you're not constantly encumbering yourself with the computational overhead of creating the smaller images every time, e.g.:
NSData *data = UIImagePNGRepresentation(smallImage);
[data writeToFile:path atomically:YES];
You can then load that PNG file corresponding to your small image in future invocations of the view.
In answer to your question why it takes up so much memory, it's because while the image is probably stored as a compressed JPG or PNG in persistent storage, I suspect in memory it's held as an uncompressed bitmap. There are many internal formats, but a common one is a 32-bit format with 8 bits each for red, green, blue, and alpha. Regardless of the specifics, you can quickly see how a 500 x 500 pixel representation, with 4 bytes per pixel could translate to a 1 mb of memory. But a 160 x 160 image should be roughly one tenth the size.
I have an NSManagedObject which has pictures that are stored somewhere like /var/mobile/Applications/.../.../uniqueIDforNSMO/Pictures/
I have no problem getting these pictures off the disk by finding them based on the NSMO's uniqueID, I have a DataController that will pull them for me, and that is very performant. But I run into issues when I try to add these pictures to a view, the UI becomes blocked. I suspect the problem is that these images are very high resolution (they are images from the iPad camera roll).
Displaying 5 images takes about 3 seconds, leaving the UI blocked. Displaying even just 2 images blocks the damn UI. Here's how I add them, on a background thread:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
UIImage *image = [self.photos objectAtIndex:index];
dispatch_async(dispatch_get_main_queue(), ^{
cell.imageView.image = image;
});
});
If I set the imageView.image without dispatching it takes even longer.
Could the problem be that the images are so large, while the cell's imageViews are only 150x150? Or is it because I only have a reference to the image at first, so the realization of the image is the slow part?
Can anyone suggest anything here? I have tried resizing the images before they are returned in the array and that did not help.
The problem is that the images are being lazily loaded - UIImage only loads the image into memory when it needs to be drawn. What you can do is eagerly load the image on a background thread:
Create a CGBitmapContextRef using UIGraphicsBeginImageContext
Draw the image into the context
Get a new image from the context using UIGraphicsGetImageFromCurrentImageContext
Pass the image back to the main thread
As they are high-resolution images, I would suggest that you should make persistent thumbnails cache for that. Generating thumbnail real-time every time would waste too much CPU time unnecessarily.