简体   繁体   English

UISearchController禁用取消UIBarButtonItem

[英]UISearchController disable cancel UIBarButtonItem

The Problem 问题

I am trying to use UISearchController to search for a destination on a map view. 我试图使用UISearchController在地图视图上搜索目的地。 I want the UISearchBar to appear in the navigation bar, but I can't seem to make it do so without it showing a cancel button to the right of it: 我希望UISearchBar出现在导航栏中,但我似乎无法在没有显示右侧的取消按钮的情况下这样做:

在此输入图像描述

This Cancel button has disappeared at times, whilst I'm playing around, but I can't get it to not appear now I have got the search table showing how I want it to: 这个取消按钮有时会消失,而我正在玩耍,但我现在无法让它显示我的搜索表显示我想要它:

在此输入图像描述

I'm sure there must be something small I'm doing ever so slightly wrong, but I can't work out what it is... 我肯定必须有一些我做得有点小错误的东西,但我无法弄清楚它是什么......

My Code 我的守则

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;

There is a way easier way... 有一种更简单的方法......

For iOS 8, and UISearchController, use this delegate method from UISearchControllerDelegate : 对于iOS 8,和UISearchController,使用该委托方法从UISearchControllerDelegate

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

Don't forget to set yourself as the delegate: searchController.delegate = self 不要忘记将自己设置为委托: searchController.delegate = self

Updated in light of comments 根据评论更新

UISearchBar has a property (see the Apple docs ) which determines whether the cancel button is displayed: UISearchBar有一个属性(参见Apple文档 ),它确定是否显示取消按钮:

self.searchBar.showsCancelButton = false;

But, as per OP comments, this does not work, because the searchController keeps switching the cancel button back on. 但是,根据OP注释,这不起作用,因为searchController不断重新打开取消按钮。 To avoid this, create a subclass of UISearchBar, and override the setShowsCancelButton methods: 要避免这种情况,请创建UISearchBar的子类,并覆盖setShowsCancelButton方法:

@implementation MySearchBar

-(void)setShowsCancelButton:(BOOL)showsCancelButton {
    // Do nothing...
}

-(void)setShowsCancelButton:(BOOL)showsCancelButton animated:(BOOL)animated {
    // Do nothing....
}

@end

To ensure this subclass is used by the searchController, we also need to subclass UISearchController , and override the searchBar method to return an instance of our subclass. 为确保searchController使用此子类,我们还需要子类化UISearchController ,并覆盖searchBar方法以返回子类的实例。 We also need to ensure that the new searchBar activates the searchController - I've chosen to use the UISearchBarDelegate method textDidChange for this: 我们还需要确保新的searchBar激活searchController - 我选择使用UISearchBarDelegate方法textDidChange

@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

Finally, change your code to instantiate this subclass: 最后,更改代码以实例化此子类:

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

(You will obviously need to import the relevant header files for these subclasses). (您显然需要导入这些子类的相关头文件)。

Easy solution in Swift3 - we need to make CustomSearchBar without cancel button and then override the corresponding property in new CustomSearchController : Swift3中的简单解决方案 - 我们需要使用CustomSearchBar而不使用取消按钮,然后覆盖新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
    }
}}

In MyViewController I initialize and configure searchController using this new custom subclass: 在MyViewController中,我使用这个新的自定义子类初始化和配置searchController:

    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 (e.g. iceland)", comment: "")
    controller.hidesNavigationBarDuringPresentation = false
    controller.dimsBackgroundDuringPresentation = false
    controller.searchBar.searchBarStyle = .minimal
    controller.searchBar.sizeToFit()
    return controller
})()

And it works properly and smooth 它运作正常,顺畅

You could do like this: 你可以这样做:

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

@pbasdf 's answer works for the most part, but checking the searchText length to determine whether the UISearchController is active can add more work to the user. @pbasdf的答案大部分都有用 ,但检查searchText长度以确定UISearchController是否处于活动状态可以为用户添加更多工作。 The corner case would be if the user hits the clear button, or deletes the only character in the search bar. 如果用户点击清除按钮,或者删除搜索栏中的唯一字符,则会出现转角情况。 This would set active to NO , which would automatically call resignFirstResponder on the UISearchBar . 这会将active设置为NO ,这将自动调用UISearchBar上的resignFirstResponder The keyboard would disappear and if the user wants to change the text or enter more text, it would require tapping again on the search bar. 键盘将消失,如果用户想要更改文本或输入更多文本,则需要在搜索栏上再次点击。

Instead, I only set active to NO if the search bar is not the first responder (keyboard is not active and displayed), since that is effectively a cancel command. 相反,如果搜索栏不是第一个响应者(键盘未激活并显示),我只将active设置为NO ,因为这实际上是取消命令。

FJSearchBar FJSearchBar

Marking searchController.searchBar.showsCancelButton = NO doesn't seem to work in iOS 8 . 标记searchController.searchBar.showsCancelButton = NO似乎在iOS 8中不起作用。 I haven't tested iOS 9 . 我还没有测试iOS 9

FJSearchBar.h FJSearchBar.h

Empty, but placed here for completeness. 空,但放在这里是为了完整。

@import UIKit;

@interface FJSearchBar : UISearchBar

@end

FJSearchBar.m FJSearchBar.m

#import "FJSearchBar.h"

@implementation FJSearchBar

- (void)setShowsCancelButton:(BOOL)showsCancelButton {
    // do nothing
}

- (void)setShowsCancelButton:(BOOL)showsCancelButton animated:(BOOL)animated {
    // do nothing
}

@end

FJSearchController FJSearchController

Here's where you want to make the real changes. 这是你想要做出真正改变的地方。 I split the UISearchBarDelegate into its own category because, IMHO, the categories make the classes cleaner and easier to maintain. 我将UISearchBarDelegate拆分为自己的类别,因为恕我直言,这些类使类更清晰,更易于维护。 If you want to keep the delegate within the main class interface/implementation, you're more than welcome to do so. 如果您希望将委托保留在主类接口/实现中,那么非常欢迎您这样做。

FJSearchController.h FJSearchController.h

@import UIKit;

@interface FJSearchController : UISearchController

@end

@interface FJSearchController (UISearchBarDelegate) <UISearchBarDelegate>

@end

FJSearchController.m 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

Some parts to note: 需要注意的部分内容:

  1. I've put the search bar and the BOOL as private variables instead of properties because 我把搜索栏和BOOL作为私有变量而不是属性,因为
    • They're more lightweight than private properties. 它们比私有财产更轻量级。
    • They don't need to be seen or modified by the outside world. 它们不需要被外界看到或修改。
  2. We check whether the searchBar is the first responder. 我们检查searchBar是否是第一个响应者。 If it's not, then we actually deactivate the search controller because the text is empty and we're no longer searching. 如果不是,那么我们实际上会停用搜索控制器,因为文本为空,我们不再搜索。 If you really want to be sure, you can also ensure that searchText.length == 0 . 如果你真的想确定,你也可以确保searchText.length == 0
  3. searchBar:textDidChange: is invoked before searchBarShouldBeginEditing: , which is why we handled it in this order. searchBar:textDidChange:searchBarShouldBeginEditing:之前调用,这就是我们按此顺序处理它的原因。
  4. I update the search results every time the text changes, but you may want to move the [self.searchResultsUpdater updateSearchResultsForSearchController:self]; 我每次文本更改时都会更新搜索结果,但您可能想要移动[self.searchResultsUpdater updateSearchResultsForSearchController:self]; to searchBarSearchButtonClicked: if you only want the search performed after the user presses the Search button. to searchBarSearchButtonClicked:如果您只想在用户按下“ 搜索”按钮后执行搜索

I was able to get the UISearchBar to behave as desired without subclassing by calling setShowsCancelButton in a couple of UISearchBarDelegate methods: 通过在几个UISearchBarDelegate方法中调用setShowsCancelButton ,我能够通过调用setShowsCancelButton来使UISearchBar无需子类化即可按行为运行:

I call it in textDidChange and searchBarCancelButtonClicked . 我在textDidChangesearchBarCancelButtonClicked调用它。 Here's what my implementation looks like: 这是我的实现的样子:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM