I followed this article on how to display a SearchBar in the NavigationBar. I integrated it like this into my view:
struct ExploreView: View {
@ObservedObject var searchBar = SearchBar()
var body: some View {
NavigationView {
ZStack {
Color(red: 250/255, green: 250/255, blue: 250/255)
.edgesIgnoringSafeArea(.all)
VStack(spacing: 0) {
Image(R.image.navigationBarBackground)
.resizable()
.scaledToFit()
.frame(width: UIScreen.main.bounds.width)
.edgesIgnoringSafeArea(.all)
Spacer()
}
}
.navigationBarTitle("", displayMode: .inline)
.add(self.searchBar)
}
.navigationViewStyle(StackNavigationViewStyle())
}
}
class SearchBar: NSObject, ObservableObject {
@Published var text: String = ""
let searchController: UISearchController = UISearchController(searchResultsController: nil)
override init() {
super.init()
self.searchController.obscuresBackgroundDuringPresentation = false
self.searchController.searchResultsUpdater = self
}
}
extension SearchBar: UISearchResultsUpdating {
func updateSearchResults(for searchController: UISearchController) {
// Publish search bar text changes.
if let searchBarText = searchController.searchBar.text {
self.text = searchBarText
}
}
}
struct SearchBarModifier: ViewModifier {
let searchBar: SearchBar
func body(content: Content) -> some View {
content
.overlay(
ViewControllerResolver { viewController in
viewController.navigationItem.searchController = self.searchBar.searchController
}
.frame(width: 0, height: 0)
)
}
}
extension View {
func add(_ searchBar: SearchBar) -> some View {
return self.modifier(SearchBarModifier(searchBar: searchBar))
}
}
final class ViewControllerResolver: UIViewControllerRepresentable {
let onResolve: (UIViewController) -> Void
init(onResolve: @escaping (UIViewController) -> Void) {
self.onResolve = onResolve
}
func makeUIViewController(context: Context) -> ParentResolverViewController {
ParentResolverViewController(onResolve: onResolve)
}
func updateUIViewController(_ uiViewController: ParentResolverViewController, context: Context) {
}
}
class ParentResolverViewController: UIViewController {
let onResolve: (UIViewController) -> Void
init(onResolve: @escaping (UIViewController) -> Void) {
self.onResolve = onResolve
super.init(nibName: nil, bundle: nil)
}
required init?(coder: NSCoder) {
fatalError("Use init(onResolve:) to instantiate ParentResolverViewController.")
}
override func didMove(toParent parent: UIViewController?) {
super.didMove(toParent: parent)
if let parent = parent {
onResolve(parent)
}
}
}
It look's like this: SearchBar: inactive, SearchBar: active
But I would like to have the inactive SearchBar in the same position as the active SearchBar to avoid the free space. In the end it should look like the SearchBar in the Instagram App. Does anyone know how to do this?
Add this piece of code inside your init()
method of SearchBar
. It will make search bar at same position when it's active.
self.searchController.hidesNavigationBarDuringPresentation = false
If you want to set search bar to navigation bar title instead of text, inside your overlay(_:)
Change this code
ViewControllerResolver { viewController in
viewController.navigationItem.searchController = self.searchBar.searchController
}
To
ViewControllerResolver { viewController in
viewController.navigationItem.titleView = self.searchBar.searchController.searchBar
}