AFNetworking downloading thousands asynchronous images slows down app - ios

I am downloading thousands of images asynchronously through AFNetworking an storing them in iDevice but my app goes slow down when errors shows on console "Response time out"
following is the code that I used to download images.
[NSThread detachNewThreadSelector:#selector(DownloadImages) toTarget:self withObject:nil];
-(void)DownloadImages
{
for(int i = 0; i<=4600;i++)
{
NSString *FrameSmall = [NSString stringWithFormat:#"myimageurl%i.png",i];
[self setbuttonImg:FrameSmall];
}
}
-(void)setbuttonImg:(NSString *)str
{
NSArray* badWords = #[#":", #"/", #".",#" "];
NSMutableString* mString = [NSMutableString stringWithString:str];
for (NSString* string in badWords) {
mString = [[mString stringByReplacingOccurrencesOfString:string withString:#""] mutableCopy];
}
NSString *encoded = [str stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
NSURLRequest *urlRequest = [NSURLRequest requestWithURL:[NSURL URLWithString:encoded]];
AFHTTPRequestOperation *requestOperation = [[AFHTTPRequestOperation alloc] initWithRequest:urlRequest];
requestOperation.responseSerializer = [AFImageResponseSerializer serializer];
[requestOperation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
NSString * documentsDirectoryPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
[self saveImage:responseObject withFileName:mString ofType:#"png" inDirectory:documentsDirectoryPath];
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
}];
[requestOperation start];
}
-(void) saveImage:(UIImage *)image withFileName:(NSString *)imageName ofType:(NSString *)extension inDirectory:(NSString *)directoryPath {
if ([[extension lowercaseString] isEqualToString:#"png"]) {
[UIImagePNGRepresentation(image) writeToFile:[directoryPath stringByAppendingPathComponent:[NSString stringWithFormat:#"%#.%#", imageName, #"png"]] options:NSAtomicWrite error:nil];
} else if ([[extension lowercaseString] isEqualToString:#"jpg"] || [[extension lowercaseString] isEqualToString:#"jpeg"]) {
[UIImageJPEGRepresentation(image, 1.0) writeToFile:[directoryPath stringByAppendingPathComponent:[NSString stringWithFormat:#"%#.%#", imageName, #"jpg"]] options:NSAtomicWrite error:nil];
} else {
// ALog(#"Image Save Failed\nExtension: (%#) is not recognized, use (PNG/JPG)", extension);
}
}

All of your images are being downloaded at the same time which isn't a good idea.
You can set the max concurrency with the operationQueue on the AFHTTPRequestOperationManager
http://cocoadocs.org/docsets/AFNetworking/2.0.0/Classes/AFHTTPRequestOperationManager.html#//api/name/operationQueue
AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
manager.operationQueue.maxConcurrentOperationCount = 5; //set to max downloads at once.

Best practice would be to only load images that your user is going to see immediately - so the ones in view only. Typically that means just storing the URL, then loading the image when its actually needed. Using just a custom category on UIImageView (AFNetworking provides a similar category) you can load an image into a custom table view cell using:
- (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;
Here's an example with a custom wrapper around that category:
- (void)tableView:(UITableView *)tableView
willDisplayCell:(GameTableViewCell *)cell
forRowAtIndexPath:(NSIndexPath *)indexPath
{
cell.backgroundColor = [UIColor clearColor];
id game = [self.games objectAtIndex:indexPath.row];
if([game isKindOfClass:[Game class]])
{
Game *aGame = (Game *)game;
cell.titleLabel.text = aGame.gameName;
cell.descriptionLabel.text = aGame.gameDescription;
cell.playGameButton.layer.cornerRadius = 8.0F;
[cell.imageView loadImageFromRemoteURL:aGame.imageURL
withPlaceholder:[UIImage imageFromAssetsNamed:#"game_icon"]
completionHandler:^(UIImage *fetchedImage, NSError *error)
{
if(nil == error)
{
aGame.image = fetchedImage;
// Note: Need to set the image in an imageView somewhere on the main thread.
}
}];
}
}
This means that only game cells on screen will have their images loaded rather than loading them all at once.

Related

Appending image in collectionView using AFNetworking

I am new for Ios app development. I am using AFNetworking for image and data load, data are binned in collection view but unable to bind image.
This is my code for collection view and service:
#import "ViewController.h"
#import "LabelCollectionViewCell.h"
#interface ViewController ()
{
NSMutableArray *idArray;
NSMutableArray *namelabelArray;
UIImage *imagArray;
NSMutableArray *dic ;
}
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
[[self userCollection]setDataSource:self];
[[self userCollection]setDelegate:self];
[self dataJson];
NSLog(#"array: %#", namelabelArray);
}
-(NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView
{
return 1;
}
-(NSInteger) collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section
{
return [namelabelArray count];
}
-(UICollectionViewCell *) collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *cellIdentifier = #"cell";
LabelCollectionViewCell *customCell = [collectionView dequeueReusableCellWithReuseIdentifier:cellIdentifier forIndexPath:indexPath];
[[customCell nameLabel]setText:[namelabelArray objectAtIndex:indexPath.item]];
[[customCell idLabel]setText:[idArray objectAtIndex:indexPath.item]];
return customCell;
}
-(void)dataJson
{
NSString *zipcode;
NSString *url = [NSString stringWithFormat:#"%#",#"http://inveera.biz/lowkall_api/index.php/product_cat"];
NSDictionary *params =[NSDictionary dictionaryWithObjectsAndKeys: zipcode,#"35005",nil];
NSLog(#"Login URL %#", params);
AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
[manager POST:url parameters:params success:^(AFHTTPRequestOperation *operation, id responseObject) {
NSLog(#"Login JSON: %#", responseObject);
NSString *data = [responseObject valueForKey:#"data"];
idArray = [data valueForKey:#"id"];
namelabelArray =[data valueForKey:#"name"];
imagArray =[data valueForKey:#"img_path"];
NSLog(#"Error image: %#", imagArray);
[self.userCollection reloadData];
}
failure:^(AFHTTPRequestOperation *operation, NSError *error)
{
NSLog(#"Error: %#", error);
}];
}
My json response is
{
"status":"true",
"city":[{"cityname":"Acmar"}],
"data":[
{"id":"1","name":"Appliances","img_path":"categories\/1\/ac1.png","status":"1"},
{"id":"2","name":"Electronics","img_path":"categories\/2\/ac2.png","status":"1"},
{"id":"3","name":"Furniture","img_path":"categories\/3\/ac3.png","status":"1"},
{"id":"4","name":"Cars","img_path":"categories\/4\/ac4.png","status":"1"},
{"id":"5","name":"Pet Supplies","img_path":"categories\/5\/ac5.png","status":"1"},
{"id":"6","name":"Others","img_path":"categories\/6\/ac6.png","status":"1"}
]
}
I am able to bind name but unable to bind image with full path with domain
name.
Please help me out. Thanks in advance.
In AFNetworking Lib itself they have added one category class for UIImageView called UIImageView+AFNetworking to load images from URL, You need call below method with valid URL request.
- (void)setImageWithURLRequest:(NSURLRequest *)urlRequest
placeholderImage:(UIImage *)placeholderImage
success:(void (^)(NSURLRequest *request, NSHTTPURLResponse * _Nullable response, UIImage *image))success
failure:(void (^)(NSURLRequest *request, NSHTTPURLResponse * _Nullable response, NSError *error))failure
-(UICollectionViewCell *) collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *cellIdentifier = #"cell";
LabelCollectionViewCell *customCell = [collectionView dequeueReusableCellWithReuseIdentifier:cellIdentifier forIndexPath:indexPath];
[[customCell nameLabel]setText:[namelabelArray objectAtIndex:indexPath.item]];
[[customCell idLabel]setText:[idArray objectAtIndex:indexPath.item]];
[[customCell imageView] setImageWithURLRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:[imagArray objectAtIndex:indexPath.row]]]
placeholderImage:nil
success:nil
failure:^(NSURLRequest * _Nonnull request, NSHTTPURLResponse * _Nullable response, NSError * _Nonnull error) {
}];
return customCell;
}
First of all, instead of UIImage *imageArray; it should be NSString *imageURLString;
and then since you are using AFNetworking better to load images async (use #import UIImageView+AFNetworking.h).
NSURL *imageURL = [NSURL URLWithString:[NSString stringWithFormat:#"%#%#", self.baseURL, imageURLString]];
[self setImageWithURLRequest:[NSURLRequest requestWithURL:imageURL]
placeholderImage:placeholder
success:^(NSURLRequest *request, NSHTTPURLResponse *response, UIImage *image) {
if (image)
{
weakSelf.image = image;
}
} failure:^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error) {
}];

How to return an NSArray in the success block, iOS, Objective-C

this is my code. I want to return an array with data after data assign from the web service to the array.I have use block to check.
this is from my header file...
typedef void(^FailureBlock)(NSError *error);
typedef void(^SuccessBlock) (NSMutableArray *responseArray);
this is my implementation file ....
- (void)setupConnectionWithsuccess:(SuccessBlock)success failure:(FailureBlock)failure
{
airportArray = nil;
NSString *airportCode = [NSString stringWithFormat:#"some code"];
NSString *authenticationCode = [NSString stringWithFormat:#"some api"];
NSString *baseurl = [NSString stringWithFormat:#"some url",authenticationCode,airportCode];
// NSString *mainurlString = [NSString stringWithFormat:#""];
// NSURL *mainurl = [NSURL URLWithString:mainurlString];
AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
[manager GET:baseurl parameters:nil progress:nil success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
NSArray *mainArray = (NSArray *)responseObject;
airportArray = [[NSMutableArray alloc] init];
for (NSDictionary *all in mainArray) {
airports = [all objectForKey:#"Airport"];
[airportArray addObject:airports];
NSLog(#"%#", airports);
}
if(success){
success(airportArray);
}
//NSLog(#"%#", responseObject);
}
failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
if (failure) {
failure(error);
}
UIAlertController *mainAlert = [UIAlertController alertControllerWithTitle:#"Something Wrong!" message:[error localizedDescription] preferredStyle:UIAlertControllerStyleAlert];
[self presentViewController:mainAlert animated:YES completion:nil];
}];
}
then after success, I want to return the array from this method.how can I do that
- (NSArray *)returnAll
{
[self setupConnectionWithsuccess:^(NSMutableArray *responseArray) {
} failure:^(NSError *error) {
}];
}
please help me with this.I'm new to iOS Block.
Try as follow here i use id because in id you can pass anyobject
- (void)setupConnectionWithsuccess:(void (^)(id responseObject, NSError *error))completion{
airportArray = nil;
NSString *airportCode = [NSString stringWithFormat:#"some code"];
NSString *authenticationCode = [NSString stringWithFormat:#"some api"];
NSString *baseurl = [NSString stringWithFormat:#"some url",authenticationCode,airportCode];
// NSString *mainurlString = [NSString stringWithFormat:#""];
// NSURL *mainurl = [NSURL URLWithString:mainurlString];
AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
[manager GET:baseurl parameters:nil progress:nil success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
NSArray *mainArray = (NSArray *)responseObject;
airportArray = [[NSMutableArray alloc] init];
for (NSDictionary *all in mainArray) {
airports = [all objectForKey:#"Airport"];
[airportArray addObject:airports];
NSLog(#"%#", airports);
}
if (completion)
completion(responseObject,nil);
//NSLog(#"%#", responseObject);
}
failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
if (completion)
completion(nil,error);
UIAlertController *mainAlert = [UIAlertController alertControllerWithTitle:#"Something Wrong!" message:[error localizedDescription] preferredStyle:UIAlertControllerStyleAlert];
[self presentViewController:mainAlert animated:YES completion:nil];
}];}
and to call it
[self setupConnectionWithsuccess:^(id responseObject, NSError *error) {
if (responseObject) {
NSLog(#"responsceObect");
}
}];
It get nothing while your function return. Because your block excute at asynchonize way. you should modify your function to handle the Array data in a block.

How to show Image in UIImageView from cache using AFNetworking?

Here is my code how to download image from URL and save it into document directory using AFNetworking.
Now, my question is if image is already downloaded from URL then image is loaded from cache instead of re-download it. I want to do this using AFNetworking. I know that the solution for this problem is inside #import "UIKit+AFNetworking/UIKit+AFNetworking.h"
If anyone have any idea of how to help, please help me solve my issue.
#import "ViewController.h"
#define URL #"https://upload.wikimedia.org/wikipedia/commons/e/ec/USA-NYC-American_Museum_of_Natural_History.JPG"
#interface ViewController ()
#end
#implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
self.progressBar.hidden = YES ;
self.lblProgressStatus.hidden = YES;
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (IBAction)Action:(UIButton *)sender
{
self.progressBar.hidden = NO ;
self.lblProgressStatus.hidden = NO ;
self.ActionDownload.enabled = NO ;
NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
AFURLSessionManager *manager = [[AFURLSessionManager alloc] initWithSessionConfiguration:configuration];
NSURL *strURL = [NSURL URLWithString:URL];
NSURLRequest *request = [NSURLRequest requestWithURL:strURL];
NSProgress *progress;
NSURLSessionDownloadTask *downloadTask = [manager downloadTaskWithRequest:request progress:&progress destination:^NSURL *(NSURL *targetPath, NSURLResponse *response)
{
NSURL *documentsDirectoryURL = [[NSFileManager defaultManager] URLForDirectory:NSDocumentDirectory inDomain:NSUserDomainMask appropriateForURL:nil create:NO error:nil];
return [documentsDirectoryURL URLByAppendingPathComponent:[response suggestedFilename]];
}
completionHandler:^(NSURLResponse *response, NSURL *filePath, NSError *error)
{
[self.progressBar setHidden:YES];
self.lblProgressStatus.text = #"Download completed" ;
NSLog(#"File downloaded to: %#", filePath);
NSString * strTemp = [NSString stringWithFormat:#"%#", filePath];
NSArray *components = [strTemp componentsSeparatedByString:#"/"];
id obj = [components lastObject];
NSLog(#"%#", obj);
NSString *docPath = [NSSearchPathForDirectoriesInDomains (NSDocumentDirectory,NSUserDomainMask, YES) objectAtIndex:0];
NSString *strFilePath = [NSString stringWithFormat:#"%#/%#",docPath, obj];
BOOL fileExists=[[NSFileManager defaultManager] fileExistsAtPath:strFilePath];
if (!fileExists)
{
NSLog(#"File Not Found");
}
else
{
UIImage * image = [UIImage imageWithContentsOfFile:strFilePath];
self.imageView.image = image ;
}
[progress removeObserver:self forKeyPath:#"fractionCompleted" context:NULL];
}];
[self.progressBar setProgressWithDownloadProgressOfTask:downloadTask animated:YES];
[downloadTask resume];
[progress addObserver:self
forKeyPath:NSStringFromSelector(#selector(fractionCompleted)) options:NSKeyValueObservingOptionNew
context:NULL];
}
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
if ([keyPath isEqualToString:#"fractionCompleted"])
{
NSProgress *progress = (NSProgress *)object;
int temp = progress.fractionCompleted * 100 ;
// NSLog(#"%d", temp);
NSString * strTemp = #"%";
dispatch_async(dispatch_get_main_queue(), ^{
// Update the UI
self.lblProgressStatus.text = [NSString stringWithFormat:#"%d %#", temp, strTemp];
});
}
else
{
[super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
}
}
#end
You can download the image using this method defined in UIImageView+AFNetworking:
[imageView setImageWithURL:[NSURL URLWithString:URL] placeholderImage:[UIImage imageNamed:#"placeholder-avatar"] success:^(NSURLRequest *request, NSHTTPURLResponse *response, UIImage *image) {
if ([[extension lowercaseString] isEqualToString:#"png"]) {
[UIImagePNGRepresentation(image) writeToFile:[directoryPath stringByAppendingPathComponent:[NSString stringWithFormat:#"%#.%#", imageName, #"png"]] options:NSAtomicWrite error:nil];
} else if ([[extension lowercaseString] isEqualToString:#"jpg"] || [[extension lowercaseString] isEqualToString:#"jpeg"]) {
[UIImageJPEGRepresentation(image, 1.0) writeToFile:[directoryPath stringByAppendingPathComponent:[NSString stringWithFormat:#"%#.%#", imageName, #"jpg"]] options:NSAtomicWrite error:nil];
}
} failure:NULL];
The success block will be called even if it gets the image from cache. Hope it helped!
It uses cache by default. To test, go to a url you have access to of an image, then delete the image, and load again, and you'll see it's cached :D The images sometimes are not cached if they're big images.
If you want to increase this cache size, put this in your app delegate:
[[NSURLCache sharedURLCache] setMemoryCapacity:(20*1024*1024)];
[[NSURLCache sharedURLCache] setDiskCapacity:(200*1024*1024)];
EDIT RE: comments:
If you're looking to only download images once to your documents path, then perhaps the best way to test if an image already exists and should be downloaded or not is a test you can create. E.g, if the last path component (the last part of an image file path) of an image exists already in your documents, don't download it, else download it.
EDIT: further comments
Inside UIKit+AFNetworking/UIImageView+AFNetworking.h
/**
Asynchronously downloads an image from the specified URL, and sets it once the request is finished. Any previous image request for the receiver will be cancelled.
If the image is cached locally, the image is set immediately, otherwise the specified placeholder image will be set immediately, and then the remote image will be set once the request is finished.
By default, URL requests have a Accept header field value of "image / *", a cache policy of NSURLCacheStorageAllowed and a timeout interval of 30 seconds, and are set not handle cookies. To configure URL requests differently, use setImageWithURLRequest:placeholderImage:success:failure:
#param url The URL used for the image request.
*/
- (void)setImageWithURL:(NSURL *)url;
This looks exactly like what you're looking for
to use:
#import <AFNetworking/UIKit+AFNetworking.h>
and use
NSURL *strURL = [NSURL URLWithString:#"http://www.example.com/image.jpg"];
[imageview setImageWithURL:strURL];
I recommend you to use this library https://github.com/rs/SDWebImage
So, you can do something like this:
- (void)loadImage:(NSURL *)url
{
__block UIImage *image = [[SDImageCache sharedImageCache] queryDiskCacheForKey:[url absoluteString]];
if(!image) {
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
[request setTimeoutInterval: 30.0]; // Will timeout after 30 seconds
[NSURLConnection sendAsynchronousRequest:request
queue:[NSOperationQueue currentQueue]
completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) {
if (data != nil && error == nil) {
image = [UIImage imageWithData:data];
NSData *pngData = UIImagePNGRepresentation(image);
[[SDImageCache sharedImageCache] storeImage:image imageData:pngData forKey:[url absoluteString] toDisk:YES];
}
else {
// There was an error, alert the user
NSLog(#"%s Error: %#", __func__, error);
}
}];
}
}

objective c - Efficient way to download an image from web and use it to show in image view

in my app lets say there is 2 views ViewA and ViewB
in ViewA there are buttons for user to select option. And if he push one of them i will pull some images from web via web service and download them to the user's machine also i will put their paths to an array.
Then in ViewB i want to get images from that array and show them in image views
this is how i download images
-(void)startDownload
{
NSMutableArray *arr = [[NSMutableArray alloc] init];
[arr addObject:#"http://xxxx.com/Tulips.jpg"];
[arr addObject:#"http://xxxx.com/Koala.jpg"];
[arr addObject:#"http://xxxx.com/Penguins.jpg"];
for (int i=0; i<[arr count]; i++) //download array have url links
{
NSURL *URL = [NSURL URLWithString:[arr objectAtIndex:i]];
NSMutableURLRequest *urlRequest = [[NSMutableURLRequest alloc]initWithURL:URL];
NSOperationQueue *queue = [[NSOperationQueue alloc]init];
[NSURLConnection sendAsynchronousRequest:urlRequest queue:queue completionHandler:^(NSURLResponse *response, NSData *data, NSError *error)
{
if([data length] > 0 && [[NSString stringWithFormat:#"%#",error] isEqualToString:#"(null)"])
{
//make your image here from data.
UIImage *imag = [[UIImage alloc] initWithData:[NSData dataWithData:data]];
NSArray *array = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *docDir = [array objectAtIndex:0];
NSString *imgstr=[NSString stringWithFormat:#"%d",i];
NSString *pngfilepath = [NSString stringWithFormat:#"%#sample%#.jpg",docDir,imgstr];
NSData *data1 = [NSData dataWithData:UIImagePNGRepresentation(imag)];
[data1 writeToFile:pngfilepath atomically:YES];
img = [[UIImageView alloc] initWithImage:[UIImage imageWithContentsOfFile:pngfilepath]];
NSLog(#"file is written");
}
else if ([data length] == 0 && [[NSString stringWithFormat:#"%#",error] isEqualToString:#"(null)"])
{
NSLog(#"No Data!");
}
else if (![[NSString stringWithFormat:#"%#",error] isEqualToString:#"(null)"]){
NSLog(#"Error = %#", error);
}
}];
}
}
when i run the app i see that file is written log is working so i think downloading the images is successful but i can't show image in imageview
you may think quiz up app on the store for understanding my problem clearly. quiz up first downloading questions' images then use them in another view. that's what i want exactly.
if my download code is correct how can i show them?
This code will allow you to download an image from the web, and does not require that the image be saved in the document directory:
NSMutableArray *arry = [[NSMutableArray alloc] init];
[arry addObject:#"https://encrypted-tbn2.gstatic.com/images?q=tbn:ANd9GcRr0WK-Q2t4Xxr1b6Kl7-lXdVEIh_Hj3HiDXk--Qg_0UAY0Y96P6w"];
[arry addObject:#"https://encrypted-tbn2.gstatic.com/images?q=tbn:ANd9GcRr0WK-Q2t4Xxr1b6Kl7-lXdVEIh_Hj3HiDXk--Qg_0UAY0Y96P6w"];
[arry addObject:#"https://encrypted-tbn2.gstatic.com/images?q=tbn:ANd9GcRr0WK-Q2t4Xxr1b6Kl7-lXdVEIh_Hj3HiDXk--Qg_0UAY0Y96P6w"];
for (int i=0; i<[arry count]; i++) //download array have url links
{
NSString *string=[arry objectAtIndex:i];
NSURL *url=[NSURL URLWithString:string];
NSURLRequest *urlRequest = [NSURLRequest requestWithURL:url];
[NSURLConnection sendAsynchronousRequest:urlRequest queue:[NSOperationQueue currentQueue] completionHandler:^(NSURLResponse *response, NSData *data, NSError *error)
{
UIImage *imagemain=[UIImage imageWithData:data];
// CGSize size=imagemain.size;
// UIImage *compimage=[appdel resizeImage:imagemain resizeSize:CGSizeMake(45,45)];
//
// Cell.imgProfile.image=compimage;
// // CGSize size1=compimage.size;
imageView.image=imagemain;
}];
}
Are you updating your UIImageView on the main thread, you can't update UI elements from a background thread. Try
dispatch_sync(dispatch_get_main_queue(),
^{
imageView.image = yourImage;
});
You have to use SDWebImage to cache the image. means the url will not be hit again and again.
#import "UIImageView+WebCache.h"
-(void)startDownload
{
NSMutableArray *arr = [[NSMutableArray alloc] init];
[arr addObject:#"http://xxxx.com/Tulips.jpg"];
[arr addObject:#"http://xxxx.com/Koala.jpg"];
[arr addObject:#"http://xxxx.com/Penguins.jpg"];
for (int i=0; i<[arry count]; i++) //download array have url links
{
NSString *string=[arry objectAtIndex:i];
NSURL *url=[NSURL URLWithString:string];
SDWebImageManager *manager = [SDWebImageManager sharedManager];
[manager downloadWithURL:url progress:^(NSUInteger receivedSize, long long expectedSize)
{
// progression tracking code
}completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, BOOL finished)
{
if (image)
{
// here you can setup imageView frames and set the image on imageView
imageView.image=image;
}
}];
}
}
}

AFNetworking for Image Downloads, Unresponsive UI

I'm using AFNetworking to pull images from a URL, resize, store to disk and log the path in Core Data, then load to a table view and store . When the code executes it freezes my UI. I'm not sure if it's the download or the manipulation that's causing my troubles.
The code I'm using is below
- (void)getPhoto:(NSInteger)type forManagedObject:(MyManagedObject*)object {
// download the photo
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:object.photoUrl]];
AFImageRequestOperation *operation = [AFImageRequestOperation imageRequestOperationWithRequest:request success:^(UIImage *image) {
// MyManagedObject has a custom setters (setPhoto:,setThumb:) that save the
// images to disk and store the file path in the database
object.photo = image;
object.thumb = [image imageByScalingAndCroppingForSize:CGSizeMake(PhotoBlockCellButtonWidth, PhotoBlockCellButtonHeight)];
NSError *nerror;
if (![[DataStore sharedDataStore].managedObjectContext save:&nerror]) {
NSLog(#"Whoops, couldn't save: %#", [nerror localizedDescription]);
return;
}
// notify the table view to reload the table
[[NSNotificationCenter defaultCenter] postNotificationName:#"ReloadTableView" object:nil];
}];
[operation start];
}
And here is a sample code relevant to the setter from my managed object
- (NSString*)uniquePath{
// prepare the directory string
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
// acquire a list of all files within the directory and loop creating a unique file name
NSFileManager *fileManager = [NSFileManager defaultManager];
NSArray *existingFiles = [fileManager contentsOfDirectoryAtPath:documentsDirectory error:nil];
NSString *uniquePath;
do {
CFUUIDRef newUniqueId = CFUUIDCreate(kCFAllocatorDefault);
CFStringRef newUniqueIdString = CFUUIDCreateString(kCFAllocatorDefault, newUniqueId);
uniquePath = [[documentsDirectory stringByAppendingPathComponent:(__bridge NSString *)newUniqueIdString] stringByAppendingPathExtension:#"png"];
CFRelease(newUniqueId);
CFRelease(newUniqueIdString);
} while ([existingFiles containsObject:uniquePath]);
return uniquePath;
}
- (NSString*)saveImage:(UIImage*)image{
NSString *path = [self uniquePath];
NSData *data = UIImagePNGRepresentation(image);
[data writeToFile:path atomically:YES];
return [NSString stringWithFormat:#"file://%#",path];
}
- (void) setPhoto:(UIImage *)image {
self.photoUrl = [self saveImage:image];
}
I would like to push this to a background thread, but I'm not sure what the implications are with AFNetworking, Core Data, and Messaging in terms of thread safety. Any thought?
AFAIK, the way you are executing your request in incorrect:
[operation start];
you should instead add the operation to an NSOperationQueue:
NSOperationQueue* operationQueue = [[NSOperationQueue alloc] init];
[operationQueue addOperation:operation];
(you should correctly memory-manage the queue).
By doing like this, your request will be executed in an async way, it won't block the UI and you will not need to deal with multithreading.
Based on Matt's suggestion, I improved the UI by reworking my call as follows.
- (void)getPhoto:(NSInteger)type forManagedObject:(MyManagedObject*)object {
// download the photo
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:object.photoUrl]];
AFImageRequestOperation *operation = [AFImageRequestOperation
imageRequestOperationWithRequest:request
imageProcessingBlock:^UIImage *(UIImage *image) {
return [image imageByScalingAndCroppingForSize:CGSizeMake(PhotoBlockCellButtonWidth, PhotoBlockCellButtonHeight)];
}
cacheName:nil
success:^(NSURLRequest *request, NSHTTPURLResponse *response, UIImage *image) {
// MyManagedObject has a custom setters (setPhoto:,setThumb:) that save the
// images to disk and store the file path in the database
object.photo = image;
object.thumb = image;
NSError *nerror;
if (![[DataStore sharedDataStore].managedObjectContext save:&nerror]) {
NSLog(#"Whoops, couldn't save: %#", [nerror localizedDescription]);
return;
}
// notify the table view to reload the table
[[NSNotificationCenter defaultCenter] postNotificationName:#"ReloadTableView" object:nil];
}
failure:^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error) {
NSLog(#"Error getting photo");
}];
[operation start];
}

Resources