I have a TableView using custom cells. I initially was setting grabbing an image from a URL in the cellForRowAtIndexPath method
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *simpleTableIdentifier = #"SimpleTableCell";
SimpleTableCell *cell = (SimpleTableCell *)[tableView dequeueReusableCellWithIdentifier:simpleTableIdentifier];
if (cell == nil)
{
NSArray *nib = [[NSBundle mainBundle] loadNibNamed:#"SimpleTableCell" owner:self options:nil];
cell = [nib objectAtIndex:0];
}
NSDictionary *dictObject = [places objectAtIndex:indexPath.row];
cell.nameLabel.text = [dictObject valueForKey:#"PlaceTitle"];
NSURL *url = [NSURL URLWithString:#"http://images1.fanpop.com/images/image_uploads/Mario-Kart-Wii-Items-mario-kart-1116309_600_600.jpg"];
NSData *data = [NSData dataWithContentsOfURL:url];
UIImage *image = [UIImage imageWithData:data];
cell.thumbnailImageView.image = image;
return cell;
}
but this was making my TableView scroll laggy. Once I removed the image fetch, it scrolled fine, so I know this is my problem.
My question is: how can I asynchronously fetch this image and set it in my cell? Is there an easy way to do this? Thanks!
Step 1: Have a cache containing images. Either just in memory, better on disk.
Step 2: When you need an image, call a method which either returns an image from the cache, or returns a default image and starts a download.
Step 3: When a download finishes, add the image to the cache. Then find out which rows need the image. Reload all the rows that reload the image.
The download should be done asynchronously using GCD. I would really recommend that you add the download code into a separate, reusable method so that you can handle download errors. Even if you don't do it now, you will do it later.
dataWithContentsOfURL is a synchronous method rather than asynchronous,as
Apple Documents described.
This method is ideal for converting data:// URLs to NSData objects, and can also be used for reading short files synchronously. If you need to read potentially large files, use inputStreamWithURL: to open a stream, then read the file a piece at a time.
In order to asynchronously load image,especially in tableViewCell,try use 3rd part Library SDWebImage
Use this code inside your tableviews cellforindexpath
NSURLRequest *req =[[NSURLRequest alloc]initWithURL:[NSURL URLWithString:#"yourimageurl.com"]];
[NSURLConnection sendAsynchronousRequest:req queue:[NSOperationQueue currentQueue] completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) {
if(!error){
UIImage *image =[UIImage imageWithData:data];
cell.thumbnailImageView.image = image;
}
else{
//error
}
}];
Create UIImageView Class File (i named it to MJTableImageView).
in MJTableImageView.h File
#interface MJTableImageView : UIImageView< NSURLConnectionDelegate, NSURLConnectionDataDelegate >
{
NSMutableData *imageData ;
long long expectedLength;
NSURLConnection *currentConnection;
NSString *File_name;
}
#property(nonatomic,readonly)UIActivityIndicatorView *loadingIndicator;
#property(nonatomic)BOOL showLoadingIndicatorWhileLoading;
-(void)setImageUrl:(NSURL *)imageUrl fileName:(NSString *)name;
#end
in MJTableImageView.m File
-(void)setImageUrl:(NSURL *)imageUrl fileName:(NSString *)name
{
// discard the previous connection
if(currentConnection)
{
[currentConnection cancel];
}
File_name = name;
//reset current image
self.image = nil;
// if(_showLoadingIndicatorWhileLoading)
// {
//show the loading indicator
if(!_loadingIndicator)
{
CGFloat width = self.bounds.size.width*0.5;
_loadingIndicator = [[UIActivityIndicatorView alloc] initWithFrame:CGRectMake((self.bounds.size.width-width)/2, (self.bounds.size.height-width)/2, 25.0 , 25.0)];
_loadingIndicator.backgroundColor = [UIColor colorWithWhite:0.0 alpha:0.5];
_loadingIndicator.layer.cornerRadius = width*0.1;
}
[self startLoadingIndicator];
// }
// initialize the placeholder data
imageData = [NSMutableData data];
// start the connection
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:imageUrl];
request.cachePolicy = NSURLRequestUseProtocolCachePolicy;
currentConnection = [NSURLConnection connectionWithRequest:request delegate:self];
}
-(void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
//if the image view is reused in a table view for example to load another image previous image is discarded
if(connection != currentConnection)
{
[connection cancel];
[self cleanUp];
return;
}
// append new Data
[imageData appendData:data];
// show the partially loaded image
self.image = [UIImage imageWithData:imageData];
}
-(void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
expectedLength = response.expectedContentLength;
}
-(void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
// clean up
[self cleanUp];
}
-(void)connectionDidFinishLoading:(NSURLConnection *)connection
{
// show the full image
self.image = [UIImage imageWithData:imageData];
NSString *filename = [NSHomeDirectory() stringByAppendingFormat:#"/Documents/%#", File_name];
NSData *data = UIImagePNGRepresentation([UIImage imageWithData:imageData]);
[data writeToFile:filename atomically:YES];
// clean up
[self cleanUp];
}
-(void)cleanUp
{
// clean up
imageData = nil;
[self stopLoadingIndicator];
}
-(void)startLoadingIndicator
{
if(!_loadingIndicator.superview)
{
[self addSubview:_loadingIndicator];
}
[_loadingIndicator startAnimating];
}
-(void)stopLoadingIndicator
{
if(_loadingIndicator.superview)
{
[_loadingIndicator removeFromSuperview];
}
[_loadingIndicator stopAnimating];
}
I am using StoryBoard so i add ImageClass(MJTableImageView) file to UItableviewcell ImageView and set tag number to it.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
NSDictionary *dict = [self.arr objectAtIndex:indexPath.row];
UITableViewCell *cell = [self.MJTableView dequeueReusableCellWithIdentifier:#"MJImageCell"];
if(cell == nil)
{
}
UILabel *appName = (UILabel*)[cell.contentView viewWithTag:2];
appName.text = [dict valueForKey:#"trackName"];
MJTableImageView *imageview = (MJTableImageView *)[cell.contentView viewWithTag:1];
NSString *url = [dict valueForKey:#"artworkUrl60"];
NSString *filename = [NSHomeDirectory() stringByAppendingFormat:#"/Documents/%#",[dict valueForKey:#"trackName"] ];
NSData *data = [NSData dataWithContentsOfFile:filename];
if(data)
{
imageview.image = [UIImage imageWithData:data];
}
else
{
[imageview setImageUrl:[NSURL URLWithString:url] fileName:[dict valueForKey:#"trackName"]];
}
return cell;
}
For More details see Github Project MJTableImageSwift it is in Swift.
Related
I have UITableViewCell and in this load images, so I want to images load Asynchronies and show activate indicator. This time images loading time is very high and all images load after images show on table view. many times images cant load and user UIInterface is hang. and do not work any process in application.. I am very tired, I used a SDWebImage library. please help how it possible, Thank You
#import "UIImageView+WebCache.h"
#interface pictureViewController ()
{
NSArray*picarray;
NSArray*titlearray;
NSArray*idarray;
}
#end
#implementation pictureViewController
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
[[[self navigationController] navigationBar] setTitleTextAttributes:#{NSForegroundColorAttributeName: [UIColor whiteColor]}];
[[UINavigationBar appearance] setBackgroundImage:[UIImage imageNamed:#"background2.png"] forBarMetrics:UIBarMetricsDefault];
_tableview.separatorColor = [UIColor yellowColor];
NSURLRequest *req=[[NSURLRequest alloc]initWithURL:[NSURL URLWithString:#"http://edutimeapp.com/toshow/chamber-of-commerc/ws/fetch_gallery.php"]];
response =[[NSMutableData alloc]init];
[NSURLConnection connectionWithRequest:req delegate:self];
}
-(void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
[response appendData:data];
NSLog(#"error receving data %#",response);
}
-(void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
}
-(void)connectionDidFinishLoading:(NSURLConnection *)connection
{
NSError *error;
//NSLog(#"Error in receiving data %#",error);
NSMutableDictionary *json = [NSJSONSerialization JSONObjectWithData:response options:NSJSONReadingMutableLeaves error:&error];
// NSLog(#"response data %#",json);
NSArray *results = [json objectForKey:#"status"];
picarray = [results valueForKey:#"img_url"];
titlearray = [results valueForKey:#"title"];
// NSLog(#"my title is%#",titlearray);
idarray=[results valueForKey:#"id"];
//NSLog(#"my galary id is%#",idarray);
[self.tableview reloadData];
}
// Do any additional setup after loading the view, typically from a nib.
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [picarray count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
// NSLog(#"tableview cell");
picture_viewcontrollerCell *cell = [tableView dequeueReusableCellWithIdentifier:#"htr"];
if (cell==nil)
{
NSArray *nib = [[NSBundle mainBundle] loadNibNamed:#"Cell" owner:self options:nil];
cell = [nib objectAtIndex:0];
}
//NSData* imageData = [[NSData alloc] initWithContentsOfURL:[NSURL URLWithString: [picarray objectAtIndex:indexPath.row]]];
// UIImage* image = [[UIImage alloc] initWithData:imageData];
//cell.picture.image =image;
cell.titlelbl.text=[NSString stringWithFormat:#"%#",[titlearray objectAtIndex:indexPath.row]];
NSURL*url= [[picarray objectAtIndex:indexPath.row]valueForKey:#"img_url"];
[cell.picture setContentMode:UIViewContentModeScaleAspectFill];
[cell.picture sd_setImageWithURL:url placeholderImage:[UIImage imageNamed:#"Placeholder.jpg"]];
return cell;
}
#end
You should try this (Show activityIndicator & Load Image using SDWebImage)-
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
// NSLog(#"tableview cell");
picture_viewcontrollerCell *cell = [tableView dequeueReusableCellWithIdentifier:#"htr"];
if (cell==nil)
{
NSArray *nib = [[NSBundle mainBundle] loadNibNamed:#"Cell" owner:self options:nil];
cell = [nib objectAtIndex:0];
}
cell.titlelbl.text=[NSString stringWithFormat:#"%#",[titlearray objectAtIndex:indexPath.row]];
NSURL*url= [NSURL URLWithString:[NSString stringWithFormat:#"%#",[picarray objectAtIndex:indexPath.row]]];
[cell.picture setContentMode:UIViewContentModeScaleAspectFill];
[cell.picture setShowActivityIndicatorView:YES];
[cell.picture setIndicatorStyle:UIActivityIndicatorViewStyleGray];
[cell.picture sd_setImageWithURL:url placeholderImage:[UIImage imageNamed:#"placeHolder.png"] options:indexPath.row == 0 ? SDWebImageRefreshCached : 0];
return cell;
}
Are you sure your code can even compile? In your cellForRowAtIndexPath I see [cell.picture sd_setImageWithURL:imageData placeholderImage:[UIImage imageNamed:#"Placeholder.jpg"]];
when you commented off your declaration of imageData.
Even if you didn't comment, this line should be
[cell.picture sd_setImageWithURL:url placeholderImage:[UIImage imageNamed:#"Placeholder.jpg"]];
you have to use
NSURL*url= [[picarray objectAtIndex:indexPath.row]valueForKey:#"img_url"];
[cell.picture setContentMode:UIViewContentModeScaleAspectFill];
[cell.picture sd_setImageWithURL:url placeholderImage:[UIImage imageNamed:#"Placeholder.jpg"]];
instead of
NSURL*url= [[picarray objectAtIndex:indexPath.row]valueForKey:#"img_url"];
[cell.picture setContentMode:UIViewContentModeScaleAspectFill];
[cell.picture sd_setImageWithURL:imageData placeholderImage:[UIImage imageNamed:#"Placeholder.jpg"]];
you have to pass url directly instead of imageData
Try this code :
Add UIActivityIndicatorView in tableview cell.
Changed following code in your cellForRowAtIndexpath.
cell.activityIndicator.center = imageView.center;
cell.activityIndicator.hidesWhenStopped = YES;
NSURL *url= [[picarray objectAtIndex:indexPath.row]valueForKey:#"img_url"];
[cell.picture sd_setImageWithURL:url placeholderImage:[UIImage imageNamed:#"placeholder.png"]
completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, NSURL *imageURL) {
[cell.activityIndicator stopAnimating];
}];
[cell.picture addSubview:activityIndicator];
[cell.activityIndicator startAnimating];
I making here simple application on XCODE 7.1. I just display 2 label and 1 image in tableview cell.I am parsing data from this URL. I am simply load data in TableviewHere i put the code of ViewController.m file
#interface ViewController ()
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
UIActivityIndicatorView *activityIndicator = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray];
activityIndicator.alpha = 1.0;
[self.view addSubview:activityIndicator];
activityIndicator.center = CGPointMake([[UIScreen mainScreen]bounds].size.width/2, [[UIScreen mainScreen]bounds].size.height/2);
[activityIndicator startAnimating];//to start animating
// Do any additional setup after loading the view, typically from a nib.
NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
AFURLSessionManager *manager = [[AFURLSessionManager alloc] initWithSessionConfiguration:configuration];
NSURL *URL = [NSURL URLWithString:#"http://www.androidbegin.com/tutorial/jsonparsetutorial.txt"];
NSURLRequest *request = [NSURLRequest requestWithURL:URL];
NSURLSessionDataTask *dataTask = [manager dataTaskWithRequest:request completionHandler:^(NSURLResponse *response, id responseObject, NSError *error) {
if (error) {
NSLog(#"Error: %#", error);
} else {
[activityIndicator stopAnimating];
_responsedic = (NSDictionary*) responseObject;
_Worldpopulation = [_responsedic valueForKey:#"worldpopulation"];
_imageURL = [_Worldpopulation valueForKey:#"flag"];
_country = [_Worldpopulation valueForKey:#"country"];
_population = [_Worldpopulation valueForKey:#"population"];
NSLog(#"Data:%#",_imageURL);
NSLog(#"Population",_population);
NSLog(#"Country",_country);
// NSLog(#"%#",_MovieList);
//NSLog(#"Array: %#",_imageURL);
//NSLog(#"%#",responseObject);
}
}];
[dataTask resume];
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
-(NSInteger) tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
return 10;
}
-(UITableViewCell *) tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
NSString *Identifier = #"mycell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:Identifier];
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:Identifier];
// Set and load the images
[cell.imageView sd_setImageWithURL:[_imageURL objectAtIndex:indexPath.row] placeholderImage:nil completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, NSURL *imageURL) {
// Get rid of the activity indicator when the image has been loaded
}];
cell.textLabel.text = [_country objectAtIndex:indexPath.row];
cell.detailTextLabel.text = [_population objectAtIndex:indexPath.row];
return cell;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
//NSString *rowValue = self.friends[indexPath.row+1];
NSString *message = [[NSString alloc] initWithFormat:#"You selected %#",[_country objectAtIndex:indexPath.row]];
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"YOU SELECT"
message:message
delegate:self
cancelButtonTitle:#"OK"
otherButtonTitles:nil];
[alert show];
}
I am using AFNetworking 3.0 and SDWebImage for image loading.Data parse successfully and and displayed in tableview.I attached screenshot below
Problem is what the all data are not displayed in the tableview cell i also put the Alert dialog on each cell of tableview data successfully loaded but not displayed in cell. I search everywhere i can't find solution for this i am using 3G connection so net speed is not an issue Please someone help.
Try to reload table view with updated data in completion block.
NSURL *URL = [NSURL URLWithString:#"http://www.androidbegin.com/tutorial/jsonparsetutorial.txt"];
NSURLRequest *request = [NSURLRequest requestWithURL:URL];
NSURLSessionDataTask *dataTask = [manager dataTaskWithRequest:request completionHandler:^(NSURLResponse *response, id responseObject, NSError *error) {
if (error) {
NSLog(#"Error: %#", error);
} else {
[activityIndicator stopAnimating];
_responsedic = (NSDictionary*) responseObject;
_Worldpopulation = [_responsedic valueForKey:#"worldpopulation"];
_imageURL = [_Worldpopulation valueForKey:#"flag"];
_country = [_Worldpopulation valueForKey:#"country"];
_population = [_Worldpopulation valueForKey:#"population"];
NSLog(#"Data:%#",_imageURL);
NSLog(#"Population",_population);
NSLog(#"Country",_country);
// NSLog(#"%#",_MovieList);
//NSLog(#"Array: %#",_imageURL);
//NSLog(#"%#",responseObject);
//Added Code -> Reloading data on Main queue for update
dispatch_async(dispatch_get_main_queue(), ^{
[self.tableview reloadData];
});
}
}];
[dataTask resume];
Hope, it'll help you.
Thanks.
1) Parse Data and after getting data relaod table
-(void)ParseData
{
NSURLSession * session = [NSURLSession sharedSession];
NSURL * url = [NSURL URLWithString: #"http://www.androidbegin.com/tutorial/jsonparsetutorial.txt"];
//Create URLReques
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url
cachePolicy:NSURLRequestUseProtocolCachePolicy
timeoutInterval:60.0];
[request addValue:#"application/x-www-form-urlencoded" forHTTPHeaderField:#"Content-Type"];
// Set Method POST/GET
[request setHTTPMethod:#"GET"];
// Asynchronously Api is hit here
NSURLSessionDataTask* dataTask=[session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
if (!error) //If error nil
{
//Serialization data
NSDictionary * json = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
NSLog(#"json %#",json);
array=[json valueForKey:#"worldpopulation"];
dispatch_async(dispatch_get_main_queue(), ^(void) {
if(array.count!=0)
{
//Reload table View
[_tblView reloadData];
}
});
}
else
{
//failure;
}
}];
[dataTask resume] ; // Executed task
}
2) Table View DataSource
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
if(array.count!=0)
{
return [array count];
}
return 0;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell;
//= [[UITableViewCell alloc]initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier];
cell.backgroundColor =[UIColor whiteColor];
cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
UILabel *lblCountry=(UILabel*)[cell viewWithTag:2];
lblCountry.text= [[array objectAtIndex:indexPath.row]valueForKey:#"country"];
UILabel *lblPopulation=(UILabel*)[cell viewWithTag:3];
lblPopulation.text= [[array objectAtIndex:indexPath.row]valueForKey:#"population"];
UIImageView *img = (UIImageView *)[cell viewWithTag:1];
[img setImageWithURL:[NSURL URLWithString:[[array objectAtIndex:indexPath.row]valueForKey:#"flag"]]];
return cell;
}
I am fetching images synchronously from an array which stores URLs of images but it work very slowly. Now i want to load them asynchronously for fast working.
Heres the code and provide answer with coding.
#import "DetailViewController.h"
#import "FinalViewController.h"
#interface DetailViewController ()
#end
#implementation DetailViewController
#synthesize jsonData;
- (void)viewDidLoad {
[super viewDidLoad];
self.title = #"Select a Photo";
// Do any additional setup after loading the view.
[UIApplication sharedApplication].networkActivityIndicatorVisible = YES;
NSURL *url = [NSURL URLWithString:#"http://json.code.com/albums/1/photos"];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
[[NSURLConnection alloc] initWithRequest:request delegate:self];
}
-(void)connection:(NSURLConnection *)connection didReceiveResponse:(nonnull NSURLResponse *)response
{
data1 = [[NSMutableData alloc] init];
}
-(void)connection:(NSURLConnection *)connection didReceiveData:(nonnull NSData *)theData
{
[data1 appendData:theData];
}
-(void)connectionDidFinishLoading:(NSURLConnection *)connection
{
[UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
jsonArray1 = [NSJSONSerialization JSONObjectWithData:data1 options:nil error:nil];
[mainTableView reloadData];
}
-(void)connection:(NSURLConnection *)connection didFailWithError:(nonnull NSError *)error
{
UIAlertView *errorView = [[UIAlertView alloc]initWithTitle:#"Error" message:#"Please make sure you are connected to either 3G or Wi-Fi." delegate:nil cancelButtonTitle:#"Dismiss" otherButtonTitles:nil, nil];
[errorView show];
[UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (int)numberOfSectionInTableView:(UITableView *)tableView
{
return 1;
}
- (int) tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [jsonArray1 count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(nonnull NSIndexPath *)indexPath
{
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"Cell"];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:#"Cell"];
}
cell.textLabel.text = [[jsonArray1 objectAtIndex:indexPath.row] objectForKey:#"title"];
cell.detailTextLabel.text = [NSString stringWithFormat:#"URL : %#", [[jsonArray1 objectAtIndex:indexPath.row] objectForKey:#"url"]];
NSURL *URL = [[NSURL alloc] initWithString:[[jsonArray1 objectAtIndex:indexPath.row] valueForKey:#"thumbnailUrl"]];
NSData *URLData = [[NSData alloc] initWithContentsOfURL:URL];
[[cell imageView]setImage:[UIImage imageWithData:URLData]];
return cell;
}
-(void)tableView:(UITableView *)tableview didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
FinalViewController *fvc = [[FinalViewController alloc] initWithNibName:#"FinalViewController" bundle:nil];
fvc.jsonData2 = [jsonArray1 objectAtIndex:indexPath.row];
[self.navigationController pushViewController:fvc animated:YES];
}
#end
We can use dispatch_async to run the operation asynchronously.
Try this:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
myCell *cell = [tableView dequeueReusableCellWithIdentifier:#"cell"];
if (cell == nil) {
cell = [[myCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:#"cell"];
}
cell.poster.image = nil; // or cell.poster.image = [UIImage imageNamed:#"placeholder.png"];
dispatch_async(kBgQueue, ^{
NSData *imgData = [NSData dataWithContentsOfURL:[NSURL URLWithString:[NSString stringWithFormat:#"http://myurl.com/%#.jpg",[[myJson objectAtIndex:indexPath.row] objectForKey:#"movieId"]]]];
if (imgData) {
UIImage *image = [UIImage imageWithData:imgData];
if (image) {
dispatch_async(dispatch_get_main_queue(), ^{
myCell *updateCell = (id)[tableView cellForRowAtIndexPath:indexPath];
if (updateCell)
updateCell.poster.image = image;
});
}
}
});
return cell;
}
You can do like this:
cell.tag = indexPath.row;
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0ul);
dispatch_async(queue, ^(void) {
NSData *imageData = [NSData dataWithContentsOfURL: URL];
UIImage* image = [[UIImage alloc] initWithData:imageData];
if (image) {
dispatch_async(dispatch_get_main_queue(), ^{
if (cell.tag == indexPath.row) {
cell.imageView.image = image;
[cell setNeedsLayout];
}
});
}
});
Ref: Asynchronous downloading of images for UITableView with GCD
Just by simply setting the following works fine for me .
cell.imageView.image =[UIImage imageWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:[NSString stringWithFormat:#"%#",[imageArray objectAtIndex:i]]]]];
You can use the activity indicator as you have asked . Just drag and drop UIActivityIndicatorView onto the UIImageView of the UITableViewCell and set the needed constraints . Once the image gets loaded you can set it as hidden .
To do it programmatically , you can add a subview to the Image view in the UITableViewCell. Once the Image gets loaded you can remove the sub view .
UIActivityIndicatorView* actInd = [[UIActivityIndicatorView alloc]init];
[cell.imageView addSubview:actInd];
Good morning,
I'm using UICollectionView for the first time to show images from a user (like a Facebook profile) and at the moment I can show the images fine but I have some problems:
1- When I visit my profile the app freezes for like 2-3 minutes due to the load of 5 images.
2- When I'm moving through the UICollectionView it freezes when the app load again the images outside the screen.
What I have to do in order to not to freeze the app when loading the user pictures? And what I have to do to navigate through the CollectionView without freezing? Maybe a cache system is what I need?
That's my code:
ProfileViewController.m
- (void)viewDidLoad {
[super viewDidLoad];
[self.view setBackgroundColor: [self colorWithHexString:#"FFFFFF"]];
self.profileimage.layer.cornerRadius = self.profileimage.frame.size.width / 2;
self.profileimage.clipsToBounds = YES;
self.profileimage.layer.borderWidth = 1.0f;
self.profileimage.layer.borderColor = [UIColor whiteColor].CGColor;
[self fetchJson];
[self fetchImages];
self.oneCollectionView.dataSource = self;
self.oneCollectionView.delegate = self;
}
- (CGFloat)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout minimumLineSpacingForSectionAtIndex:(NSInteger)section
{
return 1;
}
-(NSInteger)numberOfSectionsInCollectionView: (UICollectionView *)collectionView
{
return 1;
}
-(NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section
{
return _carImages.count;
}
// COLLECTION VIEW
-(UICollectionViewCell *)collectionView:(UICollectionView *)collectionView
cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
MyCollectionViewCell *myCell = [collectionView
dequeueReusableCellWithReuseIdentifier:#"MyCell"
forIndexPath:indexPath];
NSString *data = [[_jsonArray objectAtIndex:indexPath.row] valueForKey:#"imagen"];
NSURL * imageURL = [NSURL URLWithString:data];
NSData * imageData = [NSData dataWithContentsOfURL:imageURL];
UIImage * images = [UIImage imageWithData:imageData];
myCell.imageview.image = images;
return myCell;
}
-(void)fetchImages {
self.carImages = [[NSMutableArray alloc] init];
NSString *usersPassword = [SSKeychain passwordForService:#"login" account:#"account"];
NSString * urlString = [NSString stringWithFormat:#"http://mywebsite.com/posts.php?usersPassword=%#",usersPassword];
NSURL * url = [NSURL URLWithString:urlString];
NSData * data = [NSData dataWithContentsOfURL:url];
NSError *error;
[_jsonArray removeAllObjects];
_jsonArray = [NSJSONSerialization
JSONObjectWithData:data
options:NSJSONReadingMutableContainers|NSJSONReadingMutableLeaves
error:&error];
for(int i=0;i<_jsonArray.count;i++)
{
NSDictionary * jsonObject = [_jsonArray objectAtIndex:i];
NSString* imagen = [jsonObject objectForKey:#"imagen"];
[_carImages addObject:imagen];
}
}
Thanks in advance.
Import UIImageView+AFNetworking.h
and load your image via this method in cellForItemAtIndexPath method
[imageView setImageWithURL:[NSURL URLWithString:#"https://lh6.googleusercontent.com/-B8kSXtoaQDo/VGTVlXyIXpI/AAAAAAAAJ_M/USh6SgvMemw/w1024-h1024/IMG_20141112_103152.jpg"] placeholderImage:[UIImage imageNamed:#"placeholder-avatar"]];
it will surely speed up to load and scrolling collectionView
Download the images asynchronously, dataWithContentsOfURL is synchronous method and it will block your current thread until the download completes. You can use libraries like SDWebImage to automatically handle downloading for you or You can use NSURLSessionDownloadTask to download Images.
- (void)fetchImages {
self.carImages = [[NSMutableArray alloc] init];
NSString *usersPassword = [SSKeychain passwordForService:#"login" account:#"account"];
NSString * urlString = [NSString stringWithFormat:#"http://mywebsite.com/posts.php?usersPassword=%#",usersPassword];
NSURL * url = [NSURL URLWithString:urlString];
NSURLSession *session = [NSURLSession sharedSession];
NSURLSessionDataTask *dataTask = [session dataTaskWithURL:url completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
if (!error) {
[self.jsonArray removeAllObjects];
self.jsonArray = [NSJSONSerialization
JSONObjectWithData:data
options:NSJSONReadingMutableContainers|NSJSONReadingMutableLeaves
error:&error];
for(int i=0;i<_jsonArray.count;i++)
{
NSDictionary * jsonObject = self.jsonArray[i];
NSString* imagen = jsonObject[#"imagen"];
[self.carImages addObject:imagen];
}
}
}];
[dataTask resume];
}
// COLLECTION VIEW
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView
cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
MyCollectionViewCell *myCell = [collectionView
dequeueReusableCellWithReuseIdentifier:#"MyCell"
forIndexPath:indexPath];
NSString *data = [[self.jsonArray objectAtIndex:indexPath.row] valueForKey:#"imagen"];
NSURL * imageURL = [NSURL URLWithString:data];
NSURLSessionDownloadTask *imageDownloadTask = [[NSURLSession sharedSession]
downloadTaskWithURL:imageURL completionHandler:^(NSURL *location, NSURLResponse *response, NSError *error) {
UIImage *image = [UIImage imageWithData:
[NSData dataWithContentsOfURL:location]];
myCell.imageview.image = image;
}];
[imageDownloadTask resume];
return myCell;
}
You can use the dispatcher to create an async operation for the download of the images. This will resolve the 2 problems you have:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSData *imgData = [NSData dataWithContentsOfURL:YOUR_IMAGE_URL];
UIImage *img = [UIImage imageWithData:imgData];
[YOUR_IMAGE_VIEW_OUTLET performSelectorOnMainThread:#selector(setImage:) withObject:img waitUntilDone:YES];
});
These are the snippet you have to change:
-(UICollectionViewCell *)collectionView:(UICollectionView *)collectionView
cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
MyCollectionViewCell *myCell = [collectionView
dequeueReusableCellWithReuseIdentifier:#"MyCell"
forIndexPath:indexPath];
NSString *data = [[_jsonArray objectAtIndex:indexPath.row] valueForKey:#"imagen"];
NSURL * imageURL = [NSURL URLWithString:data];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSData *imageData = [NSData dataWithContentsOfURL: imageURL];
UIImage *img = [UIImage imageWithData:imageData];
[myCell.imageview performSelectorOnMainThread:#selector(setImage:) withObject:img waitUntilDone:YES];
});
return myCell;
}
Try to Register Nib For Collection View
Write following code in your viewController's viewDidLoad()method :
UINib *nib = [UINib nibWithNibName:#"MyCollectionCell" bundle: nil];
[self.collectionView registerNib:nib forCellWithReuseIdentifier:#"Cell"];
And I think you have to use https://github.com/nicklockwood/AsyncImageView for the image loading in collection view.
For Storyboards you have to see this tutorial : http://www.appcoda.com/ios-programming-uicollectionview-tutorial/ This will help you more.
Thanks!
For the first question the answer is in this line of code:
NSData * data = [NSData dataWithContentsOfURL:url];
From Apple Reference:
Do not use this synchronous method to request network-based URLs. For
network-based URLs, this method can block the current thread for tens
of seconds on a slow network, resulting in a poor user experience, and
in iOS, may cause your app to be terminated.
As alternative you can use NSURLSessionDataTask to download data (see Apple Reference)
-Edit
In ProfileViewController.h add these two properties:
#property (nonatomic, strong) NSURLSessionConfiguration *sessionConfig;
#property (nonatomic, strong) NSURLSession *session;
then, in - viewDidLoad initialise them:
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view
self.sessionConfig = [NSURLSessionConfiguration defaultSessionConfiguration];
self.session = [NSURLSession sessionWithConfiguration:self.sessionConfig];
//Other stuff...
}
Finally, in ProfileViewController.m
-(UICollectionViewCell *)collectionView:(UICollectionView *)collectionView
cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
MyCollectionViewCell *myCell = [collectionView
dequeueReusableCellWithReuseIdentifier:#"MyCell"
forIndexPath:indexPath];
NSString *data = [[_jsonArray objectAtIndex:indexPath.row] valueForKey:#"imagen"];
NSURL * imageURL = [NSURL URLWithString:data];
NSURLSessionDownloadTask *imageDownloadTask = [self.session dataTaskWithURL:imageURL
completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
if (error) {
NSLog(#"ERROR: %#", error);
} else {
NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response;
if (httpResponse.statusCode == 200) {
UIImage *image = [UIImage imageWithData:data];
myCell.imageview.alpha = 0.0f;
myCell.imageview.image = image;
[UIView animateWithDuration:0.45 animations:^{
myCell.imageview.alpha = 1.0f;
});
} else {
NSLog(#"Couldn't load image at URL: %#", imageURL);
NSLog(#"HTTP %d", (int)httpResponse.statusCode);
}
}
}];
[imageDownloadTask resume];
return myCell;
}
I hope this can help you.
- Edit 2
For future readers, I slightly refactored my code based on #suhit's answer (+1 for him)
I have to show some post blogs into a UITableView. When I have retreived all web service data.
This is my sample code.
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
[UIApplication sharedApplication].networkActivityIndicatorVisible=YES;
NSError *err;
NSString *strResponse=[[NSString alloc]initWithData:responseData encoding:NSUTF8StringEncoding];
NSLog(#"response is %#",strResponse);
dit=[NSJSONSerialization JSONObjectWithData:responseData options:NSJSONReadingAllowFragments error:&err];
NSArray *arrResults = [dit valueForKey:#"classifieds_mst"];
listOfObjects = [NSMutableArray array];
for(dictRes in arrResults)
{
Attributes *at = [[Attributes alloc]init];
at.classimage=[dictRes valueForKey:#"image_name"];
[listOfObjects addObject:at];
}
[tableView reloadData];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *identifier=#"cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:identifier];
if (cell == nil)
{
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:#"identifier"];
}
classifiedimage=[[UIImageView alloc]initWithFrame:CGRectMake(0, 0, 300, 80)];
[cell.contentView addSubview:classifiedimage];
Attributes *att = [listOfObjects objectAtIndex:indexPath.row];
NSString *str;
str=att.classimage;
classifiedimage.image = [UIImage imageNamed:str];
return cell;
}
My requirement is i want to display images in uitableviewcells using json parser.I wrote the above code.But the images are not displayed in uitableviewcells.I want to use to display images in uitableviewcells using Asyncronous concept.Please give me any idea.I am new to the programming.Thanks in advance.
This is my json data.
{"classifieds_mst":[{"classified_id":83,image_name":"1389006378_butterfly.jpeg"},
{"classified_id":82,"image_name":"ttt.jpj"},{"classified_id":83,”image_name":"ttttt.jpj”}
imageNamed is used to load image from local bundle (in app).
imageWithData:dataWithURL is used to load images from url's.
You have to append domain name in your image name: (image_name":"1389006378_butterfly.jpeg), so that you can get URL of the image which is stored on server, now you can use below code:
[cell.yourImageView setImage:[UIImage imageWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:yourImageNameHere]]]];
By referring to your JSON data, it just contain the image_name only. So first of all, you need to build the actual image url. (copy paste that actual url in browser to make sure image is available in server).
Once you have the image url, then you can use AsyncImageView instead of UIImageView in your cell. It will handle the asynchronous thing for you as you just need to set the image url only. Thats the way to go.
One more suggestion is to recheck you cellForRowAtIndexPath, as the code always creating imageView's and adding to cell each time the cell is created. That's not required when you are reusing a cell. You just need to create the image view once when cell is allocated and just assign a tag to imageview and then get the imageview based on this tag and then use it. Just for your reference, I'm posting a sample code:
if(cell == nil){
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:#"identifier"];
AsyncImageView *imgView= [[[AsyncImageView alloc]init]autorelease];
imgView.tag = 1000;
imgView.contentMode = UIViewContentModeScaleAspectFill;
[cell.contentView addSubview:imgView];
}
AsyncImageView *imgView= (AsyncImageView*)[cell.contentView viewWithTag:1000];
contactImgView.frame = CGRectMake(0, 0, 300, 80);
//imgUrl is the actual url of image that is available in datasource
[contactImgView setImageURL:imgUrl];
Hope it is clear.
if you are using the AFNetworking then use it to download your image like
NSArray *keys = #[#"UserID", ];
NSArray *objects = #[#(userId)];
NSDictionary *parameter = [NSDictionary dictionaryWithObjects:objects forKeys:keys];
AFHTTPClient *httpClient = [[AFHTTPClient alloc] initWithBaseURL:
[NSURL URLWithString:BaseURLString]];
[httpClient setParameterEncoding:AFJSONParameterEncoding];
[httpClient registerHTTPOperationClass:[AFJSONRequestOperation class]];
NSMutableURLRequest *request = [httpClient requestWithMethod:#"POST"
path:#"UserService.svc/GetUserInfo"
parameters:parameter];
AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request];
[httpClient registerHTTPOperationClass:[AFHTTPRequestOperation class]];
[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
NSError* error = nil;
id jsonObject = [NSJSONSerialization JSONObjectWithData:responseObject options:NSJSONReadingAllowFragments error:&error];
}
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
dispatch_async(dispatch_get_main_queue(), ^{
[SVProgressHUD dismiss];
});
}];
[operation start];
and for image use this
[cell.userImage setImageWithURLRequest:[[NSURLRequest alloc] initWithURL:[NSURL URLWithString:userBasicInfo.imageUrl]]
placeholderImage:[UIImage imageNamed:#"facebook-no-user.png"]
success:^(NSURLRequest *request, NSHTTPURLResponse *response, UIImage *image){
weakCell.userImage.image = image;
[weakCell setNeedsLayout];
} completion:^(BOOL success, NSError *error) {
NSLog(#"%#",[error localizedDescription]);
}];
}
failure:^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error){
}];
You Create a subclass of UITableViewCell (can call it blogTableViewCell) for your cell and use that in the table.
Initialize blogTableViewCell with the JSON data (you could have an NSDictionary store the JSON info)
In blogTableViewCell create a function that loads image data on another thread (so the main thread can continue its course) and once the image is loaded, update the uiimageview on the cell to display it (This should be done on the main thread as it is a UI update)
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier];
}
// Configure the cell...
//cell.textLabel.text = [[resultArray objectAtIndex:indexPath.row] valueForKey:#"name"];
cell.detailTextLabel.text = [[resultArray objectAtIndex:indexPath.row] valueForKey:#"designation"];
cell.imageView.image=[UIImage imageNamed:[[resultArray objectAtIndex:indexPath.row] valueForKey:#"image"]] ;
//[cell.imageView setImageWithURL:[NSURL URLWithString:[[resultArray objectAtIndex:indexPath.row] objectForKey:#"image"]]];
// NSURL *url = [NSURL URLWithString:[resultArray objectAtIndex:indexPath .row]];
NSData *imageData = [NSData dataWithContentsOfURL:[NSURL URLWithString:[[resultArray objectAtIndex:indexPath.row] valueForKey:#"image"] ]];
cell.imageView.image = [UIImage imageWithData:imageData];
return cell;
}