I am trying to use SDWebImage library with a custom UITableViewCell. Below is the method that is downloading the images from the web service:
- (void) downloadThumbnails:(NSURL *)finalUrl
{
SDWebImageManager *manager = [SDWebImageManager sharedManager];
[manager downloadWithURL:finalUrl
options:0
progress:nil
completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, BOOL finished)
{
if (image)
{
// do something with image
// THERE I HAVE TO ASSIGN the property "thumbnail" with the "image"
// So it can be used by the tableview controller class
}
}];
}
The above code is in a separate file named RSSItem. While my UITableViewController class has the following cellForRowAtIndexPath method:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"ItemsCell";
ItemsViewCell *cell = (ItemsViewCell *)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
RSSItem *item = [[channel items] objectAtIndex:[indexPath row]];
cell.titleLabel.text = [item title];
cell.creatorLabel.text = [item creator];
cell.pubTimeLabel.text = [item pubTime];
cell.thumbContainer.image = [item thumbnail];
return cell;
}
Can somebody point out how can i configure the if (image) inside downloadThumbnails method? I just need to assign "image" to the property "thumbnail", how can i do that?
Looks like you have already used the right method to download images asynchronously using SDWebImage. All you need to do is to set the thumbnail property to the "image". Here is how you can do it:
- (void) downloadThumbnails:(NSURL *)finalUrl
{
SDWebImageManager *manager = [SDWebImageManager sharedManager];
[manager downloadWithURL:finalUrl
options:0
progress:nil
completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, BOOL finished)
{
if (image)
{
[self setThumbnail:image];
}
}];
}
Related
I am using SDWebImage library and its working on iPhone. But I don't know why this is not called in iPad. I tried to put break points, but it doesn't hit the break point either. I put this method in cellForItemAtIndexPath.
-(UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
static NSString* cellIdentifier = #"CustomCollectionViewCell";
CustomCollectionViewCell* cell = (CustomCollectionViewCell*)[collectionView dequeueReusableCellWithReuseIdentifier:cellIdentifier forIndexPath:indexPath];
[cell.downloadButton setTag:indexPath.row];
[cell.downloadButton addTarget:self action:#selector(deleteButtonPressed:) forControlEvents:UIControlEventTouchUpInside];
catalogModel = [catalogArray objectAtIndex:indexPath.item];
cell.cellDescription.text = catalogModel.catalogName;
SDWebImageManager *manager = [SDWebImageManager sharedManager];
NSString *urlStr = catalogModel.imageNameThumbnail;
NSURL *url = [NSURL URLWithString:urlStr];
dispatch_async(dispatch_get_main_queue(), ^{
if ([self.catalogCollectionView.indexPathsForVisibleItems containsObject:indexPath])
{
catalogModel = [catalogArray objectAtIndex:indexPath.item];
[manager downloadImageWithURL:[NSURL URLWithString:catalogModel.imageNameThumbnail] options:0 progress:^(NSInteger receivedSize, NSInteger expectedSize) {}completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, BOOL finished, NSURL *imageURL)
{
cell.cellImageView.image = image;
NSLog(#"%#",catalogModel.catalogName);
}];
};
});
return cell;
}
There is a problem in concurrent image loading implementation. When -collectionView: cellForItemAtIndexPath: is calling, the device executes code on the main queue. Assuming that -downloadImageWithURL:options:progress:completed: method performs image loading on the background thread and returns instantly we can call it without dispatch_async(dispatch_get_main_queue()... wrapping. Otherwise we cannot guarantee that the completion handler is executing on the main thread, so the code should look like this
-(UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
static NSString* cellIdentifier = #"CustomCollectionViewCell";
CustomCollectionViewCell* cell = (CustomCollectionViewCell*)[collectionView dequeueReusableCellWithReuseIdentifier:cellIdentifier forIndexPath:indexPath];
...
SDWebImageManager *manager = [SDWebImageManager sharedManager];
// methods with completion block usually return instantly
[manager downloadImageWithURL:aURL options:0 progress:^(NSInteger receivedSize, NSInteger expectedSize){} completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, BOOL finished, NSURL *imageURL) {
// ensure ui is updating on main thread and for visible cell
dispatch_async(dispatch_get_main_queue(), ^{
if ([collectionView.indexPathsForVisibleItems containsObject:indexPath]) {
cell.cellImageView.image = image;
}
});
}];
return cell;
}
And the different results on iPhone and iPad can be explained by the architectural differences in technical specifications of testing devices.
I am new for Ios app development. I am using AFNetworking for image and data load, data are binned in collection view but unable to bind image.
This is my code for collection view and service:
#import "ViewController.h"
#import "LabelCollectionViewCell.h"
#interface ViewController ()
{
NSMutableArray *idArray;
NSMutableArray *namelabelArray;
UIImage *imagArray;
NSMutableArray *dic ;
}
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
[[self userCollection]setDataSource:self];
[[self userCollection]setDelegate:self];
[self dataJson];
NSLog(#"array: %#", namelabelArray);
}
-(NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView
{
return 1;
}
-(NSInteger) collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section
{
return [namelabelArray count];
}
-(UICollectionViewCell *) collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *cellIdentifier = #"cell";
LabelCollectionViewCell *customCell = [collectionView dequeueReusableCellWithReuseIdentifier:cellIdentifier forIndexPath:indexPath];
[[customCell nameLabel]setText:[namelabelArray objectAtIndex:indexPath.item]];
[[customCell idLabel]setText:[idArray objectAtIndex:indexPath.item]];
return customCell;
}
-(void)dataJson
{
NSString *zipcode;
NSString *url = [NSString stringWithFormat:#"%#",#"http://inveera.biz/lowkall_api/index.php/product_cat"];
NSDictionary *params =[NSDictionary dictionaryWithObjectsAndKeys: zipcode,#"35005",nil];
NSLog(#"Login URL %#", params);
AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
[manager POST:url parameters:params success:^(AFHTTPRequestOperation *operation, id responseObject) {
NSLog(#"Login JSON: %#", responseObject);
NSString *data = [responseObject valueForKey:#"data"];
idArray = [data valueForKey:#"id"];
namelabelArray =[data valueForKey:#"name"];
imagArray =[data valueForKey:#"img_path"];
NSLog(#"Error image: %#", imagArray);
[self.userCollection reloadData];
}
failure:^(AFHTTPRequestOperation *operation, NSError *error)
{
NSLog(#"Error: %#", error);
}];
}
My json response is
{
"status":"true",
"city":[{"cityname":"Acmar"}],
"data":[
{"id":"1","name":"Appliances","img_path":"categories\/1\/ac1.png","status":"1"},
{"id":"2","name":"Electronics","img_path":"categories\/2\/ac2.png","status":"1"},
{"id":"3","name":"Furniture","img_path":"categories\/3\/ac3.png","status":"1"},
{"id":"4","name":"Cars","img_path":"categories\/4\/ac4.png","status":"1"},
{"id":"5","name":"Pet Supplies","img_path":"categories\/5\/ac5.png","status":"1"},
{"id":"6","name":"Others","img_path":"categories\/6\/ac6.png","status":"1"}
]
}
I am able to bind name but unable to bind image with full path with domain
name.
Please help me out. Thanks in advance.
In AFNetworking Lib itself they have added one category class for UIImageView called UIImageView+AFNetworking to load images from URL, You need call below method with valid URL request.
- (void)setImageWithURLRequest:(NSURLRequest *)urlRequest
placeholderImage:(UIImage *)placeholderImage
success:(void (^)(NSURLRequest *request, NSHTTPURLResponse * _Nullable response, UIImage *image))success
failure:(void (^)(NSURLRequest *request, NSHTTPURLResponse * _Nullable response, NSError *error))failure
-(UICollectionViewCell *) collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *cellIdentifier = #"cell";
LabelCollectionViewCell *customCell = [collectionView dequeueReusableCellWithReuseIdentifier:cellIdentifier forIndexPath:indexPath];
[[customCell nameLabel]setText:[namelabelArray objectAtIndex:indexPath.item]];
[[customCell idLabel]setText:[idArray objectAtIndex:indexPath.item]];
[[customCell imageView] setImageWithURLRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:[imagArray objectAtIndex:indexPath.row]]]
placeholderImage:nil
success:nil
failure:^(NSURLRequest * _Nonnull request, NSHTTPURLResponse * _Nullable response, NSError * _Nonnull error) {
}];
return customCell;
}
First of all, instead of UIImage *imageArray; it should be NSString *imageURLString;
and then since you are using AFNetworking better to load images async (use #import UIImageView+AFNetworking.h).
NSURL *imageURL = [NSURL URLWithString:[NSString stringWithFormat:#"%#%#", self.baseURL, imageURLString]];
[self setImageWithURLRequest:[NSURLRequest requestWithURL:imageURL]
placeholderImage:placeholder
success:^(NSURLRequest *request, NSHTTPURLResponse *response, UIImage *image) {
if (image)
{
weakSelf.image = image;
}
} failure:^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error) {
}];
I have a UITableView in a ViewController which contains a custom cell.
When the view first loads, the indexPathForCell in the following code returns the indexPath without problems:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
PostCell *postCell = (PostCell *)[tableView dequeueReusableCellWithIdentifier:#"PostCell"];
if (postCell == nil) {
NSLog(#"EmptyCell Found");
}
NSDictionary *object = [postArray objectAtIndex:indexPath.row];
NSString *imageURL = [object objectForKey:#"imageURL"];
NSIndexPath *originalIndexPath = indexPath;
NSLog(#"Original IndexPath %#", originalIndexPath);
SDWebImageManager *manager = [SDWebImageManager sharedManager];
[manager downloadWithURL:[NSURL URLWithString:imageURL]
options:indexPath.row == 0 ? SDWebImageRefreshCached : 0
progress:^(NSInteger receivedSize, NSInteger expectedSize){}
completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, BOOL finished){
if (image) {
// This returns correctly on first load
NSIndexPath *currentIndexPath = [imageTableView indexPathForCell:postCell];
NSLog(#"Current IndexPath %#", currentIndexPath);
}
}
];
}
After refreshing the tableView, currentIndexPath is always (null).
The following is my refresh code:
- (void)refreshMainView {
AFHTTPRequestOperation *downloadPostOperation = [[AFHTTPRequestOperation alloc] initWithRequest:serviceRequest];
[downloadPostOperation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
//[tableView beginUpdates];
NSDictionary *json = [NSJSONSerialization JSONObjectWithData:urlData options:kNilOptions error:&error];
NSMutableArray *newPostArray = [json objectForKey:#"posts"];
// Replacing the old array with new array
postArray = newPostArray;
[self stopRefresh];
//[tableView endUpdates]
[imageTableView reloadData];
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
[self stopRefresh];
}];
If I just perform [tableView reloadData] without performing refreshMainView, there will not be any problem with getting the currentIndexPath.
There must be something I am overlooking, could someone please help me? Thank you!
Edit:
I have tried [postCell.postImageView setImageWithURL:[NSURL URLWithString:imageURL] placeholderImage:nil]; instead and the refreshing works, so I am suspecting that something is wrong with me getting the indexPath in the completed block, can anyone help out please? I need the completed block as I am doing some image processing.
Hi I have finally fixed it.
For people who are needing answers, just add
dispatch_async(dispatch_get_main_queue(), ^{
});
around the portion that is retrieving the indexPathForCell.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
PostCell *postCell = (PostCell *)[tableView dequeueReusableCellWithIdentifier:#"PostCell"];
if (cell == nil)
{
nib = [[NSBundle mainBundle] loadNibNamed:#"PostCell" owner:self options:nil];
}
NSIndexPath *originalIndexPath = indexPath;
NSLog(#"Original IndexPath %#", originalIndexPath);
SDWebImageManager *manager = [SDWebImageManager sharedManager];
[manager downloadWithURL:[NSURL URLWithString:imageURL]
options:indexPath.row == 0 ? SDWebImageRefreshCached : 0
progress:^(NSInteger receivedSize, NSInteger expectedSize){}
completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, BOOL finished){
if (image) {
// This returns correctly on first load
NSIndexPath *currentIndexPath = [imageTableView indexPathForCell:postCell];
NSLog(#"Current IndexPath %#", currentIndexPath);
}
}
];
use this it may help you
All,
I need to assign a picture to a UITableView. Each row has a different picture. The data for each row comes from a database and a row on that database has an ID, the ID in another database is linked and that has a URL in that table for the image.
So within cellforRowAtIndexPath we have :
NSDictionary *postValues = #{#"myParam": apt[#"barID"]};
// Do any additional setup after loading the view, typically from a nib.
self.client = [MSClient clientWithApplicationURLString:#"https://outnight-mobile.azure-mobile.net/" applicationKey:#"okYeRGfBagYrsbkaqWIRObeDtktjkF10"];
[self.client invokeAPI:#"photos"
body:postValues
HTTPMethod:#"POST"
parameters:nil
headers:nil
completion:^(id result, NSHTTPURLResponse *response, NSError *error) {
if (error) {
NSLog(#"Error %#", error );
} else
{
NSString *string = [NSString stringWithFormat:#"%#", [result objectForKey:#"rows"]];
NSString *stringWithoutbracketsend = [string stringByReplacingOccurrencesOfString:#")" withString:#""];
NSString *stringWithoutbracketsfront = [stringWithoutbracketsend stringByReplacingOccurrencesOfString:#"(" withString:#""];
NSString *completion = [stringWithoutbracketsfront stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
NSString *newStr = [completion substringFromIndex:1];
NSString *finalstring = [newStr substringToIndex:newStr.length-(newStr.length>0)];
NSLog(#"%#", finalstring);
[cell.imageView setImageWithURL:[NSURL URLWithString:finalstring] placeholderImage:[UIImage imageNamed:#"greybox40.png"]];
}
}];
That brings in the url from the table, which was JSON and I removed all the tags to get a string thats just http://blag.com/bah.jpg' This is the easy bit.
I am using SBWebImage for putting the image in the uitableview async which is fine too. What is the best way of putting the images on each row from the url. is it best with a SWITCH statement ? Is it best with an array of URL's and then displaying them. Is it best to move the database connectivity from cellforIndexRowPath? Can anyone advice please ?
first of all create a custom view cell for the uitableview. And then add subviews like images, labels to the custom cell.Get the data from the database to an array of images, arrays of ID'sIn the tableview delegate methods at cellforrowatindex use the code.
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{ static NSString *CellIdentifier = #"Cell";
if(tableView == myTableView){
FirstViewCustomCell *cell = (FirstViewCustomCell *)[myTableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil)
{
[[NSBundle mainBundle] loadNibNamed:#"FirstViewCustomCell" owner:self
options:nil];
cell = customCell;
customCell = nil;
}
[cell setSelectionStyle:UITableViewCellSelectionStyleNone];
//Get the data from the database.
cell.customimage.image = [imagesarr objectatIndex:indexpath.row];
cell.textlabel.text = [arrayofID objectatIndex:indexpath.row];
}
Parse the data seperately..in an method and then
Store images in an array..As you want top three images..
If you are saving the urls of image in an object and then array..Use below code
for(int i=0;i<3;i++)
{
ImagesObject *Obj=[imagesArray objectAtIndex:i];
[cell.userProfilePicImage setImageWithURL:[NSURL URLWithString:Obj.imageURLString]
placeholderImage:[UIImage imageNamed:#"greybox40.png"]
completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType)
{
if (!error && image)
{
}
}];
}
(or) if you are not saving in an object then use below code.
for(int i=0;i<3;i++)
{
NSString *imageURLString=[imagesArray objectAtIndex:i];
[cell.userProfilePicImage setImageWithURL:[NSURL URLWithString:imageURLString]
placeholderImage:[UIImage imageNamed:#"greybox40.png"]
completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType)
{
if (!error && image)
{
}
}];
}
Hope it helps you...
In my method cellForItemAtIndexPath i have this code:
SDWebImageManager *manager = [SDWebImageManager sharedManager];
[manager downloadWithURL:[NSURL URLWithString:coverURL] options:0 progress:^(NSInteger receivedSize, NSInteger expectedSize) {
} completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, BOOL finished) {
RRRBigItemCell *cell = (RRRBigItemCell *)[collectionView cellForItemAtIndexPath:indexPath];
if (cell) {
RRRImageView *coverView = (RRRImageView *)[cell viewWithTag:COVER_TAG];
coverView.image = image;
}
}];
When i launch application and i scroll down then everything is ok. But when i scroll up than cell which were already displayed don't have loaded images (cell == nil). And if i again scroll down the problem remains for already displayed cells, only new ones have loaded images.
Any idea what i'm doing wrong?
#pawan is correct, that would be simplest way to implement SDWebImage. It's basically a category of UIImage, so simply use the category methods (setImageWithURL).
A couple of other points since you asked;
In your code, you should be setting the coverImage.image on the main thread, not in the background.
It's good practice to check if the Cell is visible, or there is little point in displaying the content.
It's good practice to create a weak reference to the Collection view when accessing the cell to avoid circular references
so your code ) might be written as follows if you were insistent on doing it the way you have it (Using the category is the best and simplest way)
__weak myCollectionViewController *weakSelf = self;
SDWebImageManager *manager = [SDWebImageManager sharedManager];
[manager downloadWithURL:[NSURL URLWithString:coverURL] options:0 progress:^(NSInteger receivedSize, NSInteger expectedSize) {
} completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, BOOL finished) {
dispatch_async(dispatch_get_main_queue(), ^{
if ([weakSelf.collectionView.indexPathsForVisibleItems containsObject:indexPath]) {
RRRBigItemCell *cell = (RRRBigItemCell *)[weakSelf.collectionView cellForItemAtIndexPath:indexPath];
RRRImageView *coverView = (RRRImageView *)[cell viewWithTag:COVER_TAG];
coverView.image = image;
};
});
}];
why dont you try this
[cell.imageView setImageWithURL:[NSURL URLWithString:#"http://www.domain.com/path/to/image.jpg"]
placeholderImage:[UIImage imageNamed:#"placeholder.png"]];
reference SDWebImage