I have a UIView
subclass that contains a multi-line UILabel
. This view uses autolayout.
I would like to set this view as the tableHeaderView
of a UITableView
(not a section header). The height of this header will depend on the text of the label, which in turn depends on the width of the device. The sort of scenario autolayout should be great at.
I have found and attempted many many solutions to get this working, but to no avail. Some of the things I've tried:
- setting a
preferredMaxLayoutWidth
on each label duringlayoutSubviews
- defining an
intrinsicContentSize
- attempting to figure out the required size for the view and setting the
tableHeaderView
's frame manually. - adding a width constraint to the view when the header is set
- a bunch of other things
Some of the various failures I've encountered:
- label extends beyond the width of the view, doesn't wrap
- frame's height is 0
- app crashes with exception
Auto Layout still required after executing -layoutSubviews
The solution (or solutions, if necessary) should work for both iOS 7 and iOS 8. Note that all of this is being done programmatically. I've set up a small sample project in case you want to hack on it to see the issue. I've reset my efforts to the following start point:
SCAMessageView *header = [[SCAMessageView alloc] init];
header.titleLabel.text = @"Warning";
header.subtitleLabel.text = @"This is a message with enough text to span multiple lines. This text is set at runtime and might be short or long.";
self.tableView.tableHeaderView = header;
What am I missing?
I'll add my 2 cents since this question is highly indexed in Google. I think you should be using
in your
ViewDidLoad
. Also, to load a customUIView
to aHeader
you should really be usingviewForHeaderInSection
delegate method. You can have a customNib
file for your header (UIView
nib). ThatNib
must have a controller class which subclassesUITableViewHeaderFooterView
like-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
andYourCustomHeader.swift
(containingclass YourCustomHeader
). Then, just assignYourCustomHeader
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'sviewDidLoad
like-And then in your
heightForHeaderInSection
just returnUITableViewAutomaticDimension
. This is how the delegates should look like-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 orFooter
) 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).Some of the answers here helped me get very close to what I needed. But I encountered conflicts with the constraint "UIView-Encapsulated-Layout-Width" which is set by the system, when rotating the device back-and-forth between portrait and landscape. My solution below is largely based on this gist by marcoarment (credit to him): https://gist.github.com/marcoarment/1105553afba6b4900c10. The solution does not rely on the header view containing a UILabel. There are 3 parts:
UITableView extension
Use in UITableViewController
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.
To use:
The gist is that you should let
tableView
manage the frame oftableHeaderView
the same way as table view cells. This is done throughtableView
'sbeginUpdates/endUpdates
.The thing is that
tableView
doesn't care about AutoLayout when it updates the children frames. It uses the currenttableHeaderView
'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 thetableView
.2) To let the
tableView
knows about the latest size oftableHeaderView
, e.g., when the device is rotated, in viewDidLayoutSubviews we can call layoutIfNeeded() ontableHeaderView
. 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
Using Extension in Swift 3.0
My own best answer so far involves setting the
tableHeaderView
once and forcing a layout pass. This allows a required size to be measured, which I then use to set the frame of the header. And, as is common withtableHeaderView
s, I have to again set it a second time to apply the change.For multiline labels, this also relies on the custom view (the message view in this case) setting the
preferredMaxLayoutWidth
of each:Update January 2015
Unfortunately this still seems necessary. Here is a swift version of the layout process:
I've found it useful to move this into an extension on UITableView:
Usage:
The following
UITableView
extension solves all common problems of autolayouting and positioning of thetableHeaderView
without frame-use legacy:Explanation of the "strange" steps:
At first we tie the headerView width to the tableView width: it helps as under rotations and prevent from deep left shift of X-centered subviews of the headerView.
(the Magic!) We insert fake layoutView in the headerView: At this moment we STRONGLY need to remove all headerView constraints, expand the layoutView to the headerView and then restore initial headerView constraints. It happens that order of constraints has some sense! In the way we get correct headerView height auto calculation and also correct
X-centralization for all headerView subviews.
Then we only need to re-layout headerView again to obtain correct tableView
height calculation and headerView positioning above sections without intersecting.
P.S. It works for iOS8 also. It is impossible to comment out any code string here in common case.