In short, I'm looking for a way to use a previously cached image as my placeholder in AFNetworking's UIImageView Category:
- (void)setImageWithURLRequest:(NSURLRequest *)urlRequest
placeholderImage:(UIImage *)placeholderImage
success:(void (^)(NSURLRequest *request, NSHTTPURLResponse *response, UIImage *image))success
failure:(void (^)(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error))failure
Example Use case:
I have a model named Post that I populate from an API response:
#interface Post : NSObject
#property ... NSString *smallImagePath;
#property ... NSString *largeImagePath;
#property ... NSString *description;
#end
My first ViewController will display a grid of thumbnails (retrieved via the smallImagePath property) for all my Post objects. Tapping on a thumbnail will take you to a DetailViewController, where the large image will ultimately display with the description.
Since I've already downloaded and cached the small thumbnail, I'd like to use it as the placeholder while I wait for the large image to be downloaded. At this point I can't figure out how to get into the AFImageCache since it's not publicly accessible.
One thing I thought of is storing the UIImage that is passed as an argument in the success block:
// add property to Post model
#property ... UIImage *smallImage;
success:^(NSURLRequest *request, NSHTTPURLResponse *response, UIImage *image) {
[self.post setSmallImage:image];
}
And then I can use that image directly as the placeholder while I download the large image.
I'm not sure if this will have any noticeable/negative impact to my memory footprint by keeping references to a bunch of UIImage's though. Can you think of a better way?
Ended up going with my original idea, which was to store the image as a property on my model. That way, when needing to declare a placeholder while the larger image loads, I just access that property. Performance has been great.
Related
UIImage view show previous image not update next one.this is my code
[userImage setImageWithURL:[NSURL URLWithString:[Engin shared].imagePath] placeholderImage:[UIImage imageNamed:#"dummyImge.png"]];
when i come first time on this view where i added this code in my viewdidload function ok first time image update successfully and after that i update a new image on server and get path of that image after uploaded on server and save him a separate class like that
[Engin shared].imagePath = [JSON valueForKey:#"image"];
and after that i go back to previous view and again come on that view and run this line again
[userImage setImageWithURL:[NSURL URLWithString:[Engin shared].imagePath] placeholderImage:[UIImage imageNamed:#"dummyImge.png"]];
but now this not update the image rather they show the previous image . when i check path of that image they show updated (mean) show new image but when i call function which image path they now previous image
NSUrlRequset *request = [NSUrlRequest requestWithURL:[NSURL URLWithString:[Engin shared].imagePath]];
[userImage setImageWithURLRequest:request
placeholderImage:[UIImage imageNamed:#"dummyImge.png"]
success:^(NSURLRequest *request, NSHTTPURLResponse *response, UIImage *image){
//handle success message here
}
failure:^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error){
//handle error message here
}];
i did not actually run this code , but you can test it your self.
I am using this code for displaying the image from URL to UIImageview
UIImageView *myview=[[UIImageView alloc]init];
myview.frame = CGRectMake(50, 50, 320, 480);
NSURL *imgURL=[[NSURL alloc]initWithString:#"http://soccerlens.com/files/2011/03/chelsea-1112-home.png"];
NSData *imgdata=[[NSData alloc]initWithContentsOfURL:imgURL];
UIImage *image=[[UIImage alloc]initWithData:imgdata];
myview.image=image;
[self.view addSubview:myview];
But the problem is that its taking too long time to display the image in imageview.
Please help me...
Is there any method to fast the process...
Instead of dispatch_async, Use SDWebImage for caching the images.
This is best I have seen...
The problem of dispatch_async is that if you lost focus from image, it will load again. However SDWebImage, Caches the image and it wont reload again.
The answers given to me on my own question Understanding the behaviour of [NSData dataWithContentsOfURL:URL] inside the GCD block does makes sense.So be sure that if you use [NSData dataWithContentsOfURL:URL] inside the GCD(as many developers do these days) is not a great idea to download the files/images.So i am leaning towards the below approach(you can either use NSOperationQueue).
Load your images using [NSURLConnection sendAsynchronousRequest:queue:completionHandler: then use NSCache to prevent downloading the same image again and again.
As suggested by many developers go for SDWebimage and it does include the above strategy to download the images files .You can load as many images you want and the same URL won't be downloaded several times as per the author of the code
EDIT:
Example on [NSURLConnection sendAsynchronousRequest:queue:completionHandler:
NSURL *url = [NSURL URLWithString:#"your_URL"];
NSURLRequest *myUrlRequest = [NSURLRequest requestWithURL:url];
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
[NSURLConnection sendAsynchronousRequest:myUrlRequest queue:queue completionHandler:^(NSURLResponse *response, NSData *data, NSError *error)
{
if ([data length] > 0 && error == nil)
//doSomething With The data
else if (error != nil && error.code == ERROR_CODE_TIMEOUT)
//time out error
else if (error != nil)
//download error
}];
Use Dispatch queue to load image from URL.
dispatch_async(dispatch_get_main_queue(), ^{
});
Or add a placeholder image till your image gets load from URL.
I have a UICollectionViewCell, in which it has a UIImageView abd I am using the AFNetworking UIImageView category to help me load the images. I've profiled my app and it seems that the memory spike is over the top, it just keeps increasing and increasing after profiling it via Instruments under allocation tools.
I am not sure what happened here and how to fix this. Cells are being reused all the time, but it's just that maybe these images aren't purged from the cache or something. Here's some code of how I am using it, it's pretty straightforward:
NSURLRequest *imageURLRequest = [NSURLRequest requestWithURL:[NSURL URLWithString:imageURLString] cachePolicy:NSURLRequestReturnCacheDataElseLoad timeoutInterval:30];
[self.imageView_ setImageWithURLRequest:imageURLRequest placeholderImage:nil success:^(NSURLRequest *request, NSHTTPURLResponse *response, UIImage *image) {
weakSelf.imageView_.image = image;
weakSelf.imageView_.alpha = 0.0;
[UIView animateWithDuration:0.3 animations:^{
weakSelf.imageView_.alpha = 1.0;
}];
} failure:^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error) {
}];
If I remove this code to download the image, then the memory footprint is way smaller. Any idea on what might be causing this?
Try to wrap it in autoreleasepool and check your memory usage
#autoreleasepool {
// Code that creates autoreleased objects.
}
I'm looking to set the image within a custom tableviewcell and use the height of the returned image within the heightForRowAtIndexPath method.
Is there anyway to get this returned data into the heightForRowAtIndexPath method?
Currently I'm using a local dictionary and setting it as per below:
NSURLRequest* urlRequest = [NSURLRequest requestWithURL:[NSURL URLWithString:recent.show.showImage] cachePolicy:NSURLRequestReturnCacheDataElseLoad timeoutInterval:30];
[cell.imageView setImageWithURLRequest:urlRequest placeholderImage:[UIImage imageNamed:#"icon"] success:^(NSURLRequest *request, NSHTTPURLResponse *response, UIImage *image) {
NSNumber *imageHeight = [[NSNumber alloc] initWithFloat:image.size.height];
NSString *indexString = [NSString stringWithFormat:#"%d", indexPath.row];
[self.returnedImages setValue:imageHeight forKey:indexString];
[imageHeight release];
} failure:^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error) {
}];
any pointers on a more efficient approach greatly appreciated.
Cheers
Nik
The way you're currently doing this in a NSDictionary is appropriate. Although to make sure it updates as quickly as possible you could add [self.tableView reloadData] to your success block, since then it will immediately refresh your tableview with the updated data. Even better you could use the reloadRowsAtIndexPaths:withRowAnimation: and pass it just the index path of the newly downloaded UIImage so it won't refresh the entire tableview.
Docs
I understand that mp3s sometimes contain album artwork (in my case, I'm working with podcast files). Is there a way in iOS to extract the image data from an mp3 file?
MP3s, including podcasts, do often have embedded metadata, including artwork. The easiest way to get the embedded metadata (for MP3s not in the iTunes library) is through the AVAsset class. If you are trying to get metadata over a network use the AVURLAsset instead, but accessing the metadata is the same with either.
AVAsset has an NSArray property named commonMetadata which contains instances of the class AVMetadataItem. You can iterate over this array to find the element with the commonKey of(usually) "artwork" The value property of this AVMetadataItem should be an NSDictionary of data about the artwork. Within this dictionary you will find the key "data" which should contain data you can use to construct a UIImage. This is quite nested and this description is admittedly confusing. So here is a code example.
NSURL *url = <# url of resource here #>;
AVAsset *asset = [AVAsset assetWithURL:url];
for (AVMetadataItem *metadataItem in asset.commonMetadata) {
if ([metadataItem.commonKey isEqualToString:#"artwork"]){
NSDictionary *imageDataDictionary = (NSDictionary *)metadataItem.value;
NSData *imageData = [imageDataDictionary objectForKey:#"data"];
UIImage *image = [UIImage imageWithData:imageData];
// Display this image on my UIImageView property imageView
self.imageView.image = image;
}
}
Again this is a very simple example of how to load from a local resource; If you are loading this over a network you should use an AVURLAsset. Also be careful of the blocking nature of this call.
Take a look at Apple's sample AddMusic project and you'll see how they get the album artwork via MPMediaItem & MPMediaItemArtwork objects.
-(UIImage *)getMP3Pic:(NSURL *)url
{
AVAsset *asset = [AVAsset assetWithURL:url];
for (AVMetadataItem *metadataItem in asset.commonMetadata) {
if ([metadataItem.commonKey isEqualToString:#"artwork"]){
return [UIImage imageWithData:(NSData *)metadataItem.value];
}
}
}