Note 1: This question pertains to adding a UISearchController's search bar outside of the table view it updates - NOT as the table view's header.
Note 2: Some trial and error led me to a solution. Please see my answer below.
I am new to iOS development and am struggling to work with the UISearchController class. I have a view controller, and in my view controller's view, I plan to have a search bar above a table view. I would like the search bar to be linked to a UISearchController. Since interface builder does not come with a UISearchController, I am adding the controller programmatically. After instantiating a UISearchController, I have tried to add the search controller's search bar to my view programmatically but have not been successful. I have tried setting the search bar's frame and giving it autolayout constraints, but neither approach has worked for me (i.e. when I run the app, nothing appears). Here is the latest code I have tried:
let searchController = UISearchController(searchResultsController: nil)
// Set the search bar's frame
searchController.searchBar.frame = CGRect(x: 0, y: 0, width: self.view.frame.size.width, height: 50)
// Constraint to pin the search bar to the top of the view
let topConstraint = NSLayoutConstraint(item: searchController.searchBar, attribute: NSLayoutAttribute.Top, relatedBy: NSLayoutRelation.Equal, toItem: self.view, attribute: NSLayoutAttribute.Top, multiplier: 1, constant: 0)
searchController.searchBar.setTranslatesAutoresizingMaskIntoConstraints(false)
self.view.addSubview(searchController.searchBar)
self.view.addConstraint(topConstraint)
Any help would be greatly appreciated! Thank you!
EDIT: Using only one of either setting the search bar's frame or you giving it autolayout constraints (as opposed to a combination both as I initially tried) appears to work at first, but after tapping on the search bar you will have issues as pointed out by Dwight. I've left the code for these cases in case it's helpful to compare to what you currently have, but for a working solution see my answer below.
Using autolayout constraints:
let searchController = UISearchController(searchResultsController: nil)
let topConstraint = NSLayoutConstraint(item: searchController.searchBar, attribute: NSLayoutAttribute.Top, relatedBy: NSLayoutRelation.Equal, toItem: self.view, attribute: NSLayoutAttribute.Top, multiplier: 1, constant: (statusBarHeight + navigationBarHeight!))
let leftConstraint = NSLayoutConstraint(item: searchController.searchBar, attribute: NSLayoutAttribute.Left, relatedBy: NSLayoutRelation.Equal, toItem: self.view, attribute: NSLayoutAttribute.Left, multiplier: 1, constant: 0)
let rightConstraint = NSLayoutConstraint(item: searchController.searchBar, attribute: NSLayoutAttribute.Right, relatedBy: NSLayoutRelation.Equal, toItem: self.view, attribute: NSLayoutAttribute.Right, multiplier: 1, constant: 0)
let heightConstraint = NSLayoutConstraint(item: searchController.searchBar, attribute: NSLayoutAttribute.Height, relatedBy: NSLayoutRelation.Equal, toItem: nil, attribute: NSLayoutAttribute.NotAnAttribute, multiplier: 1, constant: 44)
searchController.searchBar.setTranslatesAutoresizingMaskIntoConstraints(false)
searchController.searchBar.addConstraint(heightConstraint)
self.view.addSubview(searchController.searchBar)
self.view.addConstraints([topConstraint, leftConstraint, rightConstraint])
I've set the topConstraint constant to the height of the status bar plus the height of the navigation bar, as my view controller is embedded in a navigation controller.
Adjusting the frame:
let searchController = UISearchController(searchResultsController: nil)
searchController.searchBar.frame = CGRect(x: 0, y: (statusBarHeight + navigationBarHeight!), width: self.view.frame.size.width, height: 44)
self.view.addSubview(searchController.searchBar)
After some more trial and error, I have the following working solution:
var searchController: UISearchController!
Then, in viewDidLoad(), you will need:
// Initialize and set up the search controller
self.searchController = UISearchController(searchResultsController: nil)
self.searchController.searchResultsUpdater = self
self.searchController.dimsBackgroundDuringPresentation = false // Optional
self.searchController.searchBar.delegate = self
// Add the search bar as a subview of the UIView you added above the table view
self.topView.addSubview(self.searchController.searchBar)
// Call sizeToFit() on the search bar so it fits nicely in the UIView
self.searchController.searchBar.sizeToFit()
// For some reason, the search bar will extend outside the view to the left after calling sizeToFit. This next line corrects this.
self.searchController.searchBar.frame.size.width = self.view.frame.size.width
This should be it! Of course, you'll need to make your view controller a UITableViewDatasource, UITableViewDelegate, UISearchBarDelegate, UISearchControllerDelegate, and UISearchResultsUpdating and take care of all required delegate methods as well, but as far as the search bar goes, this has worked for me. Please comment if you have any issues.