iphone - example - uisearchbar swift 4
UISearchbar clearButton hace que aparezca el teclado (12)
Al presionar el botón "Borrar" en una barra UISearchBar se abre automáticamente el teclado, incluso si llama a searchBar.resignFirstResponder()
dentro del método textDidChange
UISearchBarDelegate.
Para ocultar el teclado cuando presiona la "x" para borrar la barra UISearch, use el siguiente código. De esta manera, incluso si se llama a textDidChange
mientras se escribe algo en la barra UISearch, solo ocultas el teclado si eliminas todo el texto que contiene, si usas el botón Eliminar o haces clic en la "x":
extension ViewController: UISearchBarDelegate {
func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
if searchBar.text!.count == 0 {
DispatchQueue.main.async {
searchBar.resignFirstResponder()
}
} else {
// Code to make a request here.
}
}
}
Tengo una barra UISearchBar que actúa como un filtro en vivo para una vista de tabla. Cuando el teclado se descarta mediante endEditing :, el texto de consulta y el botón gris circular "borrar" permanecen. Desde aquí, si pulso el botón gris "Borrar", el teclado vuelve a aparecer a medida que se borra el texto.
¿Cómo puedo prevenir esto? Si el teclado no está abierto actualmente, quiero que el botón borre el texto sin volver a abrir el teclado.
Hay un método de protocolo al que se llama cuando toco el botón Borrar. Pero enviar un mensaje resignFirstResponder a la UISearchBar no tiene ningún efecto en el teclado.
Al tocar el botón de borrar, el texto de searchText
está vacío. Otra forma de lograr esto es buscar texto vacío en - (void)searchBar:(UISearchBar *)bar textDidChange:(NSString *)searchText
:
- (void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText
{
if([searchText length] == 0)
{
[self dismissSearch];
}
else
{
self.searchResultsTable.hidden = YES;
[self handleSearchForString:searchText];
}
}
- (void)dismissSearch
{
[self.searchBar performSelector: @selector(resignFirstResponder)
withObject: nil
afterDelay: 0.1];
self.searchResultsTable.hidden = YES;
}
De la barra de búsqueda, las llamadas de delegado le piden que acepte un cambio de un valor antiguo a uno nuevo: podría detectar que el nuevo valor era nulo, junto con el valor antiguo no nulo y un indicador de que el usuario no tenía escribió cualquier cosa desde la última vez que se colocó el teclado; en ese caso, renuncie al primer respondedor para la barra de búsqueda. Aunque no estoy seguro de si el teclado se mostrará momentáneamente.
Tengo una situación muy similar y puedo intentarlo yo mismo.
Descubrí que resignFirstResponder no funciona cuando se llama a textDidChange de un toque al "botón de borrar". Sin embargo, usar performSelection: withObject: afterDelay:
parece ser una solución efectiva:
- (void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText
{
if ([searchText length] == 0) {
[self performSelector:@selector(hideKeyboardWithSearchBar:) withObject:searchBar afterDelay:0];
}
}
- (void)hideKeyboardWithSearchBar:(UISearchBar *)searchBar
{
[searchBar resignFirstResponder];
}
En su método endEditing, ¿por qué no borra la barra UISearch y ahí está? Ya que debe ser donde renuncies al primer respondedor también, tiene sentido.
Encontré una forma bastante segura de saber si se presionó el botón de borrar e ignorar los momentos en que el usuario simplemente eliminó el último carácter de la barra UISearchBar. Aquí está :
- (BOOL)searchBar:(UISearchBar *)searchBar shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text
{
_isRemovingTextWithBackspace = ([searchBar.text stringByReplacingCharactersInRange:range withString:text].length == 0);
return YES;
}
- (void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText
{
if (searchText.length == 0 && !_isRemovingTextWithBackspace)
{
NSLog(@"Has clicked on clear !");
}
}
Bastante simple y directo, ¿no es así? Lo único que se debe tener en cuenta es que si el usuario hace clic en el botón de borrar al editar el UITextField de UISearchBar, tendrá dos pings, mientras que solo obtendrá uno si el usuario hace clic cuando no se está editando.
Edición: no puedo probarlo, pero aquí está la versión rápida, según Rotem:
var isRemovingTextWithBackspace = false
func searchBar(searchBar: UISearchBar, shouldChangeTextInRange range: NSRange, replacementText text: String) -> Bool
{
self.isRemovingTextWithBackspace = (NSString(string: searchBar.text!).stringByReplacingCharactersInRange(range, withString: text).characters.count == 0)
return true
}
func searchBar(searchBar: UISearchBar, textDidChange searchText: String)
{
if searchText.characters.count == 0 && !isRemovingTextWithBackspace
{
NSLog("Has clicked on clear !")
}
}
Actualización de @Rotem (Swift2):
var isRemovingTextWithBackspace = false
func searchBar(searchBar: UISearchBar, shouldChangeTextInRange range: NSRange, replacementText text: String) -> Bool {
self.isRemovingTextWithBackspace = (NSString(string: searchBar.text!).stringByReplacingCharactersInRange(range, withString: text).characters.count == 0)
return true
}
func searchBar(searchBar: UISearchBar, textDidChange searchText: String) {
if searchText.characters.count == 0 && !isRemovingTextWithBackspace {
NSLog("Has clicked on clear!")
}
}
Esta es una pregunta antigua y acabo de encontrar el mismo problema y logré resolverlo de la siguiente manera:
Cuando se searchBar:textDidChange:
método searchBar:textDidChange:
de UISearchBarDelegate debido a que el usuario toca el botón ''borrar'', searchBar no se ha convertido en el primer respondedor, por lo que podemos aprovechar eso para detectar cuándo el usuario de hecho destinado a borrar la búsqueda y no enfocar la barra de búsqueda y / o hacer otra cosa.
Para realizar un seguimiento de eso, debemos declarar un BOOL
ivar en nuestro viewController que también es el delegado de searchBar (llamémoslo shouldBeginEditing
a shouldBeginEditing
) y establecerlo con un valor inicial de YES
(suponiendo que nuestra clase viewController se llama SearchViewController):
@interface SearchViewController : UIViewController <UISearchBarDelegate> {
// all of our ivar declarations go here...
BOOL shouldBeginEditing;
....
}
...
@end
@implementation SearchViewController
...
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
if ((self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil])) {
...
shouldBeginEditing = YES;
}
}
...
@end
Más adelante, en UISearchBarDelegate, implementamos los searchBar:textDidChange:
y searchBarShouldBeginEditing:
:
- (void)searchBar:(UISearchBar *)bar textDidChange:(NSString *)searchText {
NSLog(@"searchBar:textDidChange: isFirstResponder: %i", [self.searchBar isFirstResponder]);
if(![searchBar isFirstResponder]) {
// user tapped the ''clear'' button
shouldBeginEditing = NO;
// do whatever I want to happen when the user clears the search...
}
}
- (BOOL)searchBarShouldBeginEditing:(UISearchBar *)bar {
// reset the shouldBeginEditing BOOL ivar to YES, but first take its value and use it to return it from the method call
BOOL boolToReturn = shouldBeginEditing;
shouldBeginEditing = YES;
return boolToReturn;
}
Básicamente, eso es todo.
Mejor
La mejor solución de mi experiencia es simplemente colocar un UIButton
(con fondo claro y sin texto) sobre el botón de borrar del sistema y luego conectar un IBAction
Con autolayout es más que fácil
- (IBAction)searchCancelButtonPressed:(id)sender {
[self.searchBar resignFirstResponder];
self.searchBar.text = @"";
// some of my stuff
self.model.fastSearchText = nil;
[self.model fetchData];
[self reloadTableViewAnimated:NO];
}
Me he encontrado con esto varias veces ahora. Realmente aprecio las respuestas que la gente ha dado.
En última instancia, realmente me gustaría ver que Apple simplemente nos permite (a los desarrolladores) detectar cuándo se ha presionado el botón Borrar.
Es obvio que al presionarlo se detecta porque se borra cualquier texto en el cuadro de búsqueda.
Supongo que ahora mismo no está muy alto en su lista de prioridades ... ¡Pero realmente deseo que alguien en Apple le dé un poco de amor a UISearchBar!
Para aquellos de ustedes que usan UISearchController
en iOS 8 y superiores, querrá simplemente subclasificar el UISearchController
. Para completar, también puede ocultar el botón de cancelar , como lo hice yo, ya que borrar el texto de la UISearchBar
es una cancelación en la búsqueda. He añadido ese código a continuación si quieres usarlo.
El beneficio de esto es que podrá usar esto para cualquier clase y cualquier vista, en lugar de requerir una subclase de UIViewController
. Incluso UISearchController
cómo inicializo mi UISearchController
en la parte inferior de esta solución.
FJSearchBar
Esta clase solo debe ser anulada si desea ocultar el botón de cancelación como lo hice yo. Marcar searchController.searchBar.showsCancelButton = NO
no parece funcionar en iOS 8 . No he probado iOS 9 .
FJSearchBar.h
Vacío, pero colocado aquí para la integridad.
@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
en su propia categoría porque, en mi humilde opinión, las categorías hacen que las clases sean más limpias y fáciles de mantener. Si desea mantener al delegado dentro de la interfaz / implementación de la clase 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 _clearedOutside;
}
- (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 cleared from outside then we should not allow any new editing
BOOL shouldAllowEditing = !_clearedOutside;
_clearedOutside = 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 cleared the search while not in typing mode, so we should deactivate searching
self.active = NO;
_clearedOutside = YES;
return;
}
// update the search results
[self.searchResultsUpdater updateSearchResultsForSearchController:self];
}
@end
Algunas partes a tener en cuenta:
- He puesto la barra de búsqueda y el
BOOL
como variables privadas en lugar de propiedades porque- Son más ligeros que las propiedades privadas.
- No necesitan ser vistos o modificados por el mundo exterior.
- Verificamos si el
searchBar
es el primer respondedor. Si no lo está, entonces desactivamos el controlador de búsqueda porque el texto está vacío y ya no estamos buscando. Si realmente quiere estar seguro, también puede asegurarse de quesearchText.length == 0
. -
searchBar:textDidChange:
se invoca antes desearchBarShouldBeginEditing:
por lo que lo manejamos en este orden. - Actualizo los resultados de la búsqueda cada vez que cambia el texto, pero es posible que desee mover el
[self.searchResultsUpdater updateSearchResultsForSearchController:self];
tosearchBarSearchButtonClicked:
si solo desea que la búsqueda se realice después de que el usuario presione el botón Buscar .
Una versión rápida de la respuesta de @boliva.
class MySearchContentController: UISearchBarDelegate {
private var searchBarShouldBeginEditing = true
func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
searchBarShouldBeginEditing = searchBar.isFirstResponder
}
func searchBarShouldBeginEditing(_ searchBar: UISearchBar) -> Bool {
defer {
searchBarShouldBeginEditing = true
}
return searchBarShouldBeginEditing
}
}
Utilicé una combinación de la respuesta de @ boliva y la respuesta de @ radiospiel a una pregunta SO diferente:
@interface SearchViewController : UIViewController <UISearchBarDelegate> {
// all of our ivar declarations go here...
BOOL shouldBeginEditing;
....
}
...
@end
@implementation SearchViewController
...
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
if ((self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil])) {
...
shouldBeginEditing = YES;
}
}
...
- (void) searchBar:(UISearchBar *)theSearchBar textDidChange:(NSString *)searchText {
// TODO - dynamically update the search results here, if we choose to do that.
if (![searchBar isFirstResponder]) {
// The user clicked the [X] button while the keyboard was hidden
shouldBeginEditing = NO;
}
else if ([searchText length] == 0) {
// The user clicked the [X] button or otherwise cleared the text.
[theSearchBar performSelector: @selector(resignFirstResponder)
withObject: nil
afterDelay: 0.1];
}
}
- (BOOL)searchBarShouldBeginEditing:(UISearchBar *)bar {
// reset the shouldBeginEditing BOOL ivar to YES, but first take its value and use it to return it from the method call
BOOL boolToReturn = shouldBeginEditing;
shouldBeginEditing = YES;
return boolToReturn;
}
@end