How to Place Uisearchcontroller to the Navigationtitle and How to Enable and Disable It by Button

How to place UISearchController to the navigationTitle and how to enable and disable it by button?

UISearchBar

UISearchBar has not been deprecated. A UISearchBar is a UIView that you can place anywhere you want, including the navigation bar, but also in the middle of the screen if that’s what you want. You can do this programatically or you can use the Interface Builder to drag a search bar and place it inside one of your views.

Using UISearchBar

One way to implement search is to use a UISearchBar, put it in you navigationItem.titleView, and set your view controller to be the delegate of this search bar. When the user interacts with the search bar, your view controller is notified and you have to deal with the search logic in this same view controller. For example, you may need to detect whether the search is active to decide what data source to use for your table view - if the search is not active you show all items; if the search is active you show only a subset of items. Or maybe you want to show one layout for your main view and a different one for your search results. Again, you would have to deal with this logic in your view controller. This is where a UISearchController comes in handy!

Using UISearchController

A UISearchController is a UIViewController that provides you with a UISearchBar. A UISearchController is useful because it helps you present your search results in a new view controller, keeping your search logic separate from your main logic.
To address a specific point in your question, keep in mind that the thing you put in your navigationItem.titleView is a UISearchBar, not a UISearchController. You can either create this UISearchBar yourself or you can create a UISearchController and use its search bar instead (searchController.searchBar).

Placing UISearchBar in navigationItem.titleView VS using navigationItem.searchController property

Placing your UISearchBar in navigationItem.titleView is a common pattern used in multiple apps. However, with iOS 11, Apple introduced the navigationItem.searchController property. There is nothing deprecated here but when it comes to using search bars in navigation bars, it seems Apple would prefer us to do it like this.

Below is an example of how this might work.

Using navigationItem.searchController property and a UISearchController to manage display of search results

Your main view controller:

class HomeViewController: UIViewController {
(…)

override func viewDidLoad() {
(…)

// Get the view controller that will be used to present search results.
let storyboard = UIStoryboard(name: "Main", bundle: nil)
guard let languagesSearchResultsViewController = storyboard.instantiateViewController(withIdentifier: "LanguagesSearchResultsViewController") as? LanguagesSearchResultsViewController else { return }

// Create a search controller and initialize it with the view controller that will be used to show the search results
let searchController = UISearchController(searchResultsController: languagesSearchResultsViewController)

// The searchResultsUpdater is the object that will be notified when the user performs a search.
// I'm setting it to be the view controller that will present the results but is doesn't have to be.
searchController.searchResultsUpdater = languagesSearchResultsViewController

// Set the searchController property in the navigationItem and a search bar will automagically appear in the navigation bar, below the title.
navigationItem.searchController = searchController
definesPresentationContext = true

(…)
}
}

The view controller that you’ll use to present the search results:

class LanguagesSearchResultsViewController: UIViewController {

// MARK: - Properties

// Complete list of items to search. If you're doing a network request for your search term then you don't need this.
let languages = ["Mandarin Chinese", "English", "Hindustani", "Spanish", "Arabic", "Malay", "Russian", "Bengali", "Portuguese", "French", "Hausa", "Punjabi", "German", "Japanese", "Persian", "Swahili", "Telugu", "Javanese", "Wu Chinese", "Korean"]

// The data source for your search results
var searchResults: [String] = []

// MARK: - IBOutlets

@IBOutlet weak var tableView: UITableView!

// MARK: - UIViewController methods

override func viewDidLoad() {
super.viewDidLoad()

tableView.dataSource = self
}

(…)
}

// MARK: - UISearchResultsUpdating

extension LanguagesSearchResultsViewController: UISearchResultsUpdating {
func updateSearchResults(for searchController: UISearchController) {
guard let searchText = searchController.searchBar.text else { return }
searchResults = languages.filter { $0.contains(searchText) }
tableView.reloadData()
}
}

// MARK: - UITableViewDataSource

extension LanguagesSearchResultsViewController: UITableViewDataSource {
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}

func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return searchResults.count
}

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard let cell = tableView.dequeueReusableCell(withIdentifier: "LanguageTableViewCell") as? LanguageTableViewCell else { return UITableViewCell() }

let language = searchResults[indexPath.row]

cell.languageNameLabel.text = language
return cell
}
}

If you still want a search bar in your titleView with the show/hide behaviour you describe, you will need something a bit more custom, with or without using a UISearchController. There are already many answers to other questions that may help you do that. Hopefully this will have helped clarify your options.

Unable to add UISearchController's search bar into navigationbar and delegate methods not getting called

Just define UISearchController as property then everything working fine.

I have tested below in the sample project.

  class ViewControllerName: UIViewController{
let searchController = UISearchController(searchResultsController: nil)

override func viewDidLoad() {
super.viewDidLoad()
let search = UISearchController(searchResultsController: nil)
self.navigationItem.searchController = search
self.navigationItem.searchController!.searchBar.delegate = self
self.navigationItem.searchController!.searchResultsUpdater = self
self.navigationItem.searchController?.searchBar.showsCancelButton = true
}

func searchBarTextDidEndEditing(_ searchBar: UISearchBar) {
print("Called")
}

func updateSearchResults(for searchController: UISearchController) {
print("Called")
}
}

Hope it will work for you.

UISearchController how to activate from SwiftUI

To activate a UISearchBar (which is what you're using), just do:

searchController.searchBar.becomeFirstResponder()

(from this answer)

Now all we need to do is reference searchController.searchBar from the SwiftUI view. First, add a function to your SearchBar class.

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
}

/// add this function
func activate() {
searchController.searchBar.becomeFirstResponder()
}
}

Then, just call it. I think this is better than setting a @State, but if you require that, let me know and I'll edit my answer.

struct ContentView: View {
@StateObject var searchBar = SearchBar()

var body: some View {
NavigationView {
Button(action: {
searchBar.activate() /// activate the search bar
}) {
Text("Activate search bar")
}
.modifier(SearchBarModifier(searchBar: searchBar))
.navigationTitle("Navigation View")
}
}
}

Result:

Press custom button to activate search bar

How do I set the UISearchController's searchBar to a view that isn't the tableHeaderView or navigationItem.titleview?

If you have a blank UIView that is placed above the tableview.

let's assume you have an outlet to that blank UIView called searchContainer.
Then you can add the search bar of the UISearchController to that view by adding the following line

searchContainer.addSubview(searchController.searchBar)

How to hide UISearchController in UINavigationBar

If you want to hide entire navigation bar and if you want to use your own navigation bar, you can first hide navigation controller's navigation bar.

[self.navigationController setNavigationBarHidden:YES];
//OR
[self.navigationController setNavigationBarHidden:YES animated:NO];

or if you have used custom title view for navigation bar, you can do

self.navigationItem.titleView.hidden = YES;

or if you want to hide back bar button item, you can do

self.navigationItem.hidesBackButton = TRUE;

or if you want to only hide title you can do

self.navigationItem.title = @"";

And

self.navigationController.navigationItem.titleView = nil ;

How to put search bar in navigation bar programmatically? - Swift

You need to create an SearchController and set it up to show the results in another TableViewController:

ViewController

import UIKit

class ViewController: UIViewController, UISearchControllerDelegate {

var controladorDeBusca: UISearchController!

var resultsTableViewController: ResultsTableViewController?

override func viewDidLoad() {
super.viewDidLoad()

resultsTableViewController = storyboard!.instantiateViewController(withIdentifier: "resultsTableViewController") as? ResultsTableViewController

configurarControladorDeBusca()
}

func configurarControladorDeBusca() {

controladorDeBusca = UISearchController(searchResultsController: resultsTableViewController)
controladorDeBusca.delegate = self
controladorDeBusca.searchResultsUpdater = resultsTableViewController
controladorDeBusca.dimsBackgroundDuringPresentation = true
definesPresentationContext = true

controladorDeBusca.loadViewIfNeeded()

//Configura a barra do Controlador de busca
controladorDeBusca.searchBar.delegate = resultsTableViewController
controladorDeBusca.hidesNavigationBarDuringPresentation = false
controladorDeBusca.searchBar.placeholder = "Search place"
controladorDeBusca.searchBar.sizeToFit()
controladorDeBusca.searchBar.barTintColor = navigationController?.navigationBar.barTintColor
controladorDeBusca.searchBar.tintColor = self.view.tintColor

//Adiciona a barra do Controlador de Busca a barra do navegador
navigationItem.titleView = controladorDeBusca.searchBar
}
}

ResultsTableViewController

import UIKit

class ResultsTableViewController: UITableViewController, UISearchResultsUpdating, UISearchBarDelegate {

var array = ["Brazil", "Bolivia", "United States", "Canada", "England", "Germany", "France", "Portugal"]

var arrayFilter = [String]()

// MARK: - Table view data source

override func numberOfSections(in tableView: UITableView) -> Int {
// #warning Incomplete implementation, return the number of sections
return 1
}

override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete implementation, return the number of rows
return arrayFilter.count
}

override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "searchCell", for: indexPath)

cell.textLabel?.text = arrayFilter[indexPath.row]

return cell
}

func updateSearchResults(for searchController: UISearchController) {

}

func searchBarSearchButtonClicked(_ searchBar: UISearchBar) {

arrayFilter.removeAll()

if let text = searchBar.text {
for string in array {
if string.contains(text) {
arrayFilter.append(string)
}
}
}

tableView.reloadData()
}

func searchBarCancelButtonClicked(_ searchBar: UISearchBar) {

arrayFilter.removeAll()
tableView.reloadData()
}

func searchBarShouldBeginEditing(_ searchBar: UISearchBar) -> Bool {

arrayFilter.removeAll()
tableView.reloadData()

return true
}
}

Sample Image

UISearchController disable cancel UIBarButtonItem

Updated in light of comments

UISearchBar has a property (see the Apple docs) which determines whether the cancel button is displayed:

self.searchBar.showsCancelButton = false;

But, as per OP comments, this does not work, because the searchController keeps switching the cancel button back on. To avoid this, create a subclass of UISearchBar, and override the setShowsCancelButton methods:

@implementation MySearchBar

-(void)setShowsCancelButton:(BOOL)showsCancelButton {
// Do nothing...
}

-(void)setShowsCancelButton:(BOOL)showsCancelButton animated:(BOOL)animated {
// Do nothing....
}

@end

To ensure this subclass is used by the searchController, we also need to subclass UISearchController, and override the searchBar method to return an instance of our subclass. We also need to ensure that the new searchBar activates the searchController - I've chosen to use the UISearchBarDelegate method textDidChange for this:

@interface MySearchController ()  <UISearchBarDelegate> {
UISearchBar *_searchBar;
}
@end

@implementation MySearchController

-(UISearchBar *)searchBar {
if (_searchBar == nil) {
_searchBar = [[MySearchBar alloc] initWithFrame:CGRectZero];
_searchBar.delegate = self;
}
return _searchBar;
}

-(void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText {
if ([searchBar.text length] > 0) {
self.active = true;
} else {
self.active = false;
}
}
@end

Finally, change your code to instantiate this subclass:

self.searchController = [[MySearchController alloc] initWithSearchResultsController:self.resultsViewController];

(You will obviously need to import the relevant header files for these subclasses).

How to disable cancel button of UISearchController.seachBar when i tap at searchBar?

You can create a custom class and subclass UISearchBar and UISearchViewController.
For example:-

class CustomizedSearchBar: UISearchBar {
override func layoutSubviews() {
super.layoutSubviews()
setShowsCancelButton(false, animated: false)
}
}

Now Create Object of the CustomizedSearchBar and use it within other viewController.

Or you can create a customized searchViewController as follows:

class CustomizedSearchController: UISearchController, UISearchBarDelegate {

lazy var _searchBar: CustomSearchBar = {
[unowned self] in
let result = CustomSearchBar(frame: CGRectZero)
result.delegate = self

return result
}()

override var searchBar: UISearchBar {
get {
return _searchBar
}
}
}

Please follow this link for more detail information.



Related Topics



Leave a reply



Submit