简体   繁体   English

iOS8无法隐藏UISearchController中搜索栏上的取消按钮

[英]iOS8 Cannot hide cancel button on search bar in UISearchController

My goal is to prevent the cancel button from appearing in a search bar in a UISearchController. 我的目标是阻止取消按钮出现在UISearchController的搜索栏中。 I started with Apple's Table Search with UISearchController sample code and hid the cancel button as seen in the code snip below. 我从Apple的Table Search开始, 使用UISearchController示例代码并隐藏了取消按钮,如下面的代码片段所示。 However, when the user taps in the text field, the cancel button still appears. 但是,当用户点击文本字段时,仍会显示取消按钮。 Any help? 有帮助吗?

override func viewDidLoad() {
    super.viewDidLoad()

    resultsTableController = ResultsTableController()

    searchController = UISearchController(searchResultsController: resultsTableController)
    searchController.searchResultsUpdater = self
    searchController.searchBar.sizeToFit()
    tableView.tableHeaderView = searchController.searchBar

    searchController.searchBar.delegate = self

    //Hide cancel button - added by me
    searchController.searchBar.showsCancelButton = false

    ...

I think there are three ways of achieving that: 我认为有三种方法可以实现这一目标:

  1. Override searchDisplayControllerDidBeginSearch and use the following code: 覆盖searchDisplayControllerDidBeginSearch并使用以下代码:

searchController.searchBar.showsCancelButton = false

  1. Subclass UISearchBar and override the layoutSubviews to change that var when the system attempts to draw it. 子类UISearchBar并覆盖layoutSubviews以在系统尝试绘制var时更改该var。

  2. Register for keyboard notification UIKeyboardWillShowNotification and apply the code in point 1. 注册键盘通知UIKeyboardWillShowNotification并应用第1点中的代码

Of course can always implement your search bar. 当然可以随时实现您的搜索栏。

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

Simply subclass UISearchController & UISearchBar . 简单地UISearchControllerUISearchBar

class NoCancelButtonSearchController: UISearchController {
    let noCancelButtonSearchBar = NoCancelButtonSearchBar()
    override var searchBar: UISearchBar { return noCancelButtonSearchBar }
}

class NoCancelButtonSearchBar: UISearchBar {
    override func setShowsCancelButton(_ showsCancelButton: Bool, animated: Bool) { /* void */ }
}

The following github project subclasses UISearchBar which is presented as solution 2: 以下github项目子类UISearchBar,它作为解决方案2呈现:

https://github.com/mechaman/CustomSearchControllerSwift https://github.com/mechaman/CustomSearchControllerSwift

On top of it, it also subclasses UISearchController to enable one to put the search bar in places other than the tableView header! 除此之外,它还是UISearchController的子类,使得人们可以将搜索栏放在tableView标题以外的位置!

Hope this helps. 希望这可以帮助。

This was the simplest solution I could come up with in Swift. 这是我在Swift中提出的最简单的解决方案。

Custom search controller: 定制搜索控制器:

class CustomSearchController: UISearchController {

    var _searchBar: CustomSearchBar

    override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: NSBundle?) {
        self._searchBar = CustomSearchBar()
        super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
    }

    override init(searchResultsController: UIViewController?) {
        self._searchBar = CustomSearchBar()
        super.init(searchResultsController: searchResultsController)
    }

    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    override var searchBar: UISearchBar {
        return self._searchBar
    }
}

Custom search bar: 自定义搜索栏:

class CustomSearchBar: UISearchBar {
    override func setShowsCancelButton(showsCancelButton: Bool, animated: Bool) {
        // do nothing
    }
}

The most important piece of this was to only create the _searchBar object once in init vs. creating it inside of the stored property. 其中最重要的部分是只在init创建一次_searchBar对象,而在存储的属性中创建它。

Just subclass your UISearchController and do the following: 只需子类化您的UISearchController并执行以下操作:

class CustomSearchController: UISearchController {

   override func viewDidLayoutSubviews() {
       super.viewDidLayoutSubviews()
       searchBar.showsCancelButton = false
   }
}

This was the easiest solution I could came up with in order to solve the flashing cancel-button issue. 这是我能想出的最简单的解决方案,以解决闪烁的取消按钮问题。

TL;DR : Subclassing UISearchBar and overriding setShowsCancelButton: and setShowsCancelButton:animated: hides the cancel button. TL; DR :子类化UISearchBar并覆盖setShowsCancelButton:setShowsCancelButton:animated:隐藏取消按钮。

SOLUTION

I 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:如果您只想在用户按下“ 搜索”按钮后执行搜索

Swift: 迅速:

The following worked for me, added under viewDidLoad, because I never wanted that button: 以下为我工作,在viewDidLoad下添加,因为我从来不想要那个按钮:

let searchBarStyle = searchBar.value(forKey: "searchField") as? UITextField
searchBarStyle?.clearButtonMode = .never

Make sure to add the ID for the searchBar in the storyboard. 确保在故事板中添加searchBar的ID。 在此输入图像描述

Use UISearchControllerDelegate. 使用UISearchControllerDelegate。

func willPresentSearchController(_ searchController: UISearchController) {

        searchController.searchBar.setValue("", forKey:"_cancelButtonText")
    }

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

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