64

So I've tried everything trying to get a search bar into the navigation bar in Swift. But sadly I haven't gotten it working, just yet...

For those of you who don't know what I'm talking about, I'm trying to do something like this

enter image description here

Note the search bar in the navigation bar. So here's what I'm currently using

self.searchDisplayController?.displaysSearchBarInNavigationBar = true

I popped that in my viewDidLoad, and then when I load up the app I'm presented with, just an empty navigation bar.... :( Any ideas?

idris
  • 1,019
  • 1
  • 9
  • 22

10 Answers10

77

Try this

let leftNavBarButton = UIBarButtonItem(customView:Yoursearchbar)
  self.navigationItem.leftBarButtonItem = leftNavBarButton

Update

You keep a lazy UISearchBar property

lazy   var searchBar:UISearchBar = UISearchBar(frame: CGRectMake(0, 0, 200, 20))

In viewDidLoad

searchBar.placeholder = "Your placeholder"
var leftNavBarButton = UIBarButtonItem(customView:searchBar)
self.navigationItem.leftBarButtonItem = leftNavBarButton

If you want to use storyboard just drag your searchbar as a outlet,then replace the lazy property with your outlet searchbar

Renetik
  • 5,887
  • 1
  • 47
  • 66
Leo
  • 24,596
  • 11
  • 71
  • 92
  • How would I identify `Yoursearchbar`? – idris Nov 04 '14 at 02:13
  • So far so good. But how would I add a placeholder? Any way through storyboards? – idris Nov 04 '14 at 02:30
  • 1
    You just change the property of searchbar to add a placeholder.I update the answer – Leo Nov 04 '14 at 02:35
  • 1
    Hi, can you show me the whole code for this? I don't really understand about `lazy` part – Gibran Mar 28 '15 at 11:53
  • Can some one please tell me how to add the `lazy` part as mentioned in the end of the accepted answer. Thanks. – bably Aug 11 '15 at 11:13
  • 1
    Can anyone explain what does lazy do? What's the difference between lazy var searchBar vs var searchBar – Happiehappie Feb 03 '16 at 08:26
  • This is not working for me, no searchBar is displayed when i try this code line for line – Jerland2 Feb 08 '17 at 04:28
  • Swift 3 CGRectMake is now CGRect. lazy var searchBar:UISearchBar = UISearchBar(frame: CGRect(x:0,y:0,width:200,height:200)) – Brian Bird Feb 22 '17 at 21:25
  • Don't put searchBar into leftBarButtonItem. Creates all types of problems. Follow the suggest given below by antonio081014 – deeJ Aug 31 '17 at 21:29
  • @Leo, bro could you please help me https://stackoverflow.com/questions/46375778/add-navigation-bar-in-uicollectionview-in-swift-4-ios-11 ? – May Phyu Sep 23 '17 at 03:47
65
    // create the search bar programatically since you won't be
    // able to drag one onto the navigation bar
    searchBar = 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 bar
    navigationItem.titleView = searchBar
antonio081014
  • 3,553
  • 1
  • 30
  • 37
  • That is a much better way than any other solutions. – Profstyle May 06 '16 at 12:32
  • @antonio081014, bro could you help me to take a look at my problem https://stackoverflow.com/questions/46375778/add-navigation-bar-in-uicollectionview-in-swift-4-ios-11 ? – May Phyu Sep 23 '17 at 03:47
31

Swift 5, XCode 11, Storyboard way so you can easily add all the search bar attributes through the storyboard and you have less code in your view controller class.

1.) Add your search bar view as external view in viewcontroller.

enter image description here

2.) Connect searchBarView to you viewcontroller.

enter image description here

3.) Add your searchBarView to your navigationBar title item.

navigationItem.titleView = searchBarView

Result:

enter image description here

Egzon P.
  • 4,498
  • 3
  • 32
  • 31
  • 4
    I wish I could upvote this more. I had no idea you could do this!! – ghostatron Nov 05 '20 at 21:38
  • 2
    @Egzon This one is changing size of navigation bar from 44 to 56. So when i am going back to previous view controller, getting blink of navigation controller updating to its original size 44. Any suggestion on it please. – Mitesh Dobareeya Apr 24 '21 at 11:01
  • Hi @MiteshDobareeya, I will check on the next week what's the issue and get back to you. Are you pushing the view controller in the same UINavigationViewController? – Egzon P. Apr 24 '21 at 16:41
  • 2
    @EgzonP. Yes, I am pushing it into the same UINavigationViewController. Main Root view controller navigation bar has a perfect size 44. And when I push second view controller and adding search bar, then size of navigation bar into second vc gets increased to 56. So when I am pop to first vc then its navigation bar gets a blink. – Mitesh Dobareeya Apr 24 '21 at 17:15
25

In your view controller:

lazy var searchBar = UISearchBar(frame: CGRectZero)

override func viewDidLoad() {
    super.viewDidLoad()

    searchBar.placeholder = "Search"

    navigationItem.titleView = searchBar
}

Doing it this way, by setting the navigationItem.titleView, the search bar is automatically centered across the iPhone and iPad devices. Note: only tested with v8.4 and v9.0

for SWIFT 3

lazy var searchBar = UISearchBar(frame: CGRect.zero)
Abhishek
  • 1,654
  • 2
  • 18
  • 31
iAmcR
  • 859
  • 11
  • 10
  • How to remove it from navigationItem.titleView when user taps "cancel" button? – Satyam Apr 28 '17 at 16:18
  • Same question ^ – Nikhil Sridhar Jul 02 '17 at 20:01
  • 1
    Off the top of my head, if you're speaking of the search bar cancel button. Implement the UISearchBarDelegate function, searchBarCancelButtonClicked(:), calling navigationItem.titleView = nil in that function. This should remove the search bar if that's what you meant by "remove it". For normal search bar functionality, also ensure you implement searchBarSearchButtonClicked(:) adding searchBar.resignFirstResponder() to remove keyboard when search button is clicked (or started via return key press) – iAmcR Jul 04 '17 at 16:39
  • 1
    Updated code for Swift 3: lazy var searchBar = UISearchBar(frame: CGRect.zero) – DoesData Sep 10 '17 at 21:47
20

In 2019, you should use UISearchController.

override func viewDidLoad() {
    super.viewDidLoad()

    let searchController = UISearchController(searchResultsController: nil)
    searchController.searchResultsUpdater = self.viewModel
    searchController.obscuresBackgroundDuringPresentation = false
    searchController.searchBar.placeholder = "Search artists"
    self.navigationItem.searchController = searchController
    self.definesPresentationContext = true
}

And some class should conform to UISearchResultsUpdating. I usually add this as extension to my ViewModel.

extension ArtistSearchViewModel: UISearchResultsUpdating {

func updateSearchResults(for searchController: UISearchController) {
    print("Searching with: " + (searchController.searchBar.text ?? ""))
    let searchText = (searchController.searchBar.text ?? "")
    self.currentSearchText = searchText
    search()
    }
}

This will spawn something like this:

search bar

Martin Berger
  • 1,639
  • 3
  • 18
  • 39
  • This solution works fine but I have an issue when my tableView has only a few rows and the height is apparently not enough. When I scroll down the searchbar disappears and then I can't get it back by scrolling up again since it doesn't need to scroll to show all rows. Any ideas how to fix that? – JDK92 Nov 18 '19 at 10:09
  • @JDK Sadly, I do not know for sure. :( You can try observing Pull to Refresh and when it is triggered, you can try to manually tell UISearchBar to show. I am just throwing an idea. – Martin Berger Dec 05 '19 at 10:50
  • @JDK92 I know its a late answer but if you want the search bar sticky always you can use this navigationItem.hidesSearchBarWhenScrolling = false – mike vorisis Jul 28 '21 at 20:57
11

For iOS 11 and above

navigationItem.searchController = searchController

enter image description here

For iOS 10 and below

navigationItem.titleView = searchController.searchBar;

enter image description here

or you can assign it as leftBarButtonItem as described in this answer

Husam
  • 8,149
  • 3
  • 38
  • 45
3

For Swift 5 or letter

also, you can use this code. Fully Programmatically

import UIKit

class SearchTableViewController: UITableViewController {

private lazy var searchController: UISearchController = {
    let sc = UISearchController(searchResultsController: nil)
    sc.searchResultsUpdater = self
    sc.delegate = self
    sc.obscuresBackgroundDuringPresentation = false
    sc.searchBar.placeholder = "Enter A Compiny Name Or Symbole"
    sc.searchBar.autocapitalizationType = .allCharacters
    return sc
}()

override func viewDidLoad() {
    super.viewDidLoad()
    setupNavigationBar()
}

   private func setupNavigationBar() {
       navigationItem.searchController = searchController
   }

 }

// MARK: - UISearchResult Updating and UISearchControllerDelegate  Extension
  extension SearchTableViewController: UISearchResultsUpdating, UISearchControllerDelegate {
    func updateSearchResults(for searchController: UISearchController) {
    
    }
 }

After Clicking Search

After Clicking Cancel Button In Search

Chandan
  • 283
  • 2
  • 6
2
    let searchBar = UISearchBar()
    searchBar.sizeToFit()
    searchBar.placeholder = ""
    self.navigationController?.navigationBar.topItem?.titleView = searchBar
luhuiya
  • 2,129
  • 21
  • 20
0
func searchBarSearchButtonClicked(_ searchBar: UISearchBar) {

    searchBar.endEditing(true)
    searchBar.text = nil
    print("## search btn clicked : \(searchBar.text ?? "")")
} 
Brandon Minnick
  • 13,342
  • 15
  • 65
  • 123
Samy Nagy
  • 190
  • 1
  • 6
  • 2
    Thank you for this code snippet, which might provide some limited, immediate help. A proper explanation [would greatly improve](//meta.stackexchange.com/q/114762) its long-term value by showing *why* this is a good solution to the problem, and would make it more useful to future readers with other, similar questions. Please [edit] your answer to add some explanation, including the assumptions you've made. – Toby Speight Nov 08 '17 at 15:14
0

Setting SearchBar as titleView, changes height of navigationBar to 56. To fix this, you can embed searchBar in view and set that as titleView.

    var offset: CGFloat = 20

    // If VC is pushed, back button should be visible
    if navigationController?.navigationBar.backItem != nil {
        offset = 40
    }

    let customFrame = CGRect(x: 0, y: 0, width: view.frame.size.width - offset, height: 44.0)
    let searchBarContainer = UIView(frame: customFrame)
    searchBar = UISearchBar(frame: customFrame)
    searchBarContainer.addSubview(searchBar)
    navigationItem.titleView = searchBarContainer
Arijan
  • 297
  • 3
  • 6
  • the solution is soso because you could have rightBarButtonItems and frame is not appropriate in that case – Gargo Jan 23 '23 at 12:36