I have problem with download image from URL string. When I use this for download image and set in on imageView on view it works but now I have to set it on imageView on Cell.
I cannot put this on cellForRowAtIndexPath: method where I have access to imageView on cell.
__weak EditViewController *weakSelf = self;
[weakSelf.imageView setImageWithURLRequest:request placeholderImage:placeholderImage
success:^(NSURLRequest *request, NSHTTPURLResponse *response, UIImage *image) {
weakSelf.imageView.image = image;
[self.tableView reloadData];
} failure:^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error) {
[self prepareAndShowAlertView];
}];
I'll be glad for any help with this. Thanks in advance.
If you are not able to put your download to cellForRow:AtIndexPath: delegate method than you need to add #property (nonatomic, strong) UIImage *image; to your controller and when image is downloaded
...success:^(NSURLRequest *request, NSHTTPURLResponse *response, UIImage *image) {
weakSelf.imageView.image = image;
self.image = image;
[self.tableView reloadData];
}
and in cellForRow:AtIndexPath: setup your image
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"cell" forIndexPath:indexPath];
[cell.imageView setImage:self.image];
return cell;
}
Related
I use the code below to scale images to my UIImageView. Everything is fine when the table loads first time. But when I scroll the tableview, all the image aspects became broken.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"ProductCell" forIndexPath:indexPath];
[self configureCustomCell:(ProductsTableViewCell*)cell atIndexPath:indexPath];
return cell;
}
- (void)configureCustomCell:(ProductsTableViewCell*)cell atIndexPath:(NSIndexPath *)indexPath {
NSURL *url = [NSURL URLWithString:[[arr_products objectAtIndex:indexPath.section] productImage]];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
__weak UITableViewCell *weakCell = cell;
[cell.productPhotoImage setImageWithURLRequest:request placeholderImage:nil
success:^(NSURLRequest *request, NSHTTPURLResponse *response, UIImage *image) {
weakCell.imageView.image = image;
weakCell.imageView.contentMode = UIViewContentModeScaleAspectFit;
weakCell.imageView.clipsToBounds = YES;
weakCell.imageView.frame = CGRectMake(5, 5, 60, 60);
}
failure:nil];
}
If you want to get the image from the server and then load it on the cell tableviewcell won't upgrade properly,
you can use SDWebImage Library to load the image from server, Check this link SDWEbImage
If you want to add activity indicator check this https://github.com/JJSaccolo/UIActivityIndicator-for-SDWebImage
#import <SDWebImage/UIImageView+WebCache.h>
in cellForRowAtIndexPath
[weakCell.myImage setImageWithURL:[NSURL URLWithString:imageUrl] placeholderImage:[UIImage imageNamed:#"mawgif-log.png"] options:SDWebImageRefreshCached usingActivityIndicatorStyle:UIActivityIndicatorViewStyleGray];
I am loading images in UITableViewCell receiving from url using below code, everything is working fine but the image load very slow in UITableViewCell and if i think as a user, it is not acceptable.
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return [checkData count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
NSInteger i =indexPath.row;
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
UIImageView propertyImage = (UIImageView *)[cell viewWithTag:10];
NSString *test = [[checkData objectAtIndex:i]valueForKey:#"image"];
CGRect frame=propertyImage.bounds;
AsyncImageView asyImage=[[AsyncImageView alloc] initWithFrame:frame];
[asyImage setImage:[UIImage imageNamed:#"loading.png"]];
self.urlForTesting=[NSURL URLWithString:test];
[asyImage loadImageFromURL:self.urlForTesting];
asyImage.imageURL =self.urlForTesting;
asyImage.contentMode = UIViewContentModeScaleToFill;
asyImage.layer.masksToBounds=YES;
[propertyImage addSubview:asyImage];
return cell;
}
Use AFNetworking Class & add additional two file to download from github "UIImageView+AFNetworking" and import in your project. then after add this code from "cellForRowAtIndexPath"
// Fetch Image from URL
NSURL *url = [NSURL URLWithString:[[checkData objectAtIndex:i]valueForKey:#"image"]];
[objWinner.actWinner startAnimating];
[objWinner.actWinner setHidden:NO];
NSURLRequest *req = [NSURLRequest requestWithURL:url];
[objWinner.imgwinner setImageWithURLRequest:req placeholderImage:nil success:^(NSURLRequest *request, NSHTTPURLResponse *response, UIImage *image){
[objWinner.actWinner stopAnimating]; // this is Activityindicator
[objWinner.actWinner setHidden:YES];
objWinner.imgwinner.image = image;
} failure:^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error){
}];
To see a demonstration of the problem along with code, please have a look at the following 60 second video.
Here is a simple project demonstrating the problem.
Challenge, see if you can make the image appear without having to touch the tableviewcontroller during runtime using the category I've created.
I have created a simple UIImageView category where I expose an imageURL property which when set should then start an asynchronous download. When the completion block is called, I set the class' image property to the image downloaded.
I then have a class that inherits from UITableViewController where I import my custom UIImageView category. I start the download in the willDisplayCell: method by setting the cell's imageview imageURL property. The image is asynchronously downloaded and is set, but the cell's imageview does not update unless I scroll the cell away from the table, or I click on the cell itself.
I have tried things like [self setNeedsDisplay]; and a few other things like executing the setting of the image on the main thread but all to no avail.
Can someone take a look and let me know what I should be doing?
Some code:
UIImageView+AsyncDLImageView.h
#import <UIKit/UIKit.h>
#interface UIImageView (AsyncDLImageView)
#property (nonatomic, retain) NSURL *imageURL;
#end
UIImageView+AsyncDLImageView.m
#import "UIImageView+AsyncDLImageView.h"
#implementation UIImageView (AsyncDLImageView)
- (void)setImageURL:(NSURL *)imageURL{
[self loadImageWithURL:imageURL completionHandler:^(UIImage *image, NSError *error) {
if(!error) {
NSLog(#"image has been set: %#", image);
self.image = image;
}
}];
}
-(NSURL *)imageURL{
return self.imageURL;
}
-(void)loadImageWithURL:(NSURL*)url completionHandler:(void(^)(UIImage *image, NSError *error))completionBlock{
NSLog(#"set image url request called: %#", [url absoluteString]);
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
[NSURLConnection sendAsynchronousRequest:request
queue:[NSOperationQueue mainQueue]
completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) {
if ( !error )
{
NSLog(#"no error");
UIImage *image = [[UIImage alloc] initWithData:data];
completionBlock(image, nil);
} else{
completionBlock(nil, error);
NSLog(#"error");
}
}];
}
It would appear that because the imageView on the table cell is not initialised that the image won't display. I added a transparent placeHolder image (make the size you want the image to be) and use that while you are waiting for the image to load. (this worked for me)
so I changed the code as follows...
DisplayXMLImageDataTVC.m
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return [_xmlImageURLArray count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *cellIdentifier = #"xmlCellIdentifier";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
if(cell == nil){
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdentifier];
cell.imageView.image = [UIImage imageNamed:#"placeHolder"];
}
[cell.imageView setImageURL:[_xmlImageURLArray objectAtIndex:indexPath.row]];
return cell;
}
and your category class back to what it was....
- (void)setImageURL:(NSURL *)imageURL
{
// objc_setAssociatedObject(self, #selector(imageURL), imageURL, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
[self loadImageWithURL:imageURL completionHandler:^(UIImage *image, NSError *error) {
if(!error) {
self.image = image;
}
}];
}
-(void)loadImageWithURL:(NSURL*)url completionHandler:(void(^)(UIImage *image, NSError *error))completionBlock{
NSLog(#"set image url request called: %#", [url absoluteString]);
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
[NSURLConnection sendAsynchronousRequest:request
queue:[NSOperationQueue mainQueue]
completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) {
if ( !error )
{
NSLog(#"no error");
UIImage *image2 = [[UIImage alloc] initWithData:data];
completionBlock(image2, nil);
} else{
completionBlock(nil, error);
NSLog(#"error");
}
}];
}
The issue seems to be that UITableViewCell uses the initial frame of the imageView to layout the cell. If it is not set, then you get a 0x0 frame, and even when you set the image later, the frame doesn't change. If you set it to some placeholder image, then it continues to use the frame from that placeholder image, which might not be what you want.
A possible solution would be to have a custom UITableViewCell, which overrides layoutSubviews and sets the frame according to the image that you have. And from your imageView category, after you set the image, call [self setNeedsLayout]. This will cause layoutSubviews to be called on the next redraw.
You could look at this also - http://www.wrichards.com/blog/2011/11/sdwebimage-fixed-width-cell-images/
The reason for that is cell is not redrawn after the image is set. Usually we send an asynchronous request for an image in cellForRowAtIndexPath and after we get the image, we call cell setNeedsLayout.
So you can do the following.
- (void)setImageURL:(NSURL *)imageURL
{
UITableViewCell *cell = (UITableViewCell *)self.superview.superview;
[self loadImageWithURL:imageURL completionHandler:^(UIImage *image, NSError *error) {
if(!error) {
NSLog(#"image has been set: %#", image);
self.image = image;
[cell setNeedsLayout];
}
}];
}
I'm using a UICollectionView and want to display images fetched from a server in it's cells. To do this I'm using UIImageView+AFNetworking. In my cellForItemAtIndexPath method I have the following code:
__weak CustomCollectionViewCell *weakCell = cell;
[cell.cellImageView setImageWithURLRequest:request
placeholderImage:placeholderImage
success:^(NSURLRequest *request, NSHTTPURLResponse *response, UIImage *image) {
weakCell.cellImageView.image = image;
[weakCell setNeedsLayout];
} failure:nil];
The placeholder always show, but sometimes the downloaded images aren't showing. Any ideas on how I could solve this?
I suspect the problem is that you're on a background thread when you try to set the image, whereas you should always perform UI operations on the main thread.
Try setting the image in the main thread with something like
__weak CustomCollectionViewCell *weakCell = cell;
[cell.cellImageView setImageWithURLRequest:request
placeholderImage:placeholderImage
success:^(NSURLRequest *request, NSHTTPURLResponse *response, UIImage *image) {
dispatch_async(dispatch_get_main_queue(), ^{
weakCell.cellImageView.image = image;
});
} failure:nil];
I'm pretty sure that AFNetworking moves all the block execution on the main thread, I'm just wondering if you are sure that those images are correctly downloaded.
Try to log the image size of the retuned image, and if it is present always use a failure block, maybe just printing the error.
Something like that:
typeof(self) __weak weakCell = cell; //but you don't need in that scenario
[cell.cellImageView setImageWithURLRequest:request
placeholderImage:placeholderImage
success:^(NSURLRequest *request, NSHTTPURLResponse *response, UIImage *image) {
NSLog(#"Image %# scale %f", NSStringFromCGSize(image.size), image.scale);
weakCell.cellImageView.image = image;
[weakCell setNeedsLayout];
} failure:^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error) {
NSLog(#"Error %#",error.localizedDescription);
}];
Solution for you question
SDWebImage
You just need to #import <SDWebImage/UIImageView+WebCache.h> to your project, and you can define also the placeholder when image is being downloaded with just this code:
-(UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
MyCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:#"MyCell"
forIndexPath:indexPath];
DLog(#"url at index %d : %#", indexPath.row, ((MyObject *)[self.list.array objectAtIndex:indexPath.row]).imageUrl);
MyObject *object = (MyObject *)[self.list.array objectAtIndex:indexPath.row];
[cell.imageView setImageWithURL:[NSURL URLWithString:#"http://www.domain.com/path/to/image.jpg"]
placeholderImage:[UIImage imageNamed:#"placeholder.png"]];
return cell;
}
It also cache downloaded images and gives you great performance.
Hope it will help you!
After downloading the images, I replaced
[self.myCollectionView reloadData]
with
[self.myCollectionView reloadSections:[NSIndexSet indexSetWithIndex:0]];
then refresh and it works well, you can try it.
I am using UITableView to list some images from Internet. So i use AFNetworking's setImageWithURLRequest:placeholderImage:success:failure:methord, when the image is downloaded, i need to do some process then display. In order to refresh the processed image i use
[wTableView beginUpdates];
[wTableView reloadRowsAtIndexPaths:[NSArray arrayWithObjects:indexPath, nil] withRowAnimation:UITableViewRowAnimationNone];
[wTableView endUpdates];
in the success block. It seems this works when first time loaded, but when i scroll the tableview, the rows are messed up, also one row is disappeared and its easy to crash in the [wTableView endUpdates]; method. what's wrong with this method?
The related code snippet is as follows:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *tableIdentifier = #"cell";
CouponTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:tableIdentifier];
if (!cell) {
cell = [[CouponTableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:tableIdentifier];
}
[cell prepareForReuse];
NSArray *coupons = [mCoupons objectAtIndex:indexPath.section];
CouponDB *couponDB = [coupons objectAtIndex:indexPath.row];
NSString *thumbLink;
if (couponDB) {
[cell.textLabel setText:couponDB.couponInfo.company];
[cell.textLabel setFont:[UIFont boldSystemFontOfSize:CELL_LABEL_FONT_SIZE]];
[cell.textLabel setTextColor:[UIColor grayColor]];
[cell.textLabel setBackgroundColor:[UIColor clearColor]];
[cell.detailTextLabel setText:[NSString stringWithFormat:#"Expires:%#",couponDB.couponInfo.deadline]];
[cell.detailTextLabel setBackgroundColor:[UIColor clearColor]];
thumbLink = [self thumbLink:couponDB.couponInfo.imgURL];
[cell setNeedsLayout];
if (couponDB.redeem_status) {
UIImageView * __weak imageView = cell.imageView;
CouponTableController * __weak table = self;
UITableView *__weak wTableView = mTableView;
[cell.imageView setImageWithURLRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:thumbLink]] placeholderImage:nil success:^(NSURLRequest *request, NSHTTPURLResponse *response, UIImage *image) {
imageView.image = [table crossedImage:image];
#synchronized(wTableView){
[wTableView beginUpdates];
[wTableView reloadRowsAtIndexPaths:[NSArray arrayWithObjects:indexPath, nil] withRowAnimation:UITableViewRowAnimationNone];
[wTableView endUpdates];
}
} failure:^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error) {
NSLog(#"cell imageview load error:%#",error);
}];
} else {
[cell.imageView setImageWithURL:[NSURL URLWithString:thumbLink] placeholderImage:[UIImage imageNamed:#"default_cover.jpg"]];
}
}
if (![cell.backgroundView isKindOfClass:[CouponTableCellBackgroud class]]) {
cell.backgroundView = [[CouponTableCellBackgroud alloc] init];
}
return cell;
}
the first time the table is loaded(no scroll) is like this:
when you scroll down and up the table rows are messed up, which is like below:
Any suggestions are appreciated.
Try this, instead of reloading the cell. Declare a weak pointer to the cell
__weak UITableViewCell *weakCell = cell;
[cell.imageView setImageWithURLRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:thumbLink]] placeholderImage:nil success:^(NSURLRequest *request, NSHTTPURLResponse *response, UIImage *image) {
weakCell.imageView.image = [table crossedImage:image];
[weakCell setNeedsLayout];
} failure:^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error) {
NSLog(#"cell imageview load error:%#",error);
}];
Can you try this:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *SimpleTableIdentifier = #"YourResuseIdentifier";
CouponTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:tableIdentifier];
if(cell == nil)
{
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:SimpleTableIdentifier];
}
else
{
for (UIView *subview in cell.contentView.subviews)
[subview removeFromSuperview];
}
NSArray *coupons = [mCoupons objectAtIndex:indexPath.section];
CouponDB *couponDB = [coupons objectAtIndex:indexPath.row];
NSString *thumbLink;
if (couponDB) {
[cell.textLabel setText:couponDB.couponInfo.company];
[cell.textLabel setFont:[UIFont boldSystemFontOfSize:CELL_LABEL_FONT_SIZE]];
[cell.textLabel setTextColor:[UIColor grayColor]];
[cell.textLabel setBackgroundColor:[UIColor clearColor]];
[cell.detailTextLabel setText:[NSString stringWithFormat:#"Expires:%#",couponDB.couponInfo.deadline]];
[cell.detailTextLabel setBackgroundColor:[UIColor clearColor]];
thumbLink = [self thumbLink:couponDB.couponInfo.imgURL];
if (couponDB.redeem_status) {
UIImageView * __weak imageView = cell.imageView;
CouponTableController * __weak table = self;
UITableView *__weak wTableView = mTableView;
[cell.imageView setImageWithURLRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:thumbLink]] placeholderImage:nil success:^(NSURLRequest *request, NSHTTPURLResponse *response, UIImage *image) {
imageView.image = [table crossedImage:image];
[wTableView beginUpdates];
[wTableView reloadRowsAtIndexPaths:[NSArray arrayWithObjects:indexPath, nil] withRowAnimation:UITableViewRowAnimationNone];
[wTableView endUpdates];
} failure:^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error) {
NSLog(#"cell imageview load error:%#",error);
}];
} else {
[cell.imageView setImageWithURL:[NSURL URLWithString:thumbLink] placeholderImage:[UIImage imageNamed:#"default_cover.jpg"]];
}
}
if (![cell.backgroundView isKindOfClass:[CouponTableCellBackgroud class]]) {
cell.backgroundView = [[CouponTableCellBackgroud alloc] init];
}
return cell;
}
I change the load image code to
UIImageView * __weak imageView = cell.imageView;
CouponTableController * __weak table = self;
[cell.imageView setImageWithURLRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:thumbLink]] placeholderImage:[UIImage imageNamed:#"default_cover.jpg"] success:^(NSURLRequest *request, NSHTTPURLResponse *response, UIImage *image) {
imageView.image = [table crossedImage:image];
} failure:^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error) {
NSLog(#"cell imageview load error:%#",error);
}];
Only add one placeholder image and remove the tableview updates. then it refresh cells automatically.