I'm building a UICollectionView and my custom cells will contain two labels and one image.
Each image is downloaded asynchronously so I don't know it's size until the download it's complete.
Once is downloaded, I want to adapt each cell to re-layout it's content and frame to fit in height the image just downloaded.
As UICollectionViewLayout, I'm using CHTCollectionViewWaterfallLayout
To download the image asynchronously I'm using SDWebImage, like this:
[cell.imageView setImageWithURL:[NSURL URLWithString:#"http://www.domain.com/path/to/image.jpg"]
placeholderImage:[UIImage imageNamed:#"placeholder.png"]
completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType)
{... some completion code here ...}];
QUESTION:
What is the right approach to resize each UICollectionViewCell right after the image is downloaded?
You should just be able to invalidate your collection view layout in an animation block when the image comes back. Things might get a little complicated if more than one image finishes completion at once, but this should work:
[cell.imageView setImageWithURL:[NSURL URLWithString:#"http://www.domain.com/path/to/image.jpg"]
placeholderImage:[UIImage imageNamed:#"placeholder.png"]
completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType)^{
[UIView animateWithDuration:0.3f animations:^{
[self.collectionView.collectionViewLayout invalidateLayout];
}];
}];
Then just return a different size in the appropriate delegate method.
- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath {
return /* a different size if the image is done downloading yet */;
}
To resize collection view cells, you could reload the collection view and return the size of you collection view cells dynamically in the method:
- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath {
UICollectionViewCell *cell = [collectionView cellForItemAtIndexPath:indexPath];
return [cell size];
}
In cases when the cell's size is dynamic, get the cell from the index path and return its size based on the size of the image. I usually create a method for the cell to return a dynamic size, like in the example above. You can use UIImage's size property to help return the size based on the image.
You could try to reloadItemsAtIndexPaths: for the cells that finished loading.
Related
I am building an Xcode application that features a vertically scrolling UICollectionView of Views with large ImageViews in them, similar to Instagram. This Collection View originally loaded very small images into the ImageViews in each UICollectionViewCell, making the app load fast and making the loading of individual cells instantaneous.
I recently made a change to the app that makes the Collection View Cells load much larger images into their Image View, which causes 2 things in some cells:
1) A wait time where the ImageView is blank until the image is loaded
2) Temporary repetition of old images in cells that have been reused from the queue while the new image is loading
Below is the code that I use to initialize my cells:
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
UICollectionViewCell *cell;
NSString *CellIdentifier;
UIImageView *thumbnailImage;
CellIdentifier = #"FollowersFeed";
cell = [collectionView
dequeueReusableCellWithReuseIdentifier:CellIdentifier forIndexPath:indexPath];
thumbnailImage=(UIImageView *)[cell viewWithTag:1];
dict=[follwerFeed objectAtIndex:indexPath.row];
NSString *nurl=[dict valueForKey:#"video"];
[thumbnailImage setImageWithURL:[NSURL URLWithString:nurl]];
//........
return cell;
}
In order to fix the functionality of this app with these new larger pictures, I need to solve the loading problem by
1) Showing an Activity Indicator spinning while the Image View in a Collection View loads.
2) Remove the image link from the previous Image View before the cell is reused in the queue.
I believe that if I can figure out how to do both of these, then the app's scrolling will work just as seamlessly now as it was with the smaller images
You can try https://github.com/rs/SDWebImage - Asynchronous image downloader with cache support as a UIImageView category
Easy to use:
#import <SDWebImage/UIImageView+WebCache.h>
...
[imageView sd_setImageWithURL:[NSURL URLWithString:#"http://www.domain.com/path/to/image.jpg"]
placeholderImage:[UIImage imageNamed:#"placeholder.png"]];
You can try NSURLRequest
NSURLRequest *request = [NSURLRequest requestWithURL:imageUrl];
[NSURLConnection sendAsynchronousRequest:request
queue:[NSOperationQueue mainQueue]
completionHandler:^(NSURLResponse * response,
NSData * data,
NSError * error) {
if (!error){
cell.thumbnailImage.image = [UIImage imageWithData:data];
}
}];
I have a few Table View Cell Heights randomly not calculating some Cell Heights by the time they are on screen.
(Scrolling down)
**Only when they go off screen, and then scrolled back up to, do they calculate as needed.
(Scrolling Back up)
Has anyone ever heard of this, or know what to do?
I'm using AutoLayout, Storyboards, SDWebImage to load the images from a URL, and this code in cellForRowAtIndexPath:
__weak TableViewCellTwo *wcell = api2Cell;
[wcell.imageViewPic
sd_setImageWithURL:[NSURL URLWithString:#"http://dummyimage.com/600x600/000/fff.png"]
placeholderImage:[UIImage imageNamed:#"fff.png"]
completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, NSURL *imageURL) {
// Put Log
}
];
My Storyboard:
UPDATE:
Wanted to add to my question to see if I could get an answer. Still having problems with this. Wondering if there's any way to do this without using heightForRowAtIndexPath. Is this just an iOS bug?
I have a TableView with custom cells, and for some reason there will be a Table View Cell added that doesn't calculate its height (like these two examples):
The Cell finally calculates it when I scroll down past the blank cell and then back up to where it was before, and I can see the Images "flashing" and placing themselves in the correct spot in the Table View. So I assume it has something to do with the image's in my Table View not loading immediately or something?
Seems to happen in my testing mostly when there are two Table View Cells next to each other in the Table View that have Images in them.
I've tried a bunch of different things, have you ever heard of this? Any idea how to fix? Thanks!
I'm using AutoLayout and Storyboard.
UPDATE
Per Request, adding cellForRowAtIndexPath code:
__weak TableViewCellTwo *wcell = api2Cell;
[wcell.imageViewPic
sd_setImageWithURL:[NSURL URLWithString:imageURL]
placeholderImage:nil
completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, NSURL *imageURL) {
// Put Log
}
];
UPDATE
- (CGFloat) tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
return tableView.rowHeight; // return the default height
}
I have custom UITableViewCell which made from xib file .
And I have UIImageView inside that cell which I set the fixed width and height and 40x40 and set to hidden
And then in cellForRowAtIndexPath datasource delegate. I check if my datasource for that cell contain a URL to image . If it has ,I set the ImageView to appear .And use SDWebImage to download the image and adjust the size for imageView.frame in the completion block when download complete
Actually, at first appearance it is working fine .I got the ImageView that is the same size and the downloaded image. But if I scroll down and then up to the upper cell again. It just re-appear to 40x40 size gain. Can anyone help ?
This is the code just in case if you are curious. I guess nothing wrong with the code the problem is how I set the UIImageView in .xib file but I don't know how to solve it.
// -(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
if([item postImageURLString]){
void (^completionBlock)(UIImage *image, NSError *error, SDImageCacheType cacheType) = ^(UIImage *image, NSError *error, SDImageCacheType cacheType) {
CGRect imageFrame = CGRectMake(cell.frame.size.width/3,cell.frame.size.height -5 -image.size.height, image.size.width, image.size.height);
[cell.postImageView setFrame:imageFrame];
};
[cell.postImageView setHidden:NO];
[cell.postImageView setImageWithURL:URL completed:completionBlock ];
}
I suspect it has something to do with the tableview reusing cells that have the 40x40 image view. It looks like SDWebImage will not run your completion block if the image is already cached, so if the tableview redraws the cell and your image is already cached, it never executes the resizing code. You need to add code to your cellForRowAtIndexPath method that checks to see if the image is already cached, and if it is then resize the image view.
I want to load an image onto a table view cell, i.e., a custom cell with an image view. And, I use SDWebImage. I am loading an image onto the cell without using setImageWithURL. This is the code inside cellForRowAtIndexPath.
[_imgManager downloadWithURL:urlArray[indexPath.row]
completed:^(UIImage *image, NSError *error//yada, yada) {
if(image)
{
NSLog(#"Image received");
//cell.pictureView.image = image; // doesn't work, so I did
dispatch_async(dispatch_get_main_queue(), ^{
UITableViewCell *tCell = [self.tableName cellForRowAtIndexPath:indexPath];
if(tCell)
tCell.imageView.image = image;
}); // doesn't work either
}
}];
So as I have mentioned in the comments, it doesn't work. What am I doing wrong? Or maybe my conception of this is wrong? The images load only after I scroll (that activates cellForRowAtIndexPath for other cells). And they keep refreshing on each appearance. It doesn't work exactly as expected.