Is it possible to obtain a dynamic table view section header height using Auto Layout?
This is possible. It is new right alongside the dynamic cell heights introduced in iOS 8.
To do this, use automatic dimension for the section header height, and if desired you can provide an estimated section header height. This can be done in Interface Builder when the table view is selected or programmatically:
tableView.sectionHeaderHeight = UITableView.automaticDimension
tableView.estimatedSectionHeaderHeight = 38
//You can use tableView(_:heightForHeaderInSection:) and tableView(_:estimatedHeightForHeaderInSection:)
//if you need to support different types of headers per section
Then implement tableView(_:viewForHeaderInSection:)
and use Auto Layout to constrain views as desired. Be sure to fully constrain to UITableViewHeaderFooterView
's contentView
, especially top-to-bottom so the height can be determined by the constraints. That's it!
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let headerView = UITableViewHeaderFooterView()
headerView.translatesAutoresizingMaskIntoConstraints = false
headerView.backgroundView = {
let view = UIView()
view.backgroundColor = myCustomColor
return view
}()
let headerLabel = UILabel()
headerLabel.translatesAutoresizingMaskIntoConstraints = false
headerLabel.text = "Hello World"
headerView.contentView.addSubview(headerLabel)
NSLayoutConstraint.activate([
headerLabel.leadingAnchor.constraint(equalTo: headerView.contentView.leadingAnchor, constant: 16),
headerLabel.trailingAnchor.constraint(equalTo: headerView.contentView.trailingAnchor, constant: -16),
headerLabel.topAnchor.constraint(equalTo: headerView.contentView.topAnchor, constant: 12),
headerLabel.bottomAnchor.constraint(equalTo: headerView.contentView.bottomAnchor, constant: -12)
])
return headerView
}
UITableView header dynamic height in run-time
This is how i have approached it
Using tableview
I have created the UI For the header in XIB
Now in the following delegate method
func tableView(tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
}
I Create a UIView for the header and calculate the height based on the content and return the same.
Now i can return the same header view from the following delegate method
func tableView(tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
}
Based on the section i again create a view from xib and return that view from the method.
In my case i needed only one headerview for table so i kept 2 sections and returned the headerview for section one.
Dynamic height of header with multiline UILabel in UITableView
Assuming you have got your Section Header setup using a XIB file, it is probably a constraint issue that is causing incorrect sizes, among other things.
I have put together a list of common errors that can happen when working with dynamic section headers. Please check them one-by-one, to see which one resolves your issue.
1. Make sure your Constraints are setup correctly.
Open up your Section Header XIB and make sure that there are enough constraints for UITableView
to calculate the view's height. Notice that I have setup the bottom constraint with a priority of 999. This is important because of Step 3.
2. Make sure your UILabel
can grow
Set the "Number of Lines" of both UILabel
s to zero as shown below.
3. Adjust the Content Hugging and Compression Resistance
Select your UILabels, and click the "Size Inspector" ().
At the bottom, set these values to 1000 so that Auto Layout treats them as important as your other constraints.
- Content Hugging Priority > Vertical
This sets how well the UILabel
's height should hugs its text content.
- Compression Resistance Priority > Vertical
This sets how much the UILabel
should resist shrinking its height beyond the height of its content.
Like so:
4. Return a reasonable value in estimatedHeightForHeaderInSection
func tableView(_ tableView: UITableView, estimatedHeightForHeaderInSection section: Int) -> CGFloat {
return 44.0
}
5. Compile and run!
Your section headers should now be properly sizing.
Notes:
Make sure your data source is not random text that doesn't depend on the section index.
If your section headers overlap or are improperly sized, make sure that your data source isn't changing while the
UITableView
is scrolling.
Dynamic section header height on runtime
When performing some action that changes the height of a cell (including header / footer cells), you have to inform the table view that the height has changed.
This is commonly done with either:
tableView.beginUpdates()
tableView.endUpdates()
or:
tableView.performBatchUpdates(_:completion:)
In this case, you want to call this when the text in your text view changes - easily done with a "callback" closure.
Here is an example of using a UITextView
in a reusable UITableViewHeaderFooterView
.
This will apply to loading a complex view from a XIB, but since this view is simple (only contains a UITextView
), we'll do it all from code. This example uses 3 sections, each with 12 rows (default table view cells).
First, the table view controller class - no @IBOutlet
or @IBAction
connections, so just create a new UITableViewController
and set its custom class to MyTestSectionHeaderTableViewController
:
class MyTestSectionHeaderTableViewController: UITableViewController {
var myHeaderData: [String] = [
"Section 0",
"Section 1",
"Section 2",
]
override func viewDidLoad() {
super.viewDidLoad()
tableView.rowHeight = 50
tableView.keyboardDismissMode = .onDrag
tableView.sectionHeaderHeight = UITableView.automaticDimension
tableView.estimatedSectionHeaderHeight = 75
tableView.register(UITableViewCell.self, forCellReuseIdentifier: "defCell")
tableView.register(MySectionHeaderView.self, forHeaderFooterViewReuseIdentifier: MySectionHeaderView.reuseIdentifier)
}
override func numberOfSections(in tableView: UITableView) -> Int {
return myHeaderData.count
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 12
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let c = tableView.dequeueReusableCell(withIdentifier: "defCell", for: indexPath)
c.textLabel?.text = "\(indexPath)"
return c
}
override func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let v = tableView.dequeueReusableHeaderFooterView(withIdentifier: MySectionHeaderView.reuseIdentifier) as! MySectionHeaderView
v.myTextView.text = myHeaderData[section]
v.textChangedCallback = { txt in
self.myHeaderData[section] = txt
tableView.performBatchUpdates(nil, completion: nil)
}
return v
}
}
and this is the UITableViewHeaderFooterView
class. Note that it needs to conform to UITextViewDelegate
so we can tell the controller the text has changed (so it can update the height when needed), and we pass back the newly edited text to update our data source:
class MySectionHeaderView: UITableViewHeaderFooterView, UITextViewDelegate {
static let reuseIdentifier: String = String(describing: self)
var myTextView: UITextView = {
let v = UITextView()
v.isScrollEnabled = false
return v
}()
var textChangedCallback: ((String) -> ())?
override init(reuseIdentifier: String?) {
super.init(reuseIdentifier: reuseIdentifier)
commonInit()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
commonInit()
}
func commonInit() -> Void {
contentView.addSubview(myTextView)
myTextView.translatesAutoresizingMaskIntoConstraints = false
let g = contentView.layoutMarginsGuide
NSLayoutConstraint.activate([
myTextView.topAnchor.constraint(equalTo: g.topAnchor),
myTextView.leadingAnchor.constraint(equalTo: g.leadingAnchor),
myTextView.trailingAnchor.constraint(equalTo: g.trailingAnchor),
myTextView.bottomAnchor.constraint(equalTo: g.bottomAnchor)
])
myTextView.delegate = self
}
func textViewDidChange(_ textView: UITextView) {
guard let str = textView.text else {
return
}
textChangedCallback?(str)
}
}
The result:
Related Topics
How Can a Web Application Send Push Notifications to iOS Devices
How to Create a Centered Uicollectionview Like in Spotify's Player
How to Resize Uiimageview Based on Uiimage's Size/Ratio in Swift 3
Swift Apply .Uppercasestring to Only the First Letter of a String
Wait Until Multiple Networking Requests Have All Executed - Including Their Completion Blocks
Adding a Uilabel to a Uitoolbar
How to Fix "No Valid 'Aps-Environment' Entitlement String Found for Application" in Xcode 4.3
Getting User Location Every N Minutes After App Goes to Background
Presentviewcontroller:Animated:Yes View Will Not Appear Until User Taps Again
How to Check If Uilabel Is Truncated
How to Load a Xib File in a Uiview
How to Obtain a Dynamic Table View Section Header Height Using Auto Layout
With Auto Layout, How to Make a Uiimageview's Size Dynamic Depending on the Image
How to Create a Category in Xcode 6 or Higher
Nscamerausagedescription in iOS 10.0 Runtime Crash
How to Dismiss the Keyboard When Editing a Uitextfield