UISearchController禁用取消UIBarButtonItem

时间:2015-02-08 16:20:00

标签: ios objective-c uikit uisearchbar uisearchcontroller

问题

我正在尝试使用UISearchController在地图视图中搜索目的地。我希望UISearchBar出现在导航栏中,但如果没有在右侧显示取消按钮,我似乎无法这样做:

enter image description here

这个取消按钮有时会消失,而我正在玩耍,但我现在无法显示它我的搜索表显示了我想要它:

enter image description here

我确信必定会有一些小问题,我有点做错,但是我无法理解它是什么......

我的代码

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;

6 个答案:

答案 0 :(得分:20)

有一种更简单的方法......

对于iOS 8和UISearchController,请使用git push origin <branch>中的此委托方法:

UISearchControllerDelegate

不要忘记将自己设为代表:func didPresentSearchController(searchController: UISearchController) { searchController.searchBar.showsCancelButton = false }

答案 1 :(得分:13)

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

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

它运作正常且顺畅

答案 2 :(得分:12)

根据评论更新

UISearchBar有一个属性(请参阅Apple docs),它确定是否显示取消按钮:

self.searchBar.showsCancelButton = false;

但是,根据OP注释,这不起作用,因为searchController不断重新打开取消按钮。要避免这种情况,请创建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 - 我已经选择使用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

最后,更改代码以实例化此子类:

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

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

答案 3 :(得分:1)

你可以这样做:

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

答案 4 :(得分:0)

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

相反,如果搜索栏不是第一个响应者(键盘未处于活动状态并显示),我只会将active设置为NO,因为这实际上是取消命令。

FJSearchBar

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

答案 5 :(得分:0)

我可以通过在几个UISearchBar方法中调用setShowsCancelButton来让UISearchBarDelegate无需子类化,从而按行动进行操作:

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