我有一个 UISearchBar,它作为表视图的实时筛选器。当使用 endEditing: 方法关闭键盘时,查询文本和灰色圆形“清除”按钮仍然存在。从这里开始,如果我点击灰色的“清除”按钮,文本被清除后键盘会重新出现。
我该如何防止这种情况发生?如果键盘当前未打开,则希望该按钮在不重新打开键盘的情况下清除文本。
当我点击清除按钮时,有一个协议方法会被调用。但是向 UISearchBar 发送 resignFirstResponder 消息对键盘没有任何影响。
我有一个 UISearchBar,它作为表视图的实时筛选器。当使用 endEditing: 方法关闭键盘时,查询文本和灰色圆形“清除”按钮仍然存在。从这里开始,如果我点击灰色的“清除”按钮,文本被清除后键盘会重新出现。
我该如何防止这种情况发生?如果键盘当前未打开,则希望该按钮在不重新打开键盘的情况下清除文本。
当我点击清除按钮时,有一个协议方法会被调用。但是向 UISearchBar 发送 resignFirstResponder 消息对键盘没有任何影响。
@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;
}
基本上就是这样。
最好的。
我发现当通过点击“清除按钮”触发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];
}
我找到了一种相当安全的方法来判断是否按下了清除按钮,并忽略用户只是删除了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,而如果用户在它没有被编辑时点击了它,您只会收到一个ping。
编辑: 我无法测试,但这是根据Rotem提供的Swift版本:
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!")
}
}
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我使用了@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
textDidChange
的UISearchBarDelegate方法内调用searchBar.resignFirstResponder()
。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.
}
}
}
从我的经验来看,最好的解决方案就是在系统清除按钮上放置一个没有文本和透明背景的 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];
}
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
}
}
如果你是在使用 iOS 8 或更新版本中的 UISearchController,你只需要简单地继承 UISearchController。为了完备起见,你还可以隐藏取消按钮(我也这样做了),因为从 UISearchBar 中清除文本等同于取消搜索。如果你想要使用它,下面附上代码。
这样做的好处在于你可以将其用于任何类和任何视图,而不需要 UIViewController 的子类。最后,我会在此解决方案底部包含如何初始化 UISearchController 的方式。
只有在你想要像我一样隐藏取消按钮时,这个类才需要被覆盖。在 iOS 8 中标记 searchController.searchBar.showsCancelButton = NO 似乎不起作用。我没有测试过 iOS 9。
这里是空的,但为了完整性而放置。
@import UIKit;
@interface FJSearchBar : UISearchBar
@end
#import "FJSearchBar.h"
@implementation FJSearchBar
- (void)setShowsCancelButton:(BOOL)showsCancelButton {
// do nothing
}
- (void)setShowsCancelButton:(BOOL)showsCancelButton animated:(BOOL)animated {
// do nothing
}
@end
这里是您想要进行真正更改的地方。我将UISearchBarDelegate
拆分为自己的类别,因为在我看来,类别使得类更加清晰易于维护。如果您希望将委托保留在主类接口/实现中,那么您完全可以这样做。
@import UIKit;
@interface FJSearchController : UISearchController
@end
@interface FJSearchController (UISearchBarDelegate) <UISearchBarDelegate>
@end
#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:
中。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;
}
- (void)searchBarTextDidEndEditing:(UISearchBar *)searchBar {
[searchBar resignFirstResponder];
}那行不通。 - Jens Kohl