I am using SDWebImage library to display images in my scrollview with page control.
How to download image one by one and display it. Suppose i have 4 URL : url1,url2,url3,url4
so i want to display url1 first then...2,3,4..so on...2,3,4 should load in background until the are not download a placeholder image show there.
Below is my code of inside for loop i try is it perfect ?
NSString* serverImgicon=<url here in a loop>;
dispatch_sync(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0),^{
NSURL *url=[NSURL URLWithString: serverImgicon];
//set your image on main thread.
dispatch_async(dispatch_get_main_queue(),^{
[img sd_setImageWithURL:url
placeholderImage:[UIImage imageNamed:#"placeholderImg"]];
});
});
Try below code, it'll work fine
-(UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath{
CollectionViewCell *cell =[collectionView dequeueReusableCellWithReuseIdentifier:#"cell" forIndexPath:indexPath];
myobject *newobject = _myarray[indexPath.item];
cell.nameLabel.text =newobject.name;
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0ul);
dispatch_async(queue, ^{
[cell.activityIndicatorForImage startAnimating];
UIImage *image = [UIImage imageWithData:[NSData dataWithContentsOfURL:
[NSURL URLWithString:newobject.image]]];
dispatch_sync(dispatch_get_main_queue(), ^{
if(image!= nil){
[[cell myimage] setImage:image];
[cell.activityIndicatorForImage setHidden:NO];
}else{
UIImage * image = [UIImage imageNamed:#"PLACEHOLDER"];
[[cell myimage] setImage:image];
[cell.activityIndicatorForImage setHidden:YES];
[cell.activityIndicatorForImage stopAnimating];
}
[cell setNeedsLayout];
});
});
return cell;
}
use sdwebCache to load images asynchronously
https://github.com/rs/SDWebImage
/******************/
#import "UIImageView+WebCache.h"
then load images like this :
NSArray *tempArray = [stickersThumbRecords objectForKey:collectioName];
if (tempArray.count>indexPath.item) {
NSURL *url = [NSURL URLWithString:[tempArray objectAtIndex:indexPath.item] ];
[imageView setImageWithURL:url placeholderImage:[UIImage imageNamed:#"placeholderFrame"]];
}
Related
Im using the following code to populate a collection view cells with images.
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *identifier = #"Cell";
UICollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:identifier forIndexPath:indexPath];
UIImageView *recipeImageView = (UIImageView *)[cell viewWithTag:100];
recipeImageView.image = nil;
if ([imageArray count] >0){
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^(void) {
NSData *data0 = [NSData dataWithContentsOfURL: [NSURL URLWithString:[imageArray objectAtIndex:indexPath.row]]];
UIImage *image = [UIImage imageWithData: data0];
dispatch_sync(dispatch_get_main_queue(), ^(void) {
recipeImageView.image = image;
});
});
}
[spinnerShow stopAnimating];
return cell;
}
The problem is that, when Im scrolling the images are flickering and are flashing. Why is that so? How can I make those images to be stable without flickering?
Just a short overview, So you get your answer
UICollectionView is highly optimized, and thus only keep On-screen visible rows in memory. Now, All rows Cells are cached in Pool and are reused and not regenerated. Whenever, user scrolls the UICollectionView, it adds the just-hidden rows in Pool and reuses them for next to be visible rows.
So, now, coming to your answer
When you scroll your CollectionView, collectionView datasource method gets called again for every indexPath, coming in visible range and your image gets downloaded again
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
SOLUTION
Instantiate a instance NSMutableDictionary, outside of method.
Now in your code
#implementation ClassName{
NSMutableDictionary *cachedImage;
}
-(void)viewDidLoad(){
[super viewDidLoad];
cachedImage = [NSMutableDictionary new];
}
/*OLD CODE*/
UIImageView *recipeImageView = (UIImageView *)[cell viewWithTag:100];
recipeImageView.image = nil;
if ([imageArray count] >0){
//IF image is already downloaded, simply use it and don't download it.
if(cachedImage[[imageArray objectAtIndex:indexPath.row]] != nil){
recipeImageView.image = cachedImage[[imageArray objectAtIndex:indexPath.row]];
}
else{
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^(void) {
NSData *data0 = [NSData dataWithContentsOfURL: [NSURL URLWithString:[imageArray objectAtIndex:indexPath.row]]];
UIImage *image = [UIImage imageWithData: data0];
dispatch_sync(dispatch_get_main_queue(), ^(void) {
recipeImageView.image = image;
//****SAVE YOUR DOWNLOADED IMAGE
cachedImage[[imageArray objectAtIndex:indexPath.row]] = image; //****SAVE YOUR DOWNLOADED IMAGE
});
});
}
}
/*OLD CODE*/
as per my knowledge, you are fetching image but not caching it that's why when your UICollectionViewCell gets reload, you get UIImageView's fresh instance so and this thing goes on and on in your code..
in this case, i recommend you to use SDWebImage OR AFNetworking Frameworks. because these frameworks does all the tricky stuff for you with the simple line of code (SDWebImage Framework),
NSURL* url = [NSURL URLWithString:str];
[yourImageView setBackgroundImageWithURL:url forState:UIControlStateNormal placeholderImage:kPlaceholder];
I've UICollectionView with UITableView in the same page. I'm using SDWebImage with UITableView and it is working fine. I'm trying to use SDWebImage with UICollectionView but couldn't make it. So I have used NSData but it is freezing with it. How I can solve this issue?
UPDATE: Is there a way to use SDWebImage with UICollectionView?
My code:
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath{
static NSString *simpleTableIdentifier = #"cell";
ProductsCollectionViewCell *pCell = [_collection dequeueReusableCellWithReuseIdentifier:simpleTableIdentifier forIndexPath:indexPath];
NSData *imageData = [NSData dataWithContentsOfURL:[NSURL URLWithString:[_arrayImage objectAtIndex:indexPath.row]]];
_imgCollection = (UIImageView *)[pCell viewWithTag:100];
_imgCollection.image = [UIImage imageWithData:imageData];
pCell.layer.shouldRasterize = YES;
pCell.layer.rasterizationScale = [UIScreen mainScreen].scale;
}
My code for table view
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *simpleTableIdentifier = #"productCell";
ProductsTableViewCell *pCell = [tableView dequeueReusableCellWithIdentifier:simpleTableIdentifier];
[pCell.imgProduct sd_setImageWithURL:[NSURL URLWithString:[_arrayImage objectAtIndex:indexPath.row]]
placeholderImage:[UIImage imageNamed:#"placeholder.png"]];
The method you are using
NSData *imageData = [NSData dataWithContentsOfURL:[NSURL URLWithString:[_arrayImage objectAtIndex:indexPath.row]]];
is synchronous, which means it will block the thread until it's done. (That's why it's freezing)
I recommend you figure out a way to use SDWebImage with your code, if you tell us why that didn't work, we can probably help you with that.
EDIT:
Look at the first example from SDWebImage's documentation, specifically at this line:
[cell.imageView setImageWithURL:[NSURL URLWithString:#"http://www.domain.com/path/to/image.jpg"]
placeholderImage:[UIImage imageNamed:#"placeholder.png"]];
Now use it instead of this line of your code:
_imgCollection.image = [UIImage imageWithData:imageData];
And also, get rid of the line where you are downloading the image.
Could it be the fact that
NSData *imageData = [NSData dataWithContentsOfURL:[NSURL ...
is performing a time consuming operation on the main thread?
Why not try putting the fetch in the background like so?
dispatch_async(
dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSURL* url = [NSURL URLWithString:[_arrayImage objectAtIndex:indexPath.row]]
NSData *imageData = [NSData dataWithContentsOfURL:url];
dispatch_async(dispatch_get_main_queue(), ^{
UICollectionViewCell* cell = [collectionView cellForItemAtIndexPath:indexPath];
if(cell) { // cell is visible
cell.imageView.image = [UIImage imageWithData:imageData];
}
});
});
Finally, make sure that
[_arrayImage objectAtIndex:indexPath.row]
is indeed a string and that _arrayImage is a string array
Lastly I would consider putting
[UIImage imageWithData:imageData]
in the background as well as the decoding could be time consuming as well
May be you are missing contentView
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath{
static NSString *simpleTableIdentifier = #"cell";
ProductsCollectionViewCell *pCell = [_collection dequeueReusableCellWithReuseIdentifier:simpleTableIdentifier forIndexPath:indexPath];
NSData *imageData = [NSData dataWithContentsOfURL:[NSURL URLWithString:[_arrayImage objectAtIndex:indexPath.row]]];
_imgCollection = (UIImageView *)[pCell.contentView viewWithTag:100];
_imgCollection.image = [UIImage imageWithData:imageData];
}
I'm parsing images from JSON data and displaying in Table View. My code to display images in table view is -
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *cellidentifier=#"MyCell";
OnlineCell *cell = [tableView dequeueReusableCellWithIdentifier:cellidentifier];
if (cell == nil)
{
cell = [[OnlineCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellidentifier];
}
cell.userLabel.text = [self.allusername objectAtIndex:indexPath.row];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
imageURL = [self.alluserphoto objectAtIndex:indexPath.row];
img = [UIImage imageWithData:[NSData dataWithContentsOfURL: [NSURL URLWithString:imageURL]]];
dispatch_async(dispatch_get_main_queue(), ^{
cell.userIMage.image = img;
[indicator stopAnimating];
});
});
return cell;
}
Simulator Output -
Device OutPut -
Please tell me the how could i get images in device.
As i can see two screenshot one is Simulator and one is Device that two image common Load at both end simulator and device. So i think issue in to you image that comes from web side. Because you code is correct if there is issue in code then that not load two image as well in device.
Please test with change the image with First loaded image from your back End side and check once that have to load. put first loaded image for all response instead of goggles image that not load in device. i am dame sure issue is in image not in code.
Debugging Suggestion 1: On the device, make sure that the image is accessible. You can try opening the image in Safari. If safari is opening the image, it means that image is accessible.
Debugging Suggestion 2: If the image is accessible, try loading the image synchronously and debug if you're getting the required data (on the device):
NSURL *url = [NSURL URLWithString:self.photoImagePath]; // Is URL valid?
NSData *imageData = [NSData dataWithContentsOfURL:url]; // Is data nil?
UIImage *image = [UIImage imageWithData:imageData]; // Is image nil?
Then, try loading the image asynchronously, like this:
__weak UIImageView *weakImgageView = cell.userIMage;
imageURL = [self.alluserphoto objectAtIndex:indexPath.row];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
UIImage *image = [UIImage imageWithData:[NSData dataWithContentsOfURL: [NSURL URLWithString:imageURL]]];
dispatch_async(dispatch_get_main_queue(), ^{
weakImgageView.image = image;
[indicator stopAnimating];
});
});
Hope this helps.
The default image is not coming properly. This is what I suspect. Please check if you are giving the name properly or not. This could be the issue.
Set default image until you fetch image asynchronously from JSON.
UIImage *img = [UIImage imageNamed:#"default.png"];
When you fetch image in asynchronously method means it will fetch depend on image size & internet speed. So you just keep a default image until it fetch completely from server
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"MyCell";
OnlineCell *cell = (OnlineCell *)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
NSArray *nib = nil;
if (cell == nil)
{
nib = [[NSBundle mainBundle] loadNibNamed:#"YourCustomCellNibName" owner:self options:nil];
cell = [nib objectAtIndex:0];
}
// Your code
cell.userLabel.text = [self.allusername objectAtIndex:indexPath.row];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
//NSData *imagedata = [NSData dataWithContentsOfURL:[NSURL URLWithString:[self.alluserphoto objectAtIndex:indexPath.row]]];
NSURL* url = [NSURL URLWithString:[self.alluserphoto objectAtIndex:indexPath.row]];
NSURLRequest* request = [NSURLRequest requestWithURL:url];
[NSURLConnection sendAsynchronousRequest:request
queue:[NSOperationQueue mainQueue]
completionHandler:^(NSURLResponse * response,
NSData * data,
NSError * error) {
if (!error){
UIImage* image = [[UIImage alloc] initWithData:data];
// do whatever you want with image
if (image) {
dispatch_async(dispatch_get_main_queue(), ^{
OnlineCell *updateCell = (id)[tableView cellForRowAtIndexPath:indexPath];
if (updateCell)
cell.userIMage.image = image;
});
}
}
}];
});
}
the problem is, that the content of the cell loads after i scrolled the cell out of the view:
i dont know how to describe it correctly, so i recorded a short 10 seconds video: http://youtu.be/GJouA3IXY0M
and i dont know how to fix it...
this is my cellForRowAtIndexPath method:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
PlaylistenTableViewCell *cell = [self.playlistenTableView dequeueReusableCellWithIdentifier:#"Cell"];
cell.selectionStyle = UITableViewCellSelectionStyleNone;
//titleLabel = label which shows the big headline
cell.titleLabel.text = [self.JsonTitle objectAtIndex:indexPath.row];
cell.titleLabel.layer.cornerRadius = 5;
cell.titleLabel.layer.masksToBounds = YES;
NSString *urlString = [self.JsonContent objectAtIndex:indexPath.row];
NSURL *url = [NSURL URLWithString:urlString];
NSData *data = [[NSData alloc] initWithContentsOfURL:url];
UIImage *tmpImage = [[UIImage alloc] initWithData:data];
//playlistThumbnailImageView = the imageVIew which shows the Image
cell.playlistThumbnailImageView.image = tmpImage;
[cell.playlistThumbnailImageView setOpaque:NO];
[cell.playlistThumbnailImageView setBackgroundColor:color];
cell.playlistThumbnailImageView.layer.cornerRadius = 5;
cell.playlistThumbnailImageView.layer.masksToBounds = YES;
cell.playlistThumbnailImageView.layer.borderWidth = 4.0;
return cell;
}
You are most probably reloading the table with background thread. Make UI updates with main thread. So, after your data is fetched in the background, use this code.
dispatch_async(dispatch_get_main_queue(), ^{
[self.myTableView reloadData];
});
Edit:
Since you have already reloading the table using main thread, I suspect that image is taking time to download and in turn it is blocking the thread. I also observed some glitch happening in your video. Try to download the image using asynchronous methods and check. initWithContentsOfURL will block your thread and is not a good option in this place.
Try this code. Let me know in case of any query...
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
-------
-------
NSString *imageUrl = #"-- URL string --";
NSURL *urlImage = [NSURL URLWithString:imageUrl];
[imgVw setImage:[UIImage imageNamed:#"placeholder.png"]];
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0);
dispatch_async(queue, ^{
NSError* error = nil;
NSData *imageData = [NSData dataWithContentsOfURL:urlImage options:nil error:&error];
if(error){
dispatch_sync(dispatch_get_main_queue(), ^{
[imgVw setImage:[UIImage imageNamed:#"placeholder.png"]];
[self.loadingIndicator stopAnimating];
[[NSURLCache sharedURLCache] removeAllCachedResponses];
});
}else{
UIImage *image = [UIImage imageWithData:imageData];
dispatch_sync(dispatch_get_main_queue(), ^{
[self.imgVw setImage:image];
[self.loadingIndicator stopAnimating];
[[NSURLCache sharedURLCache] removeAllCachedResponses];
});
}
});
}
i need to load from web/files some UIImages. I was searching and i found in other question this code:
if (![[NSFileManager defaultManager] fileExistsAtPath:user.image]) {
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0ul);
dispatch_async(queue, ^{
NSData *imageData =[NSData dataWithContentsOfURL:[NSURL URLWithString:user.imageURL]];
[imageData writeToFile:user.image atomically:YES];
dispatch_sync(dispatch_get_main_queue(), ^{
UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
UIImage *image = [UIImage imageWithData:imageData];
[self.imageFriends setObject:image forKey:[NSNumber numberWithInt:user.userId]];
cell.imageView.image = image;
[cell setNeedsLayout];
NSLog(#"Download %#",user.image);
});
});
cell.imageView.image=[UIImage imageNamed:#"xger86x.jpg"];
} else {
NSLog(#"cache");
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0ul);
dispatch_async(queue, ^{
UIImage *image = [UIImage imageWithContentsOfFile:user.image];
//[self.imageFriends setObject:image forKey:[NSNumber numberWithInt:user.userId]];
dispatch_sync(dispatch_get_main_queue(), ^{
UITableViewCell *newCell = [tableView cellForRowAtIndexPath:indexPath];
newCell.imageView.image=image;
[newCell setNeedsLayout];
});
});
}
But the problem is that when i scroll fast to bottom or top the images are loaded wrong and there is a short lag when it ends.
So the question is.. how can i load the UIImages in the correct cell when i use queues to fetch them? Thanks!
I suspect the incorrect images you see are a result of you not setting your place holder image in the event of you having a local copy of an image but still retrieving the local copy asynchronously. Also In the code you appended for loading the local copy you use UIImage a UIKit component on a background thread.
Also interestingly you seem to be doing some kind of UIImage caching. Adding the images to what I assume is an NSMutableArray property named imageFriends. But you seem to have commented out the cache add in the event you have a local copy of the file. Also your posted code never uses the cached UIImages.
While 2 levels of caching seems a bit overboard if you wanted to do this you could do something like this:
UIImage *userImage = [self.imageFriends objectForKey:[NSNumber numberWithInt:user.userId]];
if (userImage) { // if the dictionary of images has it just display it
cell.imageView.image = userImage;
}
else {
cell.imageView.image = [UIImage imageNamed:#"xger86x.jpg"]; // set placeholder image
BOOL fileExists = [[NSFileManager defaultManager] fileExistsAtPath:user.image];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSData *imageData = nil;
if (fileExists){
imageData = [NSData dataWithContentsOfFile:user.image];
}
else {
imageData = [NSData dataWithContentsOfURL:[NSURL URLWithString:user.imageURL]];
[imageData writeToFile:user.image atomically:YES];
}
if (imageData){
dispatch_async(dispatch_get_main_queue(), ^{
// UIKit, which includes UIImage warns about not being thread safe
// So we switch to main thread to instantiate image
UIImage *image = [UIImage imageWithData:imageData];
[self.imageFriends setObject:image forKey:[NSNumber numberWithInt:user.userId]];
UITableViewCell *lookedUpCell = [tableView cellForRowAtIndexPath:indexPath];
if (lookedUpCell){
lookedUpCell.imageView.image = image;
[lookedUpCell setNeedsLayout];
}
});
}
});
}
UIImages are part of UIKit and not thread safe. But you can load the NSData on another thread.
You are loading image asynchronously, so during fast scrolling cells get reused faster then images are downloaded. One way to avoid loading a wrong image, would be to check if cell already got reused when image is loaded. Or cancel all requests in progress when you dequeue new cells.
I would also recommend to look at AFNetworking, as it contains helpful category for UIImageView, so you can do something like this:
[imageView setImageWithURL:[NSURL URLWithString:#"http://i.imgur.com/r4uwx.jpg"] placeholderImage:[UIImage imageNamed:#"placeholder-avatar"]];
It also contains cancelImageRequestOperation method, to cancel requests in progress. Then your code will look like this:
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
} else {
[cell.imageView cancelImageRequestOperation];
}
[cell.imageView setImageWithURL:[NSURL URLWithString:user.imageURL] placeholderImage:[UIImage imageNamed:#"xger86x.jpg"]];