UISearchbar clearButton 强制键盘出现
Posted
技术标签:
【中文标题】UISearchbar clearButton 强制键盘出现【英文标题】:UISearchbar clearButton forces the keyboard to appear 【发布时间】:2010-11-08 16:47:42 【问题描述】:我有一个 UISearchBar 作为表格视图的实时过滤器。当通过 endEditing: 关闭键盘时,查询文本和灰色圆形“清除”按钮仍然存在。从这里,如果我点击灰色的“清除”按钮,键盘会在文本被清除时重新出现。
如何防止这种情况发生?如果键盘当前未打开,我希望该按钮在不重新打开键盘的情况下清除文本。
当我点击清除按钮时,会调用一个协议方法。但是向 UISearchBar 发送 resignFirstResponder 消息对键盘没有任何影响。
【问题讨论】:
【参考方案1】:这是一个老问题,我刚刚遇到同样的问题并设法通过以下方式解决它:
当 UISearchBarDelegate 的 searchBar:textDidChange:
方法因为用户点击“清除”按钮而被调用时,searchBar 还没有成为第一响应者,所以我们可以利用它来检测用户何时实际上是为了清除搜索,而不是将焦点放在 searchBar 和/或做其他事情。
为了跟踪它,我们需要在 viewController 中声明一个 BOOL
ivar,它也是 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;
基本上就是这样。
最好的
【讨论】:
这似乎没有考虑到用户已经在输入文本(使搜索栏成为第一响应者)的情况,同时按下清除按钮。你也有解决方案吗? @thgc 在这种情况下,键盘已经“启动”了。此解决方案适用于当用户点击“清除”按钮并且我们不希望键盘出现时的显式情况。 这个解决方案对我有用。谢谢你。我正要经历构建自己的搜索栏的痛苦。 UISearchBar 在尝试实现自定义 UI 和行为时有太多的陷阱和困难。 像魅力一样工作!谢谢@玻利维亚【参考方案2】:我发现当从触摸到“清除按钮”调用 textDidChange 时 resignFirstResponder 不起作用。但是,使用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 !");
非常简单明了,不是吗:)?唯一需要注意的是,如果用户在编辑 UISearchBar 的 UITextField 时单击清除按钮,您将收到两个 ping,而如果用户在未编辑时单击它,则只会收到一个。
编辑: 我无法测试它,但这里是 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!")
【讨论】:
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 !")
【参考方案4】:
我结合了@boliva 的答案和@radiospiel 的answer 来回答一个不同的 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】:即使您在textDidChange
UISearchBarDelegate 方法中调用searchBar.resignFirstResponder()
,按下 UISearchBar 中的“清除”按钮也会自动打开键盘。
要在按下“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.
【讨论】:
【参考方案7】:@boliva 答案的 Swift 版本。
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
【讨论】:
这是迄今为止最优雅的工作解决方案!不要忘记 searchBar.delegate = self :)【参考方案8】:在你的 endEditing 方法中,你为什么不清除 UISearchBar 呢?由于这也必须是您辞去第一响应者的地方,所以这是有道理的。
【讨论】:
我不明白你想说什么。如果我像这样辞职搜索栏: - (void)searchBarTextDidEndEditing:(UISearchBar *)searchBar [searchBar resignFirstResponder]; 这不起作用。【参考方案9】:有一个搜索栏委托调用要求您接受从旧值到新值的更改 - 您可以检测到新值是 nil,旧值不是 nil,并且指示自从键盘上次启动以来,用户没有输入任何内容 - 然后在这种情况下辞去搜索栏的第一响应者。不确定键盘是否会暂时显示。
我也有类似的情况,可以自己试试。
【讨论】:
【参考方案10】:触摸清除按钮会导致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;
【讨论】:
【参考方案11】:对于那些在 iOS 8 及更高版本中使用UISearchController
的用户,您只需将UISearchController
子类化即可。为了完整起见,您可能还想像我一样隐藏 cancel 按钮,因为从 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
拆分为它自己的类别,因为恕我直言,这些类别使类更干净且更易于维护。如果您想将委托保留在主类接口/实现中,我们非常欢迎您这样做。
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:
。
【讨论】:
【参考方案12】:我已经遇到过好几次了。我非常感谢人们给出的答案。
最终,我真的很希望看到 Apple 允许我们(开发人员)检测何时按下了清除按钮。
很明显,按下它会被检测到,因为搜索框中的所有文本都会被清除。
我猜它现在在他们的优先事项列表中并不是很高...但我真的希望 Apple 有人能给 UISearchBar 一点爱!
【讨论】:
以上是关于UISearchbar clearButton 强制键盘出现的主要内容,如果未能解决你的问题,请参考以下文章
ios中,如何设置clearbutton,且保证虚拟键盘不会一直弹出来
UITextField set placeholderColor and UITextField set clearButton Image