Since I discovered AutoLayout I use it everywhere, now I'm trying to use it with a tableHeaderView.
I made a subclass of UIView added everything (labels etc...) I wanted with their constraints, then I added this CustomView to the UITableView'tableHeaderView.
Everything works just fine except the UITableView always displays above the CustomView, by above I mean the CustomView is under the UITableView so it can't be seen !
It seems that no matter what I do, the height of the UITableView'tableHeaderView is always 0 (so is the width, x and y).
My question : is it possible at all to accomplish this without setting the frame manually ?
EDIT :
The CustomView'subview that I'm using has these constraints :
_title = [[UILabel alloc]init];
_title.text = #"Title";
[self addSubview:_title];
[_title keep:[KeepTopInset rules:#[[KeepEqual must:5]]]]; // title has to stay at least 5 away from the supperview Top
[_title keep:[KeepRightInset rules:#[[KeepMin must:5]]]];
[_title keep:[KeepLeftInset rules:#[[KeepMin must:5]]]];
[_title keep:[KeepBottomInset rules:#[[KeepMin must:5]]]];
I'm using a handy library 'KeepLayout' because writing constraints manually takes forever and way too many line for one single constraint but the methods are self-explaining.
And the UITableView has these constraints :
_tableView = [[UITableView alloc]init];
_tableView.translatesAutoresizingMaskIntoConstraints = NO;
_tableView.delegate = self;
_tableView.dataSource = self;
_tableView.backgroundColor = [UIColor clearColor];
[self.view addSubview:_tableView];
[_tableView keep:[KeepTopInset rules:#[[KeepEqual must:0]]]];// These 4 constraints make the UITableView stays 0 away from the superview top left right and bottom.
[_tableView keep:[KeepLeftInset rules:#[[KeepEqual must:0]]]];
[_tableView keep:[KeepRightInset rules:#[[KeepEqual must:0]]]];
[_tableView keep:[KeepBottomInset rules:#[[KeepEqual must:0]]]];
_detailsView = [[CustomView alloc]init];
_tableView.tableHeaderView = _detailsView;
I don't know if I have to set some constraints directly on the CustomView, I think the height of the CustomView is determined by the constraints on the UILabel "title" in it.
EDIT 2: After another investigation it seems the height and width of the CustomView are correctly calculated, but the top of the CustomView is still at the same level than the top of the UITableView and they move together when I scroll.
I asked and answered a similar question here. In summary, I add the header once and use it to find the required height. That height can then be applied to the header, and the header is set a second time to reflect the change.
- (void)viewDidLoad
{
[super viewDidLoad];
self.header = [[SCAMessageView alloc] init];
self.header.titleLabel.text = #"Warning";
self.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.";
//set the tableHeaderView so that the required height can be determined
self.tableView.tableHeaderView = self.header;
[self.header setNeedsLayout];
[self.header layoutIfNeeded];
CGFloat height = [self.header systemLayoutSizeFittingSize:UILayoutFittingCompressedSize].height;
//update the header's frame and set it again
CGRect headerFrame = self.header.frame;
headerFrame.size.height = height;
self.header.frame = headerFrame;
self.tableView.tableHeaderView = self.header;
}
If you have multi-line labels, this also relies on the custom view setting the preferredMaxLayoutWidth of each label:
- (void)layoutSubviews
{
[super layoutSubviews];
self.titleLabel.preferredMaxLayoutWidth = CGRectGetWidth(self.titleLabel.frame);
self.subtitleLabel.preferredMaxLayoutWidth = CGRectGetWidth(self.subtitleLabel.frame);
}
or perhaps more generally:
override func layoutSubviews() {
super.layoutSubviews()
for view in subviews {
guard let label = view as? UILabel where label.numberOfLines == 0 else { continue }
label.preferredMaxLayoutWidth = CGRectGetWidth(label.frame)
}
}
Update January 2015
Unfortunately this still seems necessary. Here is a swift version of the layout process:
tableView.tableHeaderView = header
header.setNeedsLayout()
header.layoutIfNeeded()
header.frame.size = header.systemLayoutSizeFittingSize(UILayoutFittingCompressedSize)
tableView.tableHeaderView = header
I've found it useful to move this into an extension on UITableView:
extension UITableView {
//set the tableHeaderView so that the required height can be determined, update the header's frame and set it again
func setAndLayoutTableHeaderView(header: UIView) {
self.tableHeaderView = header
self.tableHeaderView?.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
header.widthAnchor.constraint(equalTo: self.widthAnchor)
])
header.setNeedsLayout()
header.layoutIfNeeded()
header.frame.size = header.systemLayoutSizeFitting(UIView.layoutFittingCompressedSize)
self.tableHeaderView = header
}
}
Usage:
let header = SCAMessageView()
header.titleLabel.text = "Warning"
header.subtitleLabel.text = "Warning message here."
tableView.setAndLayoutTableHeaderView(header)
I've been unable to add a header view using constraints (in code). If I give my view a width and/or a height constraint, I get a crash with the message saying:
"terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Auto Layout still required after executing -layoutSubviews. UITableView's implementation of -layoutSubviews needs to call super."
When I add a view in the storyboard to my table view, it shows no constraints, and it works fine as a header view, so I think that the placement of the header view isn't done using constraints. It doesn't seem to behave like a normal view in that regard.
The width is automatically the width of the table view, the only thing you need to set is the height -- the origin values are ignored, so it doesn't matter what you put in for those. For instance, this worked fine (as does 0,0,0,80 for the rect):
UIView *headerview = [[UIView alloc] initWithFrame:CGRectMake(1000,1000, 0, 80)];
headerview.backgroundColor = [UIColor yellowColor];
self.tableView.tableHeaderView = headerview;
I saw a lot of methods here doing so much unnecessary stuff, but you don't need that much to use auto layout in the header view. You just have to create you xib file, put your constraints and instantiate it like this:
func loadHeaderView () {
guard let headerView = Bundle.main.loadNibNamed("CourseSearchHeader", owner: self, options: nil)?[0] as? UIView else {
return
}
headerView.autoresizingMask = .flexibleWidth
headerView.translatesAutoresizingMaskIntoConstraints = true
tableView.tableHeaderView = headerView
}
Another solution is to dispatch the header view creation to the next main thread call:
- (void)viewDidLoad {
[super viewDidLoad];
// ....
dispatch_async(dispatch_get_main_queue(), ^{
_profileView = [[MyView alloc] initWithNib:#"MyView.xib"];
self.tableView.tableHeaderView = self.profileView;
});
}
Note: It fix the bug when the loaded view has a fixed height. I haven't tried when the header height only depends on its content.
EDIT :
You can find a cleaner solution to this problem by implementing this function, and calling it in viewDidLayoutSubviews
- (void)viewDidLayoutSubviews {
[super viewDidLayoutSubviews];
[self sizeHeaderToFit];
}
You can get autolayout to provide you with a size by using the systemLayoutSizeFittingSize method.
You can then use this to create the frame for your application. This technique works whenever you need to know the size of a view that uses autolayout internally.
The code in swift looks like
//Create the view
let tableHeaderView = CustomTableHeaderView()
//Set the content
tableHeaderView.textLabel.text = #"Hello world"
//Ask auto layout for the smallest size that fits my constraints
let size = tableHeaderView.systemLayoutSizeFittingSize(UILayoutFittingCompressedSize)
//Create a frame
tableHeaderView.frame = CGRect(origin: CGPoint.zeroPoint, size: size)
//Set the view as the header
self.tableView.tableHeaderView = self.tableHeaderView
Or in Objective-C
//Create the view
CustomTableHeaderView *header = [[CustomTableHeaderView alloc] initWithFrame:CGRectZero];
//Set the content
header.textLabel.text = #"Hello world";
//Ask auto layout for the smallest size that fits my constraints
CGSize size = [header systemLayoutSizeFittingSize:UILayoutFittingCompressedSize];
//Create a frame
header.frame = CGRectMake(0,0,size.width,size.height);
//Set the view as the header
self.tableView.tableHeaderView = header
It should also be noted that in this particular instance, overriding requiresConstraintBasedLayout in your subclass, does result in a layout pass being performed, however the results of this layout pass are ignored and the system frame set to the width of the tableView and 0 height.
Code:
extension UITableView {
func sizeHeaderToFit(preferredWidth: CGFloat) {
guard let headerView = self.tableHeaderView else {
return
}
headerView.translatesAutoresizingMaskIntoConstraints = false
let layout = NSLayoutConstraint(
item: headerView,
attribute: .Width,
relatedBy: .Equal,
toItem: nil,
attribute:
.NotAnAttribute,
multiplier: 1,
constant: preferredWidth)
headerView.addConstraint(layout)
let height = headerView.systemLayoutSizeFittingSize(UILayoutFittingCompressedSize).height
headerView.frame = CGRectMake(0, 0, preferredWidth, height)
headerView.removeConstraint(layout)
headerView.translatesAutoresizingMaskIntoConstraints = true
self.tableHeaderView = headerView
}
}
Extended this solution http://collindonnell.com/2015/09/29/dynamically-sized-table-view-header-or-footer-using-auto-layout/ for table footer view:
#interface AutolayoutTableView : UITableView
#end
#implementation AutolayoutTableView
- (void)layoutSubviews {
[super layoutSubviews];
// Dynamic sizing for the header view
if (self.tableHeaderView) {
CGFloat height = [self.tableHeaderView systemLayoutSizeFittingSize:UILayoutFittingCompressedSize].height;
CGRect headerFrame = self.tableHeaderView.frame;
// If we don't have this check, viewDidLayoutSubviews() will get
// repeatedly, causing the app to hang.
if (height != headerFrame.size.height) {
headerFrame.size.height = height;
self.tableHeaderView.frame = headerFrame;
self.tableHeaderView = self.tableHeaderView;
}
[self.tableHeaderView layoutIfNeeded];
}
// Dynamic sizing for the footer view
if (self.tableFooterView) {
CGFloat height = [self.tableFooterView systemLayoutSizeFittingSize:UILayoutFittingCompressedSize].height;
CGRect footerFrame = self.tableFooterView.frame;
// If we don't have this check, viewDidLayoutSubviews() will get
// repeatedly, causing the app to hang.
if (height != footerFrame.size.height) {
footerFrame.size.height = height;
self.tableFooterView.frame = footerFrame;
self.tableFooterView = self.tableFooterView;
}
self.tableFooterView.transform = CGAffineTransformMakeTranslation(0, self.contentSize.height - footerFrame.size.height);
[self.tableFooterView layoutIfNeeded];
}
}
#end
Updated for Swift 4.2
extension UITableView {
var autolayoutTableViewHeader: UIView? {
set {
self.tableHeaderView = newValue
guard let header = newValue else { return }
header.setNeedsLayout()
header.layoutIfNeeded()
header.frame.size =
header.systemLayoutSizeFitting(UIView.layoutFittingCompressedSize)
self.tableHeaderView = header
}
get {
return self.tableHeaderView
}
}
}
The following worked for me.
Use a plain old UIView as the header view.
Add subviews to that UIView
Use autolayout on the subviews
The main benefit I see is limiting frame calculations. Apple should really update UITableView's API to make this easier.
Example using SnapKit:
let layoutView = UIView(frame: CGRect(x: 0, y: 0, width: tableView.bounds.width, height: 60))
layoutView.backgroundColor = tableView.backgroundColor
tableView.tableHeaderView = layoutView
let label = UILabel()
layoutView.addSubview(label)
label.text = "I'm the view you really care about"
label.snp_makeConstraints { make in
make.edges.equalTo(EdgeInsets(top: 10, left: 15, bottom: -5, right: -15))
}
Strange things happens. systemLayoutSizeFittingSize works great for iOS9, but doesn't for iOS 8 in my case. So this problems solves quite easy. Just get link to the bottom view in header and in viewDidLayoutSubviews after super call update header view bounds by inserting height as CGRectGetMaxY(yourview.frame) + padding
UPD: The easiest solution ever:
So, in header view place subview and pin it to left, right, top. In that subview place your subviews with auto-height constraints. After that give all the job to the autolayout (no calculation required)
- (void)viewDidLayoutSubviews {
[super viewDidLayoutSubviews];
CGFloat height = CGRectGetMaxY(self.tableView.tableHeaderView.subviews.firstObject.frame);
self.tableView.tableHeaderView.bounds = CGRectMake(0, 0, CGRectGetWidth(self.tableView.bounds), height);
self.tableView.tableHeaderView = self.tableView.tableHeaderView;
}
As a result subview is expanding/shrinking like it should, at the end it calls viewDidLayoutSubviews. At the time we know the actual size of the view, so set headerView height and update it by re-assigning.
Works like a charm!
Also works for footer view.
you can add top + horizontal location constraint between header and tableview, to place it, correctly (if the header itself contains all the necessary internal layout constraints to have a correct frame)
in the tableViewController viewDidLoad method
headerView.translatesAutoresizingMaskIntoConstraints = false
tableView.tableHeaderView = headerView
headerView.widthAnchor.constraint(equalTo: tableView.widthAnchor).isActive = true
headerView.topAnchor.constraint(equalTo: tableView.topAnchor).isActive = true
headerView.centerXAnchor.constraint(equalTo: tableView.centerXAnchor).isActive = true
For most cases the best solution is simply not to fight the framework and embrace autoresizing masks:
// embrace autoresizing masks and let the framework add the constraints for you
headerView.translatesAutoresizingMaskIntoConstraints = true
headerView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
// figure out what's the best size based on the table view width
let width = self.tableView.frame.width
let targetSize = headerView.systemLayoutSizeFitting(CGSize(width: width, height: CGFloat.greatestFiniteMagnitude), withHorizontalFittingPriority: .required, verticalFittingPriority: .fittingSizeLevel)
headerView.frame.size = targetSize
self.tableView.tableHeaderView = headerView
By using autoresizing masks you're telling the framework how your view should change its size when the superview changes its size. But this change is based on the initial frame you've set.
My table header view is a UIView subclass - I created a single contentView UIView within the initializer, with its bounds the same as the table header view's frame and added all my objects as a subview of that.
Then add the constraints for your objects within the table header view's layoutSubviews method rather than within the initializer. That solved the crash.
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:CGRectMake(0, 0, 0, 44.0)];
if (self) {
UIView *contentView = [[UIView alloc] initWithFrame:self.bounds];
contentView.autoresizingMask = UIViewAutoresizingFlexibleWidth;
// add other objects as subviews of content view
}
return self;
}
- (void)layoutSubviews
{
[super layoutSubviews];
// remake constraints here
}
My AutoLayout is working very good:
CGSize headerSize = [headerView systemLayoutSizeFittingSize:CGSizeMake(CGRectGetWidth([UIScreen mainScreen].bounds), 0) withHorizontalFittingPriority:UILayoutPriorityRequired verticalFittingPriority:UILayoutPriorityFittingSizeLevel];
headerView.frame = CGRectMake(0, 0, headerSize.width, headerSize.height);
self.tableView.tableHeaderView = headerView;
My solution is making a new class like this.
class BaseTableHeaderView: UIView {
func sizeToFitBasedOnConstraints(width: CGFloat = Screen.width) {
let size = systemLayoutSizeFitting(CGSize(width: width, height: 10000),
withHorizontalFittingPriority: .required,
verticalFittingPriority: .fittingSizeLevel)
frame = CGRect(origin: .zero, size: size)
}
override func willMove(toSuperview newSuperview: UIView?) {
sizeToFitBasedOnConstraints()
super.willMove(toSuperview: newSuperview)
}
}
To use it, simply add all your subviews onto an instance of BaseTableHeaderView and attach it to your table view.
let tableHeaderView = BaseTableHeaderView()
tableHeaderView.addSubview(...)
tableView.tableHeaderView = tableHeaderView
It will automatically resize based on its constraints.
I know this is an old post but After going through all the SO posts regarding this and passing a whole afternoon playing with this, I finally came up with a clean and yet very simple solution
First of all, My view hierarchy looks like this:
Table View
View tableHeaderView
View with an outlet called headerView
Now inside the View (No.3), I set up all the constraints as I would normally including the bottom space to container. This will make the container (i.e. 3.View i.e. headerView) to size itself based on it's subviews and their constraints.
After that, I set the constraints between 3. View and 2. View to these:
Top Space to container: 0
Leading Space to container: 0
Trailing Space to container: 0
Notice that I omit intentionally the bottom space intentionally.
Once all of this is done in the storyboard, everything that's left to do is paste those three lines of codes:
if (self.headerView.frame.size.height != self.tableView.tableHeaderView.frame.size.height) {
UIView *header = self.tableView.tableHeaderView;
CGRect frame = self.tableView.tableHeaderView.frame;
frame.size.height = self.headerView.frame.size.height + frame.origin.y;
header.frame = frame;
self.tableView.tableHeaderView = header;
}
Tips: If you use method setAndLayoutTableHeaderView, you should update subviews's frame,so in this situation UILabel's preferredMaxLayoutWidth should call before systemLayoutSizeFittingSize called, do not call in layoutSubview.
code show
Share my approach.
UITableView+XXXAdditions.m
- (void)xxx_setTableHeaderView:(UIView *)tableHeaderView layoutBlock:(void(^)(__kindof UIView *tableHeaderView, CGFloat *containerViewHeight))layoutBlock {
CGFloat containerViewHeight = 0;
UIView *backgroundView = [[UIView alloc] initWithFrame:CGRectZero];
[backgroundView addSubview:tableHeaderView];
layoutBlock(tableHeaderView, &containerViewHeight);
backgroundView.frame = CGRectMake(0, 0, 0, containerViewHeight);
self.tableHeaderView = backgroundView;
}
Usage.
[self.tableView xxx_setTableHeaderView:myView layoutBlock:^(__kindof UIView * _Nonnull tableHeaderView, CGFloat *containerViewHeight) {
*containerViewHeight = 170;
[tableHeaderView mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.equalTo(#20);
make.centerX.equalTo(#0);
make.size.mas_equalTo(CGSizeMake(130, 130));
}];
}];
An old post. But a good post. Here's my 2 cents.
Firstly, ensure that your header view has its constraints arranged so that it can support it's own intrinsic content size. Then do the following.
//ViewDidLoad
headerView.translatesAutoresizingMaskIntoConstraints = false
headerView.configure(title: "Some Text A")
//Somewhere else
headerView.update(title: "Some Text B)
private var widthConstrained = false
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
if widthConstrained == false {
widthConstrained = true
tableView.addConstraint(NSLayoutConstraint(item: headerView, attribute: .width, relatedBy: .equal, toItem: tableView, attribute: .width, multiplier: 1, constant: 0))
headerView.layoutIfNeeded()
tableView.layoutIfNeeded()
}
}
override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
super.viewWillTransition(to: size, with: coordinator)
coordinator.animate(alongsideTransition: { (context) in
self.headerView.layoutIfNeeded()
self.tableView.layoutIfNeeded()
}, completion: nil)
}
I was able to achieve it by the following approach (this works for footer the same way).
First, you will need small UITableView extension:
Swift 3
extension UITableView {
fileprivate func adjustHeaderHeight() {
if let header = self.tableHeaderView {
adjustFrame(header)
}
}
private func adjustFrame(_ view: UIView) {
view.frame.size.height = calculatedViewHeight(view)
}
fileprivate func calculatedHeightForHeader() -> CGFloat {
if let header = self.tableHeaderView {
return calculatedViewHeight(header)
}
return 0.0
}
private func calculatedViewHeight(_ view: UIView) -> CGFloat {
view.setNeedsLayout()
let height = view.systemLayoutSizeFitting(UILayoutFittingCompressedSize).height
return height
}
}
In your view controller class implementation:
// this is a UIView subclass with autolayout
private var headerView = MyHeaderView()
override func loadView() {
super.loadView()
// ...
self.tableView.tableHeaderView = headerView
self.tableView.sectionHeaderHeight = UITableViewAutomaticDimension
// ...
}
override func viewWillLayoutSubviews() {
super.viewWillLayoutSubviews()
// this is to prevent recursive layout calls
let requiredHeaderHeight = self.tableView.calculatedHeightForHeader()
if self.headerView.frame.height != requiredHeaderHeight {
self.tableView.adjustHeaderHeight()
}
}
Notes about a header UIView's subview implementation:
You have to 100% sure that your header view has the correct autolayout setup. I would recommend to start with simple header view with just one heigh constraint and try out the setup above.
Override requiresConstraintBasedLayout and return true:
.
class MyHeaderView: UIView {
// ...
override static var requiresConstraintBasedLayout : Bool {
return true
}
// ...
}
For Xamarin users:
public override void ViewDidLayoutSubviews()
{
base.ViewDidLayoutSubviews();
TableviewHeader.SetNeedsLayout();
TableviewHeader.LayoutIfNeeded();
var height = TableviewHeader.SystemLayoutSizeFittingSize(UIView.UILayoutFittingCompressedSize).Height;
var frame = TableviewHeader.Frame;
frame.Height = height;
TableviewHeader.Frame = frame;
}
Assuming you named the header view of your tableview as TableviewHeader
Here is how you can do in your UIViewController
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
if headerView.frame.size.height == 0 {
headerView.label.preferredMaxLayoutWidth = view.bounds.size.width - 20
let height = headerView.systemLayoutSizeFitting(UILayoutFittingCompressedSize).height
headerView.frame.size = CGSize(width: tableView.bounds.size.width, height: height)
}
}
Any constraint-based UIView can be a good tableHeaderView.
One needs to set a tableFooterView before and then impose additional trailing constraint on tableFooterView and tableHeaderView.
- (void)viewDidLoad {
........................
// let self.headerView is some constraint-based UIView
self.tableView.tableFooterView = [UIView new];
[self.headerView layoutIfNeeded];
self.tableView.tableHeaderView = self.headerView;
[self.tableView.leadingAnchor constraintEqualToAnchor:self.headerView.leadingAnchor].active = YES;
[self.tableView.trailingAnchor constraintEqualToAnchor:self.headerView.trailingAnchor].active = YES;
[self.tableView.topAnchor constraintEqualToAnchor:self.headerView.topAnchor].active = YES;
[self.tableFooterView.trailingAnchor constraintEqualToAnchor:self.headerView.trailingAnchor].active = YES;
}
One can find all details and code snippets here
I have figured out a workaround. wrap your autolayout wrriten xib header view in an empty uiview wrapper, and assign the header view to tableView's tableViewHeader property.
UIView *headerWrapper = [[UIView alloc] init];
AXLHomeDriverHeaderView *headerView = [AXLHomeDriverHeaderView loadViewFromNib];
[headerWrapper addSubview:headerView];
[headerView mas_makeConstraints:^(MASConstraintMaker *make) {
make.edges.equalTo(headerWrapper);
}];
self.tableView.tableHeaderView = headerView;
Here's what works for UITableViewController in ios 12,
Drap a UIView into the TableView above all the prototype cells for header and below all the prototype cells for footer. Setup your header and footer as needed. Set all the required constraints.
Now use the following extension methods
public static class UITableVIewExtensions
{
public static void MakeHeaderAutoDimension(this UITableView tableView)
{
if (tableView.TableHeaderView is UIView headerView) {
var size = headerView.SystemLayoutSizeFittingSize(UIView.UILayoutFittingCompressedSize);
if (headerView.Frame.Size.Height != size.Height) {
var frame = headerView.Frame;
frame.Height = size.Height;
headerView.Frame = frame;
tableView.TableHeaderView = headerView;
tableView.LayoutIfNeeded();
}
}
}
public static void MakeFooterAutoDimension(this UITableView tableView)
{
if (tableView.TableFooterView is UIView footerView) {
var size = footerView.SystemLayoutSizeFittingSize(UIView.UILayoutFittingCompressedSize);
if (footerView.Frame.Size.Height != size.Height) {
var frame = footerView.Frame;
frame.Height = size.Height;
footerView.Frame = frame;
tableView.TableFooterView = footerView;
tableView.LayoutIfNeeded();
}
}
}
}
and call it in ViewDidLayoutSubviews of the subclass of UITableViewController
public override void ViewDidLayoutSubviews()
{
base.ViewDidLayoutSubviews();
TableView.MakeHeaderAutoDimension();
TableView.MakeFooterAutoDimension();
}
I encountered the problem of getting width 375pt, the only way that worked for me is to relayout the tableView to get the correct width. I also preferred AutoLayout over setting Frame size.
Here's the version that works for me:
Xamarin.iOS
public static void AutoLayoutTableHeaderView(this UITableView tableView, UIView header)
{
tableView.TableHeaderView = header;
tableView.SetNeedsLayout();
tableView.LayoutIfNeeded();
header.WidthAnchor.ConstraintEqualTo(tableView.Bounds.Width).Active = true;
tableView.TableHeaderView = header;
}
Swift Version (modified from #Ben Packard answer)
extension UITableView {
//set the tableHeaderView so that the required height can be determined, update the header's frame and set it again
func setAndLayoutTableHeaderView(header: UIView) {
self.tableHeaderView = header
self.setNeedsLayout()
self.layoutIfNeeded()
header.widthAnchor.widthAnchor.constraint(equalTo: self.bounds.width).isActive = true
self.tableHeaderView = header
}
}
I created a subclass of UITableView and used UIStackView for both header and footer, it also enables setting more than one view.
https://github.com/omaralbeik/StackableTableView
After checking other solutions that used to work but no longer work I created a following solution for header view with multiline labels that works fine for me on iOS 12, iOS 13, iOS 14, iOS 15 in all cases also with device rotations. Instead of setting your view directly as a table header view use a wrapper view that automatically updates it heights if the real view changes the height. Add following class to your project:
class TableViewHeaderHelperView: UIView {
private weak var headerView: UIView?
private weak var tableView: UITableView?
private var lastUpdatedHeight: CGFloat = 0
init(headerView: UIView) {
self.headerView = headerView
super.init(frame: .zero)
translatesAutoresizingMaskIntoConstraints = true
addSubview(headerView)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func addToTableView(_ tableView: UITableView) {
self.tableView = tableView
tableView.tableHeaderView = self
headerView?.widthAnchor.constraint(equalTo: tableView.widthAnchor).isActive = true
}
func removeFromTableView() {
self.tableView?.tableHeaderView = nil
self.tableView = nil
}
override func layoutSubviews() {
super.layoutSubviews()
refreshHeaderHeightIfNeeded()
}
func refreshHeaderHeightIfNeeded() {
DispatchQueue.main.async {
let headerViewSize = self.headerView?.bounds.size ?? .zero
if headerViewSize.height != self.lastUpdatedHeight {
self.lastUpdatedHeight = headerViewSize.height
self.frame.size = headerViewSize
self.tableView?.tableHeaderView = self
}
}
}
}
Then you can use this helper class as below:
let headerWrapperView = TableViewHeaderHelperView(headerView: yourRealHeaderView)
headerWrapperView.addToTableView(tableView)
Alternatively also I used a similar approach in my library LSCategories that provides various extensions including an extension for UITableView class which allows to set an autolayout view in a table header view or footer view with a single line of code so you can also try that instead.
Accepted answer is a only useful for tables with a single section. For multi-section UITableView just make sure that your header inherits from UITableViewHeaderFooterView and you will be fine.
As an alternative, just embed your current header in the contentView of a UITableViewHeaderFooterView. Exactly like UITableViewCell works.
Related
I'm adding a subview(NSView), here is my code:
override func viewDidAppear() {
self.view.needsDisplay = true
let newView = NSView()
newView.autoresizesSubviews = true
newView.frame = view.bounds
newView.wantsLayer = true
newView.layer?.backgroundColor = NSColor.green.cgColor
view.addSubview(newView)
}
And it works fine
But when I resize the window the subview is not resizing.
Any of you knows why or how can make the subview resize with superview?
I'll really appreciate your help
You set view.autoresizesSubviews to true, which tells view to resize each of its subviews. But you also have to specify how you want each subview to be resized. You do that by setting the subview's autoresizingMask. Since you want the subview's frame to continue to match the superview's bounds, you want the subview's width and height to be flexible, and you want its X and Y margins to be fixed (at zero). Thus:
override func viewDidAppear() {
self.view.needsDisplay = true
let newView = NSView()
// The following line had no effect on the layout of newView in view,
// so I have commented it out.
// newView.autoresizesSubviews = true
newView.frame = view.bounds
// The following line tells view to resize newView so that newView.frame
// stays equal to view.bounds.
newView.autoresizingMask = [.width, .height]
newView.wantsLayer = true
newView.layer?.backgroundColor = NSColor.green.cgColor
view.addSubview(newView)
}
I found a fix for this issue:
override func viewWillLayout() {
super.viewWillLayout()
newView.frame = view.bounds
}
For UICollectionView's dynamic height cells we use,
if let layout = self.collectionViewLayout as? UICollectionViewFlowLayout {
layout.estimatedItemSize = UICollectionViewFlowLayoutAutomaticSize
}
with the proper constraint of height and width, it works fine with iOS 11.* versions but it breaks and does not make the cells dynamic for iOS 12.0
In my case, I solved this by explicitly adding the following constraints to the cell's contentView.
class Cell: UICollectionViewCell {
// ...
override func awakeFromNib() {
super.awakeFromNib()
// Addresses a separate issue and prevent auto layout warnings due to the temporary width constraint in the xib.
contentView.translatesAutoresizingMaskIntoConstraints = false
// Code below is needed to make the self-sizing cell work when building for iOS 12 from Xcode 10.0:
let leftConstraint = contentView.leftAnchor.constraint(equalTo: leftAnchor)
let rightConstraint = contentView.rightAnchor.constraint(equalTo: rightAnchor)
let topConstraint = contentView.topAnchor.constraint(equalTo: topAnchor)
let bottomConstraint = contentView.bottomAnchor.constraint(equalTo: bottomAnchor)
NSLayoutConstraint.activate([leftConstraint, rightConstraint, topConstraint, bottomConstraint])
}
}
These constraints are already in place inside the xib of the cell, but somehow they aren't enough for iOS 12.
The other threads that suggested calling collectionView.collectionViewLayout.invalidateLayout() in various places didn't help in my situation.
Sample code here: https://github.com/larrylegend/CollectionViewAutoSizingTest
This applies the workaround to code from a tutorial by https://medium.com/#wasinwiwongsak/uicollectionview-with-autosizing-cell-using-autolayout-in-ios-9-10-84ab5cdf35a2:
Based on ale84's answer and because of the fact I needed that iOS 12 fix in multiple places I created a UICollectionViewCell extension which I named UICollectionViewCell+iOS12:
extension UICollectionViewCell {
/// This is a workaround method for self sizing collection view cells which stopped working for iOS 12
func setupSelfSizingForiOS12(contentView: UIView) {
contentView.translatesAutoresizingMaskIntoConstraints = false
let leftConstraint = contentView.leftAnchor.constraint(equalTo: leftAnchor)
let rightConstraint = contentView.rightAnchor.constraint(equalTo: rightAnchor)
let topConstraint = contentView.topAnchor.constraint(equalTo: topAnchor)
let bottomConstraint = contentView.bottomAnchor.constraint(equalTo: bottomAnchor)
NSLayoutConstraint.activate([leftConstraint, rightConstraint, topConstraint, bottomConstraint])
}
}
And then in your collection view cells we do something like this (if your cell is created in IB):
override func awakeFromNib() {
super.awakeFromNib()
if #available(iOS 12, *) { setupSelfSizingForiOS12(contentView: contentView) }
}
Objective C version of above answer:
-(void)awakeFromNib{
[super awakeFromNib];
if (#available(iOS 12.0, *)) {
// Addresses a separate issue and prevent auto layout warnings due to the temporary width constraint in the xib.
self.contentView.translatesAutoresizingMaskIntoConstraints = NO;
// Code below is needed to make the self-sizing cell work when building for iOS 12 from Xcode 10.0:
NSLayoutConstraint *leftConstraint = [self.contentView.leftAnchor constraintEqualToAnchor:self.leftAnchor constant:0];
NSLayoutConstraint *rightConstraint = [self.contentView.rightAnchor constraintEqualToAnchor:self.rightAnchor constant:0];
NSLayoutConstraint *topConstraint = [self.contentView.topAnchor constraintEqualToAnchor:self.topAnchor constant:0];
NSLayoutConstraint *bottomConstraint = [self.contentView.bottomAnchor constraintEqualToAnchor:self.bottomAnchor constant:0];
[NSLayoutConstraint activateConstraints:#[leftConstraint, rightConstraint, topConstraint, bottomConstraint]];
}
}
For Objective-C lovers like me ;) cheers !!!
i have the same problem. using UILabels to size the collectionview. if i run collectionView.collectionViewLayout.invalidateLayout() before reloading the data it does an ok job of sizing the labels.
still not quite right. it's tough for me to figure because i'm running it on the simulator vs. device.
I managed to solve this problem by putting such code into my subclass of UICollectionViewCell
- (UICollectionViewLayoutAttributes *)preferredLayoutAttributesFittingAttributes:(UICollectionViewLayoutAttributes *)layoutAttributes {
[self updateConstraintsIfNeeded];
CGSize size = [self systemLayoutSizeFittingSize:CGSizeMake(1000, 1000)];
CGRect alteredRect = layoutAttributes.frame;
alteredRect.size = size;
layoutAttributes.frame = alteredRect;
return layoutAttributes;
}
and subclassing UICollectionView like this
#interface CustomCollectionView ()
#property (nonatomic) BOOL shouldInvalidateLayout;
#end
#implementation CustomCollectionView
- (void)layoutSubviews {
[super layoutSubviews];
if (self.shouldInvalidateLayout) {
[self.collectionViewLayout invalidateLayout];
self.shouldInvalidateLayout = NO;
}
}
- (void)reloadData {
self.shouldInvalidateLayout = YES;
[super reloadData];
}
#end
Situation: I've got a UITableViewController loading some data asynchronously from a service. During this time I would like to place a full screen (except navigation bar) view over the table view showing my custom indicator and text.
Problem: The problem I'm facing is that when my custom view (it has a red background) is placed over the UITableView the lines of the table view are shown trough my custom view (see image below).
What I tried:
I tried to use insertBelow and above, didn't work. I also tried to do: tableview.Hidden = true, but this also hides the custom view for some reason as seen on image 2.
Image1: For some reason I can see the lines threw my view.
Image 2: Tableview + custom view gone when hidden = true used.
My code:
public override void ViewDidLoad ()
{
base.ViewDidLoad ();
UIView view = new UIView (new RectangleF (0, 0, this.TableView.Frame.Width, this.TableView.Frame.Height));
view.BackgroundColor = UIColor.Red;
this.TableView.AddSubview (view);
TableView.Source = new SessionTableViewSource ();
}
You can use self.navigationController.view as view for adding subview.
The issue is that the View of a UITableViewController is a UITableView, so you cannot add subviews to the controller on top of the table.
I'd recommend switching from a UITableViewController to a simple UIViewController that contains a UITableView. This way the controller main view is a plain UIView that contains a table, and you can add subviews to the main UIView and they will be placed on top of the table view.
You can try to add the view to the window instead of nesting it in the table view like this:
UIWindow* mainWindow = [[UIApplication sharedApplication] keyWindow];
[mainWindow addSubview: overlayview];
UIWindow* window = [[UIApplication sharedApplication].delegate.window;
[window addSubview: your-overlayview];
Swift / Storyboard Solution
Note: The code below assumes one has a custom view (ratingView in my case) that is to be presented over a UITableView.
I've read many answers to this and similar questions on SO. The other answers from these sources worked to varying degrees for me (e.g.,view loaded but not shown or not accessible,...). I am using Swift 2.0+ and I am sharing the complete solution for doing this using a UITableViewController.
Create an outlet to the Navigation Bar and the view, which you want to bring over the tableview.
//MARK:Outlets
#IBOutlet weak var navBar:UINavigationBar!
#IBOutlet var ratingView: MNGStarRating!
In my case I also wanted to animate the view over the tableview so I used a class variable to hold a reference to the inflection point and a point above the scene (off-screen).
var centerYInflection:NSLayoutConstraint!
var aPointAboveScene = -(max(UIScreen.mainScreen().bounds.width,UIScreen.mainScreen().bounds.height) * 2.0)
Then in viewDidLoad I called a function (configureRatingViewAutoLayout) which configures and adds the constraints for the new view to be animated over the tableview.
func configureRatingViewAutoLayout() {
//REQUIRED
self.navBar.superview?.addSubview(self.ratingView)
var newConstraints:[NSLayoutConstraint] = []
newConstraints.append(self.ratingView.leadingAnchor.constraintEqualToAnchor(self.view.leadingAnchor,constant: 10))
newConstraints.append(self.ratingView.trailingAnchor.constraintEqualToAnchor(self.view.trailingAnchor,constant: 10))
newConstraints.append(self.ratingView.centerXAnchor.constraintEqualToAnchor(self.view.centerXAnchor))
//hides the rating view above the scene
self.centerYInflection = self.ratingView.centerYAnchor.constraintEqualToAnchor(self.view.centerYAnchor, constant: self.aPointAboveScene)
//the priority must be set below 1000 if you intend to change it after it has been added to a view
self.centerYInflection.priority = 750
newConstraints.append(self.centerYInflection)
//constraints must be added to the container view of the two items
self.ratingView.superview?.addConstraints(newConstraints)
}
Nota Bene - On a UITableViewController; the self.view is the
self.tableView. They point to the same thing so I guess one could also
use the self.tableView reference above.
Sometime later... In response to a UIControl event I call this method.
#IBAction func toggleRatingView (sender:AnyObject?){
//REQUIRED
self.ratingView.superview?.layoutIfNeeded()
UIView.animateWithDuration(1.0, delay: 0.0, usingSpringWithDamping: 0.37, initialSpringVelocity: 0.99, options: [.CurveEaseOut], animations: { () -> Void in
if CGRectContainsRect(self.view.frame, self.ratingView.frame) {
//in frame ~ animate away
//I play a sound to alert the user something is happening
self.centerYInflection.constant = self.aPointAboveScene
self.centerYInflection.priority = UILayoutPriority(950)
//I disable portions of the UI
self.disableUIElements(nil)
} else {
//out of frame ~ animate in
//I play a different sound here
self.centerYInflection.constant = 0
self.centerYInflection.priority = UILayoutPriority(950)
//I enable the UI fully
self.enableUIElements(nil)
}
//REQUIRED
self.ratingView.superview?.setNeedsLayout()
self.ratingView.superview?.layoutIfNeeded()
}) { (success) -> Void in
//do something else
}
}
These helper methods can be configured to control access to elements in your scene during the presentation of the view.
func disableUIElements(sender:AnyObject?) {
//UI
}
func enableUIElements(sender:AnyObject?) {
//UI
}
Caveats
My view is a custom view in the Storyboard (sitting outside of the
tableview but connected to the TableView Controller). The view has a
required user runtime attribute defined layer.zPosition with a Number value set to 2 (this ensures that it presents in front of the
UITableView).
One could also try playing around with bringSubviewToFront:
and sendSubviewToBack: methods if you don't want to set the zPosition
(I think zPosition is simpler to use)
Try this to hook a button at bottom of the UITableViewController
declare button as a variable:
var submitButton: UIButton!
and in viewDidLoad:
submitButton = UIButton(frame: CGRect(x: 5, y: UIScreen.main.bounds.size.height - 50, width: UIScreen.main.bounds.size.width - 10, height: 50))
submitButton.backgroundColor = UIColor.init(red: 180/255, green: 40/255, blue: 56/255, alpha: 1.0)
submitButton.setTitle("Submit", for: .normal)
submitButton.titleLabel?.font = UIFont(name: "Arial", size: 15)
submitButton.titleLabel?.textColor = .white
submitButton.addTarget(self, action: #selector(submit), for: .touchUpInside)
submitButton.layer.cornerRadius = 5
self.view.addSubview(submitButton)
and implement this method:
override func scrollViewDidScroll(_ scrollView: UIScrollView) {
submitButton.frame = CGRect.init(x: submitButton.frame.origin.x, y: UIScreen.main.bounds.size.height + scrollView.contentOffset.y - 50, width: submitButton.frame.width, height: submitButton.frame.height)
}
This works for me:
if let myTopView = Bundle.main.loadNibNamed("MyTopView", owner: self, options: nil)?.first as? MyTopView {
if let view = UIApplication.shared.keyWindow{
view.addSubview(myView);
myTopView.translatesAutoresizingMaskIntoConstraints = false
myTopView.topAnchor.constraint(equalTo: view.topAnchor ).isActive = true
myTopView.leftAnchor.constraint(equalTo: view.leftAnchor).isActive = true
myTopView.rightAnchor.constraint(equalTo: view.rightAnchor).isActive = true
myTopView.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
}
}
What is the easiest and most elegant way to check if a UIView is visible on the current UIScrollView's contentView? There are two ways to do this, one is involving the contentOffset.y position of the UIScrollView and the other way is to convert the rect area?
If you're trying to work out if a view has been scrolled on screen, try this:
CGRect thePosition = myView.frame;
CGRect container = CGRectMake(scrollView.contentOffset.x, scrollView.contentOffset.y, scrollView.frame.size.width, scrollView.frame.size.height);
if(CGRectIntersectsRect(thePosition, container))
{
// This view has been scrolled on screen
}
Swift 5: in case that you want to trigger an event that checks that the entire UIView is visible in the scroll view:
extension ViewController: UIScrollViewDelegate {
func scrollViewDidScroll(_ scrollView: UIScrollView) {
if scrollView.bounds.contains(targetView.frame) {
// entire UIView is visible in scroll view
}
}
}
Implement scrollViewDidScroll: in your scroll view delegate and calculate manually which views are visible (e.g. by checking if CGRectIntersectsRect(scrollView.bounds, subview.frame) returns true.
updated for swift 3
var rect1: CGRect!
// initialize rect1 to the relevant subview
if rect1.frame.intersects(CGRect(origin: scrollView.contentOffset, size: scrollView.frame.size)) {
// the view is visible
}
I think your ideas are correct. if it was me i would do it as following:
//scrollView is the main scroll view
//mainview is scrollview.superview
//view is the view inside the scroll view
CGRect viewRect = view.frame;
CGRect mainRect = mainView.frame;
if(CGRectIntersectsRect(mainRect, viewRect))
{
//view is visible
}
José's solution didn't quite work for me, it was detecting my view before it came on screen. The following intersects code works perfect in my tableview if José's simpler solution doesn't work for you.
func scrollViewDidScroll(_ scrollView: UIScrollView) {
let viewFrame = scrollView.convert(targetView.bounds, from: targetView)
if viewFrame.intersects(scrollView.bounds) {
// targetView is visible
}
else {
// targetView is not visible
}
}
Solution that takes into account insets
public extension UIScrollView {
/// Returns `adjustedContentInset` on iOS >= 11 and `contentInset` on iOS < 11.
var fullContentInsets: UIEdgeInsets {
if #available(iOS 11.0, *) {
return adjustedContentInset
} else {
return contentInset
}
}
/// Visible content frame. Equal to bounds without insets.
var visibleContentFrame: CGRect {
bounds.inset(by: fullContentInsets)
}
}
if scrollView.visibleContentFrame.contains(view) {
// View is fully visible even if there are overlaying views
}
I have gone through Apple docs about UITableView class and delegate reference but couldn't find the way to set the table header height explicitly.
I set Table cell height using following delegate:
-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
and set section header/footer height using following delegates.
-(CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section
-(CGFloat)tableView:(UITableView *)tableView heightForFooterInSection:(NSInteger)section
Could anyone please help me to set the table header/footer height?
Thanks.
Just set the frame property of the tableHeaderView.
I found a nice hack. Add the below line after modifying the frame propery
self.tableView.tableHeaderView = self.tableView.tableHeaderView;
The trick is (I think) that the UITableView is caching the height (the frame actually) when you assign the view to the tableHeaderView property. The above line just assigns the height again.
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
sizeHeaderToFit()
}
private func sizeHeaderToFit() {
let headerView = tableView.tableHeaderView!
headerView.setNeedsLayout()
headerView.layoutIfNeeded()
let height = headerView.systemLayoutSizeFitting(UILayoutFittingCompressedSize).height
var frame = headerView.frame
frame.size.height = height
headerView.frame = frame
tableView.tableHeaderView = headerView
}
More details can be found here
In case you still need it, have you tried to set the property
self.tableView.tableHeaderView
If you calculate the heigh you need, and set a new view for tableHeaderView:
CGRect frame = self.tableView.tableHeaderView.frame;
frame.size.height = newHeight;
self.tableView.tableHeaderView = [[UIView alloc] initWithFrame:frame];
It should work.
It works with me only if I set the footer/header of the tableview to nil first:
self.footer = self.searchTableView.tableFooterView;
CGRect frame = self.footer.frame;
frame.size.height = 200;
self.footer.frame = frame;
self.searchTableView.tableFooterView = nil;
self.searchTableView.tableFooterView = self.footer;
Make sure that self.footer is a strong reference to prevent the footer view from being deallocated
Swift 4 - you can manage height with HEIGHT_VIEW,Just add this cods, Its working
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
let HEIGHT_VIEW = 60
tableView.tableFooterView?.frame.size = CGSize(width: tblView.frame.width, height: CGFloat(HEIGHT_VIEW))
tableView.tableHeaderView?.frame.size = CGSize(width:tblView.frame.width, height: CGFloat(HEIGHT_VIEW))
}
Just create Footer Wrapper View using constructor UIView(frame:_)
then if you are using xib file for FooterView, create view from xib and add as subView to wrapper view. then assign wrapper to tableView.tableFooterView = fixWrapper .
let fixWrapper = UIView(frame: CGRectMake(0, 0, UIScreen.mainScreen().bounds.width, 54)) // dont remove
let footer = UIView.viewFromNib("YourViewXibFileName") as! YourViewClassName
fixWrapper.addSubview(footer)
tableView.tableFooterView = fixWrapper
tableFootterCostView = footer
It works perfectly for me! the point is to create footer view with constructor (frame:_). Even though you create UIView() and assign frame property it may not work.
If add a view as table header view in IB, set the frame of that view in IB in Tab 5(size inspector)
If you programatically set the tableHeaderView, then just set it inside viewDidLayoutSubviews.
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
setupTableViewHeader()
}
private func setupTableViewHeader() {
// Something you do to set it up programatically...
tableView.tableHeaderView = MyHeaderView.instanceFromNib()
}
If you didn't set it programatically, you need to do similar to what #Kris answered based on this link
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
sizeHeaderToFit()
}
private func sizeHeaderToFit() {
if let headerView = tableView.tableHeaderView {
headerView.setNeedsLayout()
headerView.layoutIfNeeded()
let height = headerView.systemLayoutSizeFitting(UIView.layoutFittingCompressedSize).height
var frame = headerView.frame
frame.size.height = height
headerView.frame = frame
tableView.tableHeaderView = headerView
}
}
#kris answer is helpful for me anyone want it in Objective-C.
Here is the code
-(void)viewDidLayoutSubviews{
[super viewDidLayoutSubviews];
[self sizeHeaderToFit];
}
-(void)sizeHeaderToFit{
UIView *headerView = self.tableView.tableHeaderView;
[headerView setNeedsLayout];
[headerView layoutIfNeeded];
CGFloat height = [headerView systemLayoutSizeFittingSize:UILayoutFittingCompressedSize].height;
CGRect frame = headerView.frame;
frame.size.height = height;
headerView.frame = frame;
self.tableView.tableHeaderView = headerView;
}
If you are using XIB for tableView's main headerView you can set XIB as a freeform set the Height as you want and unclick Autoresizing's top,bottom blocks and upper,lower arrows.Only horizontal pieces will be selected.Vertical will be unselected as I mentioned above.
You can create a UIView with the desired height (the width should be that of the UITableView), and inside it you can place a UIImageView with the picture of the proper dimensions: they won't stretch automatically.
You can also give margin above and below the inner UIImageView, by giving a higher height to the container view.
Additionally, you can assign a Translation transform in order to place the image in the middle of its container header view, for example.
With autolayout you could do something like:
tableView.sectionHeaderHeight = UITableViewAutomaticDimension
tableView.estimatedSectionHeaderHeight = <your-header-height>
or if your headers are of different heights, go ahead and implement:
func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
return <your-header-height>
}
In Xcode 10 you can set header and footer of section hight from "Size Inspector" tab
If you changed height of tableView's headerView, just reset headerView's frame, then, reset headerView of tableView:
self.headerView.frame = newFrame;
self.tableView.tableHeaderView = self.headerView;
func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
}
or you can use like this also
tableView.estimatedSectionHeaderHeight
Use table view default property :
- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section
{
return 35.0;
}
Thanks