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:
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:
Estoy seguro de que debe haber algo pequeño que estoy haciendo un poco mal, pero no puedo determinar qué es …
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 .
Marcando searchController.searchBar.showsCancelButton = NO
no parece funcionar en iOS 8 . No he probado iOS 9 .
Vacío, pero colocado aquí para completar.
@import UIKit; @interface FJSearchBar : UISearchBar @end
#import "FJSearchBar.h" @implementation FJSearchBar - (void)setShowsCancelButton:(BOOL)showsCancelButton { // do nothing } - (void)setShowsCancelButton:(BOOL)showsCancelButton animated:(BOOL)animated { // do nothing } @end
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.
@import UIKit; @interface FJSearchController : UISearchController @end @interface FJSearchController (UISearchBarDelegate) <UISearchBarDelegate> @end
#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:
BOOL
como variables privadas en lugar de properties porque
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
. searchBar:textDidChange:
se invoca antes de searchBarShouldBeginEditing:
por eso lo manejamos en este order. [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 } }