uitableview content size is not returning correctly swift
Asked Answered
O

4

8

I'm using uitableview in container view, so want to set my container height based on content size height. So after reloading tableview i'm changing height of container.but it seems it is calculating based on estimated height. it's not giving actual height.

This is my constraints layout.

Thank you..enter image description here

    instructiontableview.estimatedRowHeight = 55

    instructiontableview.rowHeight = UITableViewAutomaticDimension

 func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
    return UITableViewAutomaticDimension

  }

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {

    // assigning multiline text to my label 


        cell.layoutIfNeeded()
        cell.sizeToFit()
        return cell
    }
    return UITableViewCell()
}

Now I'm reloading from my container view controller and chnanging height

Orly answered 9/3, 2017 at 10:38 Comment(3)
Please add relevant code and constraints too.Cypro
@agent_stack please check my updated questionOrly
Your label content is changing and regarding with that content you have to change the cell height. Is Im right??Cypro
O
41

I tried reloadData() and layoutIfNeeded() and tried many different ways. nothing has worked for me. Finally I solved it by using KVO.

Here is my code

override  func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(animated)
    self.instructiontableview.addObserver(self, forKeyPath: "contentSize", options: .new, context: nil)

}
override func viewWillDisappear(_ animated: Bool) {
    self.instructiontableview.removeObserver(self, forKeyPath: "contentSize")
    super.viewWillDisappear(true)
}

override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?){
    if(keyPath == "contentSize"){

        if let newvalue = change?[.newKey]{
            let newsize  = newvalue as! CGSize
            self.heightofinstructioncontainerview.constant = newsize.height
        }
    }
} 
Orly answered 14/3, 2017 at 7:10 Comment(6)
Praise de lord for de duty free.Web
Bah after everything I tried, this was the only solution that worked. In iOS 13 there is a bug, where "tableView.contentSize.height" is simply returned wrong. Observing the property as shown in the answer, does provide its real value.Deter
No Way! 🤣 This is the only solution I could find for iOS 13! Thanks a mil!Colly
I can sleep now after a long day searching about this problem. This solution works like a charm.Cataphyll
Very great answer for (iOS 13, *). Thanks.Apportion
Saved my day! I would say another day :DCultrate
C
2

Most of your approach is right, add this below steps too. If you already done any step put as check mark.

  1. Change label numberOfLines property to zero

enter image description here

  1. add constraints to label

enter image description here

  1. Change height constraint relationship

enter image description here

  1. Implement these two tableView delegates methods

    func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat{
          return 55
    }
    
    func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat{
          return UITableViewAutomaticDimension
    }
    

Now run your project and check.

Expected output:

enter image description here

Cypro answered 9/3, 2017 at 11:22 Comment(1)
I didn't added uilabel heght, excpet that i did all steps,Orly
H
2

While the answer provided here is correct, here's another explanation from Apple Developer Forum, which states the contentSize is just as estimated value initially. So making that to zero worked for me.

tableView.estimatedRowHeight = 0

Here's the answer from apple developer forum:

In iOS 11, table views use estimated heights by default. This means that the contentSize is just as estimated value initially. If you need to use the contentSize, you’ll want to disable estimated heights by setting the 3 estimated height properties to zero: tableView.estimatedRowHeight = 0 tableView.estimatedSectionHeaderHeight = 0 tableView.estimatedSectionFooterHeight = 0

Hornblende answered 14/4, 2020 at 8:19 Comment(0)
C
1

If you're using iOS 13 and or Combine (like I am) then you can use the publisher from the tableview's KVO properties

tableView.publisher(for: \.contentSize)
       .receive(on: RunLoop.main)
       .sink { size in
           self.myViewsHeightConstraint.constant = size.height
           self.tableView.isScrollEnabled = size.height > self.tableView.frame.height
       }
       .store(in: &cancellables)

This works really nicely for me and publishes new values each layout pass which you can see real time on a device if you drop a breakpoint in the sink closure.

Collinear answered 21/9, 2019 at 16:12 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.