45

我有一个 UISearchBar 作为表格视图的实时过滤器。当通过 endEditing: 关闭键盘时,查询文本和灰色圆形“清除”按钮仍然存在。从这里,如果我点击灰色的“清除”按钮,键盘会在文本被清除时重新出现。

我该如何防止这种情况?如果键盘当前未打开,我希望该按钮在不重新打开键盘的情况下清除文本。

当我点击清除按钮时,会调用一个协议方法。但是向 UISearchBar 发送 resignFirstResponder 消息对键盘没有任何影响。

4

12 回答 12

124

这是一个老问题,我刚刚遇到了同样的问题并设法通过以下方式解决它:

searchBar:textDidChange:UISearchBarDelegate 的方法因为用户点击“清除”按钮而被调用时,searchBar 还没有成为第一响应者,所以我们可以利用它来检测用户实际上何时打算清除搜索而不是将焦点放在 searchBar 和/或做其他事情。

为了跟踪这一点,我们需要BOOL在我们的 viewController 中声明一个 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;
}

基本上,就是这样。

最好的

于 2010-10-04T02:36:41.147 回答
33

我发现当从触摸到“清除按钮”调用 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];   
}
于 2010-05-12T22:22:00.483 回答
10

我找到了一种非常安全的方法来了解是否已按下清除按钮,并忽略用户刚刚删除 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!")
    }
}
于 2015-06-18T09:46:04.567 回答
8

我结合了@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
于 2012-12-08T06:44:25.083 回答
3

根据我的经验,最好的解决方案是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];

}
于 2014-03-10T12:19:34.577 回答
3

即使您searchBar.resignFirstResponder()textDidChangeUISearchBarDelegate 方法中调用,按下 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.
        }
    }
}
于 2018-06-19T19:55:38.573 回答
2

@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
    }
}
于 2017-11-25T21:11:53.843 回答
0

在你的 endEditing 方法中,你为什么不清除 UISearchBar 呢?由于这也必须是您辞去第一响应者的地方,所以这是有道理的。

于 2009-07-07T13:36:51.863 回答
0

的搜索栏委托调用要求您接受从旧值到新值的更改 - 您可以检测到新值为零,旧值不是零,并且指示用户没有自键盘上次启动以来输入了​​任何内容 - 然后在这种情况下辞去搜索栏的第一响应者。不确定键盘是否会暂时显示。

我有一个非常相似的情况,可能会自己尝试。

于 2009-07-07T15:47:06.443 回答
0

触摸清除按钮导致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;
}
于 2015-02-12T01:55:53.533 回答
0

对于那些UISearchControlleriOS 8及更高版本中使用的人,您需要简单地将UISearchController. 为了完整起见,您可能还想像我一样隐藏取消按钮,因为从 中清除文本UISearchBar实际上是取消搜索。如果您想使用它,我在下面添加了该代码。

这样做的好处是您可以将它用于任何类和任何视图,而不需要UIViewController. 我什至会UISearchController在这个解决方案的底部包括我如何初始化我的。

FJ搜索栏

如果您想像我一样隐藏取消按钮,则只需要覆盖此类。标记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

一些需要注意的部分:

  1. 我把搜索栏和BOOL作为私有变量而不是属性,因为
    • 它们比私有属性更轻量级。
    • 它们不需要被外界看到或修改。
  2. 我们检查是否searchBar是第一响应者。如果不是,那么我们实际上停用了搜索控制器,因为文本为空并且我们不再搜索。如果你真的想确定,你也可以确保searchText.length == 0.
  3. searchBar:textDidChange:在之前调用searchBarShouldBeginEditing:,这就是我们按此顺序处理它的原因。
  4. 每次文本更改时,我都会更新搜索结果,但如果您只想在用户按下“搜索”按钮后执行搜索,则可能需要将其[self.searchResultsUpdater updateSearchResultsForSearchController:self];移至。searchBarSearchButtonClicked:
于 2016-03-06T00:59:29.957 回答
-1

我现在已经遇到过好几次了。我非常感谢人们给出的答案。

最终,我真的很希望看到 Apple 允许我们(开发人员)检测何时按下了清除按钮。

很明显,按下它会被检测到,因为搜索框中的任何文本都会被清除。

我猜它现在在他们的优先事项列表中并不是很高……但我真的希望 Apple 的某个人能给 UISearchBar 一点爱!

于 2016-04-12T14:05:57.320 回答