【问题标题】:UISearchController: show results even when search bar is emptyUISearchController:即使搜索栏为空也显示结果
【发布时间】:2015-06-11 19:56:02
【问题描述】:

据我了解,UISearchController 的默认行为是:

  1. 点击搜索栏时,背景会变暗并显示“取消”按钮。 SearchResultsController 直到现在才显示。
  2. SearchResultsController 仅在搜索栏不为空时显示。

我想显示SearchResultsController,即使搜索栏为空但已选中(即上面的案例 1)。

简单地说,我想显示搜索结果,而不是背景变暗。

有没有办法做到这一点?

更多说明:

我没有使用UISearchController 来过滤显示在其上的视图上显示的结果,而是一些其他不相关的结果。 这就像 facebook 在其“新闻提要”上所做的那样。点击搜索栏最初会显示搜索建议,然后,当我们开始编辑时,它会显示可能与新闻提要无关的搜索结果。

【问题讨论】:

    标签: ios iphone ios8 uisearchcontroller


    【解决方案1】:

    您可以简单地实现UISearchResultsUpdating 协议并将结果控制器视图设置为始终显示在updateSearchResultsForSearchController 中:

     func updateSearchResultsForSearchController(searchController: UISearchController) {
    
       // Always show the search result controller
       searchController.searchResultsController?.view.hidden = false
    
       // Update your search results data and reload data
       ..
    }
    

    这是有效的,因为即使在搜索栏被激活时也会调用该方法,而无需任何文本。

    【讨论】:

    • 这对我有用,而且比其他建议的方法更简单。
    【解决方案2】:

    如果您的 searchBar 处于活动状态但没有文本,则会显示底层 tableView 结果。这就是内置行为,也是 searchResultsController 在该状态下隐藏的原因。

    要更改搜索处于活动状态但未过滤时的行为,您必须在 searchResultsController 通常仍处于隐藏状态时显示它。

    通过<UISearchResultsUpdating>updateSearchResultsForSearchController: 可能有一个很好的方法来实现这一点。如果您可以通过协议解决它,那是首选的方法。

    如果这没有帮助,您将不得不破解内置行为。我不会推荐或依赖它,它会很脆弱,但如果您选择该选项,这里有一个答案:

    1. 确保你的 tableViewController 符合<UISearchControllerDelegate>,并添加

      self.searchController.delegate = self;

    2. 实现willPresentSearchController:

      - (void)willPresentSearchController:(UISearchController *)searchController
      {
          dispatch_async(dispatch_get_main_queue(), ^{
              searchController.searchResultsController.view.hidden = NO;
          });
      }
      

      这使得 searchResultsControllerUISearchController 设置为隐藏后可见。

    3. 实现didPresentSearchController:

      - (void)didPresentSearchController:(UISearchController *)searchController
      {
          searchController.searchResultsController.view.hidden = NO;
      }
      

    有关解决内置行为的更好方法,请参阅malhal's answer

    【讨论】:

    • 谢谢!!我尝试了破解,它奏效了。一个问题,'dispatch_async' 是干什么用的?
    • Dispatch_async 安排在即将到来的运行循环中调用的方法。实际上,它为被调用的方法增加了一点延迟,因此我们可以在 搜索控制器将其标记为隐藏之后取消隐藏 searchResultsController
    • 极少数情况下,屏幕会从昏暗到视野闪烁。但是工作,为我节省了很多痛苦。
    • 您可以轻松将其设置为不暗以修复闪烁
    • 为了补充答案,我还在updateSearchResultsForSearchController 委托方法上设置了view.hidden = false,即使用户清除了搜索文本,结果视图控制器也会保持可见。
    【解决方案3】:

    已针对 iOS 13 更新

    从 iOS13 开始,我们获得了对此行为的系统 API 支持。可以设置属性showsSearchResultsController = true

    适用于 iOS 12 及更低版本

    我最近正在处理UISearchController。当搜索栏为空时,我想在searchResultsController 中显示搜索历史。所以searchResultsController 需要在UISearchController 出现时出现。

    在这里,我使用另一种解决方案,通过在自定义视图中覆盖hidden 属性,使searchResultsController 始终可见。

    例如,我的searchResultsControllerUITableViewController。我创建了一个VisibleTableView 作为UITableView 的子类,然后在xib 或storyboard 中将searchResultsControllerUITableView 自定义类更改为VisibleTableView。这样,我的searchResultsController就永远不会被UISearchController隐藏了。

    这里的好东西:

    1. 比 KVO 更容易实现。

    2. 没有延迟显示searchResultsController。在“updateSearchResults”委托方法中翻转隐藏标志有效,但显示searchResultsController有延迟。

    3. 它不会重置隐藏标志,因此隐藏和可见之间没有 UI 间隙/跳跃。

    Swift 3 示例代码

    class VisibleTableView: UITableView {
    override var isHidden: Bool {
        get {
            return false
        }
        set {
            // ignoring any settings
        }
    }
    }
    

    【讨论】:

    • 这是迄今为止最好的解决方案!
    • 看起来很有希望!你在生产中使用过吗?靠谱吗?
    • 嗨@RoiMulia,是的,该解决方案非常可靠。我们已经在生产中使用它 2 年了。
    • 由于某种原因,如果您使用 UIViewController + IBOutlet,VisibleTableView 不起作用,即使在情节提要中您的 tableview 是 VisibleTableView。 isHidden 只是从未调用过
    【解决方案4】:

    我尝试过 PetahChristian 的解决方案,当我们第一次聚焦搜索栏时确实显示了预加载结果,但是当我们输入内容然后清除它时,预加载结果不会再次出现。

    我想出了另一个解决方案。我们只需要在 SearchResultsController 中添加一个委托,并在我们的 searchController.searchBar.text 为空时调用它。像这样的:

    搜索结果控制器:

    protocol SearchResultsViewControllerDelegate {
       func reassureShowingList() -> Void
    }
    
    class FullSearchResultsViewController: UIViewController, UISearchResultsUpdating{
       var delegate: SearchResultsViewControllerDelegate?
       ...
       func updateSearchResultsForSearchController(searchController: UISearchController) {
        let query = searchController.searchBar.text?.trim()
        if query == nil || query!.isEmpty {
            ...
            self.delegate?.reassureShowingList()
            ...
        }
        ...
    }
    

    在包含 SearchController 的控制器中,我们添加我们的委托:

    self.searchResultsController.delegate = self
    func reassureShowingList() {
        searchController.searchResultsController!.view.hidden = false
    }
    

    【讨论】:

    • 很好的解决方案!我还必须在主线程上调用 unhiding searchResultsController,然后完美运行。
    • 我个人认为这应该被接受为答案!它可以无缝运行!
    • 当然。简单而独立。
    【解决方案5】:

    对于像这样棘手的事情,我推荐大锤方法!那就是检测什么时候试图隐藏它,当它隐藏时,把它改回来。这可以通过 KVO(键值观察)来完成。无论如何,这将起作用,而无需处理搜索栏的所有复杂性。抱歉,代码很复杂,但 KVO 是旧式 API,但我的代码遵循推荐做法。在您的 SearchResultsViewController 中输入:

    static int kHidden;
    
    @implementation SearchResultsViewController
    
    -(void)viewDidLoad{
        [super viewDidLoad];
        [self.view addObserver:self
                       forKeyPath:@"hidden"
                          options:(NSKeyValueObservingOptionNew |
                                   NSKeyValueObservingOptionOld)
                          context:&kHidden];
    }
    
    - (void)observeValueForKeyPath:(NSString *)keyPath
                          ofObject:(id)object
                            change:(NSDictionary *)change
                           context:(void *)context {
        // if it was our observation
        if(context == &kHidden){
            // if the view is hidden then make it visible.
            if([[change objectForKey:NSKeyValueChangeNewKey] boolValue]){
                self.view.hidden = NO;
            }
        }
        else{
            // if necessary, pass the method up the subclass hierarchy.
            if([super respondsToSelector:@selector(observeValueForKeyPath:ofObject:change:context:)]){
                [super observeValueForKeyPath:keyPath
                                     ofObject:object
                                       change:change
                                      context:context];
            }
        }
    }
    
    - (void)dealloc
    {
        [self.view removeObserver:self forKeyPath:@"hidden"];
    }
    
    // Here have the rest of your code for the search results table.
    
    @end
    

    这适用于所有情况,包括清除文本时。

    最后,为了防止表格在搜索激活时出现丑陋的渐变为灰色然后变为白色,请使用以下命令:

    self.searchController.dimsBackgroundDuringPresentation = NO;
    

    【讨论】:

    • 对于同一件事的不那么冗长的版本,请参阅我的回答。
    【解决方案6】:

    Swift 3 版本:

    如果您的 searchResultController 不是 nil 并且您正在使用单独的表视图控制器来显示搜索结果,那么您可以使该表视图控制器符合 UISearchResultUpdating 并在 updateSearchResults 函数中,您可以简单地取消隐藏视图。

    func updateSearchResults(for searchController: UISearchController) {
        view.hidden = false
    }
    

    Swift 4 版本:

    func updateSearchResults(for searchController: UISearchController) {
        view.isHidden = false
    }
    

    【讨论】:

      【解决方案7】:

      隐藏的是搜索结果控制器的视图。因此,在它可能被隐藏的任何时候取消隐藏它就足够了。只需在搜索结果控制器中执行以下操作:

      override func viewWillAppear(_ animated: Bool) {
          super.viewWillAppear(animated)
          self.view.isHidden = false
      }
      
      func updateSearchResults(for searchController: UISearchController) {
          self.view.isHidden = false
          // ... your other code goes here ...
      }
      

      现在结果视图(即表格视图)始终可见,即使搜索栏文本为空。

      顺便说一句,iOS Mail 应用程序的行为是这样的,我假设它是这样实现的(除非 Apple 可以访问一些秘密的私有 UISearchController 设置)。

      [在 iOS 10 和 iOS 11 中测试;我没有在任何早期的系统上进行测试。]

      【讨论】:

      • 我知道这条评论没用,但这个答案被低估了
      【解决方案8】:

      @malhal 方法的 Swift 2.3 版本:

      class SearchResultsViewController : UIViewController {
          var context = 0
      
          override func viewDidLoad() {
              super.viewDidLoad()
              // Add observer
              view.addObserver(self, forKeyPath: "hidden", options: [ .New, .Old ], context: &context)
          }
      
          deinit {
              view.removeObserver(self, forKeyPath: "hidden")
          }
      
          override func observeValueForKeyPath(keyPath: String?, ofObject object: AnyObject?, change: [String : AnyObject]?, context: UnsafeMutablePointer<Void>) {
              if context == &self.context {
                  if change?[NSKeyValueChangeNewKey] as? Bool == true {
                      view.hidden = false
                  }
              } else {
                  super.observeValueForKeyPath(keyPath, ofObject: object, change: change, context: context)
              }
          }
      }
      

      【讨论】:

        【解决方案9】:

        Swift 4 版本的malhals answer

        class SearchController: UISearchController {
        
            private var viewIsHiddenObserver: NSKeyValueObservation?
        
            override func viewDidLoad() {
                super.viewDidLoad()
        
                viewIsHiddenObserver = self.searchResultsController?.view.observe(\.hidden, changeHandler: { [weak self] (view, _) in
                    guard let searchController = self else {return}
                    if view.isHidden && searchController.searchBar.isFirstResponder {
                    view.isHidden = false
                    }
                })
        
            }
        
        }
        

        请注意[weak self]。否则你会引入一个保留循环。

        【讨论】:

          【解决方案10】:

          我认为你错了。

          SearchResultsController 仅在有结果时出现。这与您的解释略有不同。

          结果是根据搜索栏中的文本手动加载的。因此,如果搜索栏为空,您可以拦截它并返回您自己的一组结果。

          【讨论】:

          • 我总是有结果(即使是空的搜索栏)。我应该在哪里拦截并“返回”它?在 'searchBarTextDidBeginEditing' 中将属性 'active' 设置为 YES 不起作用。
          • 目前在火车上,所以无法查找方法名称。你用什么代码来返回结果?
          • 我不会在任何地方返回结果。我的 SearchResultsController 是一个单独的视图控制器,其中的结果在方法“updateSearchResultsForSearchController”中设置。当搜索栏中的文本更改或“活动”设置为“是”时,该方法由 UISearchController 调用。当我在搜索栏中有一些文本时,这工作正常。
          【解决方案11】:

          如果您不想使结果变暗,请将dimsBackgroundDuringPresentation 属性设置为false

          这将确保在搜索期间底层内容不会变暗。

          即使 searchText 为空,您也必须确保返回结果,否则将显示一个空的 tableview。

          【讨论】:

          • 调光不是我关心的,不显示结果控制器是。请查看我添加的更多说明。
          【解决方案12】:

          我为此花了很多时间,最终我采用的解决方案就像@malhals 的一样,但是通过使用 facebook 的 KVOController:https://github.com/facebook/KVOController 显着减少了代码量。这里的另一个优点是,如果您的 searchResultsController 是 UINavigationController,那么您不需要为了添加 @malhal 的代码而对其进行子类化。

          // always show searchResultsController, even if text is empty
          [self.KVOController observe:self.searchController.searchResultsController.view keyPath:@"hidden" options:NSKeyValueObservingOptionNew block:^(id observer, UIView* view, NSDictionary *change) {
              if ([change[NSKeyValueChangeNewKey] boolValue] == YES) {
                  view.hidden = NO;
              }
          }];
          self.searchController.dimsBackgroundDuringPresentation = NO;
          

          【讨论】:

          • 块与 KVO 崩溃,看起来 Facebooks 库有同样的问题
          • Facebook 的图书馆只有一个未解决的问题,那就是当你像[self.KVOController observe:self keypath:... 一样观察自己时。我正在使用上面的代码,没有保留周期也没有崩溃。
          • 问题在于 dealloc 和视图控制器的多个实例。如果观察者没有被正确移除,那么它将崩溃。也许试试这个,因为它正确使用了 dealloc:github.com/jpmhouston/TotalObserver
          【解决方案13】:

          最简单的方法是使用带有这个扩展的 ReactiveCocoa https://github.com/ColinEberhardt/ReactiveTwitterSearch/blob/master/ReactiveTwitterSearch/Util/UIKitExtensions.swift

                  presentViewController(sc, animated: true, completion: {
                      sc.searchResultsController?.view.rac_hidden.modify({ value -> Bool in
                          return false
                      })
                  } )
          

          sc 是你的 UISearchController

          【讨论】:

            【解决方案14】:

            我真的很喜欢 Simon Wang 的回答并与之合作,这就是我所做的并且效果很好:

            我在自定义类中继承 UISearchController:

            class CustomClass: UISearchController {
              override var searchResultsController: UIViewController? {
                get {
                  let viewController = super.searchResultsController
                  viewController?.view.isHidden = false
                  return viewController
                }
                set {
                  // nothing
                }
              }
            }
            

            还要确保你的代码中没有这个:

            self.resultsSearchController.isActive = true
            

            resultsSearchController 是我的 UISearchController

            【讨论】:

              【解决方案15】:

              简单说一下我用的这个案例

              func updateSearchResults(for searchController: UISearchController) {
                  if let inputText = searchController.searchBar.text, !inputText.isEmpty {
                          self.view.isHidden = false
                  }
              }
              

              其中 self.view 是 UISearchController 初始化期间“searchResultsController”的视图。

               var searchController = UISearchController(searchResultsController: searchResultsController)
              

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 2021-01-25
                • 1970-01-01
                • 2015-06-05
                • 2018-04-25
                • 1970-01-01
                • 1970-01-01
                • 2020-12-24
                相关资源
                最近更新 更多