Display UISearchController's searchbar programmatically

19,758

After some more trial and error, I have the following working solution:

  1. Open up Main.storyboard and add to your view controller a UIView with height 44 (default height of a UISearchBar) and a UITableView. Set the autolayout constraints so that the UIView is pinned to the two sides and to the top layout guide (using the top layout guide ensures this will work whether or not your view controller is embedded in a navigation controller) and the table view is pinned to the two sides, the bottom layout guide, and its top pinned to the bottom of the UIView.
  2. Connect the UIView and UITableView as IBOutlets in your ViewController.swift. In my project, I called them topView and tableView.
  3. Declare a search controller at the top of your ViewController.swift: var searchController: UISearchController!
  4. 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.

Share:
19,758
kcstricks
Author by

kcstricks

Updated on June 13, 2022

Comments

  • kcstricks
    kcstricks almost 2 years

    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)
    
  • kcstricks
    kcstricks almost 9 years
    Thanks for your help! I'm a bit confused by your first suggestion because this seems to just add a search bar, rather than a search bar that is associated with a search controller. Please correct me if I am wrong. As for your second suggestion, setting setTranslatesAutoResizingMaskIntoConstraints to true works to display the search bar, provided you have specified its frame. However, I believe this flag is true by default, so I think you can just specify a frame and leave this line out as I have done in EDIT 1 of my post.
  • Fawkes
    Fawkes over 8 years
    "// For some reason, the search bar will extend outside the view to the left". It does so because in viewDidLoad() the frames of the views are not yet calculated right, though your search bar gets a wrong frame which is way bigger than it should be. if you want to make the things correctly, make the sizeToFit() in your viewDidAppear or after your view finished layouting around.
  • kcstricks
    kcstricks over 8 years
    @Fawkes - thanks for the input. I have a quick follow-up question for you: if this is the case, and the frame of the view is not yet calculated correctly when I call sizeToFit(), then do you know why it worked to set the searchBar's width to the width of the view on the following line of code? If the view's frame is still not properly calculated at this point, shouldn't the above not work?
  • Fawkes
    Fawkes over 8 years
    Unfortunately cannot find something very detailed about sizeToFit(), but as it is written that ->"Resizes and moves the receiver view so it just encloses its subviews." That probably means that a forceful frame calculation happens as in case of "layoutIfNeeded()", thus after you call "sizeToFit()" you obtain views for which the frame was calculated before it would've happened normally like after "viewDidLayoutSubviews" or "viewDidAppear".
  • kcstricks
    kcstricks over 8 years
    @Fawkes - thanks for the heads up. This will be good to keep in mind next time I'm messing with frames during the loading of the view :)
  • SAHM
    SAHM about 7 years
    I don't think this works if you are trying to add the search bar into a view that isn't the full width of the view controller. I seems like the search bar will not be set to a width less than that of the view controller.
  • coolcool1994
    coolcool1994 over 6 years
    When I do this and I tap onto the search bar, the search bar jumps down like 80 points. When I cancel search it goes back to where it was. Does anyone know how to fix this?
  • kcstricks
    kcstricks over 6 years
    Hey @coolcool1994! I haven't looked at this code in years, and am no longer working on the app. Something definitely could have changed between now and then. Did you figure it out?
  • Keyhan Kamangar
    Keyhan Kamangar over 3 years
    @coolcool1994 Hi, did you manage to fix the search bar jumps down like 80 points issue? I'm facing the same issue in iOS 12.