J'ai un UISearchBar qui agit comme un filtre en direct pour une vue de table. Lorsque le clavier est désactivé via endEditing :, le texte de la requête et le bouton gris "transparent" demeurent. À partir de là, si j'appuie sur le bouton gris "clear", le clavier réapparaît lorsque le texte est effacé.
Comment puis-je empêcher cela? Si le clavier n'est pas ouvert, je souhaite que ce bouton efface le texte sans rouvrir le clavier.
Il existe une méthode de protocole qui est appelée lorsque je tape sur le bouton d'effacement. Toutefois, l'envoi d'un message resignFirstResponder à UISearchBar n'a aucun effet sur le clavier.
C'est une vieille question et je viens de rencontrer le même problème et j'ai réussi à le résoudre de la manière suivante:
Lorsque la méthode searchBar:textDidChange:
de UISearchBarDelegate est appelée en raison du fait que l'utilisateur a cliqué sur le bouton 'effacer', le searchBar n'est pas encore devenu le premier répondeur. Nous pouvons donc en tirer parti pour détecter le moment précis où l'utilisateur voulait effacer la recherche et ne pas mettre en évidence le searchBar et/ou faire autre chose.
Pour garder une trace de cela, nous devons déclarer un ivar BOOL
dans notre viewController qui est également le délégué searchBar (appelons-le shouldBeginEditing
) et le définir avec une valeur initiale de YES
(supposons que notre classe viewController s'appelle 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
Plus tard, dans le UISearchBarDelegate, nous implémentons les méthodes searchBar:textDidChange:
et 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;
}
En gros, c'est ça.
Meilleur
J'ai constaté que resignFirstResponder ne fonctionnait pas lorsque textDidChange était appelé d'une touche sur le "bouton d'effacement". Cependant, utiliser performSelection: withObject: afterDelay:
semble être une solution de contournement efficace:
- (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];
}
J'ai trouvé un moyen assez sûr de savoir si le bouton Effacer a été enfoncé et d'ignorer les moments où l'utilisateur supprime simplement le dernier caractère de la barre UISearchBar. C'est ici :
- (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 !");
}
}
Assez simple et simple, n'est-ce pas :)? La seule chose à noter est que si l'utilisateur clique sur le bouton d'effacement lors de la modification de l'UITextField de UISearchBar, vous aurez deux pings, alors que vous n'en obtiendrez qu'une si l'utilisateur clique dessus sans la modifier.
Edit: Je ne peux pas le tester, mais voici la version Swift, selon 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 !")
}
}
@ Mise à jour 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!")
}
}
J'ai utilisé une combinaison de la réponse de @ boliva et de celle de @ radiospiel answer à une autre SO question:
@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
La meilleure solution de mon expérience est simplement de mettre une UIButton
(avec un arrière-plan clair et aucun texte) au-dessus du bouton d'effacement du système et de connecter une IBAction
Avec l'autolayout, c'est plus que facile
- (IBAction)searchCancelButtonPressed:(id)sender {
[self.searchBar resignFirstResponder];
self.searchBar.text = @"";
// some of my stuff
self.model.fastSearchText = nil;
[self.model fetchData];
[self reloadTableViewAnimated:NO];
}
En appuyant sur le bouton "effacer" dans une barre UISearchBar, le clavier s'ouvre automatiquement même si vous appelez searchBar.resignFirstResponder()
dans la méthode textDidChange
UISearchBarDelegate.
Pour masquer le clavier lorsque vous appuyez sur le «x» pour effacer le UISearchBar, utilisez le code suivant. Ainsi, même si vous appelez textDidChange
lors de la saisie dans la barre UISearchBar, vous ne masquez le clavier que si vous supprimez tout le texte qu'il contient, que vous utilisiez le bouton Supprimer ou que vous cliquez sur le "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.
}
}
}
Pour ceux d'entre vous qui utilisent la UISearchController
dans iOS 8 et versions ultérieures, vous voudrez simplement sous-classer la UISearchController
. Par souci d’exhaustivité, vous pouvez également souhaiter masquer le bouton cancel, comme je l’avais fait, car effacer le texte de la variable UISearchBar
est en réalité une annulation de la recherche. J'ai ajouté le code ci-dessous si vous souhaitez l'utiliser.
L'avantage de ceci est que vous pourrez l'utiliser pour n'importe quelle classe et vue, plutôt que d'exiger une sous-classe de UIViewController
. Je vais même inclure comment j'initialise ma UISearchController
au bas de cette solution.
Cette classe ne doit être remplacée que si vous souhaitez masquer le bouton d'annulation comme je l'ai fait. Le marquage searchController.searchBar.showsCancelButton = NO
ne semble pas fonctionner dans iOS 8. Je n'ai pas testé iOS 9.
Vide, mais placé ici pour être complet.
@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
Voici où vous voulez faire les vrais changements. Je divise la UISearchBarDelegate
dans sa propre catégorie car, à mon humble avis, les catégories rendent les classes plus propres et plus faciles à gérer. Si vous souhaitez conserver le délégué dans l'interface/l'implémentation de la classe principale, n'hésitez pas.
@import UIKit;
@interface FJSearchController : UISearchController
@end
@interface FJSearchController (UISearchBarDelegate) <UISearchBarDelegate>
@end
#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
Quelques pièces à noter:
BOOL
comme variables privées au lieu de propriétés, car searchBar
est le premier intervenant. Si ce n'est pas le cas, nous désactivons le contrôleur de recherche car le texte est vide et nous ne cherchons plus. Si vous (vraiment} _ voulez être sûr, vous pouvez également vous assurer que searchText.length == 0
.searchBar:textDidChange:
est appelé avant searchBarShouldBeginEditing:
, raison pour laquelle nous l'avons traité dans cet ordre.[self.searchResultsUpdater updateSearchResultsForSearchController:self];
vers searchBarSearchButtonClicked:
si vous souhaitez que la recherche soit effectuée uniquement lorsque l'utilisateur appuie sur le bouton Search.Si vous appuyez sur le bouton d'effacement, searchText
est vide. Pour cela, vous pouvez également rechercher du texte vide dans - (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;
}
Parmi les appels de délégués de la barre de recherche vous demande d’accepter le passage d’une ancienne valeur à une nouvelle - vous pouvez détecter que la nouvelle valeur est nulle, l’ancienne valeur étant non-nil et un indicateur indiquant que l’utilisateur n’a pas rien tapé depuis la dernière utilisation du clavier - puis dans ce cas, renoncez au premier répondant pour la barre de recherche. Pas sûr si le clavier va afficher momentanément si.
J'ai une situation très similaire et peut essayer moi-même.
Dans votre méthode endEditing, pourquoi ne pas effacer la barre UISearchBar et là? Etant donné que vous devez également démissionner du premier répondant, cela a du sens.
Une version rapide de la réponse 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
}
}