3
votes

I'm trying to build a ViewController subclass that leverages a UISearchController to display results. The view has an input field that I want to animate up to the top of the screen so the user has a maximum amount of room to view the results.

I built out a proof of concept using a UIView that wraps a Search Bar drop-in component and set their top, leading, trailing, and bottom constraints equal in a Storyboard. The following code is responsible for animating the different movements:

- (void)keyboardWillAppear:(NSNotification*)notification {
    self.labelToSearchBarSpaceConstraint.active = NO;
    self.searchBarAtTopConstraint.active = YES;
    self.searchBarLeadingSpaceConstraint.constant = 0;
    self.searchBarTrailingSpaceConstraint.constant = 0;

    [UIView animateWithDuration:[notification.userInfo[UIKeyboardAnimationDurationUserInfoKey] floatValue] animations:^{
        [self.view setNeedsLayout];
        [self.view layoutIfNeeded];
    }];
}

- (void)keyboardWillDisappear:(NSNotification*)notification {
    self.searchBarAtTopConstraint.active = NO;
    self.labelToSearchBarSpaceConstraint.active = YES;
    self.searchBarLeadingSpaceConstraint.constant = 20;
    self.searchBarTrailingSpaceConstraint.constant = 20;

    [UIView animateWithDuration: [notification.userInfo[UIKeyboardAnimationDurationUserInfoKey] floatValue] animations:^{
        [self.view setNeedsLayout];
        [self.view layoutIfNeeded];
    }];
}

I then tried switching over to a programatically created UISearchController and UISearchBar. The following code sets up what I thought was the equivalent relationship in code instead of in the Storyboard:

self.definesPresentationContext = YES;

self.searchController = [[UISearchController alloc] initWithSearchResultsController:nil];
UISearchBar* searchBar = self.searchController.searchBar;
searchBar.translatesAutoresizingMaskIntoConstraints = NO;
[self.searchBarView addSubview:searchBar];

[NSLayoutConstraint constraintWithItem:searchBar attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:self.searchBarView attribute:NSLayoutAttributeTop multiplier:1.0 constant:0.0].active = YES;
[NSLayoutConstraint constraintWithItem:searchBar attribute:NSLayoutAttributeLeading relatedBy:NSLayoutRelationEqual toItem:self.searchBarView attribute:NSLayoutAttributeLeading multiplier:1.0 constant:0.0].active = YES;
[NSLayoutConstraint constraintWithItem:searchBar attribute:NSLayoutAttributeTrailing relatedBy:NSLayoutRelationEqual toItem:self.searchBarView attribute:NSLayoutAttributeTrailing multiplier:1.0 constant:0.0].active = YES;
[NSLayoutConstraint constraintWithItem:searchBar attribute:NSLayoutAttributeBottom relatedBy:NSLayoutRelationEqual toItem:self.searchBarView attribute:NSLayoutAttributeBottom multiplier:1.0 constant:0.0].active = YES;

However, when done this way the UISearchBar object does not obey the constraints. When the container is animated up, the search bar flies off the page. Why is this happening?

Minor Update: I noticed in the documentation for NSLayoutConstraint to set its active property to YES instead of explicitly adding it, but the behavior is exactly the same. Updating code to match.

1

1 Answers

2
votes

I had a similar issue when adding UISearchController's searchBar to a UITableView header view.

When animating constraints of the containerView holding the UITableView after the user selected the searchBar, it would not follow the containerView.

In my case it would appear that after the delegate didPresentSearchController:(UISearchController *)searchController is called, the searchBar had a superview of UISearchBarContainerView instead of the UITableView header view.

Re-adding it back to the original header view before animation helped me.

-(void)didPresentSearchController:(UISearchController *)searchController {

    NSLog(@"did present search controller");

    self.tableView.tableHeaderView = self.searchController.searchBar;

    ...animation...

}