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.
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:
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
}
}
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
Sharing File Data Between Applications in Swift/Ios
How to Parse Firestore Fieldvalue to Date in Swift
Replace Exactly One Pixel in an Image and Put It in Another Image via Swift
How to Make Xcode Put Starting Brace on New Line in Swift
Testing If a Decimal Is a Whole Number in Swift
Swift: Can Not Use Array Filter in If Let Statement Condition
What's Wrong with My #If Target_Os_Simulator Code for Realm Path Definition
How Does Appdelegate.Swift Replace Appdelegate.H and Appdelegate.M in Xcode 6.3
Making a Uibutton a % of the Screen Size
Problem with Gesture in Xcode 12 and iOS 14
I Have Trouble Using Cornerradius and Borders on a Textfield in Swiftui
iOS Firebase: Firauthuidelegate.Authui Not Being Called
Swift 2: Invalid Conversion from Throwing Function of Type to Non-Throwing Function
Swift Package Manager Unable to Compile Ncurses Installed Through Homebrew
How to Create a Circle with Rounded Ends for Each Quadrant