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;
Ответы
Ответ 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
}
}