Ich habe eine UISearchBar, die als Live-Filter für eine Tabellenansicht fungiert. Wenn die Tastatur über endEditing: geschlossen wird, bleiben der Abfragetext und die graue Schaltfläche "Löschen" erhalten. Wenn Sie hier auf die graue Schaltfläche "Löschen" tippen, wird die Tastatur wieder angezeigt, wenn der Text gelöscht wird.
Wie kann ich das verhindern? Wenn die Tastatur derzeit nicht geöffnet ist, möchte ich, dass diese Schaltfläche den Text löscht, ohne die Tastatur erneut zu öffnen.
Es gibt eine Protokollmethode, die aufgerufen wird, wenn ich auf die Schaltfläche "Löschen" tippe. Das Senden der UISearchBar-Nachricht resignFirstResponder hat jedoch keine Auswirkungen auf die Tastatur.
Dies ist eine alte Frage und ich bin gerade auf das gleiche Problem gestoßen und habe es auf folgende Weise gelöst:
Wenn die searchBar:textDidChange:
-Methode des UISearchBarDelegate aufgerufen wird, weil der Benutzer auf die Schaltfläche "Clear" tippt, ist die searchBar noch nicht die erste Antwort, so dass wir dies nutzen können, um festzustellen, wann der Benutzer tatsächlich löschen wollte die Suche und nicht den Fokus auf die Suchleiste bringen und/oder etwas anderes tun.
Um dies zu verfolgen, müssen wir in unserem viewController einen BOOL
ivar deklarieren, der auch der searchBar-Delegat ist (nennen wir ihn shouldBeginEditing
) und den Anfangswert von YES
setzen (vorausgesetzt, unsere viewController-Klasse heißt 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
Später implementieren wir in UISearchBarDelegate die Methoden searchBar:textDidChange:
und 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;
}
Im Grunde ist es das.
Beste
Ich habe festgestellt, dass der resignFirstResponder nicht funktioniert, wenn textDidChange von einer Berührung zum "clear button" aufgerufen wird. Die Verwendung von performSelection: withObject: afterDelay:
scheint jedoch eine effektive Problemumgehung zu sein:
- (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];
}
Ich habe einen ziemlich sicheren Weg gefunden, um zu erfahren, ob die Schaltfläche "Löschen" gedrückt wurde, und ignoriere die Zeiten, zu denen der Benutzer nur das letzte Zeichen der UISearchBar löscht. Hier ist es :
- (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 !");
}
}
Ziemlich einfach und unkompliziert, nicht wahr :)? Die einzige Sache, die Sie beachten sollten, ist, dass, wenn der Benutzer beim Bearbeiten des UITextFields des UISearchBar auf die Schaltfläche "Löschen" klickt, zwei Pings vorhanden sind, wohingegen Sie nur einen erhalten, wenn der Benutzer darauf klickt, wenn er nicht bearbeitet wird.
Edit: .__ Ich kann es nicht testen, aber hier ist die Swift-Version gemäß 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 !")
}
}
@ Rotems Update (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!")
}
}
Ich habe eine Kombination aus @ bolivas Antwort und auch @ radiospiels answer auf eine andere SO Frage verwendet:
@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
Die beste Lösung aus meiner Erfahrung ist, einfach eine UIButton
(mit klarem Hintergrund und ohne Text) über die Schaltfläche zum Löschen des Systems zu setzen und dann eine IBAction
zu verbinden
Mit Autolayout ist das mehr als einfach
- (IBAction)searchCancelButtonPressed:(id)sender {
[self.searchBar resignFirstResponder];
self.searchBar.text = @"";
// some of my stuff
self.model.fastSearchText = nil;
[self.model fetchData];
[self reloadTableViewAnimated:NO];
}
Durch Drücken der Schaltfläche "clear" in einer UISearchBar wird die Tastatur automatisch geöffnet, auch wenn Sie searchBar.resignFirstResponder()
innerhalb der textDidChange
UISearchBarDelegate-Methode aufrufen.
Verwenden Sie den folgenden Code, um die Tastatur tatsächlich auszublenden, wenn Sie "x" drücken, um die UISearchBar zu löschen. Selbst wenn textDidChange
aufgerufen wird, während Sie etwas in die UISearchBar eingeben, blenden Sie die Tastatur nur aus, wenn Sie den gesamten Text darin löschen.
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.
}
}
}
Für diejenigen von Ihnen, die UISearchController
in iOS 8 oder höher verwenden, möchten Sie einfach die UISearchController
-Klasse unterteilen. Der Vollständigkeit halber sollten Sie auch die Schaltfläche cancel ausblenden, wie ich es tat, da das Löschen des Texts aus der UISearchBar
tatsächlich ein Abbruch der Suche ist. Ich habe diesen Code unten hinzugefügt, wenn Sie ihn verwenden möchten.
Der Vorteil davon ist, dass Sie dies für jede Klasse und Ansicht verwenden können, anstatt eine Unterklasse von UIViewController
zu benötigen. Ich füge sogar hinzu, wie ich meine UISearchController
am Ende dieser Lösung initialisiere.
Diese Klasse muss nur überschrieben werden, wenn Sie die Abbrechen-Schaltfläche wie ich ausblenden möchten. Die Markierung von searchController.searchBar.showsCancelButton = NO
scheint in iOS 8 nicht zu funktionieren. Ich habe iOS 9 nicht getestet.
Leer, aber der Vollständigkeit halber hier platziert.
@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
Hier möchten Sie die wirklichen Änderungen vornehmen. Ich teile das UISearchBarDelegate
in seine eigene Kategorie auf, weil meiner Meinung nach die Klassen sauberer und leichter zu pflegen sind. Wenn Sie den Delegaten innerhalb der Hauptklassenschnittstelleimplementierung behalten möchten, können Sie dies gerne tun.
@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
Einige Punkte zu beachten:
BOOL
als private Variablen anstelle von Eigenschaften angegeben, da searchBar
der erste Responder ist. Ist dies nicht der Fall, wird der Such-Controller deaktiviert, da der Text leer ist und wir nicht mehr suchen. Wenn Sie/- wirklich sicher sein möchten, können Sie auch searchText.length == 0
sicherstellen.searchBar:textDidChange:
wird vor searchBarShouldBeginEditing:
aufgerufen. Deshalb haben wir es in dieser Reihenfolge behandelt.[self.searchResultsUpdater updateSearchResultsForSearchController:self];
nach searchBarSearchButtonClicked:
verschieben, wenn die Suche nur ausgeführt werden soll, nachdem der Benutzer die Taste Search gedrückt hat.Durch Berühren der Löschtaste wird searchText
leer. Eine andere Möglichkeit, dies zu erreichen, ist die Überprüfung auf leeren Text in - (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;
}
Bei Delegate-Aufrufen der Suchleiste werden Sie aufgefordert, eine Änderung von einem alten zu einem neuen Wert zu akzeptieren. Sie können feststellen, dass der neue Wert null ist, der alte Wert ist nicht null und ein Hinweis darauf, dass der Benutzer dies nicht getan hat tippte irgendetwas, seit die Tastatur zuletzt aufgestanden war - dann müssen Sie den Erstbeantworter für die Suchleiste aufgeben. Nicht sicher, ob die Tastatur vorübergehend angezeigt wird.
Ich habe eine sehr ähnliche Situation und kann das selbst versuchen.
Warum löschen Sie in Ihrer endEditing-Methode nicht die UISearchBar und dort? Da das auch sein muss, wo Sie den Ersthelfer auch kündigen, ist es sinnvoll.
Eine schnelle Version der Antwort von @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
}
}