17

In iOS 13 the behavior has changed so that by default when Navigation controller appears the search bar is visible (when UISearchController is assigned to a navigationItem.searchController). Some system apps appear with the search bar hidden (you need to swipe down for it to appear), but I don't see any specific property that would allow this. How to achieve this - maybe there is some property or some method to do that?

Ivan Ičin
  • 9,672
  • 5
  • 36
  • 57
  • Navigation controllers don't have search bars by default so please update your question with details about what you are doing to get a search bar in place. – rmaddy Aug 20 '19 at 21:10
  • @rmaddy I will but just wandering if you really have no idea what I am doing as showing search bar in navigation controller is a standard procedure since iOS 11. – Ivan Ičin Aug 20 '19 at 21:39
  • There are different solutions so it is important for you to be clear on what exactly it is that you are doing. Your edit is far from enough detail. – rmaddy Aug 20 '19 at 22:24
  • @rmaddy as some people (possibly you) downvote, please answer what you are missing. Did you try to reproduce the problem with the description above. I can, and I don't see how implementation of searchbar controller would influence this as even the simplest one works. – Ivan Ičin Aug 21 '19 at 09:59
  • 9
    This question seems clear to me. Navigation Items with search controllers assigned (thus with a search bar) behave differently in iOS 13 - they are visible on load. The obvious ideas (setting a content offset of the table, or programatically scrolling the table to a the first row) don't work. – Andrew Bennet Sep 04 '19 at 22:17

8 Answers8

9

Via experimentation, I have discovered that if you delay assigning the search controller to the navigation item until viewWillLayoutSubviews or viewDidLayoutSubviews, the search controller starts out hidden, as desired. However, this if you do this on iOS 12 or earlier, the search controller will not be revealed when scrolling down.

I ended up doing the following with a messy version check, which is working for me:

override func viewDidLoad() {
    super.viewDidLoad()

    searchController = /* make search controller... */

    if #available(iOS 13, *) {
        // Attaching the search controller at this time on iOS 13 results in the
        // search bar being initially visible, so assign it later
    }
    else {
        navigationItem.searchController = searchController
    }
}

override func viewWillLayoutSubviews() {
    super.viewWillLayoutSubviews()
    navigationItem.searchController = searchController
}

bunnyhero
  • 757
  • 2
  • 10
  • 23
6

To start with a hidden searchBar, simply set the navigationItem.searchController property after your table view (or collection view) has been populated with data.

Ely
  • 8,259
  • 1
  • 54
  • 67
  • None of these answers work for me. With this approach it either is not there at all until I switch to a different view and then back or it is already there. It seams a bit random and one time it just popped in, not smooth at all. I get the feeling they don't want to hide it. In this tutorial I'm following he said you 'might' have to pull down to see it, but when I run his example, its already there. https://www.raywenderlich.com/4363809-uisearchcontroller-tutorial-getting-started Must be dependant on the iOS version – Kurt L. Aug 13 '20 at 04:24
2

Inspired by bunnyhero's answer I put the code responsible for setting the UISearchController in navigationItem inside the viewDidAppear method. Seems to be working every time for me on iOS 14/15

override func viewDidAppear(_ animated: Bool) {
    super.viewDidAppear(animated)
    if navigationItem.searchController == nil {
        navigationItem.searchController = searchController
    }
}

Edit: I was overly optimistic. On iOS 15.2 this method stopped working for me. What I did to fix it was to move the code after reloading my table/collection view.

Adam
  • 1,776
  • 1
  • 17
  • 28
  • I couldn't get the desired behavior even when I sequenced the searchBar setup after the collectionView reload. I finally got it working by delaying the searchBar setup using DispatchQueue.main.asyncAfter(deadline: .now() + 0.1). – theogood Mar 18 '22 at 13:47
1

This is what works for me. I have a UISegmentedControl that reloads the tableView when filter changes.

With FRC:

guard let count = try? fetchedResultsController.managedObjectContext.count(for: request) else { return }

called after tableView.reloadData()

navigationItem.searchController = count > 20 ? searchController : nil
Gil
  • 21
  • 1
0

I find this works:

self.searchController.searchBar.hidden = YES;

You will need to unhide at the appropriate time.

MichaelR
  • 1,681
  • 15
  • 28
  • 1
    I guess in some way it would work. But I don’t thin it is like in Mail app where the bar appears animated on scroll down – Ivan Ičin Sep 15 '19 at 16:41
0

I managed to make this work by setting isTransculent false on the navigationBar and having initial data on UITableView or UICollectionView. If you have 0 cells initially and trigger reloadData after some time (maybe a network call), SearchBar is visible initially. So have a dummy cell or something similar initially and load the data later, if that's the case for you.

navigationController?.navigationBar.isTranslucent = false
frkncngz
  • 125
  • 2
  • 11
0

One should set searchController after tableView gets frame

override func scrollViewDidScroll(_ scrollView: UIScrollView) {
    super.scrollViewDidScroll(scrollView)

    if !scrollView.frame.isEmpty, navigationItem.searchController == nil {
        navigationItem.searchController = searchController
    }
}
malex
  • 9,874
  • 3
  • 56
  • 77
-2

Swift 5.2 & iOS 13.3.1:-

Try like this. It works fine

        navigationItem.hidesSearchBarWhenScrolling = false
  • 1
    Have you tried? According to documentation it doesn’t work like that: https://developer.apple.com/documentation/uikit/uinavigationitem/2897296-hidessearchbarwhenscrolling – Ivan Ičin Feb 27 '20 at 09:25