UISearchBar clearButton принудительно выводит клавиатуру на экран
-
11-09-2019 - |
Вопрос
У меня есть UISearchBar, который действует как живой фильтр для табличного представления.Когда клавиатура отключается с помощью endEditing:, текст запроса и серая круглая кнопка "Очистить" остаются.Отсюда, если я нажму серую кнопку "Очистить", клавиатура снова появится, когда текст будет очищен.
Как мне предотвратить это?Если клавиатура в данный момент не открыта, я хочу, чтобы эта кнопка очищала текст без повторного открытия клавиатуры.
Существует метод протокола, который вызывается, когда я нажимаю кнопку очистить.Но отправка UISearchBar сообщения resignFirstResponder не оказывает никакого влияния на клавиатуру.
Решение
Это старый вопрос, и я только что столкнулся с той же проблемой, и мне удалось решить ее следующим образом:
Когда в searchBar:textDidChange:
метод UISearchBarDelegate вызывается из-за того, что пользователь нажимает кнопку "Очистить", панель поиска еще не стала первым ответчиком, поэтому мы можем воспользоваться этим, чтобы определить, когда пользователь на самом деле намеревался очистить поиск, а не переключать внимание на панель поиска и / или делать что-то еще.
Чтобы отслеживать это, нам нужно объявить BOOL
ivar в нашем ViewController, который также является делегатом панели поиска (давайте назовем его 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;
}
В принципе, это все.
Лучшие
Другие советы
Я обнаружил, что 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];
}
Я нашел довольно безопасный способ узнать, была ли нажата кнопка очистить, и игнорировать случаи, когда пользователь просто удаляет последний символ панели пользовательского поиска.Вот оно :
- (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, у вас будет два пинга, тогда как вы получите только один, если пользователь нажмет на него, когда оно не редактируется.
Редактировать :Я не могу это протестировать, но вот версия swift, согласно 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!")
}
}
Я использовал комбинацию ответа @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
Лучшее решение, исходя из моего опыта, - это просто поставить 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];
}
Нажатие кнопки "очистить" на панели пользовательского поиска автоматически открывает клавиатуру, даже если вы вызываете searchBar.resignFirstResponder()
в пределах textDidChange
Метод UISearchBarDelegate.
Чтобы на самом деле скрыть клавиатуру, когда вы нажимаете "x", чтобы очистить панель пользовательского поиска, используйте следующий код.Таким образом, даже если textDidChange
вызывается при вводе чего-либо на панели пользовательского поиска, вы скрываете клавиатуру, только если удаляете весь текст внутри нее, независимо от того, используете ли вы кнопку удаления или нажимаете "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.
}
}
}
В вашем методе завершения редактирования, почему бы вам не очистить панель пользовательского поиска и там?Поскольку именно там вы также должны уволить сотрудника службы экстренного реагирования, это имеет смысл.
Из вызовов делегата панели поиска запрашивает вас принять изменение со старого значения на новое - вы могли бы обнаружить, что новое значение равно нулю, а старое значение не равно нулю, и индикатор того, что пользователь ничего не вводил с момента последнего нажатия на клавиатуру - тогда в этом случае откажитесь от первого респондента для панели поиска.Однако не уверен, что клавиатура отобразится на мгновение.
У меня очень похожая ситуация, и я могу попробовать это сам.
Нажатие кнопки очистить приводит к 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;
}
Для тех из вас, кто использует UISearchController
в iOS 8 и выше, вы захотите просто подклассировать UISearchController
.Для полноты картины вы также можете скрыть Отмена кнопка, как я и сделал, с момента удаления текста из UISearchBar
фактически это отмена поиска.Я добавил этот код ниже, если вы хотите его использовать.
Преимущество этого заключается в том, что вы сможете использовать это для любого класса и любого представления, вместо того чтобы требовать подкласса UIViewController
.Я даже включу то, как я инициализирую свой UISearchController
в нижней части этого решения.
Панель поиска FJSearchBar
Этот класс нужно переопределить только в том случае, если вы хотите скрыть кнопку отмены, как это сделал я.Маркировка searchController.searchBar.showsCancelButton = NO
похоже, это не работает в iOS 8.Я еще не проверял iOS 9.
Панель поиска FJ.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
в свою собственную категорию, потому что, ИМХО, категории делают классы более чистыми и простыми в обслуживании.Если вы хотите сохранить делегат в основном интерфейсе / реализации класса, мы более чем рады это сделать.
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:
если вы хотите, чтобы поиск выполнялся только после того, как пользователь нажмет Поиск кнопка.
Быстрая версия ответа @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
}
}
Я уже несколько раз сталкивался с этим.Я действительно ценю ответы, которые дали люди.
В конечном счете, мне бы очень хотелось, чтобы Apple просто позволила нам (разработчикам) определять, когда была нажата кнопка очистить.
Очевидно, что нажатие на нее будет обнаружено, потому что любой текст в окне поиска будет очищен.
Я предполагаю, что сейчас это просто не очень высоко в их списке приоритетов...Но я действительно хотел бы, чтобы кто-нибудь в Apple уделил UISearchBar немного любви!