I have a UIView
subclass that contains a multi-line UILabel
. This view uses autolayout.
I'll add my 2 cents since this question is highly indexed in Google. I think you should be using
self.tableView.sectionHeaderHeight = UITableViewAutomaticDimension
self.tableView.estimatedSectionHeaderHeight = 200 //a rough estimate, doesn't need to be accurate
in your ViewDidLoad
. Also, to load a custom UIView
to a Header
you should really be using viewForHeaderInSection
delegate method. You can have a custom Nib
file for your header (UIView
nib). That Nib
must have a controller class which subclasses UITableViewHeaderFooterView
like-
class YourCustomHeader: UITableViewHeaderFooterView {
//@IBOutlets, delegation and other methods as per your needs
}
Make sure your Nib file name is the same as the class name just so you don't get confused and it's easier to manage. like YourCustomHeader.xib
and YourCustomHeader.swift
(containing class YourCustomHeader
). Then, just assign YourCustomHeader
to your Nib file using identity inspector in the interface builder.
Then register the Nib
file as your header view in the main View Controller's viewDidLoad
like-
tableView.register(UINib(nibName: "YourCustomHeader", bundle: nil), forHeaderFooterViewReuseIdentifier: "YourCustomHeader")
And then in your heightForHeaderInSection
just return UITableViewAutomaticDimension
. This is how the delegates should look like-
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let headerView = tableView.dequeueReusableHeaderFooterView(withIdentifier: "YourCustomHeader") as! YourCustomHeader
return headerView
}
func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
return UITableViewAutomaticDimension
}
This is a much simpler and the appropriate way of doing without the "Hackish" ways suggested in the accepted answer since multiple forced layouts could impact your app's performance, especially if you have multiple custom headers in your tableview. Once you do the above method as I suggest, you would notice your Header
(and or Footer
) view expand and shrink magically based on your custom view's content size (provided you are using AutoLayout in the custom view, i.e. YourCustomHeader
, nib file).
For anyone still looking for a solution, this is for Swift 3 & iOS 9+. Here is one using only AutoLayout. It also updates correctly on device rotation.
extension UITableView {
// 1.
func setTableHeaderView(headerView: UIView) {
headerView.translatesAutoresizingMaskIntoConstraints = false
self.tableHeaderView = headerView
// ** Must setup AutoLayout after set tableHeaderView.
headerView.widthAnchor.constraint(equalTo: self.widthAnchor).isActive = true
headerView.centerXAnchor.constraint(equalTo: self.centerXAnchor).isActive = true
headerView.topAnchor.constraint(equalTo: self.topAnchor).isActive = true
}
// 2.
func shouldUpdateHeaderViewFrame() -> Bool {
guard let headerView = self.tableHeaderView else { return false }
let oldSize = headerView.bounds.size
// Update the size
headerView.layoutIfNeeded()
let newSize = headerView.bounds.size
return oldSize != newSize
}
}
To use:
override func viewDidLoad() {
...
// 1.
self.tableView.setTableHeaderView(headerView: customView)
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
// 2. Reflect the latest size in tableHeaderView
if self.tableView.shouldUpdateHeaderViewFrame() {
// **This is where table view's content (tableHeaderView, section headers, cells)
// frames are updated to account for the new table header size.
self.tableView.beginUpdates()
self.tableView.endUpdates()
}
}
The gist is that you should let tableView
manage the frame of tableHeaderView
the same way as table view cells. This is done through tableView
's beginUpdates/endUpdates
.
The thing is that tableView
doesn't care about AutoLayout when it updates the children frames. It uses the current tableHeaderView
's size to determine where the first cell/section header should be.
1) Add a width constraint so that the tableHeaderView
uses this width whenever we call layoutIfNeeded(). Also add centerX and top constraints to position it correctly relative to the tableView
.
2) To let the tableView
knows about the latest size of tableHeaderView
, e.g., when the device is rotated, in viewDidLayoutSubviews we can call layoutIfNeeded() on tableHeaderView
. Then, if the size is changed, call beginUpdates/endUpdates.
Note that I don't include beginUpdates/endUpdates in one function, as we might want to defer the call to later.
Check out a sample project