krisk
krisk

Reputation: 7117

Sticky search bar and section header behavior similar to native Contacts app

I'd like to replicate the sticky behavior of the search bar in the iPhone Contacts app.

Normal stage

When the user scrolls the view down, the search bar also comes down along with the view:

User scrolls the view down

If the user scrolls up, the table scrolls accordingly, with the following two behaviors:

(1) the search bar remains fixed at the top, and
(2) the subsequent section header stops appropriately below the search bar:

User scrolls up

When the next section header comes, the previous header disappears below the search bar:

User scroll up

Note: the section index control (a-z on the right hand side) appears on top of the search bar as well. Ergo, fiddling with the contentInset will push the section index control down along with it.

I've created a custom UIViewController, added a UITableView, set its contentInset to the height of the search bar. I created a UIView, added the search bar as its subview, and then added the UIView to the UITableView. However, as noted above, when the user is scrolling, the section headers still stick at the y-position zero, and not at the header height. Additionally, the section header index control position is adversely affected.

I'd appreciate a solution to this problem.

Upvotes: 9

Views: 8819

Answers (2)

Fabian Kreiser
Fabian Kreiser

Reputation: 8337

It has been quite some work to get all things right, but I just had to prove that it's possible to recreate that behavior, at least almost.
Check out this GitHub project I've created: https://github.com/fabiankr/TableViewSearchBar

Actually, it's not even that complicated:
1) Add the search bar directly to the table view and set the tableView's contentInset
2) In -scrollViewDidScroll: adjust the search bar's frame

There are some caveats you have to take care of, though:
1) When scrolling the table view to the top, the section headers shortly appear above the search bar. In order to solve it, set the search bar's zPosition when scrolling to the top
2) Your content controller needs to be a subclass of UIViewController instead of UITableViewController, because UISearchDisplayController adds the dimming view to the controller's view. Because table view controllers' viewis a table view, the dimming view would be at the wrong position when the table view is scrolled.

One thing that isn't possible using public API only is to make the section index control on the right of the table overlap the search bar. It's only a minor thing and even without it the behavior is very similar to the one found in the Contacts app.

In order to achieve the exact same behavior, you'll have to use private API. There's a method on UITableView called _setPinsTableHeaderView: that needs to be used. The sample project contains implementations for both: 1) public API only and 2) private API to get the section index control overlap the search bar.
Reminder: You shouldn't use private API when you plan to submit the app to the App Store!

Upvotes: 15

Adam Levy
Adam Levy

Reputation: 97

The way I achieved this behavior was by detaching my floating cell form the UITableView and making it a subview of the UITableView and animated the floating cell in scrollViewDidScroll. And just so the UITableView scrolls down far enough to reveal the floating cell I also stuck an invisible cell in the tableview which gets covered by the floating cell when scrollViewDidScroll is called.

- (void)viewDidLoad {
   // add floating cell to tableview
   FloatingCell *cell = [[FloatingCell alloc] init];
   [self.tableView addSubview:cell];
   self.floatingCell = cell; 
}
// overwrite scrollViewDidScroll
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
    CGRect floatingCellFrame = floatingCell.frame;
    CGFloat floatingCellHeight = floatingCellFrame.size.height;

    // when contentOffset is is more then cellHeight scroll floating cell
    if (scrollView.contentOffset.y > floatingCellHeight) {
       floatingCellFrame.origin.y = -scrollView.contentOffset.y + floatingCellHeight; 

    // when contentOffset is less then cellHeight stick it to the top of UITableView
    else if (scrollView.contentOffset.y < floatingCellHeight)
       floatingCellFrame.origin.y = 0;

   floatingCell.frame = floatingCellFrame;
}

You might have a to add a couple corner case conditions when scrolling so the floating cell doesn't appear to jump but this should get ya started. Good luck!

Upvotes: 0

Related Questions