37

I have a case in which I have to reload only height of a UITableViewCell.

but if I call the function

tableView.reloadRowsAtIndexPaths([NSIndexPath(forRow: webView.tag, inSection: 0)], withRowAnimation: .Automatic)

it reloads the height as well as the data of the cell. How can i just control the height of cell in Swift?

This is my cellForRow block :

    override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
    if indexPath.row == 0 {
        let cell = tableView.dequeueReusableCellWithIdentifier("cell1", forIndexPath: indexPath) as! CustomTableViewCell1
    cell.heading.text = headerText
        return cell
    }

    else if indexPath.row == 1 {
        let cell = tableView.dequeueReusableCellWithIdentifier("cell2", forIndexPath: indexPath) as! CustomTableViewCell2
        ImageLoader.sharedLoader.imageForUrl(self.headerImage , completionHandler:{(image: UIImage?, url: String) in
            cell.mainImage.image = image
        })
        return cell
    }
    else if indexPath.row == 2 {
        let cell = tableView.dequeueReusableCellWithIdentifier("cell3", forIndexPath: indexPath) as! CustomTableViewCell3
        //cell.aurthorImage.image = UIImage(named : "obama")
        ImageLoader.sharedLoader.imageForUrl(self.headerImage , completionHandler:{(image: UIImage?, url: String) in
            cell.aurthorImage.image = image
        })
        cell.aurthorImage.tag = aurthorID
        cell.aurthorImage.layer.cornerRadius = cell.aurthorImage.frame.height/2
        cell.aurthorImage.clipsToBounds = true
        cell.aurthorImage.userInteractionEnabled = true
        cell.aurthorImage.addGestureRecognizer(aurthorImageTapRecignizer)
        cell.aurthorName.text = self.authorName
        cell.time.text = self.time
        self.followButton = cell.followButton
        return cell
    }

    else if indexPath.row == 3 {
        let cell = tableView.dequeueReusableCellWithIdentifier("cell4", forIndexPath: indexPath) as! CustomTableViewCell4
        let htmlHeight = contentHeights[indexPath.row]

        cell.webElement.tag = indexPath.row
        cell.webElement.delegate = self
        cell.webElement.loadHTMLString(HTMLContent, baseURL: nil)
        cell.webElement.frame = CGRectMake(0, 0, cell.frame.size.width, htmlHeight)

        return cell
    }
    else if indexPath.row == 4 {
        let cell = tableView.dequeueReusableCellWithIdentifier("cell1", forIndexPath: indexPath) as! CustomTableViewCell1
        cell.heading.text = "Related Posts"
        return cell
    }
    else if indexPath.row == 5{
        let cell = tableView.dequeueReusableCellWithIdentifier("cell6", forIndexPath: indexPath) as! CustomTableViewCell6
        return cell

    }
    else if indexPath.row == 6 {
        let cell = tableView.dequeueReusableCellWithIdentifier("cell1", forIndexPath: indexPath) as! CustomTableViewCell1
        cell.heading.text = "Comments"
        return cell
    }
    else if indexPath.row == 7 {

        let cell = tableView.dequeueReusableCellWithIdentifier("cell5", forIndexPath: indexPath) as! CustomTableViewCell5


        let htmlHeight = contentHeights[indexPath.row]
        self.commentSection = cell.commentsView
        self.commentSection.tag = indexPath.row
        self.commentSection.delegate = self
        let url = NSURL(string: commentsURL)
        let requestObj = NSURLRequest(URL: url! )
        self.commentSection.loadRequest(requestObj)
        self.commentSection.frame = CGRectMake(0, 0, cell.frame.size.width, htmlHeight)
            commentSectionDidNotLoad = false

            return cell
    }
    else {
        let cell = tableView.dequeueReusableCellWithIdentifier("cell1", forIndexPath: indexPath) as! CustomTableViewCell1
        cell.heading.text = headerText
        return cell
    }
Talha Ahmad Khan
  • 3,416
  • 5
  • 23
  • 38

4 Answers4

95

You can use this code to update the cell's height without reloading their data:

tableView.beginUpdates()
tableView.endUpdates()

You can also use this method followed by the endUpdates method to animate the change in the row heights without reloading the cell.

UITableView Class Reference

Diogo Antunes
  • 2,241
  • 1
  • 22
  • 37
  • 2
    where and how should I use it?? – Talha Ahmad Khan May 24 '16 at 07:16
  • Whenever you want to update the height. Probably after clicking a cell, depends of your use case – Diogo Antunes May 24 '16 at 07:18
  • let suppose i want to change the row height of 5th row to CGFloat(100) , what should I write between tableView.beginUpdates() and tableView.endUpdates() – Talha Ahmad Khan May 24 '16 at 07:23
  • I use reloadRowsAtIndexPaths:withRowAnimation: to reload my cell when a label's height changes (based on its text)...I'm not sure what the main difference is, but I ran into issues where it seemed that my table view wasn't loaded yet and I got the internal inconsistency crash on the endUpdates call. With reloadRowsAtIndexPaths: you can get the cell (cellForRowAtIndexPath:), verify it exists and update it, and then reload, and you can control the animation. – Ethan G Sep 01 '17 at 15:13
  • Hmmm...actually I just ran into the assertion error with reloadRowsAtIndexPaths, so there's still issues with that if you don't do things right :) But being able to control the animation is nice. – Ethan G Sep 01 '17 at 15:46
  • 1
    It is important to note that these lines will call `cellForRowAt` for invisible cells, so for example, if you have a completion handler for image downloading task that has been instantiated from `cellForRowAt`, you must check if the cell is visible or not via `if tableView.cellForRow(at: indexPath) != nil` before calling `beginUpdates\endUpdates`, else, an infinite loop would occur. – Ahmed Osama May 27 '19 at 02:50
27

start an update of the tableview and then end it without changing anything.

tableView.beginUpdates()
tableView.endUpdates()

This should make the tableview set the new height

You can regulate the height by either implementing the (a)heightForRowAtIndexPath with logic setting the heights or (b)with auto layout and automatic tableview row height

A.

override func tableView(tableView: UITableView!, heightForRowAtIndexPath indexPath: NSIndexPath!) -> CGFloat {
     if [your condition, row == 5 in your comment] {
          return 100
     } else {
         return 40
     }
}

Whenever you want to change the height you would just call these two rows

tableView.beginUpdates()
tableView.endUpdates()

B.

in viewDidLoad

tableView.estimatedRowHeight = 40.0
tableView.rowHeight = UITableViewAutomaticDimension

and then you can just expose the layout constraint that set's the cells height and access it to set the height when you want

cell.heightConstraint.constant = 100
tableView.beginUpdates()
tableView.endUpdates()
Moriya
  • 7,750
  • 3
  • 35
  • 53
3

About your question for example, to give a specific height dimension :

override func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
  if indexPath.row == 3 {
    return 50.0
  }

  return 72.0
}

But I think you have a webView inside a cell so, generally, to calculate the dynamic height of a UITableViewCell with a UIWebView: (this example have two webViews)

class MyTableViewController: UITableViewController, UIWebViewDelegate
{
    var content : [String] = ["<!DOCTYPE html><html><head><title>Page Title</title></head><body><h1>My First Heading</h1><p>My first paragraph</p></body></html>", "<HTML><HEAD><TITLE>Coca-Cola</TITLE></HEAD><BODY>In Chinese, Coca-Cola means Bite the Wax Tadpole</BODY></HTML>"]
    var contentHeights : [CGFloat] = [0.0, 0.0]

    override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell
    {
        let cell = tableView.dequeueReusableCellWithIdentifier("myCustomCell", forIndexPath: indexPath) as! MyCustomCell
        let htmlString = content[indexPath.row]
        let htmlHeight = contentHeights[indexPath.row]

        cell.webView.tag = indexPath.row
        cell.webView.delegate = self
        cell.webView.loadHTMLString(htmlString, baseURL: nil)
        cell.webView.frame = CGRectMake(0, 0, cell.frame.size.width, htmlHeight)
        return cell
    }
    override func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat
    {
        return contentHeights[indexPath.row]
    }
    func webViewDidFinishLoad(webView: UIWebView)
    {
        if (contentHeights[webView.tag] != 0.0)
        {
            // height knowed, no need to reload cell
            return
        }
        contentHeights[webView.tag] = webView.scrollView.contentSize.height
        tableView.reloadRowsAtIndexPaths([NSIndexPath(forRow: webView.tag, inSection: 0)], withRowAnimation: .Automatic)
    }
}
Alessandro Ornano
  • 34,887
  • 11
  • 106
  • 133
  • yeah coincidently you wrote my case in your reply, but I am loading the webview from URL. and this is not working in my case. the webview doesnt load completely and before it finish loading, the webViewDidFinishLoad is called and the row height is stuck on lesser height than required – Talha Ahmad Khan May 24 '16 at 08:23
  • Probably you load your html and there are also other javascripts function loaded after the main html file, in this case you can handle it with webView.stringByEvaluatingJavaScriptFromString. – Alessandro Ornano May 24 '16 at 08:29
  • 1
    @AlessandroOrnano reloadRowsAtIndexPaths will cause webview to load the html once again. Is there any way to increase cell's height without reloading. I saw a lot of posts suggesting beginUpdates endUpdates but It doesn't seem to work. Can you suggest anything? – Shivam Pokhriyal Mar 03 '19 at 14:21
  • @ShivamPokhriyal The beginUpdates endUpdates combination is what the official Apple guidelines suggest, and it works well if you call it in the main thread, not inside callbacks or animations context..you should call it inside `DispatchQueue.main.async { ... }` to understand what I said.. – Alessandro Ornano Mar 03 '19 at 14:37
  • Actually It does call tableView's heightForRow delegate, which increases row's height, but cell's view is not updated. I updated constraints but it doesn't affect. Called layoutIfNeeded, setNeedsLayout, setNeedsUpdateCOnstraints but nothing works – Shivam Pokhriyal Mar 03 '19 at 14:41
  • @ShivamPokhriyal Following your description seems more an issue related to constraints priority, try to remove the cell content first of all and increase/decrease the height of your cell without content: after this step, you can try to add something to that cell, so you can isolate every issues.. – Alessandro Ornano Mar 03 '19 at 14:46
  • @AlessandroOrnano Thanks for the suggestion, I was doing the same but these playgrounds are so buggy, they never run to completion and always show Running MyPlayground. Thanks for the advice though. Will try this. – Shivam Pokhriyal Mar 03 '19 at 15:37
-7

You can use the view height constraint in the cell, by updating the view in cell height you can update the specific cell height.

let height = cell.Height_constraint.constant
cell.Height_constraint.constant = height + 200 //200 you can use any number

Height_constraint: is the height constraint of subView in my cell.