UISearchController:即使搜索栏为空也显示结果
Posted
技术标签:
【中文标题】UISearchController:即使搜索栏为空也显示结果【英文标题】:UISearchController: show results even when search bar is empty 【发布时间】:2015-06-11 19:56:02 【问题描述】:据我了解,UISearchController
的默认行为是:
-
点击搜索栏时,背景变暗并显示“取消”按钮。
SearchResultsController
直到现在才显示。
SearchResultsController
仅在搜索栏不为空时显示。
我想显示SearchResultsController
,即使搜索栏为空但已选中(即上面的案例 1)。
简单地说,我想显示搜索结果,而不是背景变暗。
有没有办法做到这一点?
更多说明:
我没有使用UISearchController
来过滤显示在其上的视图上显示的结果,而是一些其他不相关的结果。
这就像 facebook 在其“新闻提要”上所做的那样。点击搜索栏最初会显示搜索建议,然后,当我们开始编辑时,它会显示可能与新闻提要无关的搜索结果。
【问题讨论】:
【参考方案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:
可能有一个很好的方法来实现这一点。如果您可以通过协议解决它,那是首选的方法。
如果这没有帮助,您将不得不破解内置行为。我不会推荐或依赖它,它会很脆弱,但如果您选择该选项,这里有一个答案:
确保你的 tableViewController 符合<UISearchControllerDelegate>
,并添加
self.searchController.delegate = self;
实现willPresentSearchController:
- (void)willPresentSearchController:(UISearchController *)searchController
dispatch_async(dispatch_get_main_queue(), ^
searchController.searchResultsController.view.hidden = NO;
);
这使得 searchResultsController
在 UISearchController
设置为隐藏后可见。
实现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
始终可见。
例如,我的searchResultsController
是UITableViewController
。我创建了一个VisibleTableView 作为UITableView
的子类,然后在xib 或storyboard 中将searchResultsController
的UITableView
自定义类更改为VisibleTableView。这样,我的searchResultsController
就永远不会被UISearchController
隐藏了。
这里的好东西:
比 KVO 更容易实现。
没有延迟显示searchResultsController
。在“updateSearchResults”委托方法中翻转隐藏标志有效,但显示searchResultsController
有延迟。
它不会重置隐藏标志,因此隐藏和可见之间没有 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)
【讨论】:
以上是关于UISearchController:即使搜索栏为空也显示结果的主要内容,如果未能解决你的问题,请参考以下文章
UISearchController 的搜索栏与第一个 tableview 单元格重叠
UITableView 中 UISearchController 的背景颜色
UISearchController 中的取消按钮在 iOS 13 中没有正确消失