Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

UITableView: Set table header view height based on screen size

I have a table view with a table header view created through interface builder inside the same xib. I want to set the height of the header based on the screen size, for example 50% of the screen height.

Here is what I get with a static height on an iPhone 4s:

Tableview header in iPhone 4s

And here is is what I get on an iPhone 6:

Tableview header in iPhone 6

Note that the content of the header is static.

I cannot set constraints to the header using auto layout. I tried to set a height constraint based on the height of the table view but it does not seem to be possible in interface builder. Control-dragging does not work. I cannot drag a line from the header to the table view or even to the header itself.

enter image description here

How can I set the header's height based on the screen size?

like image 585
Alaeddine Avatar asked Jun 25 '15 12:06

Alaeddine


2 Answers

Unfortunately, table header views cannot be sized using auto layout. You can use auto layout for elements inside the header but you have to specify the header's size by explicitly setting its frame. If the header's height is static and known at compile time you can use IB. However, if the height is dynamic or depends on the device (as in your case), you have to set it in code.

A quite flexible solution would be to create a custom subclass of UITableView and adapt the header's frame in the layoutSubviews method. This way the header's size gets automatically adjusted when the table view is resized. You have to be careful, however, to only re-apply the header's frame when a change is actually needed to avoid an infinite loop.

Here's what it would look like in Objective-C:

@interface MyTableView : UITableView
@end

@implementation MyTableView : UITableView

- (void)layoutSubviews {
    [super layoutSubviews];

    if (self.tableHeaderView) {
        UIView *header = self.tableHeaderView;
        CGRect rect = CGRectMake(0, 0, self.bounds.size.width,
                                 self.bounds.size.height / 2);

        // Only adjust frame if needed to avoid infinite loop
        if (!CGRectEqualToRect(self.tableHeaderView.frame, rect)) {
            header.frame = rect;

            // This will apply the new header size and trigger another
            // call of layoutSubviews
            self.tableHeaderView = header;
        }
    }
}

@end

The Swift version looks like this:

class MyTableView: UITableView {

    override func layoutSubviews() {
        super.layoutSubviews()

        if let header = tableHeaderView {
            let rect = CGRectMake(0, 0, bounds.size.width, bounds.size.height / 2)

            // Only adjust frame if needed to avoid infinite loop
            if !CGRectEqualToRect(header.frame, rect) {
                header.frame = rect

                // This will apply the new header size and trigger 
                // another call of layoutSubviews
                tableHeaderView = header
            }
        }
    }

}

Note that the above snippets use the bounds of the table view rather than the screen size to calculate the header size.

Update: Note that sometimes an additional call to layoutIfNeeded is needed after setting the tableHeaderView property. I ran into an issue where section headers were drawn above the header view without calling layoutIfNeeded.

like image 194
hennes Avatar answered Oct 01 '22 18:10

hennes


I have tried the following code and it seems to work on iOS7 and iOS8. It changes the height of the header frame to half the screen height. You might want to subtract the height of the navigation and status bar from the screen height before /2, if the header has to be half the size of the table view area only.

- (void)viewDidLoad
{
    [super viewDidLoad];

    // Your other code

    // Set the table header height
    CGRect headerFrame = self.tableView.tableHeaderView.frame;
    headerFrame.size.height = [[UIScreen mainScreen] bounds].size.height/2;
    self.tableView.tableHeaderView.frame=headerFrame;
}
like image 36
Rory McKinnel Avatar answered Oct 01 '22 17:10

Rory McKinnel