How to store image data in cache - ios

I have a mutable array of dictionary store image from url.So how to store image in application cache and i can view image when not have internet connection.
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath{
static NSString * CellIndentifier=#"CustomCell";
CustomCell *cell = (CustomCell*)[collectionView dequeueReusableCellWithReuseIdentifier:CellIndentifier forIndexPath:indexPath];
NSDictionary *dictVideo = [self.videoList objectAtIndex:indexPath.row];
[cell.indicator startAnimating];
//set title
NSString *titleVideo = [dictVideo objectForKey:#"Title"];
[cell.myLabel setText:titleVideo];
// set image url
NSString *urlVideo = [dictVideo objectForKey:#"Url"];
NSURL *url = [NSURL URLWithString:urlVideo];
cell.imageView.image = nil;
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSData *data = [NSData dataWithContentsOfURL:url];
UIImage * img = [[UIImage alloc]initWithData:data];
dispatch_sync(dispatch_get_main_queue(), ^{
NSLog(#"%ld %# %#",(long)indexPath.row,titleVideo,url);
[cell.imageView setImage: img];
[cell.indicator stopAnimating];
});
});
return cell;
}

you can use SDWebImage library as Umar Farooq said,Its support for both iOS 6 and 7
you can then use below code in your .m file
#import "UIImageView+WebCache.h"
UIImage *Noimg = [UIImage imageNamed:#"noimg.png"];
[cell.PlaceImg setImageWithURL:[NSURL URLWithString:strImgname] placeholderImage:Noimg];
Here Noimg is the placeholder image, its automatically manage by library when there is no image found on image location

NSURLCache already does this for you. Your application already has an implicit NSURLCache that may not have on disk persistence, so you want to explicitly set it:
NSURLCache *cache = [[NSURLCache alloc]initWithMemoryCapacity:(5 * 1024 * 1024) diskCapacity:(10 * 1024 *1024) diskPath:#"NSURLCache.db"];
[NSURLCache setSharedURLCache:cache];
And from that point on, the URL loading system will store data both in memory and on disk. This will allow it to be available when there is no connection. You continue to make requests as usual, they just return a lot faster when served out of the cache.
If you want to modify this behavior, you should modify the cache policy used by your NSURLRequest (for example, NSURLRequestReturnCacheDataElseLoad or NSURLRequestReturnCacheDataDontLoad). Unfortunately, you are using dataWithContentsOfURL: which does not provide an opportunity to do that. Using NSURLSessionTask or NSURLConnection would allow you to do so.
Keep in mind that the server does tell the client how long a response is valid for, and the default cache policy does obey that.

Related

Cache UIImage using uitableview from url

I hava a uitableview , with custom cell containing two UImages. The logo images are taken from an online website, that's why there's a need to cache the images. Loading the image till now is made like this :
NSURL * imageURL = [NSURL URLWithString:[arra1 objectAtIndex:indexPath.row / 2]];
NSData * imageData = [NSData dataWithContentsOfURL:imageURL];
NSURL * imageURL2 = [NSURL URLWithString:[arra2 objectAtIndex:indexPath.row / 2]];
NSData * imageData2 = [NSData dataWithContentsOfURL:imageURL2];
cell.ima1.image = [UIImage imageWithData:imageData];
cell.ima2.image2 = [UIImage imageWithData:imageData2];
What i learned from searching , is that dataWithContentsOfURL is not asynchronous , and while scrolling it will take a lot of time. I tried several methods but i can't seem to get to right one. This is my first time caching UIImages , i would highly appreciate a detailed explanation with implementation so i could learn aside from getting the job done.
Many Thanks
I use this Library which is just perfect
SDWebImage
You just need to #import <SDWebImage/UIImageView+WebCache.h> to your project, and you can define also the placeholder when image is being downloaded with just this code:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *MyIdentifier = #"MyIdentifier";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:MyIdentifier];
if (cell == nil)
{
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:MyIdentifier] autorelease];
}
// Here we use the new provided setImageWithURL: method to load the web image
[cell.imageView setImageWithURL:[NSURL URLWithString:#"http://www.domain.com/path/to/image.jpg"]
placeholderImage:[UIImage imageNamed:#"placeholder.png"]];
cell.textLabel.text = #"My Text";
return cell;
}
It also cache downloaded images and gives you great performance.
Hope it will help you!
SDWebImage, in my opinion, is the best option.
You simply include it in your app and use it like this:
SDWebImageManager *manager = [SDWebImageManager sharedManager];
[manager downloadWithURL:[NSURL URLWithString:image_url]
options:0
progress:nil
completed:^(UIImage *images, NSError *error, SDImageCacheType cacheType, BOOL complete) {
myImageView.image = images;
}] ;
It download images asynchronously, so it does not block UI.
You can check these sample application
LazyTableImages - Sample application from Apple
MonoTouch-LazyTableImages
robertmryan- LazyTableImages - Explains clearly the limitations from apple's sample application.
Hope this helps.
Checkout UIImageLoader https://github.com/gngrwzrd/UIImageLoader
Easy to load an image, and you get callbacks for all the scenarios you would want to handle:
NSURL * imageURL = myURL;
[[UIImageLoader defaultLoader] loadImageWithURL:imageURL \
hasCache:^(UIImage *image, UIImageLoadSource loadedFromSource) {
//there was a cached image available. use that.
self.imageView.image = image;
} sendRequest:^(BOOL didHaveCachedImage) {
//a request is being made for the image.
if(!didHaveCachedImage) {
//there was not a cached image available, set a placeholder or do nothing.
self.loader.hidden = FALSE;
[self.loader startAnimating];
self.imageView.image = [UIImage imageNamed:#"placeholder"];
}
} requestCompleted:^(NSError *error, UIImage *image, UIImageLoadSource loadedFromSource) {
//network request finished.
[self.loader stopAnimating];
self.loader.hidden = TRUE;
if(loadedFromSource == UIImageLoadSourceNetworkToDisk) {
//the image was downloaded and saved to disk.
//since it was downloaded it has been updated since
//last cached version, or is brand new
self.imageView.image = image;
}
}];

Best way to cache UIImage not from web

Hi i have high resolution image in local(document). Now i want to list all local images in thumb nail size. I am using UICollectionView to show image while loading resize the image from high resolution to thumbnail. So scrolling speed lack in collectionView. Now i have confusion with how can i cache that resized thumbnail images. which one is the best option to do that. I have used SDWebimages to download and cached images from web. Thank you
- (UICollectionViewCell *)collectionView:(UICollectionView *)cv cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
// NSLog(#"%i path", indexPath.row);
CollectionViewCell *cell = [cv dequeueReusableCellWithReuseIdentifier:#"CollectionViewCell" forIndexPath:indexPath];
ImageData *imgData = [self readFromLocal:indexpath.item];
UIImage *thumb = [self resizeImgToTumb:imgData.image];
[cell.imgView setimage:thumg];
return cell;
}
I recommend storing the images on the file system in app-specific storage and reading those files when necessary. Use the NSFileManager class to accomplish this. Be sure you write code to clean up any files you create so that they don't take up too much space on the user's device.
UIImage *image = ...;
NSData *imageData = UIImagePNGRepresentation(image);
NSString *rootDirectory = [NSSearchPathForDirectoriesInDomain(NSDocumentDirectory, NSUserDomainMask, YES) firstObject];
NSString *imagePath = [rootDirectory stringByAppendingPathComponent:#"filename.png"];
NSError *error;
[imageData writeToFile:imagePath options:NSDataWritingAtomic error:&error];
If you are not completely worried about memory consumption you can create an NSDictionary and add your resized image to that. You will need to remove the objects from the dictionary once they are far enough off screen to be reloaded later.
[dictionary setObject:yourSmallImage forKey:indexPath];
Then retrieve it later:
UIImage *image = [dictionary objectForKey:indexPath];
if(!image)
{
//Load it from your source, no cache found
}

UIImage not loading URL

My UIImage is not loading the URL I specified with [NSData datawithContentsOfURL:], even though the URL is (seemingly) correct.
Here's my configureCell: method:
- (void)configureCell:(UITableViewCell *)cell atIndexPath:(NSIndexPath *)indexPath
{
NSString *url = [photoUrls objectAtIndex:indexPath.section];
cell.imageView.image = [UIImage imagewithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:url]]];
}
Here's what is inside of the "photoUrls" array (excluding bullet points):
(
http://fbcdn.net/fakeurl1/839.jpg
http://fbcdn.net/fakeurl2/840.jpg
http://fbcdn.net/fakeurl3/841.jpg
)
I wouldn't use [nsdata dataWithContentsOfURL:] in configure cell as it will block the UI. And also the way you have it, it will redownload all the images over and over again.
The following links will help you download the data asynchronously with out blocking the UI.
http://www.raywenderlich.com/4295/multithreading-and-grand-central-dispatch-on-ios-for-beginners-tutorial
https://github.com/rs/SDWebImage
With This SDWebImage all you have to do is:
[cell.imageView setImageWithURL:[NSURL URLWithString:urlString]
placeholderImage:[UIImage imageNamed:#"placeholder.png"]];
it will take care of downloading the image.
Your URLs don't point to valid images. All I see is "Not found. Back to facebook". Your call to [NSData dataWithContentsOfURL:] will get the data, but [UIImage imageWithData:] will fail to generate a UIImage because the data doesn't represent a valid image.

Response Lag in UITableView displaying JSON data

In my application I am parsing JSON data and then displaying that data in UITableView. The information is displayed in the table but the touch response is lagging really bad. I did some research and found out that it is recommended to implement asynchronous loading for the information and especially the image, but I could not find any relevant solutio that worked with my JSON application. I would appreciate some suggestions and comments of how to sort out this problem, here is the code:
jURL Definition www.website.com/info.json
- (void)viewDidLoad
{
[super viewDidLoad];
dispatch_async(jQueue, ^{
NSData* data = [NSData dataWithContentsOfURL:
jURL];
[self performSelectorOnMainThread:#selector(fetchedData:)
withObject:data waitUntilDone:NO];
});
}
- (void)fetchedData:(NSData *)responseData {
NSError* error;
NSDictionary* jsonDict = [NSJSONSerialization
JSONObjectWithData:responseData
options:kNilOptions
error:&error];
calRes = [jsonDict objectForKey:#"results"];
[self.tableView reloadData];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
NSDictionary *calDict = [calRes objectAtIndex:indexPath.row];
NSURL *imageURL = [NSURL URLWithString:[calDict objectForKey:#"image"]];
NSData *imageData = [NSData dataWithContentsOfURL:imageURL];
UIImage *imageLoad = [[UIImage alloc] initWithData:imageData];
cell.textLabel.text = [calDict objectForKey:#"name"];
cell.detailTextLabel.text = [calDict objectForKey:#"description"];
cell.imageView.image = imageLoad;
return cell;
}
I like to use the AFNetworking library for easy asynchronous image loading. You will need to include the library
#import "AFNetworking.h"
Then use it in the cellForRowAtIndexPath
[cell.imageView setImageWithURL:[NSURL URLWithString:[calDict objectForKey:#"image"]]
placeholderImage:[UIImage imageNamed:#"placeholder"]];
You also need to supply a placeholder image. I use a blank JPG of the size you will want the final image will be.
You can use SDWebImage from this link : https://github.com/rs/SDWebImage
It is the simplest and fastest library for asynchronous image loading.
It also provides image caching.
You can just done the whole operations by simply calling this function:
[cell.imageView setImageWithURL:jURL
placeholderImage:[UIImage imageNamed:#"your place holder here"]];
Just enjoy it.
As a follow up, it will be good to cache the images locally using this very handy plugin by Jake Marsh: JMImageCache
This way, the images will not need to be loaded from the URL the next time you boot the app.
See, the lagging in cellforRowAtIndexPath is because of this
NSURL *imageURL = [NSURL URLWithString:[calDict objectForKey:#"image"]];
NSData *imageData = [NSData dataWithContentsOfURL:imageURL];
UIImage *imageLoad = [[UIImage alloc] initWithData:imageData];
why don't you, use GCD for adding the image? Further more you can have your own NSCache to store the image so that every time the table reloads, the image can be directly loaded from the memory instead of firing the url.

Easy load images asynchronously in a UITableView using GCD

After two days searching this answer I found a solution.
Use GCD for download images asynchronously.
Use NSMutableDictionary for save images in memory.
I found the solution explained here by Duncan C:
http://iphonedevsdk.com/forum/iphone-sdk-development/104438-grand-central-dispatch-tableview-images-from-the-web.html
How to implement:
- (void)viewDidLoad
{
(...)
dispatch_queue_t mainQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
imagesDictionary = [[NSMutableDictionary alloc] init];
(...)
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
(...)
NSData *imageData = [imagesDictionary objectForKey:#"IMAGE URL"];
if (imageData)
{
UIImage* image = [[UIImage alloc] initWithData:imageData];
imageFlag.image = image;
NSLog(#" Reatriving ImageData...: %#", #"IMAGE URL");
}
else
{
dispatch_async(mainQueue, ^(void) {
NSString *url = #"IMAGE URL";
NSData *imageData = [[NSData alloc] initWithContentsOfURL:[NSURL URLWithString:url]];
UIImage* image = [[UIImage alloc] initWithData:imageData];
imageFlag.image = image;
[imagesDictionary setObject:imageData forKey:#"IMAGE URL"];
NSLog(#" Downloading Image...: %#", #"IMAGE URL");
});
}
(...)
}
The project in GitHub: https://github.com/GabrielMassana/AsynchronousV2.git
I know that if the project is large and with a lot of cells I can go run out of memory. But I think this solution is a nice approach for a newbie.
What do you think about the project?
If the project is really large, the best option is to save the images in disk? But then the problem is the possibility to go run out of memory in the disk, isn't it? Maybe, then, we need a mechanism to delete all the images from disk.
Use NSCache to hold your downloaded images instead of NSDictionary. This will manage itself in low memory situations and remove items that haven't been accessed for a while. In that situation, they will be loaded from the URL again if needed.

Resources