2

Does anyone have working Swiftui code that will produce a search bar in the navigation bar that is inline with the back button? As if it is a toolbar item.

Currently I have code that will produce a search bar below the navigation back button but would like it in line like the picture attached shows (where the "hi" is): enter image description here

I am using code that I found in an example:

    var body: some View {

        let shopList = genShopList(receiptList: allReceipts)

        
        VStack{
            
        }
        .navigationBarSearch(self.$searchInput)

        
        
    
    }
public extension View {
    public func navigationBarSearch(_ searchText: Binding<String>) -> some View {
        return overlay(SearchBar(text: searchText)
                        .frame(width: 0, height: 0))
    }
}

fileprivate struct SearchBar: UIViewControllerRepresentable {
    @Binding
    var text: String
    
    init(text: Binding<String>) {
        self._text = text
    }
    
    func makeUIViewController(context: Context) -> SearchBarWrapperController {
        return SearchBarWrapperController()
    }
    
    func updateUIViewController(_ controller: SearchBarWrapperController, context: Context) {
        controller.searchController = context.coordinator.searchController
    }
    
    func makeCoordinator() -> Coordinator {
        return Coordinator(text: $text)
    }
    
    class Coordinator: NSObject, UISearchResultsUpdating {
        @Binding
        var text: String
        let searchController: UISearchController
        
        private var subscription: AnyCancellable?
        
        init(text: Binding<String>) {
            self._text = text
            self.searchController = UISearchController(searchResultsController: nil)
            
            super.init()
            
            searchController.searchResultsUpdater = self
            searchController.hidesNavigationBarDuringPresentation = true
            searchController.obscuresBackgroundDuringPresentation = false
            
            self.searchController.searchBar.text = self.text
            self.subscription = self.text.publisher.sink { _ in
                self.searchController.searchBar.text = self.text
            }
        }
        
        deinit {
            self.subscription?.cancel()
        }
        
        func updateSearchResults(for searchController: UISearchController) {
            guard let text = searchController.searchBar.text else { return }
            self.text = text
        }
    }
    
    class SearchBarWrapperController: UIViewController {
        var searchController: UISearchController? {
            didSet {
                self.parent?.navigationItem.searchController = searchController
            }
        }
        
        override func viewWillAppear(_ animated: Bool) {
            self.parent?.navigationItem.searchController = searchController
        }
        override func viewDidAppear(_ animated: Bool) {
            self.parent?.navigationItem.searchController = searchController
        }
    }
}

If anyone has a solution to this problem that would be greatly appreciated! I know that in IoS 15 they are bringing out .searchable but looking for something that will work for earlier versions too.

mdav132
  • 125
  • 2
  • 11
  • It is difficult to understand what you want. What do you mean 'search bar in the navigation bar that is inline with the back button?' – mahan Aug 16 '21 at 13:07

1 Answers1

2

You can put any control in the position you want by using the .toolbar modifier (iOS 14+) and an item with .principal placement, e.g.:

var body: some View {
  VStack {
    // rest of view
  }
  .toolbar {
    ToolbarItem(placement: .principal) {
      MySearchField(text: $searchText)
    }
  }
}

A couple of things to note:

  1. The principal position overrides an inline navigation title, either when it's set with .navigationBarTitleDisplayMode(.inline) or when you have a large title and scroll up the page.

  2. It's possible that your custom view expands horizontally so much that the back button loses any text component. Here, I used a TextField to illustrate the point:

Search field in principal position

You might be able to mitigate for that by assigning a maximum width with .frame(maxWidth:), but at the very least it's something to be aware of.

ScottM
  • 7,108
  • 1
  • 25
  • 42