Image Flickering everytime table is reloaded (iOS, objective-C) - ios

I am having an issue where my images in my tableview cell flicker everytime the table reloadData method is called. The flickering is occuring because the image is being downloaded EVERY time the table is reloaded. How do I make it so this image isnt being downloaded everytime, but only once?
This is the cellForRowAtIndexPath code in SelectStationViewController.m. This class handles the tableView.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath;
{
StationRest * StationRest = [[CurrentUser sharedInstance].userStations objectAtIndex:indexPath.row];
StationListCell *cell= [[StationListCell alloc]initWithFrame:CGRectMake(0, 0, 375,88)];
cell.cellDelegate = self;
//This method below downloads the image into the cell.
[cell configureCellWithStationRest:StationRest forCellType:StationListCellTypeSelect];
return cell;
}
This is the code in StationListCell.m, the class that is hooked up with the cell. Here is where the image is downloaded using AFNetworking. I can use GCD with [[NSData alloc] initWithContentsOfURL method instead of AFNetworking, but I still achieve the same result.
-(void)configureCellWithStationRest:(StationRest *)stationRest forCellType:(StationListCellType) cellType{
NSURL *url = [NSURL URLWithString:stationRest.thumbURL];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
AFHTTPRequestOperation *requestOperation = [[AFHTTPRequestOperation alloc] initWithRequest:request];
requestOperation.responseSerializer = [AFImageResponseSerializer serializer];
[requestOperation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
[self.thumbButton setImage:responseObject forState:UIControlStateNormal];
[self.thumbButton setContentMode:UIViewContentModeScaleAspectFill];
}
failure:^(AFHTTPRequestOperation *operation, NSError *error) {
DLog(#"Image error: %#", error);
}];
[requestOperation start];
}

You can use UIImageView+AFNetworking.h category.It provides method to download the image from url and cache it.Please check its documentation for more information.
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString: url]];
[request addValue:#"image/*" forHTTPHeaderField:#"Accept"];
[yourImageView setImageWithURLRequest:request placeholderImage:nil success:^(NSURLRequest *request, NSHTTPURLResponse *response, UIImage *image) {
yourImageView.image = image;
} failure:^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error) {
NSLog(#"Failed to load image");
}];

Related

iOS Block image return

So I am working on a project and I must use AFImageDownloader in order to download some images that we need to use in our project. I use the following code:
-(void) downloadImage:(NSURL*) url
{
AFHTTPSessionManager *sessionManager = [[AFHTTPSessionManager alloc] init];
AFImageDownloader *imgDownloader = [[AFImageDownloader alloc] initWithSessionManager:sessionManager downloadPrioritization:AFImageDownloadPrioritizationFIFO maximumActiveDownloads:1 imageCache:nil];
AFHTTPResponseSerializer *responseSerializer = [AFHTTPResponseSerializer serializer];
responseSerializer.acceptableContentTypes = [NSSet setWithObjects:#"application/json", #"text/json",#"binary/octet-stream",nil];
sessionManager.responseSerializer = responseSerializer;
NSURLRequest *req = [NSURLRequest requestWithURL:url];
[imgDownloader downloadImageForURLRequest:req success:^(NSURLRequest *request, NSHTTPURLResponse *response, UIImage *responseObject){
self.image = responseObject;
[self.delegate updateImageWithImage:self.image]; // ** CRASH **
} failure:^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error){
NSLog(#"Error");
}];
}
The delegate is of course not nil and code of the updateImageWithImage is:
-(void) updateImageWithImage:(UIImage*) img {
self.image.image = img;
}
So basically when I try to get the UIImage I get it as a response and assign it to the UICollectionViewCell and it crashes! I guess that I should do some kind of "copy" of the responseObject before using it in order parts of my program, but I am not really sure what the problem is. Any ideas?
I figured it out. I was basically using AFImagedownloader wrong, this is the proper way to use this Class:
[self.imageView setImageWithURLRequest:jpegURLRequest
placeholderImage:placeholder
success:^(NSURLRequest *request, NSHTTPURLResponse * _Nullable response, UIImage *image){
NSLog(#"Success");
completionBlock(image);
}
failure:^(NSURLRequest * _Nonnull request, NSHTTPURLResponse * _Nullable response, NSError * _Nonnull error) {
NSLog(#"ERROR!!!");
}];

What is the proper way to display a loading indicator while getting data from a URL

I'm somewhat new to objective-c, i'm developing a news iOS application, the app gets all its contents using JSON parsing from a url, i'm using AFNetworking for that and this is the method that i made:
- (void)getContents
{
NSString *urlString = #"http://some-url-that-has-json-output/";
urlString = [urlString stringByAppendingString:self.articleId];
NSLog(#"The call url is: %#",urlString);
NSURL *url = [NSURL URLWithString:urlString];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
//AFNetworking asynchronous url request
AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc]
initWithRequest:request];
operation.responseSerializer = [AFJSONResponseSerializer serializer];
[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
NSLog(#"The JSON data is: %#", responseObject);
jsonContents = [responseObject objectForKey:#"article"];
[self LoadStructure];
} failure:nil];
[operation start];
}
Now the data loads fine with this method.
My Question: How to display a loading indicator (could be a GIF) while getting the data ? and is this method above is the proper or best way to get data from a url ?
You can use default loading indicator of iOS(UIActivityIndicator). You should start animating it before completion block and should hide inside success and failure block.
You should create a method using indicator as class variable:
indicator = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhiteLarge];
indicator.hidesWhenStopped = YES;
indicator.frame = CGRectMake(35, 15, 30, 30);
[self.view addSubview:indicator];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
//AFNetworking asynchronous url request
AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc]
initWithRequest:request];
operation.responseSerializer = [AFJSONResponseSerializer serializer];
[indicator startAnimating];
[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
NSLog(#"The JSON data is: %#", responseObject);
// to stop:
[indicator stopAnimating];
jsonContents = [responseObject objectForKey:#"article"];
[self LoadStructure];
} failure::^(AFHTTPRequestOperation *operation, NSError *error){
[indicator stopAnimating];
}];
[operation start];
Drag and drop UIActivityIndicatorView into your XIB view and connect it with IBOutlet.
.h file
#property(nonatomic, strong)IBOutlet UIActivityIndicatorView * activityIndicator;
Add UIActivity indicator view into your view. show it before
NSString *urlString = #"http://some-url-that-has-json-output/";
using:[self.activityIndicator startAnimating];
and stop it inside completion block and failure block
using: [self.activityIndicator stopAnimating];

iOS - getting image with AFNetworking keeps cache

In my app I'm using RESTKit, so my AFNetworking version isn't the newest. I'm not sure how to check the version of it.
I want to download a picture from my server, and because the response is a jpg file, I'm using AFNetworking. On the first download of the image, it works well. Then I delete the image on the server and upload a new image with the same name. Then if I delete the image in the app and re-download it. In this scenario I still get the old picture from the first time I downloaded.
This is my code:
AFHTTPClient *client = [AFHTTPClient clientWithBaseURL:[NSURL URLWithString:#"http://myserver.com"]];
[client setAuthorizationHeaderWithUsername:name password:password];
[client getPath:[NSString stringWithFormat:#"profile-images/%#.jpg", user.name] parameters:#{} success:^(AFHTTPRequestOperation *operation, id responseObject) {
NSLog(#"SUCCES");
NSData *imageData = responseObject;
self.tmpImage = [UIImage imageWithData:imageData];
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"FAIL");
self.tmpImage = [UIImage imageNamed:#"myImage.png"];
}];
It looks to me like the app "remembers" the first request I sent to the server when I downloaded the image. And then when I re-download it, it gives me the old picture. Does anyone know how to solve it?
I finally got this to work with using NSMutableURLRequest. When creating the request I set the cachePolicy to NSURLRequestReloadIgnoringLocalCacheData. Meaning: request.cachePolicy = NSURLRequestReloadIgnoringLocalCacheData;
Here is the code for sending the request to the server:
AFHTTPClient *client = [AFHTTPClient clientWithBaseURL:[NSURL URLWithString:#"http://server.com"]];
[client setAuthorizationHeaderWithUsername:name password:password];
NSMutableURLRequest *request = [client requestWithMethod:#"GET" path:[NSString stringWithFormat:#"profile-images/%#.jpg", user.name] parameters:nil];
request.timeoutInterval = 10;
request.cachePolicy = NSURLRequestReloadIgnoringLocalCacheData;
AFImageRequestOperation *operation;
operation = [AFImageRequestOperation imageRequestOperationWithRequest:request imageProcessingBlock:^UIImage *(UIImage *image) {
NSLog(#"block");
return image;
} success:^(NSURLRequest *request, NSHTTPURLResponse *response, UIImage *image) {
NSLog(#"SUCCES");
self.tmpImage = image;
} failure:^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error) {
NSLog(#"FAIL");
self.tmpImage = [UIImage imageNamed:#"myImage.png"];
}];
[operation start];
when you upload image on server first of all save image in document directory with same name each time. in the code image save name is #""Profile1.jpeg"
- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info
{
UIImage *img = [info objectForKey:UIImagePickerControllerOriginalImage];
img = [self imageWithImage:img scaledToSize:CGSizeMake(70, 70)];
[btn_Photo setImage:img forState:UIControlStateNormal];
NSData *webData = UIImageJPEGRepresentation(img, 0.5);
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
self.savedImagePath = [documentsDirectory stringByAppendingPathComponent:[NSString stringWithFormat:#"%#",#"Profile1.jpeg"]];
[webData writeToFile:self.savedImagePath atomically:YES];
[picker dismissViewControllerAnimated:YES completion:^{
//[[UIApplication sharedApplication] setStatusBarHidden:YES];
}];
}
Using New ANetworking Upload image as below code New AFNetworking
AFHTTPRequestOperationManager *Manager = [AFHTTPRequestOperationManager manager];
[Manager POST:str_Submit parameters:parameters constructingBodyWithBlock:^(id<AFMultipartFormData> formData)
{
NSURL *filePath_Profile_Logo = [NSURL fileURLWithPath:self.savedImagePath];
[formData appendPartWithFileURL:filePath_Profile_Logo name:#"userfile" error:nil];
}
success:^(AFHTTPRequestOperation *operation, id responseObject)
{
NSLog(#"Success");
}
failure:^(AFHTTPRequestOperation *operation, NSError *error)
{
NSLog(#"Fail")
}];
please check your document directory path image is change before upload it or not.
please

AFImageRequestOperation in order not asynchronous

I am implementing a code that downloads images and saves them in the database of the app,
I have an array of objects, each object contains the image url and some other information. To Download the images I'm using the class library AFImageRequestOperation.h AFNetworking.
My code downloads and saves the data in the database, but need to notify the user which image is downloaded, eg: if I have an array containing 5 objects (quoted just above what each object), will have to do downloading the same order that is in the array, but as AFImageRequestOperation makes downloading asynchronously item 4 can be downloaded before the first item.
In short, I want to have control and only release for the next download when the previous one is completed.
I have a for that runs through the array of objects and calls a function for each position, the function has the following code:
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:[arrImagem valueForKey:#"urlimagem"]]];
AFImageRequestOperation *operation = [AFImageRequestOperation imageRequestOperationWithRequest:request imageProcessingBlock:nil success:^(NSURLRequest *request, NSHTTPURLResponse *response, UIImage *image){
Imagens *imagem = (Imagens *)[NSEntityDescription insertNewObjectForEntityForName:#"IMAGENS" inManagedObjectContext:managedObjectContext];
// Save Image
NSData *imageData = UIImageJPEGRepresentation(image, 90);
[imagem setCategoria:cat];
[imagem setTitulo:[arrImagem valueForKey:#"titulo"]];
[imagem setDescricao:[arrImagem valueForKey:#"descricao"]];
[imagem setImagem:imageData];
NSError *error;
if(![managedObjectContext save:&error]){
NSLog(#"houve um erro muito grave");
//return false;
}else{
NSLog(#"Salvou imagem");
}
}failure:^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error){
NSLog(#"%#", [error localizedDescription]);
}];
[operation start];
I do not know if my question was very clear, but basically my question is similar to this link
AFImageRequestOperation is a subclass of NSOperation so you can use:
- (void) addDependency: (NSOperation*) operation
to make sure that one operation finishes before the other.
For example:
NSOperation *op1 = [[NSOperation alloc]init];
NSOperation *op2 = [[NSOperation alloc]init];
[op1 addDependency: op2];
This way op1 won't start before op2 is finished.
You can create a method in your class where you call the download code and add a block as parameter which receives the downloaded UIImage, the image url (and other infos that you need) or you can implement a delegate with the same params from the block. It can lock something like:
-(void)downloadImageWithSuccess:(void (^)(UIImage *image, NSString *url, OtherParms here))success
failure:(void (^)(NSError *error)failure {
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:[arrImagem valueForKey:#"urlimagem"]]];
AFImageRequestOperation *operation = [AFImageRequestOperation imageRequestOperationWithRequest:request imageProcessingBlock:nil success:^(NSURLRequest *request, NSHTTPURLResponse *response, UIImage *image){
Imagens *imagem = (Imagens *)[NSEntityDescription insertNewObjectForEntityForName:#"IMAGENS" inManagedObjectContext:managedObjectContext];
// Save Image
NSData *imageData = UIImageJPEGRepresentation(image, 90);
[imagem setCategoria:cat];
[imagem setTitulo:[arrImagem valueForKey:#"titulo"]];
[imagem setDescricao:[arrImagem valueForKey:#"descricao"]];
[imagem setImagem:imageData];
NSError *error;
if(![managedObjectContext save:&error]){
NSLog(#"houve um erro muito grave");
//return false;
}else{
NSLog(#"Salvou imagem");
}
success(imagem, [request.URL absoluteString], otherParams);
}failure:^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error){
NSLog(#"%#", [error localizedDescription]);
failure(error);
}];
[operation start];
}
}
And when you call the method from your code you can do something like(using blocks):
[catalog downloadImageWithSuccess:^(UIImage *image, NSString *url, OtherParms here) {
//NOTIFY USER THAT THE IMAGE WITH URL HAS BEEN DOWNLOADED
}
failure:^(NSError *error) {
//NTOFIY USER THAT THE IMAGE FAILED
}
];
managed to solve my problem, sorry for the delay in posting the solution, follow the code below:
dispatch_group_t group = dispatch_group_create();
dispatch_group_enter(group);
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:strUrl]];
AFImageRequestOperation *operation = [AFImageRequestOperation imageRequestOperationWithRequest:request imageProcessingBlock:nil success:^(NSURLRequest *request, NSHTTPURLResponse *response, UIImage *image){
//your implementation
dispatch_group_leave(group); //<== NOTICE THIS
}failure:^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error){
//your implementation
NSLog(#"%#", [error localizedDescription]);
}];
[operation start];
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
Thus the downloads ocorerão in order, if this code is inside a case, the next download will only be called when the method success^: or failure^: is called

Cannot use Success and Failure block with UIImageView+AFNetworking.h

I am trying to add a Success and Error block to check when the image is loaded so that I can use NSCache to dynamically resize the image. But when I try
[scribbleImage setImageWithURL: [NSURL URLWithString:scribble[#"scribble_image"]] placeholderImage:[UIImage imageNamed:#"Default.png"] success:^(UIImage *image) {
NSLog(#"done");
} failure:^(NSError *error) {
NSLog(#"error %#",error);
}];
Xcode gives me an error, saying No visible #interface for 'UIImageView' declares the selector 'setImageWithURL:placeholderImage:success:failure:'
I am not sure why.
PS. I am importing UIImageView+AFNetworking.h and things work just fine without a success and failure block
This is because the there is no method setImageWithURL:placeholderImage:success:failure: there is just setImageWithURLRequest:placeholderImage:success:failure:
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:scribble[#"scribble_image"]]];
[scribbleImage setImageWithURLRequest:request placeholderImage:[UIImage imageNamed:#"Default.png"] success:^(NSURLRequest *request, NSHTTPURLResponse *response, UIImage *image) {
NSLog(#"Done");
} failure:^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error) {
NSLog(#"Failed with error: %#", error);
}];
If you look at what setImageWithURL:placeholderImage: does :
- (void)setImageWithURL:(NSURL *)url
placeholderImage:(UIImage *)placeholderImage
{
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
[request setHTTPShouldHandleCookies:NO];
[request addValue:#"image/*" forHTTPHeaderField:#"Accept"];
[self setImageWithURLRequest:request placeholderImage:placeholderImage success:nil failure:nil];
}
You see that this is the method is used to fetch the internally as well.

Resources