Show search bar in navigation bar without scrolling on iOS 11
Asked Answered
J

6

87

I’m attaching a UISearchController to the navigationItem.searchController property of a UITableViewController on iOS 11. This works fine: I can use the nice iOS 11-style search bar.

However, I’d like to make the search bar visible on launch. By default, the user has to scroll up in the table view to see the search bar. Does anyone know how is this is possible?

enter image description here enter image description here

Left: default situation after launch. Right: search bar made visible (by scrolling up). I’d like to have the search bar visible after launch, as in the right screenshot.

I already found that the search bar can be made visible by setting the property hidesSearchBarWhenScrolling of my navigation item to false. However, this causes the search bar to always be visible — even when scrolling down —, which is not what I want.

Joleen answered 15/9, 2017 at 12:42 Comment(5)
in where you added the code hidesSearchBarWhenScrollingProbably
what about setting it as s firstResponder?Lancey
The selected answer below works for me on load, but I'd also like to re-display the Search Controller when programmatically scrolling to the top with scrollView.setContentOffset(_:animated). Anyone have a suggestion?Foliolate
@Joleen hidesSearchBarWhenScrolling = false puts the search bar over large title in iOS 13. Any idea if I can update this somehow?Selby
nothing works from answers, did someone solve issue?(Jalapa
V
209

The following makes the search bar visible at first, then allows it to hide when scrolling:

override func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(animated)
    if #available(iOS 11.0, *) {
        navigationItem.hidesSearchBarWhenScrolling = false
    }
}

override func viewDidAppear(_ animated: Bool) {
    super.viewDidAppear(animated)
    if #available(iOS 11.0, *) {
        navigationItem.hidesSearchBarWhenScrolling = true
    }
}

Using isActive didn't do what I wanted, it makes the search bar active (showing cancel button, etc.), when all I want is for it to be visible.

Veta answered 21/9, 2017 at 19:55 Comment(5)
This solution works, but there are side effects if you are also displaying a navigation bar if it was previously hidden (i.e. pushing this view w/ search bar onto the navigation stack) The search bar will appear static in place as the navigation bar animates. Looks awful :(Yaker
Not if you put the first part in viewDidLoad instead of viewWillAppearStadia
Can someone please elaborate on why this works and why searchController.searchBar.isHidden = false in viewDidLoad does not? The latter seems far more logical to meBahamas
This causes visual bug in iOS 13 on going back from child screen.Ligneous
Is there a way to overcome the visual bug that this idea brings? The tableView 'jumps' when getting back from the child screen. The issue happens only if the view is scrolled from the top and the child view is opened and then closed.Proteolysis
G
6

You can set the property isActive to true after adding the searchController to the navigationItem.

Just like this:

override func viewDidAppear(_ animated: Bool) {
    super.viewDidAppear(animated)
    searchController.isActive = true
}
Goldoni answered 20/9, 2017 at 4:12 Comment(2)
No setter method 'setIsActive:'Unwished
You're right, @NikolayKrasnov. isActive is a read-only boolean. You will want to use searchController.active = true, instead.Sperling
T
5

For me it worked after adding following lines in viewDidLoad() method:

navigationController?.navigationBar.prefersLargeTitles = true
navigationController!.navigationBar.sizeToFit()
Tungting answered 26/4, 2020 at 23:13 Comment(0)
C
3

On iOS 13, @Jordan Wood's answer didn't work. Instead I did:

override func viewDidAppear(_ animated: Bool) {
    super.viewDidAppear(animated)
    UIView.performWithoutAnimation {
        searchController.isActive = true
        searchController.isActive = false
    }
}
Competitive answered 12/7, 2020 at 17:50 Comment(1)
Almost! The search bar animates to become visible after the view appears. This will annoy users. :(Herewith
S
1

@JordanWood's answer caused a visual bug when popping VC from navigation stack back to the screen with SearchBar, so i came up with solution that fixed it

NOTE: This solution seems to be reasonable if your collection content will never overgrow self.view's frame, otherwise u'll need to tune it a bit to get correct dynamic collectionViewContentOffset. i.e when user leaves the screen which is scrolled far down and comes back, collectionView will appear scrolled to top, which is mostly not what you want

private var collectionViewContentOffset: CGPoint?

override func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(animated)

    navigationItem.hidesSearchBarWhenScrolling = false
    collectionView.setContentOffset(
        collectionViewContentOffset ?? .zero,
        animated: false
    )
}

override func viewDidAppear(_ animated: Bool) {
    super.viewDidAppear(animated)

    navigationItem.hidesSearchBarWhenScrolling = true
    if collectionViewContentOffset == nil {
        collectionViewContentOffset = CGPoint(
            x: 0,
            y: -view.safeAreaInsets.top
        )
    }
    
}

Another good solution i found (which works fine when collection's contentSize is bigger than self.view's frame) is to run it like that:

override func viewDidLoad() {
    super.viewDidLoad()

    navigationItem.hidesSearchBarWhenScrolling = false
}

override func viewDidAppear(_ animated: Bool) {
    super.viewDidAppear(animated)

    navigationItem.hidesSearchBarWhenScrolling = true
}

Downside of that option is that your searchBar will hide when you try to do a swipe up gesture on a collectionView (collectionView's contentSize needs to be smaller than self.view's frame), leave the screen and come back. But there are no visual bugs, just the fact that it hides in one corner case

Shutout answered 31/8, 2023 at 9:25 Comment(0)
S
0
For (iOS 13.0, *) and SwiftUI

navigationController?.navigationBar.sizeToFit()

Example:

struct SearchBarModifier: ViewModifier {
        let searchBar: SearchBar
        func body(content: Content) -> some View {
        content
            .overlay(
                ViewControllerResolver { viewController in
                    viewController.navigationItem.searchController = self.searchBar.searchController
                    viewController.navigationController?.navigationBar.sizeToFit()

                }
                .frame(width: 0, height: 0)
            )
    }
}
Selfeducated answered 27/5, 2021 at 11:14 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.