UITableViewCell height resize when image is downloaded
You need to store downloaded image in the memory or on the disk, so next time when you will try to get image from this URL you will received from cache.
So if you do that than you will have to do something like this:
[tableView beginUpdates];
[tableView reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationFade];
[tableView endUpdates];
And you should return new cell's height in this method of table view data source:
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
I would recomend you to use SDWebImage library instead of AFNetworking
because it can cache your images to memcache and disk for you and it is very easy to use. So if you decide to use it, your code of download images will be looking like this:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
...
[cell.imageView setImageWithURL:[NSURL URLWithString:@"http://www.domain.com/path/to/image.jpg"]
placeholderImage:[UIImage imageNamed:@"placeholder.png"]
success:^(UIImage *image, BOOL cached) {
// save height of an image to some cache
[self.heightsCache setObject:[NSNumber numberWithFloat:imHeight]
forKey:urlKey];
[tableView beginUpdates];
[tableView reloadRowsAtIndexPaths:@[indexPath]
withRowAnimation:UITableViewRowAnimationFade];
[tableView endUpdates];
}
failure:^(NSError *error) {... failure code here ...}];
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
// try to get image height from your own heights cache
// if its is not there return default one
CGFloat height = [[self.heightsCache objectForKey:urlKeyFromModelsArrayForThisCell] floatValue];
...
return newHeight;
}
Updating cell height after image downloads
Short Answer
- Give enough breathing space to
estimatedRowHeight
- Changing a
UITableViewCell
once returned bydequeueReusableCellWithIdentifier
will not work with cached cells - Trigger a single cell reload with
reloadRowsAtIndexPaths
- Manage your cache with Core Data and let
NSFetchedResultsController
boilerplate code can do all the UI work.
In Details
No unexpected scroll, only updates images as they come:
- If the cell being refreshed is at or below the horizon, the
UITableView
will not scroll - if the cell being refreshed is above the top, the
UITableView
will not scroll - the
UITableView
will only scroll when the cell is in plain sight, and requires more space than available.
Let UITableViewAutomaticDimension
do the hard work
You need to tell Cocoa Touch that the cell is stale, so that it will trigger a new dequeueReusableCellWithIdentifier
, to which you are going to return a cell with the proper height.
Short of reloading the entire table view or one of its section, and assuming that your indexes are stable, invoke -tableView:reloadRows:at:with:
passing the indexPath of the cell that just changed, and a .fade
animation.
Code:
override func viewDidLoad() {
super.viewDidLoad()
tableView.estimatedRowHeight = 250 // match your tallest cell
tableView.rowHeight = UITableViewAutomaticDimension
}
Use URLSession
. When an image becomes available, fire reloadRows:at:with
:
func loadImage(_ url: URL, indexPath: IndexPath) {
let downloadTask:URLSessionDownloadTask =
URLSession.shared.downloadTask(with: url, completionHandler: {
(location: URL?, response: URLResponse?, error: Error?) -> Void in
if let location = location {
if let data:Data = try? Data(contentsOf: location) {
if let image:UIImage = UIImage(data: data) {
self.cachedImages[indexPath.row] = image // Save into the cache
DispatchQueue.main.async(execute: { () -> Void in
self.tableView.beginUpdates()
self.tableView.reloadRows(
at: [indexPath],
with: .fade)
self.tableView.endUpdates()
})
}
}
}
})
downloadTask.resume()
}
Once in the cache, cellForRow
merely reads into it from the UI thread:
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
{
let cell = tableView.dequeueReusableCell(withIdentifier: "id") as! CustomCell
cell.imageView.image = cachedImages[indexPath.row] // Read from the cache
return cell
}
Example: fetch a random set of images from *Wikipedia*
► Find this solution on GitHub and additional details on Swift Recipes.
Dynamic TableViewCell height to fit downloaded image
Storyboard:
- Add a new cell to your table view
- Add a new image view to the cell
- Optionally set a placeholder image for the image view
- Make cell's constraints describe its height unambiguously
Be sure to leave Row Height of the cell set to Default
Code:
Make the table view to self-size cells:
var tableview: UITableView {
didSet {
tableView.rowHeight = UITableViewAutomaticDimension
tableView.estimatedRowHeight = 44
}
}
At runtime, start loading the image asynchronously as soon as possible.
When downloaded, load the image to the image view and execute:
tableView.beginUpdates()
tableView.endUpdates()
Related Topics
Where Is the Official Documentation for Cvopenglestexture Method Types
Delete Cell from Uicollectionview Without Reloading from Top
Customizing the Colors of a Uisegmentedcontrol
Wait for Swift Animation to Complete Before Executing Code
Opening Testflight App from Another App and Deep Link to Specific App
Strong Reference to a Weak References Inside Blocks
Better Way to Get the User's Name from Device
How to Add Watermark on a Exist Image
How to Get Selected Text from Uitextfield in Iphone
How to Install the "App Store" in an iOS Simulator
Coreanimation - Opacity Fade in and Out Animation Not Working
Has Anyone Found a Good Way of Using the New iOS5 Keyboard Events
Google Analytics 3.08 iOS Idfa Class Missing, Won't Collect Idfa
How to Efficiently Find Cgrects for Visible Words in Uitextview
Using Generic In-App Purchase Items for a Dynamic Range of Digital Products