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

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

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

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


person Jens Kohl    schedule 07.07.2009    source источник


Ответы (12)


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

Когда метод searchBar:textDidChange: UISearchBarDelegate вызывается из-за того, что пользователь нажимает кнопку «очистить», searchBar еще не стал первым респондентом, поэтому мы можем воспользоваться этим, чтобы определить, когда пользователь действительно намеревался очистить поиск, а не переводить фокус на 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;
}

В основном это все.

Лучший

person boliva    schedule 04.10.2010
comment
Это, похоже, не учитывает случай, когда пользователь уже набирает текст (делая панель поиска первым респондентом), и при этом нажимает кнопку очистки. У вас есть решение для этого? - person thgc; 06.12.2012
comment
@thgc, в этом случае клавиатура уже «поднята». Это решение предназначено для явного случая, когда пользователь нажимает кнопку «Очистить», а мы не хотим, чтобы отображалась клавиатура. - person boliva; 18.12.2012
comment
Это решение сработало для меня. Спасибо. Я как раз собирался пройти через боль создания собственной панели поиска. UISearchBar имеет слишком много подводных камней и трудностей при попытке реализовать пользовательский интерфейс и поведение. - person n8tr; 25.01.2015
comment
Работает как шарм! Спасибо @bolivia - person iosCurator; 03.03.2015

Я обнаружил, что resignFirstResponder не работает, когда textDidChange вызывается от прикосновения к «кнопке очистки». Однако использование 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];   
}
person Gregory Cosmo Haun    schedule 12.05.2010
comment
Потратил часы, пытаясь решить подобную проблему. Это решение работает для меня. Большое спасибо. - person Vignesh PT; 10.08.2014

Я нашел довольно безопасный способ узнать, была ли нажата кнопка очистки, и игнорировать моменты, когда пользователь просто удаляет последний символ 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 !");
    }
}

Довольно просто и понятно, не правда ли :)? Единственное, что следует отметить, это то, что если пользователь нажимает кнопку очистки при редактировании UITextField UISearchBar, у вас будет два эхо-запроса, тогда как вы получите только один, если пользователь щелкнет его, когда он не редактируется.


Изменить: я не могу проверить это, но вот быстрая версия, согласно 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!")
    }
}
person CyberDandy    schedule 18.06.2015
comment
Код для swift 2: 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 !") } } - person Rotem; 20.12.2015

Я использовал комбинацию ответа @boliva, а также ответа от @radioospiel на другой вопрос 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
person benvolioT    schedule 08.12.2012
comment
Спасибо друг. Вы сделали мой день. Большое спасибо. - person MS.; 14.08.2013

Лучшее решение из моего опыта - просто поставить 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];

}
person Peter Lapisu    schedule 10.03.2014
comment
этот взлом потенциально может вызвать проблемы, например, если устройство больше и т. д., это не надежное решение. - person Ali Mir; 06.06.2017

Нажатие кнопки «очистить» в UISearchBar автоматически открывает клавиатуру, даже если вы вызываете searchBar.resignFirstResponder() в методе textDidChange UISearchBarDelegate.

Чтобы фактически скрыть клавиатуру, когда вы нажимаете «x», чтобы очистить UISearchBar, используйте следующий код. Таким образом, даже если textDidChange вызывается при вводе чего-либо в UISearchBar, вы скроете клавиатуру только в том случае, если удалите весь текст в нем, независимо от того, используете ли вы кнопку удаления или щелкаете «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.
        }
    }
}
person luismgb    schedule 19.06.2018

Быстрая версия ответа @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
    }
}
person swordray    schedule 25.11.2017
comment
Это, безусловно, самое элегантное рабочее решение! Не забывайте searchBar.delegate = self :) - person Volodymyr Davydenko; 21.02.2020

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

person Corey Floyd    schedule 07.07.2009
comment
Я не понимаю, что вы пытаетесь сказать. Если я откажусь от панели поиска следующим образом: - (void) searchBarTextDidEndEditing: (UISearchBar *) searchBar {[searchBar resignFirstResponder]; } это не работает. - person Jens Kohl; 07.07.2009

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

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

person Kendall Helmstetter Gelner    schedule 07.07.2009

При прикосновении к кнопке очистки 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;
}
person H K    schedule 12.02.2015

Те из вас, кто использует 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

Некоторые детали, на которые следует обратить внимание:

  1. I've put the search bar and the BOOL as private variables instead of properties because
    • They're more lightweight than private properties.
    • Внешний мир не должен их видеть или изменять.
  2. Мы проверяем, является ли searchBar первым респондентом. Если это не так, мы фактически деактивируем контроллер поиска, потому что текст пуст, и мы больше не ищем. Если вы действительно хотите быть уверенным, вы также можете убедиться, что searchText.length == 0.
  3. searchBar:textDidChange: вызывается перед searchBarShouldBeginEditing:, поэтому мы обработали его в таком порядке.
  4. Я обновляю результаты поиска каждый раз, когда изменяется текст, но вы можете переместить [self.searchResultsUpdater updateSearchResultsForSearchController:self]; на searchBarSearchButtonClicked:, если хотите, чтобы поиск выполнялся только после того, как пользователь нажмет кнопку Search.
person mikeho    schedule 06.03.2016

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

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

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

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

person Perlguy    schedule 12.04.2016