Sweeper Sweeper - 4 months ago 18x
Swift Question

How can I implement a method such that, when called, table view cells' height changes?

I want to implement a method that looks something like this:

setCellHeightForIndexPath(someIndexPath, 80)

and then the table view cell at that index path will suddenly have a height of 80.

The reason I want to do this is because I want the height of the cell to be set to the height of the web view's content after it has finished loading the HTML. Since I can only get the web view's content size after it has finished loading, I can't just set the cell height right away.

See this question for more info.

So in the
method, I can just get the web view's content height, set the web view's height to that, and call this method to set the cell's height.

It seems like that the cell height can only change when
is called. I think the method would use a similar approach as my answer to this question. I think I need to store an array of heights maybe? I just can't think of a way to implement this!

How can I implement such a method?

Note: don't tell me this is not possible. In the Instagram app, I can see different images that have different heights fit perfectly in a table view cell. And those images are similar to my web views. They both need time to load.


Let me show some of my attempts at this:

var results: [Entry] = []
var cellHeights: [CGFloat] = []
var webViews: [UIWebView] = []

override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1

override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return results.count

override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("resultCell")
let webView = cell!.contentView.viewWithTag(1) as! UIWebView
webView.loadHTMLString(results[indexPath.row].htmlDescriptionForSearchMode(.TitleOnly), baseURL: nil)
webView.delegate = self
webView.scrollView.scrollEnabled = false


return cell!

override func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
return indexPath.row < cellHeights.count ? cellHeights[indexPath.row] : 400

func webViewDidFinishLoad(webView: UIWebView) {
let height = CGFloat(webView.stringByEvaluatingJavaScriptFromString("document.height")!.toFloat()!)
webView.frame = CGRect(origin: webView.frame.origin, size: CGSizeMake(webView.frame.width, height))
if let index = webViews.indexesOf(webView).first {
cellHeights[index] = height
tableView.reloadRowsAtIndexPaths([NSIndexPath(forRow: index, inSection: 0)], withRowAnimation: .None)

is the stuff that I want to show in the web views.
is used to store the height of each cell. I put all the web views into the
array so I can call
to identify which web view is loaded.


You cannot "push" the new cell height onto a table view. Instead, you need to make table view "pull" the new height from your heightForRowAtIndexPath, and be ready to supply the new height.

When the cell load finishes for row r, you need to update your model in such a way that it knows the new height of row r. After that you need to tell your table view to reload itself, like this:

tableView.reloadRowsAtIndexPaths([indexPath], withRowAnimation: UITableViewRowAnimation.None)

This will start the process of updating your cell. heightForRowAtIndexPath will be called. Your code will return the new height. After that cellForRowAtIndexPath will be called. Your code should be prepared to return the cell that has finished loading, without initiating a new data load.