4

Has anyone been successful implementing a UIViewController that contais both a UISearchController searchBar and a UItableView while laying everything out using Auto Layout?

I'm trying to achieve something similar to what 1Password does on the iPhone: a fixed searchBar on top of a tableView (not part of its tableHeaderView). When the UISearchController that owns the searchBar gets activated, its searchBar animates to show the scope buttons and thus the tableView moves down a bit.

I have got the basics of this layout working correctly with this class:

//
//  ViewController.m
//  UISearchControllerIssues
//
//  Created by Aloha Silver on 05/02/16.
//  Copyright © 2016 ABC. All rights reserved.
//

#import "ViewController.h"

@interface ViewController () <UISearchResultsUpdating, UITableViewDataSource, UITableViewDelegate>

@property (nonatomic, strong) UISearchController *searchController;

@property (nonatomic, strong) UITableView *tableView;

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];

    [self setupTableView];
    [self setupSearchInterface];
    [self setupConstraints];

    self.edgesForExtendedLayout = UIRectEdgeNone;

    self.extendedLayoutIncludesOpaqueBars = YES;
}

- (void)viewDidAppear:(BOOL)animated {
    [super viewDidAppear:animated];

    [self.searchController.searchBar sizeToFit];
}

- (void)setupTableView {
    self.tableView = [[UITableView alloc] initWithFrame:CGRectZero style:UITableViewStyleGrouped];
    self.tableView.dataSource = self;
    self.tableView.delegate = self;
    self.tableView.translatesAutoresizingMaskIntoConstraints = NO;

    [self.view addSubview:self.tableView];
}

- (void)setupSearchInterface {
    self.definesPresentationContext = YES;

    self.searchController = [[UISearchController alloc] initWithSearchResultsController:nil];
    self.searchController.dimsBackgroundDuringPresentation = NO;
    self.searchController.hidesNavigationBarDuringPresentation = NO;
    self.searchController.searchBar.scopeButtonTitles = @[@"One", @"Two"];
    self.searchController.searchBar.translatesAutoresizingMaskIntoConstraints = NO;
    self.searchController.searchResultsUpdater = self;

    [self.view addSubview:self.searchController.searchBar];
}

- (void)setupConstraints {
    NSDictionary *layoutViews = @{@"searchBar": self.searchController.searchBar, @"tableView": self.tableView, @"topLayoutGuide": self.topLayoutGuide};

    [self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[searchBar]|" options:0 metrics:nil views:layoutViews]];
    [self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[tableView]|" options:0 metrics:nil views:layoutViews]];
    [self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[searchBar(44)][tableView]|" options:0 metrics:nil views:layoutViews]];
}

- (void)updateSearchResultsForSearchController:(UISearchController *)searchController {
    NSLog(@"Update should happen here");
}

#pragma mark - UITableViewDataSource

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
    return 1;
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    return 100;
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    static NSString *CellID = @"CellID";

    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellID];

    if (!cell) {
        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellID];
    }

    cell.textLabel.text = [NSString stringWithFormat:@"Cell number %ld", (long)indexPath.row];

    return cell;
}

@end

It is embedded in a UINavigationController instance and initially runs as expect, like the following screenshots show:

Initial Layout Fixed SearchBar

Trouble arises when the searchBar gets activated. It seems to disappear from screen, but after carefully inspecting the view, we determine that it is actually onscreen, but with a width of zero. Here's a picture showing what is presented at this time:

UISearchBar Gone

I'm not that experienced with Auto Layout, so I'm left thinking there must be something wrong with my constraints, although I don't mess with them when activating the UISearchController.

Is there any way of making this work?

Aloha Silver
  • 1,394
  • 17
  • 35
  • Did you search for "UISearchBar disappears" here on SO? This question has been asked a lot. One of those answers might fit your situation. – Dave Batton Feb 05 '16 at 17:59
  • I have, and unfortunately haven’t found anything that quite describes what I’m experiencing. http://stackoverflow.com/q/30458336/382834 is close enough, but there’s no Auto Layout involved (I’ve managed to make it work by setting frames manually, but I’m really trying to understand Auto Layout a but better). – Aloha Silver Feb 05 '16 at 18:50

1 Answers1

11

The UISearchController moves the search bar around when it's tapped, so it doesn't always play well with your constraints.

Instead of setting your constraints directly on the search bar, add an empty placeholder view that will hold your search bar and then place the search bar in it procedurally in viewDidLoad(). Set your constraints on this placeholder instead. Just match the search bar's frame to the placeholder's bounds and leave translatesAutoresizingMaskIntoConstraints set to true.

Sorry, I'm not sure how this placeholder view will handle the size change with the scope buttons. Hopefully you can figure out how to get that part working with auto layout after the change.

Dave Batton
  • 8,795
  • 1
  • 46
  • 50
  • **Set your constraints on this placeholder instead.** That's what I was missing! Thanks I lot, Dave. I had experimented with using placeholder views before, but I just didn't get the constraints right. This did it! FYI, with this approach the `tableView` automatically goes down, animating alongside the appearance of the scope buttons (its scroll indicator insets don't get updated, though - but that's another issue). – Aloha Silver Feb 06 '16 at 04:19