How do I set the height of tableHeaderView (UITableView) with autolayout?

I'm been smashing my head against the wall with this for last 3 or 4 hours and I can't seem to figure it out. I have a UIViewController with a full screen UITableView inside of it (there's some other stuff on the screen, which is why I can't use a UITableViewController) and I want to get my tableHeaderView to resize with autolayout. Needless to say, it's not cooperating.

See screenshot below.

在这里输入图像描述

Because the overviewLabel (eg the "List overview information here." text) has dynamic content, I'm using autolayout to resize it and it's superview. I've got everything resizing nicely, except for the tableHeaderView, which is right below Paralax Table View in the hiearchy.

The only way I've found to resize that header view is programatically, with the following code:

CGRect headerFrame = self.headerView.frame;
headerFrame.size.height = headerFrameHeight;
self.headerView.frame = headerFrame;
[self.listTableView setTableHeaderView:self.headerView];

In this case, headerFrameHeight is a manual calculation of the tableViewHeader height as follows (innerHeaderView is the white area, or the second "View", headerView is tableHeaderView):

CGFloat startingY = self.innerHeaderView.frame.origin.y + self.overviewLabel.frame.origin.y;
CGRect overviewSize = [self.overviewLabel.text
                       boundingRectWithSize:CGSizeMake(290.f, CGFLOAT_MAX)
                       options:NSStringDrawingUsesLineFragmentOrigin
                       attributes:@{NSFontAttributeName: self.overviewLabel.font}
                       context:nil];
CGFloat overviewHeight = overviewSize.size.height;
CGFloat overviewPadding = ([self.overviewLabel.text length] > 0) ? 10 : 0; // If there's no overviewText, eliminate the padding in the overall height.
CGFloat headerFrameHeight = ceilf(startingY + overviewHeight + overviewPadding + 21.f + 10.f);

The manual calculation works, but it's clunky and prone to error if things change in the future. What I want to be able to do is have the tableHeaderView auto-resize based on the provided constraints, like you can anywhere else. But for the life of me, I can't figure it out.

There's several posts on SO about this, but none are clear and ended up confusing me more. Here's a few:

  • Auto layout on UITableViewHeader

  • Auto Layout for tableHeaderView

  • Is it possible to use AutoLayout with UITableView's tableHeaderView?

  • table header view height is wrong when using auto layout, IB, and font sizes

  • It doesn't really make sense to change the translatesAutoresizingMaskIntoConstraints property to NO, since that just causes errors for me and doesn't make sense conceptually anyway.

    Any help would really be appreciated!

    EDIT 1: Thanks to TomSwift's suggestion, I was able to figure it out. Instead of manually calculating the height of the overview, I can have it calculated for me as follows and then re-set the tableHeaderView as before.

    [self.headerView setNeedsLayout];
    [self.headerView layoutIfNeeded];
    CGFloat height = [self.innerHeaderView systemLayoutSizeFittingSize:UILayoutFittingCompressedSize].height + self.innerHeaderView.frame.origin.y; // adding the origin because innerHeaderView starts partway down headerView.
    
    CGRect headerFrame = self.headerView.frame;
    headerFrame.size.height = height;
    self.headerView.frame = headerFrame;
    [self.listTableView setTableHeaderView:self.headerView];
    

    Edit 2: As others have noted, the solution posted in Edit 1 doesn't seem to work in viewDidLoad. It does, however, seem to work in viewWillLayoutSubviews. Example code below:

    // Note 1: The variable names below don't match the variables above - this is intended to be a simplified "final" answer.
    // Note 2: _headerView was previously assigned to tableViewHeader (in loadView in my case since I now do everything programatically).
    // Note 3: autoLayout code can be setup programatically in updateViewConstraints.
    - (void)viewWillLayoutSubviews {
        [super viewWillLayoutSubviews];
    
        [_headerWrapper setNeedsLayout];
        [_headerWrapper layoutIfNeeded];
        CGFloat height = [_headerWrapper systemLayoutSizeFittingSize:UILayoutFittingCompressedSize].height;
    
        CGRect headerFrame = _headerWrapper.frame;
        headerFrame.size.height = height;
        _headerWrapper.frame = headerFrame;
        _tableView.tableHeaderView = _headerWrapper;
    }
    

    You need to use the UIView systemLayoutSizeFittingSize: method to obtain the minimum bounding size of your header view.

    I provide further discussion on using this API in this Q/A:

    How to resize superview to fit all subviews with autolayout?


    I really battled with this one and plonking the setup into viewDidLoad didn't work for me since the frame is not set in viewDidLoad, I also ended up with tons of messy warnings where the encapsulated auto layout height of the header was being reduced to 0. I only noticed the issue on iPad when presenting a tableView in a Form presentation.

    What solved the issue for me was setting the tableViewHeader in viewWillLayoutSubviews rather than in viewDidLoad.

    func viewWillLayoutSubviews() {
            super.viewWillLayoutSubviews()
            if tableView.tableViewHeaderView == nil {
                let header: MyHeaderView = MyHeaderView.createHeaderView()
                header.setNeedsUpdateConstraints()
                header.updateConstraintsIfNeeded()
                header.frame = CGRectMake(0, 0, CGRectGetWidth(tableView.bounds), CGFloat.max)
                var newFrame = header.frame
                header.setNeedsLayout()
                header.layoutIfNeeded()
                let newSize = header.systemLayoutSizeFittingSize(UILayoutFittingCompressedSize)
                newFrame.size.height = newSize.height
                header.frame = newFrame
                self.tableView.tableHeaderView = header
            }
        }
    

    I've found an elegant way to way to use auto layout to resize table headers, with and without animation.

    Simply add this to your View Controller.

    func sizeHeaderToFit(tableView: UITableView) {
        if let headerView = tableView.tableHeaderView {
            let height = headerView.systemLayoutSizeFittingSize(UILayoutFittingCompressedSize).height
            var frame = headerView.frame
            frame.size.height = height
            headerView.frame = frame
            tableView.tableHeaderView = headerView
            headerView.setNeedsLayout()
            headerView.layoutIfNeeded()
        }
    }
    

    To resize according to a dynamically changing label:

    @IBAction func addMoreText(sender: AnyObject) {
        self.label.text = self.label.text! + "nThis header can dynamically resize according to its contents."
    }
    
    override func viewDidLayoutSubviews() {
        // viewDidLayoutSubviews is called when labels change.
        super.viewDidLayoutSubviews()
        sizeHeaderToFit(tableView)
    }
    

    To animate a resize according to a changes in a constraint:

    @IBOutlet weak var makeThisTallerHeight: NSLayoutConstraint!
    
    @IBAction func makeThisTaller(sender: AnyObject) {
    
        UIView.animateWithDuration(0.3) {
            self.tableView.beginUpdates()
            self.makeThisTallerHeight.constant += 20
            self.sizeHeaderToFit(self.tableView)
            self.tableView.endUpdates()
        }
    }
    

    See the AutoResizingHeader project to see this in action. https://github.com/p-sun/Swift2-iOS9-UI

    AutoResizingHeader

    链接地址: http://www.djcxy.com/p/28278.html

    上一篇: 如何在使用自动布局时调整CALayer的锚点?

    下一篇: 如何使用自动布局设置tableHeaderView(UITableView)的高度?