Set UITableView's height to the height of its content with Auto Layout

46,929

Solution 1

You have to override updateViewConstraints() in your UIViewController and set the height constraint's constant to tableView.contentSize.height:

override func updateViewConstraints() {
    tableHeightConstraint.constant = tableView.contentSize.height
    super.updateViewConstraints()
}

Then you have to make sure that Label2 has a top constraint that is greaterThanOrEqual to the table view's bottom. And you also have to change the table view's height constraint's priority from Required to High to avoid conflicting constraints when the table view's contentHeight is larger than the available height.

Solution 2

There is another way to do that. You can add an observer on table view contentSize variable, and self size when change in table view content.

@IBOutlet weak var tableView: UITableView!
@IBOutlet weak var tableHeightConstraint: NSLayoutConstraint!

override func viewDidLoad() {
    super.viewDidLoad()
    self.tableView.addObserver(self, forKeyPath: "contentSize", options: NSKeyValueObservingOptions.new, context: nil)
}
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
    tableView.layer.removeAllAnimations()
    tableHeightConstraint.constant = tableView.contentSize.height
    UIView.animate(withDuration: 0.5) {
        self.updateViewConstraints()
    }

}

Solution 3

The below code worked for me.

  1. Create an outlet for tableViewHeight.
@IBOutlet weak var tableViewHeight: NSLayoutConstraint!
var tableViewHeight: CGFloat = 0  // Dynamically calcualted height of TableView.
  1. For the dynamic height of the cell, I used the below code:
func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat {
    return tableView.estimatedRowHeight
}
  1. For height of the TableView, with dynamic heights of the TableView Cells:
func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
    print(cell.frame.size.height, self.tableViewHeight)
    self.tableViewHeight += cell.frame.size.height
    tableViewBillsHeight.constant = self.tableViewHeight
}

Explanation:

After the TableView cell is created, we fetch the frame height of the cell that is about to Display and add the height of the cell to the main TableView.

Solution 4

In Swift 4.2 and Xcode 10.1

Here you have two options to set height for UITableView based on content dynamically.

1) If you get content size use this code in viewDidLayoutSubviews()

DispatchQueue.main.async {
  var frame = self.TblView.frame
  frame.size.height = self.TblView.contentSize.height
   self.TblView.frame = frame
}

2) Based on table view height constraint update table view height. After got the response from server when reload table view write this code.

DispatchQueue.main.async {
    self.TblViewHeightConstraint.constant = CGFloat((self.array.count) * 30)//Here 30 is my cell height
     self.TblView.reloadData()
}
Share:
46,929

Related videos on Youtube

Dumpen
Author by

Dumpen

Updated on July 09, 2022

Comments

  • Dumpen
    Dumpen almost 2 years

    I have a View which has two labels and a Table View inside it. I want label 1 to always stay above my Table View and label 2, to be below the Table View. The problem is that the Table View needs to auto-size meaning either increase in height or decrease.

    Right now I have a constraint saying the Table View's height is always equal to 85 and a @IBOutlet to the height constraint where i'm able to change the constant.

    I'm guessing I need to change the constant to the height of all the cells, but i'm not sure how.

    Menu constraints

    • rob mayoff
      rob mayoff over 8 years
      Another solution is to make a subclass of UITableView that sets its own intrinsicContentSize.height to its contentSize.height. See stackoverflow.com/a/17335818/77567 .
    • Altimac
      Altimac almost 7 years
      @robmayoff answer is better in my opinion as it is more dynamic than joern answer.
    • Emre Önder
      Emre Önder about 4 years
      I know this isn't the answer of your question but did you consider using UIStackview instead of tableview?
  • GMHSJ
    GMHSJ about 7 years
    Adding this in the viewDidLayoutSubviews, worked for me.
  • vahotm
    vahotm almost 7 years
    @GMHSJ, afaik viewDidLayoutSubviews is called after layout system finish calculating frames, therefore if you change constraints here, it will trigger layout process again, which should create infinite recursion in the end.
  • GMHSJ
    GMHSJ almost 7 years
    @vahotm: Ya true, But updateViewConstraints didn't work for me. Donno why :(
  • Nate4436271
    Nate4436271 about 6 years
    While I think this is a good solution, it requires you to be adjusting the height of the tableview based on a constraint. I found the solution below by nova to be better because it is not dependent on using constraints to change the height. In my case, my tableView was a subview and the superview needed to adjust its own frame based on the size of the tableView.
  • Kirti Nikam
    Kirti Nikam over 5 years
    It is working for me. But 'observeValueForKeyPath' method getting called many times.
  • Hamed Nova
    Hamed Nova over 5 years
    @iKT when you observe on a key with NSKeyValueObservingOptions.new you will get called each time that key updated to a new value so whenever you want you can remove that observer
  • andesta.erfan
    andesta.erfan over 5 years
    this saved my life
  • Iulian Onofrei
    Iulian Onofrei over 5 years
    @GMHSJ, Does calling the super.updateViewConstraints() method at the end of updateViewConstraints help? Because this is the correct way to override it according to the documentation.
  • Jignesh Chanchiya
    Jignesh Chanchiya about 5 years
    Excellent, Save my day
  • Bruno Reis Portela
    Bruno Reis Portela about 5 years
    Useful. Not an elegant way to do. Actually MANY years and APPLE didn't implement a fashion way to do that as "tableview.contentSize" with an event didFinishLoad. But so far this is the cleaner way to do that without create an specific class or extension to handle such ordinary task.
  • Pepeng Hapon
    Pepeng Hapon over 4 years
    This really helps me a lot! Thanks @nova
  • lawicko
    lawicko over 4 years
    Remember to remove the observer on deinit with removeObserver
  • androidguy
    androidguy over 3 years
    But every time updateViewConstraints is called, you create a new constraint. So you are accumulating multiple height constraints, so if tableView.contentSize changes, you'll have height constraints with conflicting values. So it's worth it to store a height constraint and always update that one (you can create it in viewDidLoad). Besides, even if size never changes, auto layout has to work more to process extra constraints.
  • Poornima Mishra
    Poornima Mishra over 2 years
    how to remove observer in deinit @HamedNova
  • keshav
    keshav about 2 years
    i have used same method but now i am getting a long table more than its content