Reputation: 6742
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?
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.
Upvotes: 88
Views: 33415
Reputation: 361
@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
UPD:
Another visual bug i faced was searchBar hiding after swiping up on a collectionView
(collectionView's
contentSize
needs to be smaller than self.view
's frame), navigating to another screen and coming back
Here's a common fix:
private var collectionViewContentOffset: CGPoint?
override func viewDidLoad() {
super.viewDidLoad()
navigationItem.hidesSearchBarWhenScrolling = false
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
guard
let initialSafeAreaTopInset,
initialSafeAreaTopInset - view.safeAreaInsets.top < 1,
initialSafeAreaTopInset - view.safeAreaInsets.top > 0
else {
return
}
let collectionContentSizeHeight = collectionView.contentSize.height
let collectionAvailableHeight = collectionView.frame.size.height - view.safeAreaInsets.top - configuration.collectionView.insets.top - configuration.collectionView.insets.bottom
guard collectionContentSizeHeight < collectionAvailableHeight else {
return
}
collectionView.setContentOffset(
CGPoint(
x: 0,
y: collectionView.contentOffset.y - 1
),
animated: false
)
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
navigationItem.hidesSearchBarWhenScrolling = true
if initialSafeAreaTopInset == nil {
initialSafeAreaTopInset = view.safeAreaInsets.top
}
}
Upvotes: 1
Reputation: 1610
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)
)
}
}
Upvotes: 0
Reputation: 4930
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
}
}
Upvotes: 3
Reputation: 2899
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.
Upvotes: 211
Reputation: 103
For me it worked after adding following lines in viewDidLoad()
method:
navigationController?.navigationBar.prefersLargeTitles = true
navigationController!.navigationBar.sizeToFit()
Upvotes: 5
Reputation: 479
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
}
Upvotes: 6