Reputation: 947
I know there are a lot of posts about it, but maybe in newest iOS there are some updates on this...
I think all of us had a task to create viewController
that has a lot of content at the top, most of them are self-sizing, and at the very bottom it figures out that you need to show some tableView
with many items...
The first solution that can be done is to use UIScrollView
, and don't care about reusableCells at all.
The second is to use UITableView
's headerView
and adjust its height manually (or by calling systemLayoutSizeFittingSize:
) each time when it is needed.
Maybe the third solution is to use UITableView
and self-sized UIView
separately, with having UIEdgeInsets
on tableView
. And depending on what object has higher "zIndex", it can bring problems with handling interactions...
The forth solution is to use whole content above the cell, like a separate cell. Not sure this is a good idea at all...
Question: Is there any new solution to this problem? I haven't dig into it for like 2 years... Maybe in new iOS there is something like reusableViews for UIScrollView
... Of course, the goal is to have reusable cells, and header with using autolayout without necessity of updating its height manually...
Upvotes: 23
Views: 30165
Reputation: 1
You can use the width of the tableView to set the width constraint of the headerView. The systemLayoutSizeFitting(_ targetSize: CGSize)
method supports directly passing a width.
extension UITableView {
func layoutTableHeaderView() {
guard let headerView = self.tableHeaderView else { return }
headerView.setNeedsLayout()
headerView.layoutIfNeeded()
let headerSize = headerView.systemLayoutSizeFitting(CGSize(width: self.frame.width, height: UIView.layoutFittingCompressedSize.height))
let height = headerSize.height
var frame = headerView.frame
frame.size.height = height
headerView.frame = frame
self.tableHeaderView = headerView
}
}
Upvotes: 0
Reputation: 951
Xcode 14 + Swift 5
This is how I made it.
First step - configuring of table view for dynamic height headers usage:
In viewViewDidLoad you need to add:
tableView.sectionHeaderHeight = UITableView.automaticDimension
tableView.estimatedSectionHeaderHeight = 40
This also can be done in xib/storyboard where your tableView is located instead of configuring in code:
Second step - creation of header view:
You need to create custom view to use as table's header. For example view with label as a subview, pinned with constraints to superview. Any view that contains some subviews that could cause it to have different height. The main thing - you should set up content of header with constraints to make it resize itself.
Third step - configuration of tableView's delegate:
In your tableView delegate implement viewForHeaderInSection method and just return configured instance of your custom header view.
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView?
IMPORTANT - heightForHeaderInSection method shouldn't be implemented, just don't add it.
func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat
Upvotes: 0
Reputation: 2956
You can do that in viewDidLayoutSubviews
of the UIViewController
that contains your UITableView
:
@IBOutlet weak var tableView: UITableView!
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
// Resize header view with dynamic size in UITableView
guard let headerView = tableView.tableHeaderView else {
return
}
let size = headerView.systemLayoutSizeFitting(UIView.layoutFittingCompressedSize)
if headerView.frame.height != size.height {
tableView.tableHeaderView?.frame = CGRect(
origin: headerView.frame.origin,
size: size
)
tableView.layoutIfNeeded()
}
}
Upvotes: 1
Reputation: 8322
Copied from useyourloaf.com
Swift 5.0
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
guard let headerView = tableView.tableHeaderView else {return}
let size = headerView.systemLayoutSizeFitting(UIView.layoutFittingCompressedSize)
if headerView.frame.size.height != size.height {
headerView.frame.size.height = size.height
tableView.tableHeaderView = headerView
tableView.layoutIfNeeded()
}
}
Upvotes: 11
Reputation: 783
If you are using Interface Builder simply you check these buttons
Upvotes: 2
Reputation: 141
I'm using XCode 10.3 and this is my solution worked with your second solution using table header view.
First, you would create a separating view with xib file, for example with a label inside. And you apply the constraints for this label, top, left, bottom, right to the cell's container view. And set numberOfLines
= 0.
Update your awakeFromNib()
function inside your view class.
override func awakeFromNib() {
super.awakeFromNib()
ourLabel.translatesAutoresizingMaskIntoConstraints = false
}
Second, on your viewController, setup your tableView:
tableView.sectionHeaderHeight = UITableView.automaticDimension
tableView.estimatedSectionHeaderHeight = 64
Remember don't delegate this method:
func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat
because we set the constraints of our view already.
Finally, you return it on the delegate method
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView?
the view for header.
let view = UINib(nibName: String(describing: SimpleHeaderTitleView.self), bundle: nil).instantiate(withOwner: nil, options: nil)[0] as! SimpleHeaderTitleView
view.ourLabel.text = "Your longgggg text"
return view
Done! Check it works.
Upvotes: 14
Reputation: 786
I like the way it's done here:
• If you want to set your tableview header height dynamically based on it's content, just call self.tableView.layoutTableFooterView()
right after you have set your headerView as TableViewHeader (so after self.tableView.tableHeaderView = view
)
• If you need to update your tableview header height on runtime, also call self.tableView.layoutTableFooterView()
right after you have updated the values of your tableview header.
(This obviously also works with tableviewFooters, though, is not to be used for sectionHeaders/Footers)
extension UITableView {
//Variable-height UITableView tableHeaderView with autolayout
func layoutTableHeaderView() {
guard let headerView = self.tableHeaderView else { return }
headerView.translatesAutoresizingMaskIntoConstraints = false
let headerWidth = headerView.bounds.size.width
let temporaryWidthConstraint = headerView.widthAnchor.constraint(equalToConstant: headerWidth)
headerView.addConstraint(temporaryWidthConstraint)
headerView.setNeedsLayout()
headerView.layoutIfNeeded()
let headerSize = headerView.systemLayoutSizeFitting(UILayoutFittingCompressedSize)
let height = headerSize.height
var frame = headerView.frame
frame.size.height = height
headerView.frame = frame
self.tableHeaderView = headerView
headerView.removeConstraint(temporaryWidthConstraint)
headerView.translatesAutoresizingMaskIntoConstraints = true
}
}
Upvotes: 17
Reputation: 2092
Step One :
From interface builder Drag a UIView and drop into
UItableView
This view will automatically act as a UITableView Header (Mind it not section Header) . Suppose this the width of this view is 200 .If you run the UItableView This view will automatically appear as a Header .
Create a Outlet of this drag-drop View .
@property (weak, nonatomic) IBOutlet UIView *tableViewHeader; // height of this view is 200
Now my goal is increase the height of the table View header .
Step Two :
Add this method
- (void)viewDidLayoutSubviews
{
int increasedHeight = 100;
// set a frame of this view Like example
self.tableViewHeader.frame = CGRectMake(0, 0, self.view.frame.size.width , 200 + increasedHeight );
self.tableView.tableHeaderView = self.tableViewHeader;
}
Now you tableview header height will be 300 .
Upvotes: 3
Reputation: 1957
I am guessing you are talking about section headers of table view here. If that is so you can absolutely use auto layout for section headers.
Use the below two code in viewDidLoad:
tableView.sectionHeaderHeight = UITableViewAutomaticDimension
tableView.estimatedSectionHeaderHeight = 36;
Now in viewForHeaderInSection:
try the below code just to get an idea how things are working out. Change it according to your requirement.
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let label: UILabel = {
let lb = UILabel()
lb.translatesAutoresizingMaskIntoConstraints = false
lb.text = "HEADER \(section) with a loooooooooooooooonnngngngngngngngng texxxxxxxxxxxxxxxxt"
lb.textColor = .black
lb.backgroundColor = .yellow
lb.numberOfLines = 0
return lb
}()
let header: UIView = {
let hd = UIView()
hd.backgroundColor = .blue
hd.addSubview(label)
label.leadingAnchor.constraint(equalTo: hd.leadingAnchor, constant: 8).isActive = true
label.topAnchor.constraint(equalTo: hd.topAnchor, constant: 8).isActive = true
label.trailingAnchor.constraint(equalTo: hd.trailingAnchor, constant: -8).isActive = true
label.bottomAnchor.constraint(equalTo: hd.bottomAnchor, constant: -8).isActive = true
return hd
}()
return header
}
Upvotes: 16
Reputation: 745
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.
Upvotes: 2