13

I'm trying to create a tableview programmatically that has a search bar in the tableHeaderView. For some reason the search bar appears on top of the first cell.

I'm using Masonry to build constraints.

Can someone point me to what i'm doing wrong.

- (void)setupViews {

...
    self.tableView = [[UITableView alloc] initWithFrame:CGRectZero];
    [self.view addSubview:self.tableView];

    self.searchBar = [[UISearchBar alloc] initWithFrame:CGRectZero];
    self.tableView.tableHeaderView = self.searchBar;
...
}

- (void)updateViewConstraints {

    [self.searchBar mas_updateConstraints:^(MASConstraintMaker *make) {
        make.width.equalTo(self.view);
        make.height.equalTo(@(44));
    }];

    [self.tableView mas_updateConstraints:^(MASConstraintMaker *make) {
        make.self.top.equalTo(self.view);
        make.self.bottom.equalTo(self.toolbar.mas_top);
        make.width.equalTo(self.view);
    }];
...
}

Problem

You can see here that the header is at the same level as the cells. View hierarchy

Midhun MP
  • 103,496
  • 31
  • 153
  • 200

4 Answers4

11

Thanks for your help, I found a gist on GitHub which talked about changing the size of tableViewHeader using AutoLayout:

https://gist.github.com/andreacremaschi/833829c80367d751cb83

- (void) sizeHeaderToFit {
    UIView *headerView = self.tableHeaderView;

    [headerView setNeedsLayout];
    [headerView layoutIfNeeded];
    CGFloat height = [headerView systemLayoutSizeFittingSize:UILayoutFittingCompressedSize].height;

    headerView.frame = ({
        CGRect headerFrame = headerView.frame;
        headerFrame.size.height = height;
        headerFrame;
    });

    self.tableHeaderView = headerView;
}

If I call this method during updateViewConstraints then it works.

However, I don't fully understand it.

Micah Stairs
  • 235
  • 2
  • 11
  • Please post as a comment to the answer – Christian Garbin Mar 16 '15 at 03:09
  • 1
    self.tableHeaderView = headerView; is the key. If you don't call that, even if the tableHeaderView height changes, the tableview do not reposition the view containing the cells. That leads to cells being displayed beyond the header. – CedricSoubrie Sep 12 '16 at 11:13
11

Using Extension in Swift 3.0

extension UITableView {

    func setTableHeaderView(headerView: UIView?) {
        // prepare the headerView for constraints
        headerView?.translatesAutoresizingMaskIntoConstraints = false

        // set the headerView
        tableHeaderView = headerView

        // check if the passed view is nil
        guard let headerView = headerView else { return }

        // check if the tableHeaderView superview view is nil just to avoid
        // to use the force unwrapping later. In case it fail something really
        // wrong happened
        guard let tableHeaderViewSuperview = tableHeaderView?.superview else {
            assertionFailure("This should not be reached!")
            return
        }

        // force updated layout
        headerView.setNeedsLayout()
        headerView.layoutIfNeeded()

        // set tableHeaderView width
        tableHeaderViewSuperview.addConstraint(headerView.widthAnchor.constraint(equalTo: tableHeaderViewSuperview.widthAnchor, multiplier: 1.0))

        // set tableHeaderView height
        let height = headerView.systemLayoutSizeFitting(UILayoutFittingCompressedSize).height
        tableHeaderViewSuperview.addConstraint(headerView.heightAnchor.constraint(equalToConstant: height))
    }

    func setTableFooterView(footerView: UIView?) {
        // prepare the headerView for constraints
        headerView?.translatesAutoresizingMaskIntoConstraints = false

        // set the footerView
        tableFooterView = footerView

        // check if the passed view is nil
        guard let footerView = footerView else { return }

        // check if the tableFooterView superview view is nil just to avoid
        // to use the force unwrapping later. In case it fail something really
        // wrong happened
        guard let tableFooterViewSuperview = tableFooterView?.superview else {
            assertionFailure("This should not be reached!")
            return
        }

        // force updated layout
        footerView.setNeedsLayout()
        footerView.layoutIfNeeded()

        // set tableFooterView width
        tableFooterViewSuperview.addConstraint(footerView.widthAnchor.constraint(equalTo: tableFooterViewSuperview.widthAnchor, multiplier: 1.0))

        // set tableFooterView height
        let height = footerView.systemLayoutSizeFitting(UILayoutFittingCompressedSize).height
        tableFooterViewSuperview.addConstraint(footerView.heightAnchor.constraint(equalToConstant: height))
    }
}
Carmelo Gallo
  • 273
  • 4
  • 12
0

Here issue regarding space between table view Header and table view Cell. You can handle using Attribute Inspector. Please review that. - select UITableView - Under attribute inspector -> Scroll view size -> Content insets, set Top = 44 (or whichever is your nav bar height).

Or you can Handle it programmatically.

  - (void)viewDidLoad {
       UIEdgeInsets inset = UIEdgeInsetsMake(20, 0, 0, 0);
       self.tableView.contentInset = inset;
   }
Andrii Chernenko
  • 9,873
  • 7
  • 71
  • 89
Renish Dadhaniya
  • 10,642
  • 2
  • 31
  • 56
0

I think the problem is because you are using autolayout and setting frames to views manually, replace this code:

self.tableView = [[UITableView alloc] initWithFrame:CGRectZero];
[self.view addSubview:self.tableView];

self.searchBar = [[UISearchBar alloc] initWithFrame:CGRectZero];
self.tableView.tableHeaderView = self.searchBar;

with something like this:

self.tableView = [UITableView new];
self.tableView.translatesAutoresizingMasksIntoConstraints = NO;
[self.view addSubview:self.tableView];

self.searchBar = [UISearchBar new];
self.searchBar.translatesAutoresizingMasksIntoConstraints = NO;
self.tableView.tableHeaderView = self.searchBar;

It may not work, because maybe searchBar's frame needs to be set manually without constraints.

hsafarya
  • 1,043
  • 1
  • 10
  • 21