UISearchController禁用取消UIBarButtonItem

问题

我正在尝试使用UISearchController在地图视图上search目的地。 我想UISearchBar出现在导航栏中,但我似乎无法做到这一点,没有它显示右边的取消button:

在这里输入图像说明

这个取消button有时会消失,虽然我在玩,但我不能得到它现在没有出现我已经得到了search表显示我希望它:

在这里输入图像说明

我敢肯定,我一直在做一些小小的事情,但是我搞不清楚它是什么…

我的代码

self.resultsViewController = [UITableViewController new]; self.searchController = [[UISearchController alloc] initWithSearchResultsController:self.resultsViewController]; self.searchController.searchResultsUpdater = self; self.searchController.hidesNavigationBarDuringPresentation = false; self.searchController.delegate = self; self.searchBar = self.searchController.searchBar; self.searchBar.placeholder = self.stage.title; self.searchBar.searchBarStyle = UISearchBarStyleMinimal; self.definesPresentationContext = true; self.navigationItem.titleView = self.searchBar; self.resultsTableView = self.resultsViewController.tableView; self.resultsTableView.dataSource = self; self.resultsTableView.delegate = self; 

根据评论更新

UISearchBar有一个属性(请参阅Apple文档 ),它确定是否显示取消button:

 self.searchBar.showsCancelButton = false; 

但是,根据OP评论,这是行不通的,因为searchController继续打开取消button。 为了避免这种情况,创build一个UISearchBar的子类,并重写setShowsCancelButton方法:

 @implementation MySearchBar -(void)setShowsCancelButton:(BOOL)showsCancelButton { // Do nothing... } -(void)setShowsCancelButton:(BOOL)showsCancelButton animated:(BOOL)animated { // Do nothing.... } @end 

为了确保searchController使用这个子类,我们还需要子类UISearchController ,并重写searchBar方法来返回我们子类的一个实例。 我们还需要确保新的searchBar激活searchController – 我select使用UISearchBarDelegate方法textDidChange来实现这个function:

 @interface MySearchController () <UISearchBarDelegate> { UISearchBar *_searchBar; } @end @implementation MySearchController -(UISearchBar *)searchBar { if (_searchBar == nil) { _searchBar = [[MySearchBar alloc] initWithFrame:CGRectZero]; _searchBar.delegate = self; } return _searchBar; } -(void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText { if ([searchBar.text length] > 0) { self.active = true; } else { self.active = false; } } @end 

最后,改变你的代码来实例化这个子类:

 self.searchController = [[MySearchController alloc] initWithSearchResultsController:self.resultsViewController]; 

(你显然需要为这些子类导入相关的头文件)。

有一种更简单的方法…

对于iOS 8和UISearchController,请使用UISearchControllerDelegate中的此委托方法:

 func didPresentSearchController(searchController: UISearchController) { searchController.searchBar.showsCancelButton = false } 

不要忘记将自己设置为委托: searchController.delegate = self

Swift3中简单的解决scheme – 我们需要使CustomSearchBar没有取消button,然后覆盖新的CustomSearchController中相应的属性:

 class CustomSearchBar: UISearchBar { override func setShowsCancelButton(_ showsCancelButton: Bool, animated: Bool) { super.setShowsCancelButton(false, animated: false) }} class CustomSearchController: UISearchController { lazy var _searchBar: CustomSearchBar = { [unowned self] in let customSearchBar = CustomSearchBar(frame: CGRect.zero) return customSearchBar }() override var searchBar: UISearchBar { get { return _searchBar } }} 

在MyViewController中,我使用这个新的自定义子类来初始化和configurationsearchController:

  var videoSearchController: UISearchController = ({ // Display search results in a separate view controller // let storyBoard = UIStoryboard(name: "Main", bundle: Bundle.main) // let alternateController = storyBoard.instantiateViewController(withIdentifier: "aTV") as! AlternateTableViewController // let controller = UISearchController(searchResultsController: alternateController) let controller = CustomSearchController(searchResultsController: nil) controller.searchBar.placeholder = NSLocalizedString("Enter keyword (eg iceland)", comment: "") controller.hidesNavigationBarDuringPresentation = false controller.dimsBackgroundDuringPresentation = false controller.searchBar.searchBarStyle = .minimal controller.searchBar.sizeToFit() return controller })() 

它的工作正常顺利

你可以这样做:

 - (void)willPresentSearchController:(UISearchController *)searchController { dispatch_async(dispatch_get_main_queue(), ^{ searchController.searchBar.showsCancelButton = NO; }); } 

@pbasdf的答案大部分工作,但检查searchText长度,以确定是否UISearchController是活动的可以添加更多的工作给用户。 如果用户点击清除button,或者删除search栏中唯一的字符,则会出现转angular情况。 这将被设置为NO ,这将自动调用UISearchBar上的resignFirstResponder 。 键盘将消失,如果用户想要更改文本或input更多文本,则需要在search栏上再次点击。

相反,如果search栏不是第一个响应者(键盘未激活并显示),我只设置为“ NO ,因为这实际上是取消命令。

FJSearchBar

标记searchController.searchBar.showsCancelButton = NO似乎不适用于iOS 8 。 我还没有testingiOS 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. 我已经把search栏和BOOL作为私有variables,而不是属性,因为
    • 他们比私有财产更轻量。
    • 他们不需要被外界看到或修改。
  2. 我们检查searchBar是否是第一个响应者。 如果不是,那么我们实际上停用search控制器,因为文本是空的,我们不再search。 如果你真的想确定,你也可以确保searchText.length == 0
  3. searchBar:textDidChange:searchBarShouldBeginEditing:之前被调用,这就是为什么我们按这个顺序处理它的原因。
  4. 我每次更新文本都会更新search结果,但是您可能需要移动[self.searchResultsUpdater updateSearchResultsForSearchController:self]; searchBarSearchButtonClicked:如果您只想在用户按下searchbutton后执行search

我能够通过在几个UISearchBarDelegate方法中调用setShowsCancelButton来获得UISearchBar的行为,而不需要子类化:

我在textDidChangesearchBarCancelButtonClicked调用它。 这是我的实现看起来像:

 extension MyViewController: UISearchBarDelegate { func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) { if searchText.characters.isEmpty == false { searchBar.setShowsCancelButton(true, animated: true) // whatever extra stuff you need to do } else { searchBar.setShowsCancelButton(false, animated: true) // whatever extra stuff you need to do } // whatever extra stuff you need to do } func searchBarCancelButtonClicked(_ searchBar: UISearchBar) { searchBar.setShowsCancelButton(false, animated: false) searchBar.text = nil searchBar.resignFirstResponder() tableView.scrollToRow(at: IndexPath(row: 0, section: 0), at: .top, animated: true) // whatever extra stuff you need to do } }