10

问题

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

在此处输入图像描述

这个取消按钮有时会消失,而我正在玩,但我不能让它不出现现在我有搜索表显示我想要它的方式:

在此处输入图像描述

我敢肯定我做错了什么小事,但我无法弄清楚它是什么......

我的代码

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;
4

6 回答 6

20

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

对于 iOS 8 和 UISearchController,请使用以下委托方法UISearchControllerDelegate

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

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

于 2015-07-09T16:28:35.193 回答
13

根据评论更新

UISearchBar有一个属性(参见Apple 文档)确定是否显示取消按钮:

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];

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

于 2015-02-08T17:24:44.853 回答
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
})()

它工作正常且流畅

于 2017-02-03T17:29:19.633 回答
1

你可以这样做:

- (void)willPresentSearchController:(UISearchController *)searchController {
dispatch_async(dispatch_get_main_queue(), ^{
    searchController.searchBar.showsCancelButton = NO;
}); }
于 2016-09-24T16:57:47.290 回答
0

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

相反,我只设置搜索栏不是第一响应者(键盘未激活和显示),因为这实际上是一个active取消命令NO

FJ搜索栏

标记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:
于 2016-03-06T06:04:48.127 回答
0

UISearchBar通过调用setShowsCancelButton几个方法,我能够在不进行子类化的情况下获得所需的行为UISearchBarDelegate

我把它叫做textDidChangeand searchBarCancelButtonClicked。这是我的实现的样子:

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
    }
}
于 2017-05-18T12:58:55.630 回答