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

后端 未结 12 1170
后悔当初
后悔当初 2020-11-27 10:23

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 insi

相关标签:
12条回答
  • 2020-11-27 11:08

    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
            }
        }
    
    0 讨论(0)
  • 2020-11-27 11:10

    This worked for me on ios10 and Xcode 8

    func layoutTableHeaderView() {
    
        guard let headerView = tableView.tableHeaderView else { return }
        headerView.translatesAutoresizingMaskIntoConstraints = false
    
        let headerWidth = headerView.bounds.size.width;
        let temporaryWidthConstraints = NSLayoutConstraint.constraintsWithVisualFormat("[headerView(width)]", options: NSLayoutFormatOptions(rawValue: UInt(0)), metrics: ["width": headerWidth], views: ["headerView": headerView])
    
        headerView.addConstraints(temporaryWidthConstraints)
    
        headerView.setNeedsLayout()
        headerView.layoutIfNeeded()
    
        let headerSize = headerView.systemLayoutSizeFittingSize(UILayoutFittingCompressedSize)
        let height = headerSize.height
        var frame = headerView.frame
    
        frame.size.height = height
        headerView.frame = frame
    
        self.tableView.tableHeaderView = headerView
    
        headerView.removeConstraints(temporaryWidthConstraints)
        headerView.translatesAutoresizingMaskIntoConstraints = true
    
    }
    
    0 讨论(0)
  • 2020-11-27 11:10

    It is quite possible to use generic AutoLayout-based UIView with any AL inner subview structure as a tableHeaderView.

    The only thing one needs is to set a simple tableFooterView before!

    Let self.headerView is some constraint-based UIView.

    - (void)viewDidLoad {
    
        ........................
    
        self.tableView.tableFooterView = [UIView new];
    
        [self.headerView layoutIfNeeded]; // to set initial size
    
        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;
    
        // and the key constraint
        [self.tableFooterView.trailingAnchor constraintEqualToAnchor:self.headerView.trailingAnchor].active = YES;
    }
    

    If self.headerView changes height under UI rotation one have to implement

    - (void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id<UIViewControllerTransitionCoordinator>)coordinator {
        [super viewWillTransitionToSize:size withTransitionCoordinator:coordinator];
    
        [coordinator animateAlongsideTransition: ^(id<UIViewControllerTransitionCoordinatorContext> context) {
            // needed to resize header height
            self.tableView.tableHeaderView = self.headerView;
        } completion: NULL];
    }
    

    One can use ObjC category for this purpose

    @interface UITableView (AMHeaderView)
    - (void)am_insertHeaderView:(UIView *)headerView;
    @end
    
    @implementation UITableView (AMHeaderView)
    
    - (void)am_insertHeaderView:(UIView *)headerView {
    
        NSAssert(self.tableFooterView, @"Need to define tableFooterView first!");
    
        [headerView layoutIfNeeded];
    
        self.tableHeaderView = headerView;
    
        [self.leadingAnchor constraintEqualToAnchor:headerView.leadingAnchor].active = YES;
        [self.trailingAnchor constraintEqualToAnchor:headerView.trailingAnchor].active = YES;
        [self.topAnchor constraintEqualToAnchor:headerView.topAnchor].active = YES;
    
        [self.tableFooterView.trailingAnchor constraintEqualToAnchor:headerView.trailingAnchor].active = YES;
    }
    @end
    

    And also Swift extension

    extension UITableView {
    
        func am_insertHeaderView2(_ headerView: UIView) {
    
            assert(tableFooterView != nil, "Need to define tableFooterView first!")
    
            headerView.layoutIfNeeded()
    
            tableHeaderView = headerView
    
            leadingAnchor.constraint(equalTo: headerView.leadingAnchor).isActive = true
            trailingAnchor.constraint(equalTo: headerView.trailingAnchor).isActive = true
            topAnchor.constraint(equalTo: headerView.topAnchor).isActive = true
    
            tableFooterView?.trailingAnchor.constraint(equalTo: headerView.trailingAnchor).isActive = true
        }
    }
    
    0 讨论(0)
  • 2020-11-27 11:11

    Your solution using systemLayoutSizeFittingSize: works if the header view is just updated once on each view appearance. In my case, the header view updated multiple times to reflect status changes. But systemLayoutSizeFittingSize: always reported the same size. That is, the size corresponding to the first update.

    To get systemLayoutSizeFittingSize: to return the correct size after each update I had to first remove the table header view before updating it and re-adding it:

    self.listTableView.tableHeaderView = nil;
    [self.headerView removeFromSuperview];
    
    0 讨论(0)
  • 2020-11-27 11:11

    Copied from this post

    override func viewDidLayoutSubviews() {
        super.viewDidLayoutSubviews()
        
        if let headerView = tableView.tableHeaderView {
    
            let height = headerView.systemLayoutSizeFitting(UIView.layoutFittingCompressedSize).height
            var headerFrame = headerView.frame
            
            //Comparison necessary to avoid infinite loop
            if height != headerFrame.size.height {
                headerFrame.size.height = height
                headerView.frame = headerFrame
                tableView.tableHeaderView = headerView
            }
        }
    }
    
    0 讨论(0)
  • 2020-11-27 11:12

    This solution works perfectly for me:

    https://spin.atomicobject.com/2017/08/11/swift-extending-uitableviewcontroller/

    It extends the UITableViewController. But if you are just using a UITableView, it will still work, just extend the UITableView instead of the UITableViewController. Call the methods sizeHeaderToFit() or sizeFooterToFit() whenever there is an event that changes the tableViewHeader height.

    0 讨论(0)
提交回复
热议问题