There's this cool feature in the UITableViews in Game Center and the search bars they have at their tops. Unlike apps where the search bar is placed in the table header view (so it counts as a standard table cell), instead, it seems to be bolted to the parent navigation bar above it. So when scrolling the table, the search bar does indeed move, but if you scroll above the boundaries of the table, the search bar never stops touching the navigation bar.
Does anyone know how this might have been done? I was wondering if Apple maybe placed both the search bar and the table in a parent scroll view, but I'm wondering if it may be simpler than that.
You could put the searchBar in the table header and implement the - (void)scrollViewDidScroll:(UIScrollView *)scrollView delegate method for the tableView. Doing something like this should work:
If you used the searchDisplayController, you would access the searchbar using self.searchDisplayController.searchbar.
If your deployment target is iOS 9 and higher then you can use anchors and set UISearchBar and UITableView programmatically:
I assume that you create UISearchBar and UITableView from code, not in storyboard.
There's one more step if you want to fully emulate the search bars in Game Center.
If you start with friedenberg's excellent answer, as well as followben's modification for iOS 6+ mentioned in the comments, you still need to adjust the functionality when the search bar itself is active.
In Game Center, the search bars will scroll with the table as you scroll down, but will remain fixed below the navigation bar when you attempt to scroll up past the boundaries of the table. However, when the search bar is active and search results are being displayed, the search bar no longer scrolls with the table; it remains fixed in place below the navigation bar.
Here's the complete code (for iOS 6+) for implementing this. (If you're targeting iOS 5 or below, you don't need to wrap the UISearchBar in a UIView)
CustomTableViewController.m
Voilà! Now, when the search bar is inactive it will move with the table, and remain fixed when attempting to move beyond the table boundaries. When active, it will remain fixed in place, just like in Game Center.
All of the other answers here provided me with helpful information, but none of them worked using iOS 7.1. Here's a simplified version of what worked for me:
MyViewController.h:
MyViewController.m:
Note: If you're using "pull down to refresh" on your list, you'll need to replace
scrollView.contentInset.top
inscrollViewDidScroll:
with a constant to allow the search bar to scroll over the refresh animation.Bob's answer is reversed: it ought to be MIN(0, scrollView.contentOffset.y).
Also, in order to properly support resizing (which would occur when rotated), the other frame values should be reused.
While other answers seem helpful and partially do the job, it doesn't solve the issue of search bar not receiving the user's touches because it moves outside the bounds of its parent view as you change its frame.
What's worse is that, when you click on the search bar to make it the first responder, it is very likely that the tableView delegate will call
tableView:didSelectRowAtIndexPath:
on cell that is laid out under the search bar.In order to address those issues described above, you need to wrap the search bar in a plain UIView, a view which is capable of processing touches occurred outside of its boundaries. By this way, you can relay those touches to the search bar.
So let's do that first:
Right now, we have a UIView subclass named
SearchBarView
which is capable of receiving touches occurred outside of its boundaries.Secondly, we should put the search bar into that new view while the view controller is loading its view:
At last, we should update the frame of the search bar as user scrolls down the table view so that it will stay fixed at the top:
Here is the result:
--
Important note: If your table view has sections, they will probably shadow your search bar so you need to bring the search bar on top of them every time the table view's
bounds
gets updated.viewDidLayoutSubviews
is a good place to do that:--
Hope this helps. You can download the example project from here.