I have implemented a custom NavigationView with a search bar in SwiftUI.
The SearchResultsContent doesn’t update properly though. I can’t seem to figure out why.
I’ve done all the UISearchResultsUpdating related things, but that doesn’t fix the problem.
Implementation in View
The SearchResultsContent doesn’t update properly though. I can’t seem to figure out why.
I’ve done all the UISearchResultsUpdating related things, but that doesn’t fix the problem.
Code Block Swift struct SearchBarNavigationView<SearchResultsContent: View, Content: View>: UIViewControllerRepresentable { class Coordinator: NSObject, UISearchResultsUpdating { private let parent: SearchBarNavigationView init(_ parent: SearchBarNavigationView) { self.parent = parent } func updateSearchResults(for searchController: UISearchController) { guard let searchText = searchController.searchBar.text else { return } DispatchQueue.main.async { self.parent.text = searchText } } } @Binding private var text: String private let searchResultsContent: SearchResultsContent private let content: Content init(text: Binding<String>, @ViewBuilder searchResultsContent: () -> SearchResultsContent, @ViewBuilder content: () -> Content) { _text = text self.searchResultsContent = searchResultsContent() self.content = content() } func makeUIViewController(context: Context) -> UINavigationController { let rootViewController = UIHostingController(rootView: content) let navigationController = UINavigationController(rootViewController: rootViewController) navigationController.navigationBar.prefersLargeTitles = true let searchResultsController = UIHostingController(rootView: searchResultsContent) let searchController = UISearchController(searchResultsController: searchResultsController) searchController.searchResultsUpdater = context.coordinator searchController.obscuresBackgroundDuringPresentation = false searchController.searchBar.autocapitalizationType = .none navigationController.navigationBar.topItem?.searchController = searchController return navigationController } func updateUIViewController(_ uiViewController: UINavigationController, context: Context) { if let searchResultsController = uiViewController.navigationBar.topItem?.searchController?.searchResultsController as? UIHostingController<SearchResultsContent> { searchResultsController.rootView = searchResultsContent searchResultsController.view.setNeedsDisplay() } if let rootViewController = uiViewController.topViewController as? UIHostingController<Content> { rootViewController.rootView = content rootViewController.view.setNeedsDisplay() } uiViewController.navigationBar.topItem?.searchController?.searchBar.text = text } func makeCoordinator() -> Coordinator { Coordinator(self) } }
Implementation in View
Code Block Swift SearchBarNavigationView(text: $searchText) { Text(searchText) // doesn’t change whilst typing } content: { List(0 ..< 20) { Text("Row \($0)") } } .ignoresSafeArea() .onChange(of: searchText) { value in print(value) // works correctly }
It turns out I needed to change some code in the updateUIViewController(_:context:) method to reload the SearchResultsContent
Code Block Swift if let searchResultsController = uiViewController.navigationBar.topItem?.searchController?.searchResultsController as? UIHostingController<SearchResultsContent> { searchResultsController.rootView = searchResultsContent searchResultsController.view.setNeedsDisplay() }