UISearchController:即使搜索栏为空,也显示结果

问题描述 投票:30回答:14

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

  1. 在点击搜索栏时,背景变暗,并显示“取消”按钮。到目前为止,SearchResultsController没有显示。
  2. 仅当搜索栏不为空时才显示SearchResultsController

我想显示SearchResultsController,即使搜索栏是空的但是被选中(即上面的情况1)。

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

有没有办法做到这一点?

更多澄清:

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

ios iphone ios8 uisearchcontroller
14个回答
31
投票

如果您的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


0
投票

我花了很多时间用这个,最终我使用的解决方案就像@ 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;

0
投票

最简单的方法是使用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


0
投票

我非常喜欢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


0
投票

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]。否则,您将引入保留周期。


-1
投票

我觉得你错了。

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

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


32
投票

您可以简单地实现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
   ..
}

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


13
投票

我已经尝试过PetahChristian解决方案,当我们第一次关注搜索栏时,预加载结果显示出来,但是当我们输入内容然后清除它时,预加载结果将不再出现。

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

SearchResultsController:

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
}

11
投票

有了这样棘手的事情,我推荐大锤方法!那是为了检测何时某些东西试图将其隐藏起来以及何时隐藏它,将其更改回来。这可以通过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;

7
投票

Swift 3版本:

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

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

Swift 4版本:

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

3
投票

Swift 2.3版本的@ malhal方法:

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)
        }
    }
}

3
投票

隐藏的是搜索结果控制器的视图。因此,只要隐藏它就足以取消隐藏它。只需在搜索结果控制器中执行以下操作:

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中测试过;我没有测试任何早期的系统。]


3
投票

针对iOS 13进行了更新

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

适用于iOS 12及更低版本

我最近在研究UISearchController。我想在搜索栏为空时在searchResultsController中显示搜索历史记录。所以当searchResultsController出现时,UISearchController需要出现。

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

例如,我的searchResultsControllerUITableViewController。我创建一个VisibleTableView作为UITableView的子类,然后将UITableView自定义类searchResultsController更改为xib或storyboard中的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
    }
}
}

0
投票

如果您不想对结果进行调暗,请将dimsBackgroundDuringPresentation属性设置为false

这将确保在搜索过程中底层内容不会变暗。

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

© www.soinside.com 2019 - 2024. All rights reserved.