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 немного любви!