Andrea
Andrea

Reputation: 11

SwiftUI SearchBar problem with NavigationLink

I've a problem in SwiftUI with the searchBar appear. There's a delay on its appear when I use NavigationLink. I saw that the problem appears only with NavigationLinks, if I use a conditional overlay or others "handmade" way to move between Views the problem doesn't appear. You know what I could do to fix the problem?

Here's my views code:

import SwiftUI

struct ContentView: View {
    
    @State var searchText = ""
    
    var body: some View {
        
        NavigationView{
            
            NavigationLink(destination: ContentView2()){
                Text("Go to Sub View")
            }
            .navigationBarTitle("Main View")
            .add(SearchBar(text: self.$searchText, hide: true, placeholder: "Search", cancelButton: true, autocapitalization: .sentences))
        }
    }
}

struct ContentView2 : View {
    
    @State var searchText = ""
    
    var body: some View {
        
        Text("Hello, world!")
            
            .navigationBarTitle("Sub View")
            .add(SearchBar(text: self.$searchText, hide: true, placeholder: "Search", cancelButton: true, autocapitalization: .sentences))
        
    }
}

My SearchBar code

import SwiftUI

class SearchBar: NSObject, ObservableObject {
    
    
    let searchController: UISearchController = UISearchController(searchResultsController: nil)
    
    @Binding var text: String
    let hide : Bool
    let placeholder : String
    let cancelButton : Bool
    let autocapitalization : UITextAutocapitalizationType
    
    init(text: Binding<String>, hide: Bool, placeholder: String, cancelButton: Bool, autocapitalization: UITextAutocapitalizationType) {
        
        self._text = text
        self.hide = hide
        self.placeholder = placeholder
        self.cancelButton = cancelButton
        self.autocapitalization = autocapitalization
        
        super.init()
        
        
        self.searchController.obscuresBackgroundDuringPresentation = false
        self.searchController.searchResultsUpdater = self
        
        self.searchController.hidesNavigationBarDuringPresentation = hide
        self.searchController.automaticallyShowsCancelButton = cancelButton
        self.searchController.searchBar.placeholder = placeholder
        self.searchController.searchBar.autocapitalizationType = autocapitalization
        
    }
}

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))
    }
    
}

My ViewController code

import SwiftUI

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)
        }
    }
    
    override func viewDidAppear(_ animated: Bool) {
        
            self.parent?.navigationItem.hidesSearchBarWhenScrolling = false
            self.parent?.definesPresentationContext = true
            self.parent?.navigationController?.navigationBar.sizeToFit()
        
    }
    
    override func viewDidDisappear(_ animated: Bool) {
        
            self.parent?.navigationItem.hidesSearchBarWhenScrolling = false
            self.parent?.definesPresentationContext = true
            self.parent?.navigationController?.navigationBar.sizeToFit()
        
    }


}

And here's a video of the problem

Upvotes: 1

Views: 326

Answers (1)

Evan Butler
Evan Butler

Reputation: 1

Set the hidesSearchBarWhenScrolling property before the SearchBar is displayed on the screen. This can be done in viewWillAppear or as in the example below:

struct SearchBarModifier: ViewModifier {
    let searchBar: SearchBar

    func body(content: Content) -> some View {
        content
            .overlay(
                ViewControllerResolver { viewController in
                    viewController.navigationItem.searchController = self.searchBar.searchController
                    viewController.navigationItem.hidesSearchBarWhenScrolling = false
                }
                    .frame(width: 0, height: 0)
            )
    }
}

Upvotes: 0

Related Questions