Search Bar Guide

Overview

Providing a way for users to search through a collection of items is a
fairly common task in iOS projects. A standard interface for
implementing search behaviors is the search bar.

There are a few common ways to work with Search Bars:

Directly using a UISearchBar. This is the most
bare bones way to use UISearchBars. This can be extremely flexible if
you want to design and program your own search interface, however does
not provide as many built-in features as the other methods.

Using a UISearchDisplayController to
help manage a search interface. The UISearchDisplayController
allows you to present a standard search interface with built-in
animations. This method forces you to display search results in a table
view. - DEPRECATED

Using a UISearchController to help manage a
search interface. The UISearchController is a newer controller
(available only in iOS 8+) that helps you present a search interface
using any kind of view to display the search results.

This guide covers the very basics of working with each of these classes.
None of these classes actually implements the "searching" behavior of
finding items that match a given query string, since determining which
objects match will vary with the domain specific use case (e.g. when
searching for "people" you might want to match on just their names,
whereas you may want a full-text pre-indexed search when searching
through e-mails). You'll have to implement any search/filtering
behavior yourself.

Working with UISearchBars directly

At its core, a search bar is nothing more than a glorified text field
packaged with a scope control and some animations
and a couple of buttons. Each search bar has a delegate that gives you
an opportunity to respond to user actions. The most important delegate
methods are:

textDidChange - most of the time you'll respond to
this event by updating the displayed set of search results as the user
is typing out a query

searchBarSearchButtonClicked - in some cases
if the search operation is slow (e.g. requires making a slow API call)
you'll want to wait until the user taps the search button before
updating the search results.

Example searching a table

We start out with a single view application with a basic
UITableView. You can add a
UISearchBar as you would with any other control by dragging one to
your view controller in interface builder or by programmatically adding
it.

The delegate property of search bar must be set to an object that
implements UISearchBarDelegate. Typically you make your view controller
implement UISearchBarDelegate and set searchBar.delegate = self
in viewDidLoad method.

The code to implement the search behavior is as follows. We maintain an
additional array filteredData to represent rows of data that match our
search text. When the search text changes we update filteredData and
reload our table. Notice that we use filteredData as the backing array
for the table view's data source. The original data array is our source
of truth since filteredData will be changing constantly.

classViewController:UIViewController,UITableViewDataSource,UISearchBarDelegate{@IBOutletweakvartableView:UITableView!@IBOutletweakvarsearchBar:UISearchBar!letdata=["New York, NY","Los Angeles, CA","Chicago, IL","Houston, TX","Philadelphia, PA","Phoenix, AZ","San Diego, CA","San Antonio, TX","Dallas, TX","Detroit, MI","San Jose, CA","Indianapolis, IN","Jacksonville, FL","San Francisco, CA","Columbus, OH","Austin, TX","Memphis, TN","Baltimore, MD","Charlotte, ND","Fort Worth, TX"]varfilteredData:[String]!overridefuncviewDidLoad(){super.viewDidLoad()tableView.dataSource=selfsearchBar.delegate=selffilteredData=data}functableView(_tableView:UITableView,cellForRowAtindexPath:IndexPath)->UITableViewCell{letcell=tableView.dequeueReusableCell(withIdentifier:"TableCell",for:indexPath)asUITableViewCellcell.textLabel?.text=filteredData[indexPath.row]returncell}functableView(_tableView:UITableView,numberOfRowsInSectionsection:Int)->Int{returnfilteredData.count}// This method updates filteredData based on the text in the Search BoxfuncsearchBar(_searchBar:UISearchBar,textDidChangesearchText:String){// When there is no text, filteredData is the same as the original data// When user has entered text into the search box// Use the filter method to iterate over all items in the data array// For each item, return true if the item should be included and false if the// item should NOT be includedfilteredData=searchText.isEmpty?data:data.filter{(item:String)->Boolin// If dataItem matches the searchText, return true to include itreturnitem.range(of:searchText,options:.caseInsensitive,range:nil,locale:nil)!=nil}tableView.reloadData()}}

Here's what this looks like when running. Notice that the search
results are displayed in the same table, and there is no presentation of
a separate search interface.

Example searching a collection view

Since the UISearchBar is quite simple, it can be combined with any
abitrary view to build your own search interface. Here's what it might
look like paired with a collection view.

The code for this is essentially the same as in the case with table views.

Cancelling out of Search and hiding keyboard

Once user taps on search bar, the keyboard will appear, and you will notice
that it won't go away when you tap on X.
You can show Cancel button when user taps on search bar, and when user taps
on Cancel, hide the keyboard.

There is a nifty searchBarTextDidBeginEditing method for UISearchBarDelegate
that gets called when user starts editing search text. You can show Cancel
button in that method:

When user taps on cancel button, delegate's searchBarCancelButtonClicked
method gets called. At this point, you can hide the Cancel button,
clear existing text in search bar and hide the keyboard like this:

Using UISearchControllers (iOS 8+)

A newer way to manage the presentation of a search interface (only
available in iOS 8 and above) is via the UISearchController. This
controller handles some of the logic and animation of presenting a
separate search interface for you while still allowing you to specify
how your search results are displayed.

Example searching a table

There is currently no built-in object in the Interface Builder Object
Library for a UISearchController. The easiest way to create one is to
do it programatically. This also creates a UISearchBar and sets the
search controller's
searchBar
property to it. You can add this search bar to your view hierarchy
programatically.

You don't need to implement the
UISearchControllerDelegate
unless you need to hook into the events around the presentation of the
search interface.

Putting it all together the code looks like this. Notice that we have
to read the search text from the search bar in
updateSearchResultsForSearchController. One other thing to note is
that we set this view controller's definesPresentationContext property
to true. This means that the search controller should use this view
controller's frame (as oppposed to the root view controller) when
presenting the search interface. In this case it means that the search
interface will expand above the carrier bar.

classViewController:UIViewController,UITableViewDataSource,UISearchResultsUpdating{@IBOutletweakvartableView:UITableView!letdata=["New York, NY","Los Angeles, CA","Chicago, IL","Houston, TX","Philadelphia, PA","Phoenix, AZ","San Diego, CA","San Antonio, TX","Dallas, TX","Detroit, MI","San Jose, CA","Indianapolis, IN","Jacksonville, FL","San Francisco, CA","Columbus, OH","Austin, TX","Memphis, TN","Baltimore, MD","Charlotte, ND","Fort Worth, TX"]varfilteredData:[String]!varsearchController:UISearchController!overridefuncviewDidLoad(){super.viewDidLoad()tableView.dataSource=selffilteredData=data// Initializing with searchResultsController set to nil means that// searchController will use this view controller to display the search resultssearchController=UISearchController(searchResultsController:nil)searchController.searchResultsUpdater=self// If we are using this same view controller to present the results// dimming it out wouldn't make sense. Should probably only set// this to yes if using another controller to display the search results.searchController.dimsBackgroundDuringPresentation=falsesearchController.searchBar.sizeToFit()tableView.tableHeaderView=searchController.searchBar// Sets this view controller as presenting view controller for the search interfacedefinesPresentationContext=true}functableView(tableView:UITableView,cellForRowAtIndexPathindexPath:NSIndexPath)->UITableViewCell{letcell=tableView.dequeueReusableCellWithIdentifier("TableCell")asUITableViewCellcell.textLabel?.text=filteredData[indexPath.row]returncell}functableView(tableView:UITableView,numberOfRowsInSectionsection:Int)->Int{returnfilteredData.count}funcupdateSearchResultsForSearchController(searchController:UISearchController){ifletsearchText=searchController.searchBar.text{filteredData=searchText.isEmpty?data:data.filter({(dataString:String)->BoolinreturndataString.rangeOfString(searchText,options:.CaseInsensitiveSearch)!=nil})tableView.reloadData()}}}

//UISearchViewController.h
@interfaceUISearchViewController:UIViewController<UITableViewDelegate,UITableViewDataSource,UISearchResultsUpdating>@end//UISearchViewController.m
@interfaceUISearchViewController()@property(strong,nonatomic)IBOutletUITableView*tableView;@property(strong,nonatomic)UISearchController*searchController;@property(strong,nonatomic)NSArray*data;@property(strong,nonatomic)NSArray*filteredData;@end@implementationUISearchViewController-(void)viewDidLoad{[superviewDidLoad];self.tableView.delegate=self;self.tableView.dataSource=self;self.data=@[@"New York, NY",@"Los Angeles, CA",@"Chicago, IL",@"Houston, TX",@"Philadelphia, PA",@"Phoenix, AZ",@"San Diego, CA",@"San Antonio, TX",@"Dallas, TX",@"Detroit, MI",@"San Jose, CA",@"Indianapolis, IN",@"Jacksonville, FL",@"San Francisco, CA",@"Columbus, OH",@"Austin, TX",@"Memphis, TN",@"Baltimore, MD",@"Charlotte, ND",@"Fort Worth, TX"];self.filteredData=self.data;// Initializing with searchResultsController set to nil means that
// searchController will use this view controller to display the search results
self.searchController=[[UISearchControlleralloc]initWithSearchResultsController:nil];self.searchController.searchResultsUpdater=self;// If we are using this same view controller to present the results
// dimming it out wouldn't make sense. Should probably only set
// this to yes if using another controller to display the search results.
self.searchController.dimsBackgroundDuringPresentation=NO;[self.searchController.searchBarsizeToFit];self.tableView.tableHeaderView=self.searchController.searchBar;// Sets this view controller as presenting view controller for the search interface
self.definesPresentationContext=YES;}-(NSInteger)tableView:(UITableView*)tableViewnumberOfRowsInSection:(NSInteger)section{returnself.filteredData.count;}-(UITableViewCell*)tableView:(UITableView*)tableViewcellForRowAtIndexPath:(NSIndexPath*)indexPath{UITableViewCell*cell=[self.tableViewdequeueReusableCellWithIdentifier:@"TableCell"forIndexPath:indexPath];cell.textLabel.text=self.filteredData[indexPath.row];returncell;}-(void)updateSearchResultsForSearchController:(UISearchController*)searchController{NSString*searchText=searchController.searchBar.text;if(searchText){if(searchText.length!=0){NSPredicate*predicate=[NSPredicatepredicateWithBlock:^BOOL(NSString*evaluatedObject,NSDictionary*bindings){return[evaluatedObjectcontainsString:searchText];}];self.filteredData=[self.datafilteredArrayUsingPredicate:predicate];}else{self.filteredData=self.data;}[self.tableViewreloadData];}}@end

Here's what this looks like when running. Notice that unlike in the
search display controller example, we are using the same table view to
display the search results instead of overlaying of a separate table
view. However, unlike when working with just the search bar, we still
have the built in animation when transitioning to the search interface.

Also, you get the logic to show Cancel button and hide keyboard when
user taps on cancel button for free when you use this.

Example searching a collection view

We can just as easily use the search controller to search a collection
view in place. We still have the presentation of a search interface,
but unlike when working with the search display controller we are not
restricted to using a table view to display the search results.

The code for this is almost the same as when searching the the table
view above. The only notable difference is that we had to introduce a
placeholder view in interface builder for the search bar since there are
still some quirks with placing a search controller's search bar inside a
collection view's supplementary view.

Search Bar in Navigation View

A common requirement is to place the search bar inside the navigation
bar.

This can be configured programatically in your view controller's
viewDidLoad as follows.

When working directly with a search bar:

// create the search bar programatically since you won't be// able to drag one onto the navigation barsearchBar=UISearchBar()searchBar.sizeToFit()// the UIViewController comes with a navigationItem property// this will automatically be initialized for you if when the// view controller is added to a navigation controller's stack// you just need to set the titleView to be the search barnavigationItem.titleView=searchBar

// create the search bar programatically since you won't be
// able to drag one onto the navigation bar
self.searchBar=[[UISearchBaralloc]init];[self.searchBarsizeToFit];// the UIViewController comes with a navigationItem property
// this will automatically be initialized for you if when the
// view controller is added to a navigation controller's stack
// you just need to set the titleView to be the search bar
self.navigationItem.titleView=self.searchBar;

[self.searchControllerNavi.searchBarsizeToFit];self.navigationItem.titleView=self.searchControllerNavi.searchBar;// By default the navigation bar hides when presenting the
// search interface. Obviously we don't want this to happen if
// our search bar is inside the navigation bar.
self.searchControllerNavi.hidesNavigationBarDuringPresentation=NO;