【问题标题】:UISearchbar clearButton forces the keyboard to appearUISearchbar clearButton 强制键盘出现
【发布时间】:2010-11-08 16:47:42
【问题描述】:

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

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

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

【问题讨论】:

    标签: iphone keyboard uisearchbar appearance uisearchbardelegate


    【解决方案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) -&gt; 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 &amp;&amp; !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 = NOiOS 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:

          【讨论】:

            【解决方案12】:

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

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

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

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

            【讨论】:

              猜你喜欢
              • 2022-12-18
              • 1970-01-01
              • 2012-09-27
              • 2012-10-02
              • 2013-01-18
              • 2014-03-17
              • 1970-01-01
              • 2012-09-30
              • 2018-03-28
              相关资源
              最近更新 更多