UISearchbar clearButton заставляет клавиатуру появляться

У меня есть UISearchBar, который действует как живой фильтр для представления таблицы. Когда клавиатура отклоняется с помощью endEditing: текст запроса и серая круглая "четкая" кнопка остаются. Отсюда, если я коснусь серой кнопки "clear", клавиатура снова появляется, когда текст очищается.

Как это предотвратить? Если клавиатура в данный момент не открыта, я хочу, чтобы эта кнопка очищала текст без повторного открытия клавиатуры.

Существует метод протокола, который вызывается при нажатии кнопки очистки. Но отправка UISearchBar сообщения resignFirstResponder не оказывает никакого влияния на клавиатуру.

Ответ 1

Это старый вопрос, и я просто столкнулся с той же проблемой и сумел решить его следующим образом:

Когда метод searchBar:textDidChange: UISearchBarDelegate вызван из-за того, что пользователь нажал кнопку "clear", searchBar еще не стал первым ответчиком, поэтому мы можем воспользоваться этим, чтобы обнаружить, когда пользователь на самом деле предназначен для очистки поиска, а не для привлечения внимания к поисковому бару и/или выполнения чего-то еще.

Чтобы отслеживать это, нам нужно объявить BOOL ivar в нашем viewController, который также является делегатом searchBar (позвоните ему shouldBeginEditing) и установите его с начальным значением YES (предположим, что наш viewController класс называется 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

Позже, в UISearchBarDelegate, мы реализуем методы searchBar:textDidChange: и 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;
}

В принципе, это.

Лучшие

Ответ 2

Я обнаружил, что resignFirstResponder не работает, когда textDidChange вызывается с сенсорного экрана на кнопку "clear". Однако использование performSelection: withObject: afterDelay: кажется эффективным способом:

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

Ответ 3

Я нашел довольно безопасный способ узнать, нажата ли кнопка очистки, и игнорировать времена, когда пользователь просто удаляет последний символ UISearchBar. Вот он:

- (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 !");
    }
}

Довольно простой и понятный, не так ли? Единственное, что нужно отметить, это то, что если пользователь нажимает кнопку очистки при редактировании UISextBield UITextField, у вас будет два пинга, тогда как вы получите только один, если пользователь нажмет на него, когда он не редактируется.


Изменить: Я не могу проверить его, но вот быстрая версия, как в 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 !")
    }
}

Обновление @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!")
    }
}

Ответ 4

Я использовал комбинацию ответа @boliva, а также @radiospiel ответ на другой вопрос SO:

@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

Ответ 5

Лучшее решение из моего опыта - просто положить UIButton (с четким фоном и без текста) над кнопкой очистки системы и подключить IBAction

С автозапуском его просто больше, чем просто

- (IBAction)searchCancelButtonPressed:(id)sender {

    [self.searchBar resignFirstResponder];
    self.searchBar.text = @"";

    // some of my stuff
    self.model.fastSearchText = nil;
    [self.model fetchData];
    [self reloadTableViewAnimated:NO];

}

Ответ 6

В вашем методе endEditing, почему бы вам не очистить UISearchBar и нет? Так как это должно быть там, где вы тоже уходите в отставку, это имеет смысл.

Ответ 7

Из вызовов делегата панели поиска предлагается принять изменение от старого значения до нового: вы можете обнаружить, что новое значение было равно нулю, а также старое значение, которое не было равно нулю, и индикатор того, что пользователь не набрал ничего, поскольку клавиатура была в последний раз - тогда в этом случае отмените первый ответчик для панели поиска. Не уверен, будет ли отображаться клавиатура на мгновение.

У меня очень похожая ситуация и я могу попробовать это.

Ответ 8

Прикосновение к кнопке очистки приводит к тому, что параметр searchText пуст. Другой способ добиться этого - проверить пустой текст в - (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;
}

Ответ 9

Для тех из вас, кто использует UISearchController в iOS 8 и выше, вам нужно просто подклассифицировать UISearchController. Для полноты вам также может потребоваться скрыть кнопку отмены, так же как и я, так как очистка текста от UISearchBar фактически отменяет поиск. Я добавил этот код ниже, если вы хотите его использовать.

Преимущество этого в том, что вы сможете использовать его для любого класса и любого представления, вместо того, чтобы требовать подкласс UIViewController. Я даже включу, как инициализировать мой UISearchController в нижней части этого решения.

FJSearchBar

Этот класс нужно переопределить, если вы хотите скрыть кнопку отмены, как я. Маркировка searchController.searchBar.showsCancelButton = NO, похоже, не работает в iOS 8. Я не тестировал iOS 9.

FJSearchBar.h

Пусто, но размещена здесь для полноты.

@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

Здесь вы хотите внести реальные изменения. Я разделил UISearchBarDelegate на свою собственную категорию, потому что, IMHO, категории делают классы более чистыми и удобными в обслуживании. Если вы хотите сохранить делегат в интерфейсе/реализации основного класса, вы более чем можете это сделать.

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

Некоторые примечания:

  • Я положил строку поиска и BOOL как частные переменные вместо свойств, потому что
    • Они более легкие, чем частные.
    • Им не нужно видеть или изменять внешний мир.
  • Мы проверяем, является ли searchBar первым ответчиком. Если это не так, мы фактически деактивируем контроллер поиска, потому что текст пуст, и мы больше не ищем. Если вы уверены, что действительно, вы также можете убедиться, что searchText.length == 0.
  • searchBar:textDidChange: вызывается перед searchBarShouldBeginEditing:, поэтому мы обработали его в этом порядке.
  • Я обновляю результаты поиска каждый раз, когда текст изменяется, но вы можете переместить [self.searchResultsUpdater updateSearchResultsForSearchController:self]; в searchBarSearchButtonClicked:, если вы хотите, чтобы поиск выполнялся после нажатия пользователем кнопки поиска.

Ответ 10

Быстрая версия ответа @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
    }
}

Ответ 11

Я столкнулся с этим несколько раз. Я очень ценю ответы, которые дали люди.

В конечном счете, мне бы очень хотелось, чтобы Apple просто позволила нам (разработчикам) обнаружить, когда нажата кнопка очистки.

Очевидно, что нажатие на него обнаруживается, потому что любой текст в окне поиска очищается.

Я догадываюсь, что сейчас это не очень высоко в их списке приоритетов... Но я действительно хочу, чтобы кто-то из Apple дал UISearchBar немного любви!