Подтвердить что ты не робот

UISearchController отключить отмену UIBarButtonItem

Проблема

Я пытаюсь использовать 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;
4b9b3361

Ответ 1

Обновлено в свете комментариев

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

Чтобы этот подкласс использовался поисковым контроллером, нам также нужен подкласс 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];

(Вам нужно будет импортировать соответствующие файлы заголовков для этих подклассов).

Ответ 2

Есть простой способ...

Для iOS 8 и UISearchController используйте этот метод делегата из UISearchControllerDelegate:

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

Не забудьте указать себя как делегата: searchController.delegate = self

Ответ 3

Простое решение в 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
})()

И он работает правильно и плавно

Ответ 4

Вы можете сделать следующее:

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

Ответ 5

@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 на свою категорию, потому что, IMHO, категории делают классы более чистыми и удобными в обслуживании. Если вы хотите сохранить делегат в интерфейсе/реализации основного класса, вы более чем можете это сделать.

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

Некоторые примечания:

  • Я положил строку поиска и BOOL как частные переменные вместо свойств, потому что
    • Они более легкие, чем частные.
    • Им не нужно видеть или изменять внешний мир.
  • Мы проверяем, является ли searchBar первым ответчиком. Если это не так, мы фактически деактивируем контроллер поиска, потому что текст пуст, и мы больше не ищем. Если вы уверены, что действительно, вы также можете убедиться, что searchText.length == 0.
  • searchBar:textDidChange: вызывается перед searchBarShouldBeginEditing:, поэтому мы обработали его в этом порядке.
  • Я обновляю результаты поиска каждый раз, когда текст изменяется, но вы можете переместить [self.searchResultsUpdater updateSearchResultsForSearchController:self]; в searchBarSearchButtonClicked:, если вы хотите, чтобы поиск выполнялся после нажатия пользователем кнопки поиска.

Ответ 6

Мне удалось заставить UISearchBar вести себя по желанию без подкласса, вызвав setShowsCancelButton в пару методов UISearchBarDelegate:

Я называю это textDidChange и 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
    }
}