UISearchController desactivar cancelar UIBarButtonItem

El problema

Estoy intentando usar UISearchController para search un destino en una vista de map. Quiero que el UISearchBar aparezca en la barra de navigation, pero parece que no puedo hacerlo sin que aparezca un button de cancelar a la derecha:

introduzca la descripción de la imagen aquí

Este button Cancelar ha desaparecido a veces, mientras estoy jugando, pero no puedo hacer que no aparezca ahora. Tengo la tabla de búsqueda que muestra cómo quiero que aparezca:

introduzca la descripción de la imagen aquí

Estoy seguro de que debe haber algo pequeño que estoy haciendo un poco mal, pero no puedo determinar qué es …

Mi código

self.resultsViewController = [UITableViewController new]; self.searchController = [[UISearchController alloc] initWithSearchResultsController:self.resultsViewController]; self.searchController.searchResultsUpdater = self; self.searchController.hidesNavigationBarDuringPresentation = false; self.searchController.delegate = self; self.searchBar = self.searchController.searchBar; self.searchBar.placeholder = self.stage.title; self.searchBar.searchBarStyle = UISearchBarStyleMinimal; self.definesPresentationContext = true; self.navigationItem.titleView = self.searchBar; self.resultsTableView = self.resultsViewController.tableView; self.resultsTableView.dataSource = self; self.resultsTableView.delegate = self; 

Actualizado a la luz de comentarios

UISearchBar tiene una propiedad (consulte los documentos de Apple ) que determina si se muestra el button cancelar:

 self.searchBar.showsCancelButton = false; 

Pero, según los comentarios de OP, esto no funciona, porque el Controlador de búsqueda sigue volviendo a activar el button cancelar. Para evitar esto, cree una subclass de UISearchBar y anule los methods setShowsCancelButton :

 @implementation MySearchBar -(void)setShowsCancelButton:(BOOL)showsCancelButton { // Do nothing... } -(void)setShowsCancelButton:(BOOL)showsCancelButton animated:(BOOL)animated { // Do nothing.... } @end 

Para garantizar que este subclass sea utilizado por el UISearchController , también necesitamos subclass UISearchController y anular el método searchBar para devolver una instancia de nuestra subclass. También debemos asegurarnos de que la nueva barra de búsqueda active el controller de búsqueda: he elegido utilizar el método textDidChange de textDidChange para esto:

 @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 

Finalmente, cambia tu código para instanciar esta subclass:

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

(Obviamente, necesitará importar los files de encabezado relevantes para estas subclasss).

Hay una forma más fácil …

Para iOS 8 y UISearchController, use este método delegado de UISearchControllerDelegate :

 func didPresentSearchController(searchController: UISearchController) { searchController.searchBar.showsCancelButton = false } 

No olvide establecerse como el delegado: searchController.delegate = self

Solución fácil en Swift3 : necesitamos hacer CustomSearchBar sin cancelar el button y luego anular la propiedad correspondiente en el nuevo CustomSearchController :

 class CustomSearchBar: UISearchBar { override func setShowsCancelButton(_ showsCancelButton: Bool, animated: Bool) { super.setShowsCancelButton(false, animated: false) }} class CustomSearchController: UISearchController { lazy var _searchBar: CustomSearchBar = { [unowned self] in let customSearchBar = CustomSearchBar(frame: CGRect.zero) return customSearchBar }() override var searchBar: UISearchBar { get { return _searchBar } }} 

En MyViewController, inicializo y configuro searchController usando esta nueva subclass personalizada:

  var videoSearchController: UISearchController = ({ // Display search results in a separate view controller // let storyBoard = UIStoryboard(name: "Main", bundle: Bundle.main) // let alternateController = storyBoard.instantiateViewController(withIdentifier: "aTV") as! AlternateTableViewController // let controller = UISearchController(searchResultsController: alternateController) let controller = CustomSearchController(searchResultsController: nil) controller.searchBar.placeholder = NSLocalizedString("Enter keyword (eg iceland)", comment: "") controller.hidesNavigationBarDuringPresentation = false controller.dimsBackgroundDuringPresentation = false controller.searchBar.searchBarStyle = .minimal controller.searchBar.sizeToFit() return controller })() 

Y funciona correctamente y sin problemas.

La respuesta de @pbasdf funciona en su mayor parte, pero al comprobar la longitud del text de searchText para determinar si el UISearchController está activo puede agregar más trabajo al usuario. El caso de la esquina sería si el usuario golpea el button de borrar , o borra el único carácter en la barra de búsqueda. Esto pondría a NO active , lo que automáticamente llamaría resignFirstResponder en el UISearchBar . El keyboard desaparecería y si el usuario desea cambiar el text o ingresar más text, necesitará tocar de nuevo en la barra de búsqueda.

En cambio, solo establezco en NO si la barra de búsqueda no es el primer respondedor (el keyboard no está activo y se muestra), ya que efectivamente es un command de cancelación .

FJSearchBar

Marcando searchController.searchBar.showsCancelButton = NO no parece funcionar en iOS 8 . No he probado iOS 9 .

FJSearchBar.h

Vacío, pero colocado aquí para completar.

 @import UIKit; @interface FJSearchBar : UISearchBar @end 

FJSearchBar.m

 #import "FJSearchBar.h" @implementation FJSearchBar - (void)setShowsCancelButton:(BOOL)showsCancelButton { // do nothing } - (void)setShowsCancelButton:(BOOL)showsCancelButton animated:(BOOL)animated { // do nothing } @end 

FJSearchController

Aquí es donde quieres hacer los cambios reales. UISearchBarDelegate el UISearchBarDelegate en su propia categoría porque, en mi humilde opinión, las categorías hacen que las classs sean más limpias y fáciles de mantener. Si desea mantener al delegado dentro de la interfaz / implementación de la class principal, puede hacerlo.

FJSearchController.h

 @import UIKit; @interface FJSearchController : UISearchController @end @interface FJSearchController (UISearchBarDelegate) <UISearchBarDelegate> @end 

FJSearchController.m

 #import "FJSearchController.h" #import "FJSearchBar.h" @implementation FJSearchController { @private FJSearchBar *_searchBar; BOOL _cleanetworkingOutside; } - (UISearchBar *)searchBar { if (_searchBar == nil) { // if you're not hiding the cancel button, simply uncomment the line below and delete the FJSearchBar alloc/init // _searchBar = [[UISearchBar alloc] init]; _searchBar = [[FJSearchBar alloc] init]; _searchBar.delegate = self; } return _searchBar; } @end @implementation FJSearchController (UISearchBarDelegate) - (BOOL)searchBarShouldBeginEditing:(UISearchBar *)searchBar { // if we cleanetworking from outside then we should not allow any new editing BOOL shouldAllowEditing = !_cleanetworkingOutside; _cleanetworkingOutside = NO; return shouldAllowEditing; } - (void)searchBarSearchButtonClicked:(UISearchBar *)searchBar { // hide the keyboard since the user will no longer add any more input [searchBar resignFirstResponder]; } - (void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText { if (![searchBar isFirstResponder]) { // the user cleanetworking the search while not in typing mode, so we should deactivate searching self.active = NO; _cleanetworkingOutside = YES; return; } // update the search results [self.searchResultsUpdater updateSearchResultsForSearchController:self]; } @end 

Algunas partes a tener en count:

  1. Puse la barra de búsqueda y BOOL como variables privadas en lugar de properties porque
    • Son más ligeros que las properties privadas.
    • No necesitan ser vistos ni modificados por el mundo exterior.
  2. Verificamos si el searchBar es el primer respondedor. Si no es así, entonces desactivamos el controller de búsqueda porque el text está vacío y ya no estamos buscando. Si realmente quiere estar seguro, también puede asegurarse de que searchText.length == 0 .
  3. searchBar:textDidChange: se invoca antes de searchBarShouldBeginEditing: por eso lo manejamos en este order.
  4. Actualizo los resultados de búsqueda cada vez que el text cambia, pero es posible que desee mover el [self.searchResultsUpdater updateSearchResultsForSearchController:self]; to searchBarSearchButtonClicked: si solo desea que la búsqueda se realice después de que el usuario presione el button Buscar .

Podrías hacer esto:

 - (void)willPresentSearchController:(UISearchController *)searchController { dispatch_async(dispatch_get_main_queue(), ^{ searchController.searchBar.showsCancelButton = NO; }); } 

Pude lograr que el UISearchBar comporte como se desee sin subclass llamando a setShowsCancelButton en un par de methods UISearchBarDelegate :

Lo llamo en textDidChange y searchBarCancelButtonClicked . Aquí es cómo se ve mi implementación:

 extension MyViewController: UISearchBarDelegate { func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) { if searchText.characters.isEmpty == false { searchBar.setShowsCancelButton(true, animated: true) // whatever extra stuff you need to do } else { searchBar.setShowsCancelButton(false, animated: true) // whatever extra stuff you need to do } // whatever extra stuff you need to do } func searchBarCancelButtonClicked(_ searchBar: UISearchBar) { searchBar.setShowsCancelButton(false, animated: false) searchBar.text = nil searchBar.resignFirstResponder() tableView.scrollToRow(at: IndexPath(row: 0, section: 0), at: .top, animated: true) // whatever extra stuff you need to do } }