How to resize a tableHeaderView of a UITableView?

Viewed 94101

I'm having trouble resizing a tableHeaderView. It simple doesn't work.

1) Create a UITableView and UIView (100 x 320 px);

2) Set the UIView as tableHeaderView of the UITableView;

3) Build and Go. Everything is ok.

Now, I want to resizing the tableHeaderView, so I add this code in viewDidLoad:

self.tableView.autoresizesSubviews = YES;

self.tableView.tableHeaderView = myHeaderView;
self.tableView.tableFooterView = myFooterView;

CGRect newFrame = self.tableView.tableHeaderView.frame;
newFrame.size.height = newFrame.size.height + 100;
self.tableView.tableHeaderView.frame = newFrame;

The height of the tableHeaderView should appear with 200, but appears with 100.

If I write:

self.tableView.autoresizesSubviews = YES;


CGRect newFrame = myHeaderView.frame;
newFrame.size.height = newFrame.size.height + 100;
myHeaderView.frame = newFrame;


self.tableView.tableHeaderView = myHeaderView;
self.tableView.tableFooterView = myFooterView;

Then it starts with 200 of height, as I want. But I want to be able to modify it in runtime.

I've also tried this, without success:

self.tableView.autoresizesSubviews = YES;

self.tableView.tableHeaderView = myHeaderView;
self.tableView.tableFooterView = myFooterView;

CGRect newFrame = self.tableView.tableHeaderView.frame;
newFrame.size.height = newFrame.size.height + 100;
self.tableView.tableHeaderView.frame = newFrame;

[self.tableView.tableHeaderView setNeedsLayout];
[self.tableView.tableHeaderView setNeedsDisplay];
[self.tableView setNeedsLayout];
[self.tableView setNeedsDisplay];

The point here is: How do we resize a tableHeaderView in runtime ???

Have anyone able to do this?

Thanks

iMe

19 Answers

FYI: I've gotten this to work by modifying the tableHeaderView and re-setting it. In this case, i'm adjusting the size of the tableHeaderView when the UIWebView subview has finished loading.

[webView sizeToFit];
CGRect newFrame = headerView.frame;
newFrame.size.height = newFrame.size.height + webView.frame.size.height;
headerView.frame = newFrame;
[self.tableView setTableHeaderView:headerView];

This answer is old and apparently doesn't work on iOS 7 and above.

I ran into the same problem, and I also wanted the changes to animate, so I made a subclass of UIView for my header view and added these methods:

- (void)adjustTableHeaderHeight:(NSUInteger)newHeight{
    NSUInteger oldHeight = self.frame.size.height;
    NSInteger originChange = oldHeight - newHeight;

    [UIView beginAnimations:nil context:nil];

    [UIView setAnimationDuration:1.0f];
    [UIView setAnimationDelegate:self];
    [UIView setAnimationDidStopSelector:@selector(animationDidStop:finished:context:)];

    self.frame = CGRectMake(self.frame.origin.x, 
                        self.frame.origin.y, 
                        self.frame.size.width, 
                        newHeight);

    for (UIView *view in [(UITableView *)self.superview subviews]) {
        if ([view isKindOfClass:[self class]]) {
            continue;
        }
        view.frame = CGRectMake(view.frame.origin.x, 
                            view.frame.origin.y - originChange, 
                            view.frame.size.width, 
                            view.frame.size.height);
    }

    [UIView commitAnimations];
}

- (void)animationDidStop:(NSString *)animationID finished:(NSNumber *)finished context:(void *)context{
    [(UITableView *)self.superview setTableHeaderView:self];
}

This essentially animates all the subviews of the UITableView that aren't the same class type as the calling class. At the end of the animation, it calls setTableHeaderView on the superview (the UITableView) – without this the UITableView contents will jump back the next time the user scrolls. The only limitation I've found on this so far is if the user attempts to scroll the UITableView while the animation is taking place, the scrolling will animate as if the header view hasn't been resized (not a big deal if the animation is quick).

I think it should work if you just set the height of myHeaderView like so:

CGRect newFrame = myHeaderView.frame;
newFrame.size.height = newFrame.size.height + 100;
myHeaderView.frame = newFrame;

self.tableView.tableHeaderView = myHeaderView;

This is only for when you use auto-layout and set translatesAutoresizingMaskIntoConstraints = false to a custom header view.

The best and the simplest way is to override intrinsicContentSize. Internally UITableView uses intrinsicContentSize to decide its header/footer size. Once you have override intrinsicContentSize in your custom view, What you need to do is as below

  1. configure the custom header/footer view's layout(subviews)
  2. invoke invalidateIntrinsicContentSize()
  3. invoke tableView.setNeedsLayout() and tableView.layoutIfNeeded()

Then the UITableView's header/footer will be updated as you want. No need to set the view nil or reset.

One thing really interesting for the UITableView.tableHeaderView or .tableFooterView is that UIStackView loose its ability to manage its arrangedSubviews. If you want to use UIStackView as a tableHeaderView or tableFooterView, you have to embed the stackView in a UIView and override UIView's intrinsicContentSize.

For swift 5 Tested code

override func viewDidLayoutSubviews() {
      super.viewDidLayoutSubviews()

        guard let headerView = self.tblProfile.tableHeaderView else {
            return
        }

        let size = headerView.systemLayoutSizeFitting(UIView.layoutFittingCompressedSize)

        if headerView.frame.size.height != size.height {
            headerView.frame.size.height = size.height
            self.tblProfile.tableHeaderView = headerView
            self.tblProfile.layoutIfNeeded()
        }
    }

Note : You need to give all subview's constraints form top, bottom, leading, trailing. So it will get whole required size.

Reference taken from : https://useyourloaf.com/blog/variable-height-table-view-header/

If your tableHeaderView is a content adjustable webView,you can try:

[self.webView.scrollView addObserver:self forKeyPath:@"contentSize" options:NSKeyValueObservingOptionNew context:nil];

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context {
    self.webView.height = self.webView.scrollView.contentSize.height;
    self.tableView.tableHeaderView = self.webView;
}

I tested it on iOS9 and iOS11,worked well.

Did you try [self.tableView reloadData] after changing the height?

Related