it-swarm.com.de

UISearchbar clearButton zwingt die Tastatur zur Anzeige

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.

43
Jens Kohl

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

120
boliva

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];   
}
33

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!")
    }
}
9
CyberDandy

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
8
benvolioT

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];

}
3
Peter Lapisu

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.
        }
    }
}
1
luismgb

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.

FJSearchBar

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.

FJSearchBar.h

Leer, aber der Vollständigkeit halber hier platziert.

@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

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.

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

Einige Punkte zu beachten:

  1. Ich habe die Suchleiste und die BOOL als private Variablen anstelle von Eigenschaften angegeben, da
    • Sie sind leichter als private Immobilien.
    • Sie müssen von der Außenwelt nicht gesehen oder modifiziert werden.
  2. Wir prüfen, ob 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.
  3. searchBar:textDidChange: wird vor searchBarShouldBeginEditing: aufgerufen. Deshalb haben wir es in dieser Reihenfolge behandelt.
  4. Ich aktualisiere die Suchergebnisse jedes Mal, wenn sich der Text ändert, Sie können jedoch [self.searchResultsUpdater updateSearchResultsForSearchController:self]; nach searchBarSearchButtonClicked: verschieben, wenn die Suche nur ausgeführt werden soll, nachdem der Benutzer die Taste Search gedrückt hat.
0
mikeho

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;
}
0
H K

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.

0
Corey Floyd

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
    }
}
0
swordray