I have a custom cell with UIImageView, and it is not showing the image. I have tried setting image to default cell.imageView.image property, and it works just fine, but doesn't work with my custom ImageView.
I load my custom Cell from Xib, and I believe it has to do with lazy loading of UIImageView. How do I make it work?
Here is my code:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"MyCell";
DVGTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
cell.tag = indexPath.row;
if (self.loader.parsedData[indexPath.row] != nil)
{
cell.imageCustom.image = nil;
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0ul);
dispatch_async(queue, ^(void) {
NSString *url = [self.loader.parsedData[indexPath.row] objectForKey:#"imageLR"];
NSData *imageData = nil;
if ([self.cache objectForKey:url] != nil)
{
imageData = [self.cache objectForKey:url];
}
else
{
imageData = [NSData dataWithContentsOfURL:[NSURL URLWithString:url]];
[self.cache setObject:imageData forKey:[self.loader.parsedData[indexPath.row] objectForKey:#"imageLR"]];
}
dispatch_async(dispatch_get_main_queue(), ^{
if (cell.tag == indexPath.row) {
UIImage *image = [[UIImage alloc] initWithData:imageData];
cell.imageCustom.image = image;
[cell setNeedsLayout];
}
});
});
}
return cell;
}
I usually do Lazy loading for ImageViews with this piece of code, hope it helps:
- (void) loadImageForImageView:(UIImageView *)theImageView WithURL:(NSURL *)url {
NSOperationQueue *queue = [NSOperationQueue new];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url cachePolicy:NSURLRequestReturnCacheDataElseLoad timeoutInterval:3.0];
[NSURLConnection sendAsynchronousRequest:request queue:queue completionHandler:^(NSURLResponse *reponse, NSData *data, NSError *error) {
UIImage *image = [UIImage imageWithData:data];
dispatch_async(dispatch_get_main_queue(), ^{
theImageView.image = image;
for (UIActivityIndicatorView *spinner in theImageView.subviews) {
[spinner removeFromSuperview];
break;
}
});
}];
}
In your cellForRowAtIndexPath:
UIActivityIndicatorView *spinner = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray];
[spinner setColor:[UIColor darkGrayColor]];
spinner.frame = CGRectMake(130 , 53, 20, 20);
[spinner startAnimating];
[imageCell addSubview:spinner];
[self loadImageForImageView:imageCell WithURL:imageURL];
Where imageCell is your UIImageView.
Related
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"cell";
TVcell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[TVcell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
cell.titleLabel.text = [[[arrayData objectAtIndex:indexPath.row]valueForKey:#"title"]stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
cell.txtlabel.text = [[[arrayData objectAtIndex:indexPath.row] valueForKey:#"description"]stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
cell.tag = indexPath.row;
cell.imageView.image = nil;
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0ul);
dispatch_async(queue, ^(void) {
NSData *imageData = [NSData dataWithContentsOfURL:[NSURL URLWithString:[enclosureUrlArray objectAtIndex:indexPath.row]]];
UIImage* image = [[UIImage alloc] initWithData:imageData];
if (image) {
dispatch_async(dispatch_get_main_queue(), ^{
if (cell.tag == indexPath.row) {
cell.IMGLabel.image = image;
[cell setNeedsLayout];
}
});
}
});
return cell;
}
while i'm scrolling that same images are appearing on the cells,which cells im reusing.i used scroll view delegate also.im storing urls in enclosureUrlArray and i'm passing.
Put cell.IMGLabel.image = nil in your cellForRowAtIndexPath before downloading image, i.e. below cell.imageView.image = nil; this line.
Or set some place holder image (no image available kind of image to cell's image in interface builder), so if image is not downloaded then it will show this place holder image otherwise it will show downloaded image.
SDWebImage is good for this kind of case. It will cache the images so it will give better performance also. There is nothing wrong to use any good third party library.
By using SDWeb image:
#import <SDWebImage/UIImageView+WebCache.h>
...
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *MyIdentifier = #"MyIdentifier";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:MyIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:MyIdentifier] autorelease];
}
// Here we use the new provided sd_setImageWithURL: method to load the web image
[cell.imageView sd_setImageWithURL:[NSURL URLWithString:#"http://www.domain.com/path/to/image.jpg"]
placeholderImage:[UIImage imageNamed:#"placeholder.png"]];
cell.textLabel.text = #"My Text";
return cell;
}
You can also use NSURLSession as well,see this link
Async image loading from url inside a UITableView cell - image changes to wrong image while scrolling
NSURL *url = [NSURL URLWithString:[enclosureUrlArray objectAtIndex:indexPath.row]];
NSURLSessionTask *task = [[NSURLSession sharedSession] dataTaskWithURL:url completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
if (data) {
UIImage *image = [UIImage imageWithData:data];
if (image) {
dispatch_async(dispatch_get_main_queue(), ^{
MyCell *updateCell = (id)[tableView cellForRowAtIndexPath:indexPath];
if (updateCell)
updateCell.IMGLabel.image = image;
});
}
}
}];
[task resume];
works perfect for me..
with the help of sdwebimage you can show the image like this:-
NSString *string2 = [[businessListArray objectAtIndex:indexPath.row ]valueForKey:#"logo"];
NSURL *url1 = [NSURL URLWithString:
[NSString stringWithFormat:
#"%#%#",
#"http://dev-demo.info.bh-in-15./upload/post/",
string2]];
UIImageView *imageView = (UIImageView *)[cell.contentView viewWithTag:301];
[imageView sd_setImageWithURL:url1
placeholderImage:[UIImage imageNamed:#"pocket.png"]
completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, NSURL *imageURL) {
imageView.image = image;
}];
In the below code whenever I am scrolling the tableview, images in each cell are changing, which shouldn't happen. Please help. Thanks in advance.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
UserDetails *userDetails = [arrUserDetails objectAtIndex:indexPath.row];
static NSString *CellIdentifier = #"CustomCell";
__weak TableViewCell *cell = (TableViewCell *)[_tableViewUsername dequeueReusableCellWithIdentifier:CellIdentifier];
cell.tag = indexPath.row;
cell.userName.text = userDetails.userName;
[self.operationQueue addOperationWithBlock: ^ {
NSURL *aURL = [NSURL URLWithString:userDetails.userImageURL];
NSError *error = nil;
NSData *data = [NSData dataWithContentsOfURL:aURL options:nil error:&error];
UIImage *image = nil;
if (cell.tag == indexPath.row)
{
image = [UIImage imageWithData:data];
[[NSOperationQueue mainQueue] addOperationWithBlock: ^ {
cell.customImageView.image = image;
cell.customImageView.contentMode = UIViewContentModeScaleToFill;
cell.accessoryType = UITableViewCellAccessoryCheckmark;
}];
}
}];
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
UserDetails *userDetails = [arrUserDetails objectAtIndex:indexPath.row];
static NSString *CellIdentifier = #"CustomCell";
__weak TableViewCell *cell = (TableViewCell *)[_tableViewUsername dequeueReusableCellWithIdentifier:CellIdentifier];
cell.tag = indexPath.row;
cell.userName.text = userDetails.userName;
//Add Default placeholder
cell.customImageView.image = [UIImage imageNamed:#"Default.png"];
[self.operationQueue addOperationWithBlock: ^ {
NSURL *aURL = [NSURL URLWithString:userDetails.userImageURL];
NSError *error = nil;
NSData *data = [NSData dataWithContentsOfURL:aURL options:nil error:&error];
UIImage *image = nil;
if (cell.tag == indexPath.row)
{
image = [UIImage imageWithData:data];
[[NSOperationQueue mainQueue] addOperationWithBlock: ^ {
cell.customImageView.image = image;
cell.customImageView.contentMode = UIViewContentModeScaleToFill;
cell.accessoryType = UITableViewCellAccessoryCheckmark;
}];
}
}];
Add Default placeholder for image before loading it from url,As cells are being reused it takes the previous image
You can use SDWebImage.framework to load image
[cell.customImageView sd_setImageWithURL:[NSURL URLWithString:userDetails.userImageURL] placeholderImage:nil options:SDWebImageCacheMemoryOnly completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, NSURL *imageURL) {
if (image) {
[cell.customImageView setImage:image];
}
}];
You can use Afnetworking class for this .
Simply import #import "UIImageView+AFNetworking.h"
And use this line:-
[cell.imgProfile setImageWithURL:imgurl placeholderImage:[UIImage imageNamed:#""]];
Imgurl is the image Url which you are getting from response
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];
here is the code for my collectionview it is showing records but loading really please tell me how can i implement lazy loading on this i also have a placeholder pic in my project
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
CollectionViewCell* cell = [collectionView dequeueReusableCellWithReuseIdentifier:#"MJCell" forIndexPath:indexPath];
// Setup image name
NSString *url = [[rssOutputData objectAtIndex:indexPath.row]xmllink];
UIImage *img = nil;
NSData *data = [[NSData alloc] initWithContentsOfURL:[NSURL URLWithString:url]];
img = [[UIImage alloc] initWithData:data];
cell.MJImageView.image = img;
return cell;
}
right now it is working but very very slow.
It's pretty easy to do lazy loading using GCD.
// Create a queue for the operations
dispatch_queue_t queue = dispatch_queue_create("photoList", NULL);
// Start getting the data in the background
dispatch_async(queue, ^{
NSData* photoData = [NSData dataWithContentsOfURL:[NSURL URLWithString:object.photoURL]];
UIImage* image = [UIImage imageWithData:photoData];
// Once we get the data, update the UI on the main thread
dispatch_sync(dispatch_get_main_queue(), ^{
cell.photoImageView.image = image;
});
});
The easiest way to implement that is use SDWebImage library, it does right what you need. There is UIImageView category that will allow you to modify code for that:
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
CollectionViewCell* cell = [collectionView dequeueReusableCellWithReuseIdentifier:#"MJCell" forIndexPath:indexPath];
// Setup image name
NSString *url = [[rssOutputData objectAtIndex:indexPath.row]xmllink];
[cell.MJImageView sd_setImageWithURL:[NSURL URLWithString:url]];
return cell;
}
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString: #"url.com"]];
UIActivityIndicatorView * indicator = [[UIActivityIndicatorView alloc]initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray];
[yourImageView addSubview:indicator];
indicator.center = yourImageView.center;
[indicator startAnimating];
[yourImageView setImageWithURLRequest:request
placeholderImage:[UIImage imageNamed:#"placeholder.png"]
success:^(NSURLRequest *request, NSHTTPURLResponse *response, UIImage *image) {
dispatch_async(dispatch_get_main_queue(), ^(void){
yourImageView.image = image;
});
[indicator stopAnimating];
[indicator removeFromSuperview];
} failure:nil];
Maybe, because your image is so large
You can use NSThread for loading
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
CollectionViewCell* cell = [collectionView dequeueReusableCellWithReuseIdentifier:#"MJCell" forIndexPath:indexPath];
cell.tag = indexPath.row; //For index
[NSThread detachNewThreadSelector:#selector(loadImage:) toTarget:self withObject:cell];
return cell;
}
- (void) loadImage:(CollectionViewCell *)cell {
NSString *url = [[rssOutputData objectAtIndex:cell.tag]xmllink];
UIImage *img = nil;
NSData *data = [[NSData alloc] initWithContentsOfURL:[NSURL URLWithString:url]];
img = [[UIImage alloc] initWithData:data];
cell.MJImageView.image = img;
}
I'm loading images for my uitableview asynchronously using GCD, but there is a problem - when scrolling down UITableview the images are dissappeared on top.And sometimes the images are not displayed in UITableviewcell. This is my code.please give me any idea.Is there any error in my code.Please help me.
- (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"];
}
image=[[UIImageView alloc]initWithFrame:CGRectMake(0, 0, 300, 80)];
[cell.contentView addSubview:image];
str=att.classimage;
url=#"My URL";
if(str!=nil){
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0ul);
dispatch_async(queue, ^(void) {
dispatch_async(dispatch_get_main_queue(), ^{
NSString *imageStr=[url stringByAppendingString:str];
image.image = [UIImage imageNamed:imageStr];
});
});
image.image=[UIImage imageNamed:#"nav-bar-logo.png"];
}
return cell;
}
Before setting Image check if that cell is still in memory.
UITableViewCell *updateCell = (id)[tableView cellForRowAtIndexPath:indexPath];
if (updateCell)
{
yourImageView.image = [imagePost fixOrientation];
}
I am doing similar thing, but Using NSURLConnection.
NSURL *postImage = [NSURL URLWithString:#"Your URL"];
NSMutableURLRequest *imageRequest = [[NSMutableURLRequest alloc]initWithURL:postImage cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:30];
[imageRequest setHTTPMethod:#"GET"];
[NSURLConnection sendAsynchronousRequest:imageRequest queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *response, NSData *data, NSError *error)
{
imagePost = [UIImage imageWithData:data]; // UIImage Object
UITableViewCell *updateCell = (id)[tableView cellForRowAtIndexPath:indexPath];
if (updateCell)
{
postImageView.image = [imagePost fixOrientation];// setting my ImageView
}
}];
Hope this will help you.
You can use AFNetworknig for showing images from remote URL.
get AFNetworking from here
just
#import "AFNetworking.h"
[imageView setImageWithURL:url placeholderImage:[UIImage imageNamed:#"placeHolderImage"]];
- (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];
UIImageView *imageView =[[UIImageView alloc]initWithFrame:CGRectMake(0, 0, 300, 80)];
imageView.tag = 100;
[cell.contentView addSubview:imageView];
}
str=att.classimage;
url=#"My URL";
UIImageView *cellImageView = [cell.contentView viewWithTag:100];
cellImageView.image = nil;
if(str!=nil) {
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0ul);
dispatch_async(queue, ^(void) {
NSString *imageStr = [url stringByAppendingString:str];
UIImage *image = [UIImage imageWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:imageStr]]];
dispatch_async(dispatch_get_main_queue(), ^{
cellImageView.image = image;
});
});
}
return cell;
}