Asynchronous images - Meant for displaying images from web only? - iphone

I have images in my documents folder which I am displaying on one of my screens. It takes times to load the images and display them on the screen similar to when loading images from web. As far as I know asynchronous imageView works for the later case. I might be wrong.
Is there anyway we can display images from documents folder asynchronously?

Take a look at SDWebImage. It is a UIImageView subclass that lets you display image asynchronously from a URL and with a useful cache. It is designed to work with Internet URLs, but I think it will also go with internal URLs.

Put the loading of images in the background thread as following
-(void)backgroundLoadImageFromPath:(NSString*)path {
UIImage *newImage = [UIImage imageWithContentsOfFile:path];
[myImageView performSelectorOnMainThread:#selector(setImage:) withObject:newImage waitUntilDone:YES];
}
Then call that thread wherever you need to set the image
[self performSelectorInBackground:#selector(backgroundLoadImageFromPath:) withObject:path];
Note, in backgroundLoadImageFromPath you need to wait until the setImage: selector finishes, otherwise the background thread's autorelease pool may deallocate the image before the setImage: method can retain it.

Related

UIImage in uitableViewcell slowdowns scrolling table

I am loading image with data in my table view. Images are coming from web. I created method to get image url in model class.Model class has Nsdictionary and objects.
But this images is slowing down scrolling .Here is my code
UIImage *image = [UIImage imageWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:[NSString stringWithFormat:#"%#",
[(Tweet *)[recentTweets objectAtIndex:indexPath.row]urlString]]]]];
cell.imageView.image = image;
Please tell Where I am going wrong?
Use lazy loading and do your own drawing. Try to understand the techniques on the sample projects I linked. These are the best ways to improve the performance of tables with images.
here is the methodology I use for loading images into a UITableView from a remote location:
in your .h file, declare a mutable dictionary for storing the images:
NSMutableDictionary *images;
initialize the dictionary in -init... or in -viewDidLoad
images = [[NSMutableDictionary alloc]init];
in the .m, tableView:cellForRowAtIndexPath:, see if the image exists in your dictionary for the indexPath
UIImage *img = [images objectForKey: indexPath];
if the image does exist, just set it.
if (img) cell.imageView.image = img;
if the image does NOT exist, set the cell's image to a temporary image...
if (!img) cell.imageView.image = [UIImage imageNamed:#"imageUnavailable.png"];
AND add that image to your dictionary so it doesnt try to refetch the image if you scroll off and back to that image before it loads...
[images setObject:[UIImage imageNamed:#"imageUnvailable.png"] forKey: indexPath];
then, in this same block, use an NSOperationQueue, and a custom NSOperation ( here is a reference - NSOperation and SetImage) to get your image and call back into your UITableViewController with that image.
in your callback, add your image to the dictionary (overwriting the temp image) and call [tableView reloadData]
this will give you a nice non blocking user experience.
The are a couple of ways how to do it. I had the best experience with a Queue for the HttpRequests, which I pause during the scrolling process.
I highly recommend this framework for that:
http://allseeing-i.com/ASIHTTPRequest/
I also implemented an image cache, which only loads the images if there weren't in the cache.
And the last tweak was to actually draw the images instead of using a high level uicomponent like the UIImageView
The number one reason your code is slow right now is because you're making a network call on the main thread. This blocks an UI from being updated and prevents the OS from handling events (such as taps).
As already suggested, I recommend ASIHTTPRequest. Combine asynchronous requests with an NSOperationQueue with a smaller concurrency count (say, 2) for the image requests, caching, and reloading the rows when images come in (on the main thread) (also only reloading the row if its currently visible). You can get a smooth scrolling table view.
I have an example of this on github here: https://github.com/subdigital/iphonedevcon-boston
It's in the Effective Network Programming project and includes a set of projects that progressively improve the experience.
Download the images before you load the tableView, and store them in an NSArray. Then when the cellForRowAtIndexPath method is called, show a loading image until the image is in the array.

Load UIImages correctly? Memory Management with "Leaks"

i´ve created a little game in which images are loaded when the user is touching the screen while moving. It depends on the screen position to load different images immediately.
I´ve got an UIImageView and because within the "touchesMoved" function, i´m loading the different images like this:
imageView.image = [UIImage imageNamed: [photos objectAtIndex: newImage ] ] ;
Now i want to improve my memory management using Instruments with "Allocations" & "Memory Monitor". Here´s i´m setting different snapshots with "Mark Heap" points and look for leaks. The line above is highlighted and now i want to know what´s wrong with it.. How can i improve that image-loading (without caching)?
Read this Dispelling the UIImage imageNamed: FUD
and also read the links in the question as well. Should answer everything you need.
First of all: imageNamed: does cache images.
If you're getting this line highlighted then there are 2 possible reasons (or both):
you're not properly releasing imageView
you're not properly releasing the photos array
+imageNamed returns an autoreleased object, yet the #property image in UIImageView retains it through its use until overwritten. -objectAtIndex: must return an object with a retain count of 1 which is not released.
If that indeed is the problem then the fix is
imageView.image = [UIImage imageNamed: [[photos objectAtIndex: newImage ] autorelease]];
However I doubt that is the real issue here.

Memory Management

I need a clarification from all of you,That is I am implementing an iPad application. In that I tried to download and animate the images. The image count should be more than 100,000.The code I used to download and adding to the view is as follows.
UIImageView* imageView=[[UIImageView alloc]initWithFrame:CGRectMake(0, 0,100,100)];
NSData *receivedData=nil;
receivedData = [NSData dataWithContentsOfURL:[NSURL URLWithString:#"http://path/prudently/iphone/image_s/e545afbf-4e3e-442e-92f9-a7891fc3ea9f/test.png"]];
imageView.image = [[UIImage alloc] initWithData:receivedData] ;
[subView addSubview:imageView];
[imageView release];
But I am getting exception after I successfully added more than 8000 image to my subview. I am getting exception at getting data from the url. And one more thing I am not releasing the subview because once I downloaded them I need to animate the subview.
Please give me your suggessions
Thank you,
Sekhar Bethalam.
100,000 images would seem a lot for desktop applications, let alone a smart phone like an iPhone. Is there not another approach you can take to solve this problem that wouldn't need such a high resource count?
You can write the URL , images or something to a cached file, and divide some pages to animate the images...
When the user press a page link , application read and animate the images of this page, images of the page which user don't use need not display.
The only way you are going to accomplish this is to dynamically create and destroy the UIImageViews as they are needed on the screen. The iPhone/iPad/iAnything are incapable of doing what you want because of the limited memory available on the device.

AssetsLibrary and ImageView -setImage Slowness

So this one is pretty odd ad I'm not sure if the trouble is with the AssetsLibrary API, but I can't figure out what else might be happening.
I am loading an array with ALAssets using the -enumerateAssetsUsingBlock method on ALAssetsGroup. When it completes, I am loading a custom image scroller. As the scroller finishes scrolling, I use NSInvocationOperations to load the images for the currently visible views (pages) from the photo library on disk. Once the image is loaded and is cached, it notifies the delegate which then grabs the image from the cache and displays it in an image view in the scroller.
Everything works fine, but the time it takes from when -setImage: actually gets called to the time it actually shows up visibly on the screen is unbearable--sometimes 10 seconds or more to actually show up.
I have tried it both with and without image resizing which adds almost nothing to the processing time when I do the resizing. As I said, the slowdown is somewhere after I call -setImage on the image view. Is anyone aware of some sort of aspect of the AssetLibrary API that might cause this?
Here's some relevant code:
- (void)setImagesForVisiblePages;
{
for (MomentImageView *page in visiblePages)
{
int index = [page index];
ALAsset *asset = [photos objectAtIndex:index];
UIImage *image = [assetImagesDictionary objectForKey:[self idForAsset:asset]];
// If the image has already been cached, load it into the
// image view. Otherwise, request the image be loaded from disk.
if (image)
{
[[page imageView] setImage:image];
}
else {
[self requestLoadImageForAsset:asset];
[[page imageView] setImage:nil];
}
}
}
This will probably mess up any web searches looking to solve problems with the AssetsLibrary, so for that I apologize. It turns out that the problem wasn't the AssetsLibrary at all, but rather my use of multi-threading. Once the image finished loading, I was posting a notification using the default NSNotificationCenter. It was posting it on the background thread which was then updating (or trying to update, at least) the UIImageView with -setImage. Once I changed it to use -performSelectorOnMainThread and had that selector set the image instead, all was well.
Seems no matter how familiar I get with multi-threading, I still forget the little gotchas from time to time.

UIButton setBackgroundImage consumes a lot of memory

I'm using the following code:
UIImage *buttonImage;
if (p.placeImage != nil) {
buttonImage = [UIImage imageWithData:p.placeImage];
} else {
buttonImage = [UIImage imageNamed:#"bg_place_noimg.png"];
}
[imageButton setBackgroundImage:buttonImage forState:UIControlStateNormal];
When executing the app with Instruments I can see the setBackgroundImage consumes a lot of memory. However, if I comment the last line it doesn't happen. Any possible reason?
EDIT:
If p.placeImage == nil and imageNamed:#"bg_place_noimg.png" is used memory usage is normal. p.placeImage is a Transformable value I use in Core Data to store images NSData downloaded from Internet.
I'm not surprised that commenting out the last line causes less memory to be consumed. When you set that image as the background of your button, the image is most likely retained by the button and so the image remains in memory. If you don't apply the image as the button background, the UIImage's retain count is 0 and so its memory can be reclaimed by the system if necessary.
Kristopher's theory about the difference between imageWithData and imageNamed is also correct. Check out the Discussion section for each of those initializers in the documentation for UIImage.
I'm not sure, but I would guess that your problem is that imageWithData: creates a whole new image each time, whereas the imageNamed: method returns the same image over and over again.
You may need to add some code to cache and reuse images that are identical. For example, maybe you could use a URL as a key into a dictionary of images, and only create new images for URLs that have not been loaded before.