I want to save multiple images to my documents path folder. My web service returns more than one image url path and i want to save each image to my documents folder.This process working successfully, but my view FREEZES at this time(converting image url to NSData).I am using following code.How to avoid this issue?
NSData * data = [NSData dataWithContentsOfURL:[NSURL URLWithString:[[usersArray objectAtIndex:i]objectForKey:#"message"]]];
NSString *localImgpath = [directoryPath stringByAppendingPathComponent:[NSString stringWithFormat:#"%#.%#",imageName, #"png"]];
[data writeToFile:localImgpath atomically:YES];
You would want to process that in a background thread like this:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSData * data = [NSData dataWithContentsOfURL:[NSURL URLWithString:[[usersArray objectAtIndex:i]objectForKey:#"message"]]];
NSString *localImgpath = [directoryPath stringByAppendingPathComponent:[NSString stringWithFormat:#"%#.%#",imageName, #"png"]];
[data writeToFile:localImgpath atomically:YES];
});
Related
I am creating a app in which i am saving some user detail and their messages, This app works similar to Facebook or many other i mean first time data come from api's and then i will store it in our local database. So the data which is loaded once is store in local database and user can see it without internet connection. I have store messages and detail in sqlite database it works fine with text messages and save detail of user.
The Problem is that i want to save profile image with user detail in local storage, i don't want to save it in database but save it into app directory for that i am using this below code
UIImageView *ProfileImage = [[UIImageView alloc]initWithFrame:CGRectMake(5, 1, 30, 30)];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0), ^{
NSData *imageData =[NSData dataWithContentsOfURL:[NSURL URLWithString: [NSString stringWithFormat:#"http://ton.hiitap.com/p/%#",[TOUSERUDID objectAtIndex:i]]]];
NSString *imagePath = [[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0] stringByAppendingPathComponent:[NSString stringWithFormat:#"/%#.png",[TOUSERUDID objectAtIndex:i] ]];
[imageData writeToFile:imagePath atomically:YES];
UIImage* img = [UIImage imageWithData:imageData];
NSData *pngData = UIImagePNGRepresentation(img);
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsPath = [paths objectAtIndex:0]; //Get the docs directory
NSString *filePath = [documentsPath stringByAppendingPathComponent:[NSString stringWithFormat:#"%#-%d.png",[TOUSERUDID objectAtIndex:i],i]]; //Add the file name
[pngData writeToFile:filePath atomically:YES];
pngData = [NSData dataWithContentsOfFile:filePath];
UIImage *img2 = [UIImage imageWithData:pngData];
dispatch_async(dispatch_get_main_queue(),^{
ProfileImage.image=img2;
});
});
The Problem with this code is it only save 2 users profile image and the images which are repeated are not shown when no internet connection is available. Can anyone suggest how to save all users profile images even they are repeated. So that they are shown when no internet connection is available. Thanks in advance.
if you are using core data you can create a column for example userImage with the type NSData in your user model
and you can convert you image to NSData like this :
if PNG image :
UIImage *image = [UIImage imageNamed:#"imageName.jpg"];
NSData *imageData = UIImageJPEGRepresentation(image, 1.0);
if JPG image :
UIImage *image = [UIImage imageNamed:#"imageName.jpg"];
NSData *imageData = UIImageJPEGRepresentation(image, 1.0);
and you can load the image from the database like this :
NSManagedObject *selectedObject = [[self yourFetchCOntroller] objectAtIndexPath:indexPath];
UIImage *image = [UIImage imageWithData:[selectedObject valueForKey:#"image"]];
// and set this image in to your image View
yourimageView.image=image;
I am doing the following when I get an image back from the server. But this code is bringing my app to its knees. It freezes up the UI.
Can this be run on a background thread in iOS? Can I use async?
if (![NSString isEmpty:user.avatarURL])
{
NSString *pathToImage = user.avatarURL;
NSURL *url = [NSURL URLWithString:pathToImage];
NSData *data = [NSData dataWithContentsOfURL:url];
UIImage *image = [[UIImage alloc] initWithData:data];
[[NSUserDefaults standardUserDefaults] setObject:UIImageJPEGRepresentation(image, 1) forKey:kUserImage];
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString* path = [documentsDirectory stringByAppendingPathComponent:#"userAvatar.png" ];
NSData* jpegdata = UIImageJPEGRepresentation(image, 1);
[jpegdata writeToFile:path atomically:YES];
}
As #valentin says, you can do everything inside the if() statement in a dispatch_async() call.
Note I suspect what’s probably slowing you down is actually the -dataWithContentsOfURL:, not the UIImageJPEGRepresentation(), so you’ll want to make sure that’s inside your dispatch_async, not outside.
Also, I’m not clear why you’re decompressing the data into an image, then re-compressing it. You’re going to get artifacts doing this, and with most services the avatar image is going to be compressed anyhow.
I’d do the following:
if (user.avatarURL)
dispatch_async(dispatch_get_main_queue(), ^{
NSURL *const imageURL = [NSURL URLWithString:user.avatarURL];
if (!imageURL)
return;
NSData *const imageDdata = [NSData dataWithContentsOfURL:imageURL];
if (!imageDdata.length)
return;
[[NSUserDefaults standardUserDefaults] setObject:imageDdata forKey:kUserImage];
[imageDdata writeToFile:[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).lastObject stringByAppendingPathComponent:#"userAvatar.png"] atomically:YES];
});
The line which is doing most damage is:
NSData *data = [NSData dataWithContentsOfURL:url];
Because it does the download from the network. Nothing in your code updates the UI so it can all be run on a background thread. Just ensure that if you post a notification or subsequently update the UI after the image is saved that you switch back to the main thread.
I'm downloading an image and then displaying it with UIImageView. Using this approach the image is downloaded every time the view is loaded. How would I go about storing it locally to avoid an unnecessary server request?
[self requestData ] ;
UIImage *userImage = [UIImage imageWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:userProfileImageUrl ]]] ;
[self.profileImageView setImage:userImage ] ;
I would suggest using a library like SDWebImage that handles caching and asynchronous download.
Very first time, you have to save the image into NSDocumentsDirectory. For that you have to take the path of directory and append imageName like this
NSString * documentsPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
NSString * documentsPathAppend = [documentsPath stringByAppendingFormat:#"/%#",[userProfileImageUrl lastPathComponent]];
And you have to write image with folowing condition
if(![[NSFileManager defaultManager]fileExistsAtPath:documentsPathAppend])
{
NSData *data = [NSData dataWithContentsOfURL:[NSURL URLWithString:userProfileImageUrl]]];
[data writeToFile:documentsPathAppend atomically:YES];
}
After that you have to add this path into your local storage like core data. From next time you have to check whether image is there or not for particular URL in your core data.If it is there, then fetch the path from your core data and show like this
[self.profileImageView setImage:[UIImage imageWithContentsOfFile:imageEntity.imagePath]];
try this method to store UIImage in Local Directory..
- (void)addImage:(UIImage *)image toCacheWithIdentifier:(NSString *)identifier {
NSString *folderPath = #"LOCAL DIRECTORY PATH ";
if(![[NSFileManager defaultManager] fileExistsAtPath:folderPath isDirectory:nil])
{
[[NSFileManager defaultManager] createDirectoryAtPath:folderPath withIntermediateDirectories:YES attributes:nil error:nil];
}
NSString *fileName = [NSString stringWithFormat:#"%#.png",identifier];
fileName = [folderPath stringByAppendingPathComponent:fileName];
NSData *imageData = UIImagePNGRepresentation(image);
[imageData writeToFile:fileName atomically:YES];
}
to retrieve image from Local Directory try this method...
- (UIImage *)imageFromCacheWithIdentifier:(NSString *)identifier
{
NSString *folderPath = #"LOCAL DIRECTORY PATH ";
NSString *fileName = [NSString stringWithFormat:#"%#.png",identifier];
fileName = [folderPath stringByAppendingPathComponent:fileName];
if([UIImage imageWithContentsOfFile:fileName])
{
return [UIImage imageWithContentsOfFile:fileName];
}
return nil;
}
One other viable approach is to utilize NSURLCache (see also: Understanding Cache Access) in the official documentation.
The HTTP Cache was invented exactly for this requirement: Caching in HTTP in RFC 2616.
Unfortunately, HTTP Caching is an advanced topic, and the exact behavior of NSURLCache is quite obscure.
I'm trying to load images using the following method.
I first check if I already have the images on the disk, if I do then I will just get the image data from the disk and load it otherwise I will get the image from the server and write to disk so that the second time that I need the image I won't have to access the server.
The problem is it doesn't seem to write or read from the disk. Everytime that I want to load images for the second time it's still reads them from the server and the NSLog(#"disk"); never gets called.
I don't know what I'm doing wrong if anyone has any idea?
-(UIImage *)imageWith:(NSString *)imageName isPreview:(BOOL)preview
{
//imageName is something like "56.jpg"
NSString *mainOrPreview = #"Preview";
if (!preview) {
mainOrPreview = #"Main";
}
NSString *pathSuffix = [[#"Images" stringByAppendingPathComponent:mainOrPreview] stringByAppendingPathComponent:imageName];
NSString *path = [[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0] stringByAppendingPathComponent:pathSuffix];
NSData *imageData = [NSData dataWithContentsOfFile:path];
if (imageData) {
NSLog(#"disk");
}
if (!imageData && [self connected]) {
imageData = [NSData dataWithContentsOfURL:[NSURL URLWithString:[serverURL stringByAppendingPathComponent: pathSuffix]]];
if (imageData) {
[imageData writeToFile:path atomically:YES];
}
NSLog(#"server");
}
return [UIImage imageWithData:imageData];
}
The problem is that the directories don't exist beyond Documents. Therefore the attempts to write the files are failing. It's always a good idea to use the file methods that have the NSError parameters so you can check the result.
You need to update only the code that actually writes the image from the server.
if (!imageData && [self connected]) {
// This needs to be done in on a background thread!
imageData = [NSData dataWithContentsOfURL:[NSURL URLWithString:[serverURL stringByAppendingPathComponent: pathSuffix]]];
if (imageData) {
[[NSFileManager defaultManager] createDirectoryAtPath:[path stringByDeletingLastPathComponent] withIntermediateDirectories:YES attributes:nil error:nil];
[imageData writeToFile:path atomically:YES];
}
NSLog(#"server");
}
I am making an app that downloads files from an online server by clicking an Icon representing the file to be downloaded, then the file will be saved to the app's sandbox.
I have managed to do those mentioned above. Now, what I want to do is to put a "Check" image on the icon to allude the user that it is already downloaded.
I already tried it by putting another UIImageViewon top of the icon when the download is successful but when I fully terminate the app and re-open it, the image is gone and I need to download it again to show it.
How will I able to that?
Just keep an array of all the downloaded files, and at startup check to see which files to apply the check mark to.
For example:
When you get the image:
NSMutableArray *array;
array = [[NSUserDefaults standardUserDefaults] objectForKey:#"downloaded"].mutableCopy;
if (!array) array = [[NSMutableArray alloc] init];
[array addObject:somethingToIdentifyTheImageBy]; //For example, a string with the name of the image
[[NSUserDefaults standardUserDefaults] setObject:array forKey:#"downloaded"]
On startup:
NSArray *downloadedImages = [[NSUserDefaults standardUserDefaults] objectForKey:#"downloaded"];
forin (NSString *string in downloadedImages)
{
//Apply check mark
}
Use following code to Save your Downloaded Image to Library Directory of Device and Retrive it, as NSUserDefaults will not allow you to store Large amount of Data.
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES);
NSString *libraryDirectory = paths[0];
NSString *imageFilePath = [libraryDirectory stringByAppendingPathComponent:#"myImageIcon.jpg"];
NSError *error;
if (![[NSFileManager defaultManager] fileExistsAtPath:imageFilePath]) {
// File Exist at Library Directory, Just Read it.
NSData *imageData = [NSData dataWithContentsOfFile:imageFilePath];
}
else
{
// File Doesn't Exist at Library Directory, Download it From Server
NSURL *url = [NSURL URLWithString:#"YOUR Image File URL"];
NSData *imageData = [NSData dataWithContentsOfURL:url];
NSString *imagePath = [libraryDirectory stringByAppendingPathComponent:[url lastPathComponent]];
NSLog(#"Image Stored At %#",imagePath);
[imageData writeToFile:imagePath atomically:YES];
}
You cannot save anything inside the app's bundle, but you can store the image in your app's documents directory by using [NSData dataWithContentsOfURL:] method. This is not exactly permanent, but it stays there at least until the user deletes the app.
NSData *imageData = [NSData dataWithContentsOfURL:myImageURL];
NSString *imagePath = [[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0] stringByAppendingPathComponent:#"/myImage.png"];
[imageData writeToFile:imagePath atomically:YES];