Hiding Cancel button on search bar in UISearchController - ios

I'm trying to hide the Cancel button of the search bar in the UISearchController, but unfortunately setting the following in viewDidLoad() does not work:
override func viewDidLoad() {
super.viewDidLoad()
searchResultsTableController = UITableViewController()
searchResultsTableController.tableView.delegate = self
searchController = UISearchController(searchResultsController: searchResultsTableController)
searchController.searchResultsUpdater = self
searchController.searchBar.sizeToFit()
searchResultsView.tableHeaderView = searchController.searchBar
searchController.delegate = self
searchController.dimsBackgroundDuringPresentation = false
searchController.searchBar.delegate = self
searchController.searchBar.searchBarStyle = .Minimal
searchController.searchBar.showsCancelButton = false
definesPresentationContext = true
}
I have also tried using the above code in this delegate method:
func didPresentSearchController(searchController: UISearchController) {
searchController.searchBar.showsCancelButton = false
}
This approach works but will show the Cancel button briefly before hiding it, which is not ideal. Any suggestions?

I ended up subclassing both UISearchBar and UISearchController as suggested:
CustomSearchBar.swift
import UIKit
class CustomSearchBar: UISearchBar {
override func layoutSubviews() {
super.layoutSubviews()
setShowsCancelButton(false, animated: false)
}
}
CustomSearchController.swift
import UIKit
class CustomSearchController: 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
}
}
}

Rejoice! As of iOS 13, there is access to automaticallyShowsCancelButton on UISearchController. Set it to false to hide the cancel button.

func didPresentSearchController(_ searchController: UISearchController) {
searchController.searchBar.becomeFirstResponder()
searchController.searchBar.showsCancelButton = true
}
func didDismissSearchController(_ searchController: UISearchController) {
searchController.searchBar.showsCancelButton = false
}
In my case all the above solutions dint work. You need to show in didPresentSearchController and hide it in didDismissSearchController. Earlier I was just hiding in didDismissSearchController which was still showing Cancel button on cancelling.

Hide the Cancel button in search bar delegate methods and set your delegate searchController.searchBar.delegate=self UISearchBarDelegate
func searchBarTextDidBeginEditing(searchBar: UISearchBar) {
}
func searchBarTextDidEndEditing(searchBar: UISearchBar) {
}

Try subclassing UISearchBar and implement:
override func layoutSubviews() {
super.layoutSubviews()
self.setShowsCancelButton(false, animated: false)
}
This SO thread may help you more in this direction.

The same answer as given by #Griffith and #Abhinav but using extension:
extension UISearchBar {
override open func layoutSubviews() {
super.layoutSubviews()
setShowsCancelButton(false, animated: false)
}
}
This code is from Swift 4.

Related

How to focus search bar on barbutton click?

I am trying to autofocus a searchbar when i click on a bar button, but it does not seem to work with becomeFirstResponder, follow the GIF:
I've already tried using becomeFirstResponder and it does not work, this is how i am handling the searchbar:
// MARK: SearchBar Delegate
#IBAction func showSearchBarAction(_ sender: Any) {
let searchController = UISearchController(searchResultsController: nil)
searchController.searchBar.delegate = self
searchController.searchResultsUpdater = self
searchController.searchBar.keyboardAppearance = .dark
searchController.dimsBackgroundDuringPresentation = false
navigationItem.searchController = searchController
navigationItem.searchController?.isActive = true
}
func searchBarCancelButtonClicked(_ searchBar: UISearchBar) {
navigationItem.searchController?.isActive = false
self.navigationItem.searchController = nil
}
All of it works perfectly, but the autofocus is something i really want and i can't change my design as well
Solution
This code is the solution:
This is how my class declaration looks like:
class YourViewController: UITableViewController, UISearchResultsUpdating, UISearchBarDelegate, UISearchControllerDelegate {
// MARK: SearchBar Delegate
#IBAction func showSearchBarAction(_ sender: Any) {
let searchController = UISearchController(searchResultsController: nil)
searchController.delegate = self
searchController.searchBar.delegate = self
searchController.searchResultsUpdater = self
searchController.searchBar.keyboardAppearance = .dark
searchController.dimsBackgroundDuringPresentation = false
self.navigationItem.searchController = searchController
self.navigationItem.searchController?.isActive = true
}
func didPresentSearchController(_ searchController: UISearchController) {
DispatchQueue.main.async {
searchController.searchBar.becomeFirstResponder()
}
}
func searchBarCancelButtonClicked(_ searchBar: UISearchBar) {
navigationItem.searchController?.isActive = false
self.navigationItem.searchController = nil
}

UISearchController landscape orientation layout issue

In landscape orientation UISearchController getting UI glitch issue. Using Xcode 10.2. There is not built-in navigationBar used.
Landscape Preview
Portrait Preview
Instance declaration
let searchController = UISearchController(searchResultsController: nil)
#IBOutlet weak var vwSearch: UIView!
#IBOutlet weak var vwSearchHeightConstant: NSLayoutConstraint!
Initialization in viewDidload
func setupSerachResultController()
{
searchController.searchResultsUpdater = self
searchController.obscuresBackgroundDuringPresentation = true
searchController.searchBar.placeholder = "Search Email"
searchController.searchBar.searchBarStyle = .minimal
searchController.dimsBackgroundDuringPresentation = false
searchController.searchBar.scopeBarBackgroundImage = UIImage.imageWithColor(color: UIColor.clear)
searchController.searchBar.tintColor = UIColor.white
vwSearch.addSubview(searchController.searchBar)
searchController.searchBar.scopeButtonTitles = ["One","Two","Three","Four"]
searchController.searchBar.delegate = self
}
Initial Preview
UISearchBarDelegate methods
extension ListViewController: UISearchBarDelegate,UISearchResultsUpdating {
func searchBar(_ searchBar: UISearchBar, selectedScopeButtonIndexDidChange selectedScope: Int) {
}
func searchBarShouldBeginEditing(_ searchBar: UISearchBar) -> Bool {
UIView.animate(withDuration: 0.3) {
self.vwSearchHeightConstant.constant = 100
self.view.layoutIfNeeded()
}
return true
}
func updateSearchResults(for searchController: UISearchController) {
let searchBar = searchController.searchBar
let scope = searchBar.scopeButtonTitles![searchBar.selectedScopeButtonIndex]
}
func searchBarCancelButtonClicked(_ searchBar: UISearchBar) {
UIView.animate(withDuration: 0.3) {
self.vwSearchHeightConstant.constant = 60
self.view.layoutIfNeeded()
}
}
}
As shown in initial preview there is not scope bar visible so height
of vwSearch 60, which managed using vwSearchHeightConstant constraint. For scope bar height changed to 100, which toggle managed in searchBarShouldBeginEditing and searchBarCancelButtonClicked.
Any help much appreciated.
Thank you.

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

i want to disable right cancel button when a tap at search Bar.
Because of using Google Place search i put i should use searchController?.searchBar
I try to disable cancel button at
func presentSearchController(_ searchController: UISearchController) {
searchController.searchBar.showsCancelButton = false
}
but when i tap a searchBar i see how cancel button appear and disappear, thats looks ugly
Please give me advice!
override func viewDidLoad() {
super.viewDidLoad()
resultsViewController = GMSAutocompleteResultsViewController()
resultsViewController?.delegate = self
searchController = UISearchController(searchResultsController: resultsViewController)
searchController?.searchResultsUpdater = resultsViewController
searchController?.searchBar.sizeToFit()
navigationItem.titleView = searchController?.searchBar
searchController?.searchBar.placeholder = searchBarPlaceholderText
searchController?.searchBar.tintColor = #colorLiteral(red: 0.1019607843, green: 0.5490196078, blue: 1, alpha: 1)
searchController?.searchBar.delegate = self
searchController?.delegate = self
searchController?.searchBar.searchBarStyle = .prominent
definesPresentationContext = true
mapView.delegate = self
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyBest
checklocationAuthorizationStatus()
locationManager.requestWhenInUseAuthorization()
locationManager.requestLocation()
locationManager.startUpdatingLocation()
}
func presentSearchController(_ searchController: UISearchController) {
searchController.searchBar.showsCancelButton = false
}
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.

UISearchController can't set delegate to self

I'm trying to get a UISearchController working in my navigation bar using swift3
I currently have it so a search icon exists in the top right corner. When you click on the icon, it will show you the UISearchController.
The problem is, when I try to do searchController.delegate = self, I get an error saying
Cannot assign value of type 'ParentViewController' to type 'UISearchControllerDelegate'?
I'm assuming since I can't set the delegate, none of the actions are linked, but I can't figure out why it won't let me?
Here's the code I'd like to work. Am I implementing the protocol wrong and just not seeing it, or missing something entirely?
class ParentViewController: ButtonBarPagerTabStripViewController, UISearchResultsUpdating, UISearchBarDelegate {
var searchBarButtonItem: UIBarButtonItem?
override func viewDidLoad() {
searchBarButtonItem = navigationItem.rightBarButtonItem
super.viewDidLoad()
}
func updateSearchResults(for searchController: UISearchController) {
print("update search results")
}
func searchBarCancelButtonClicked(_ searchBar: UISearchBar) {
print("search cancelled")
}
func filterContentForSearchText(searchText: String, scope: String = "All") {
print("Filter: " + searchText)
}
#IBAction func startSearchAction(sender: UIBarButtonItem) {
print("Clicked")
let searchController = UISearchController(searchResultsController: nil)
searchController.delegate = self
searchController.searchResultsUpdater = self
searchController.searchBar.delegate = self
searchController.hidesNavigationBarDuringPresentation = false
searchController.searchBar.showsCancelButton = true
searchController.dimsBackgroundDuringPresentation = true
self.navigationItem.titleView = searchController.searchBar
definesPresentationContext = true
navigationItem.setRightBarButton(nil, animated: true)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}

Show keyboard automatically when UISearchController is loaded

I created a UISearchController in a table view controller. I segue to this table view controller using a push segue from another view controller. I want the keyboard to show up with the cursor in the search bar as soon as the table view controller is pushed.
I made the search controller active in the viewDidLoad method using
self.mySearchController.active = true
It does make the search controller active but this does not bring up the keyboard nor is the cursor placed in the search bar. I also tried
self.mySearchController.searchBar.becomeFirstResponder()
This line does not seem to have any effect.
How do I bring up the keyboard automatically/programmatically? Below is a more detailed version of my code
class PickAddressViewController: UITableViewController, UISearchResultsUpdating {
var searchText = ""
var mySearchController = UISearchController()
override func viewDidLoad() {
super.viewDidLoad()
self.mySearchController = ({
let controller = UISearchController(searchResultsController: nil)
controller.searchResultsUpdater = self
controller.dimsBackgroundDuringPresentation = false
controller.searchBar.sizeToFit()
controller.searchBar.text = self.searchText
self.tableView.tableHeaderView = controller.searchBar
return controller
})()
self.mySearchController.active = true
self.mySearchController.searchBar.becomeFirstResponder()
}
BecomeFirstResponder is the way to go, but you should do it not in viewDidLoad. Look at following discussion for details - Cannot set searchBar as firstResponder
I also tried the suggestions listed in the link mentioned by Nikita Leonov. I needed to add make the class a UISearchControllerDelegate & UISearchBarDelegate and then it worked. I don't u
class PickAddressViewController: UITableViewController, UISearchControllerDelegate, UISearchBarDelegate, UISearchResultsUpdating {
override func viewDidLoad() {
super.viewDidLoad()
self.mySearchController = ({
controller.searchBar.delegate = self
})()
self.mySearchController.active = true
self.mySearchController.delegate = self
}
func didPresentSearchController(searchController: UISearchController) {
self.mySearchController.searchBar.becomeFirstResponder()
}
…
}
Swift 5
in viewDidLoad:
searchViewController.delegate = self
in viewDidAppear:
searchViewController.isActive = true
This activates the SearchController
Define a delegate method:
extension MyViewController: UISearchControllerDelegate {
func didPresentSearchController(_ searchController: UISearchController) {
DispatchQueue.main.async {
searchController.searchBar.becomeFirstResponder()
}
}
}
Swift 3 solution in my case:
override func viewDidLoad() {
super.viewDidLoad()
navigationItem.titleView = mySearchController.searchBar
mySearchController.searchResultsUpdater = self
mySearchController.delegate = self
}
override func viewDidAppear(_ animated: Bool) {
DispatchQueue.main.async {
self.mySearchController.isActive = true
}
}
func presentSearchController(_ searchController: UISearchController) {
mySearchController.searchBar.becomeFirstResponder()
}
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(true)
self.navigationItem.titleView = searchController!.searchBar
dispatch_async(dispatch_get_main_queue(), {
self.searchController?.active = true
self.searchController!.searchBar.becomeFirstResponder()
})
}
and this code
func presentSearchController(searchController: UISearchController) {
searchController.searchBar.becomeFirstResponder()
}
make sure you give
searchController?.delegate = self in viewDidLoad(). Tested on iOS 9.* device
Besides doing what the other users suggested, I also did the following, and it worked:
searchController.definesPresentationContext = true

Resources