Ответ 1
попробуйте это
for(id subview in [yourSearchBar subviews])
{
if ([subview isKindOfClass:[UIButton class]]) {
[subview setEnabled:YES];
}
}
В приложении для контактов на iPhone, если вы вводите поисковый запрос, нажмите кнопку "Поиск", клавиатура скрыта, НО кнопка отмены все еще включена. В моем приложении кнопка отмены отключается, когда я вызываю resignFirstResponder.
Кто-нибудь знает, как скрыть клавиатуру, сохраняя кнопку отмены в разрешенном состоянии?
Я использую следующий код:
- (void)searchBarSearchButtonClicked:(UISearchBar *)searchBar
{
[searchBar resignFirstResponder];
}
Клавиатура выдвигается из вида, но кнопка "Отмена" справа от текстового поля поиска отключена, поэтому я не могу отменить поиск. Приложение контактов поддерживает кнопку отмены в разрешенном состоянии.
Я думаю, может быть, одним из решений является погружение в объект searchBar и вызов resignFirstResponder в реальном текстовом поле, а не в строке поиска.
Любой вход оценивается.
попробуйте это
for(id subview in [yourSearchBar subviews])
{
if ([subview isKindOfClass:[UIButton class]]) {
[subview setEnabled:YES];
}
}
Этот метод работал в iOS7.
- (void)enableCancelButton:(UISearchBar *)searchBar
{
for (UIView *view in searchBar.subviews)
{
for (id subview in view.subviews)
{
if ( [subview isKindOfClass:[UIButton class]] )
{
[subview setEnabled:YES];
NSLog(@"enableCancelButton");
return;
}
}
}
}
(Также обязательно вызовите его в любом месте после использования [_searchBar resignFirstResponder].)
Принятое решение не будет работать, когда вы начнете прокручивать таблицу, а не нажимаете кнопку "Поиск". В этом случае кнопка "Отмена" будет отключена.
Это мое решение, которое повторно включает кнопку "Отмена" при каждом отключении с помощью KVO.
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
// Search for Cancel button in searchbar, enable it and add key-value observer.
for (id subview in [self.searchBar subviews]) {
if ([subview isKindOfClass:[UIButton class]]) {
[subview setEnabled:YES];
[subview addObserver:self forKeyPath:@"enabled" options:NSKeyValueObservingOptionNew context:nil];
}
}
}
- (void)viewWillDisappear:(BOOL)animated
{
[super viewWillDisappear:animated];
// Remove observer for the Cancel button in searchBar.
for (id subview in [self.searchBar subviews]) {
if ([subview isKindOfClass:[UIButton class]])
[subview removeObserver:self forKeyPath:@"enabled"];
}
}
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
// Re-enable the Cancel button in searchBar.
if ([object isKindOfClass:[UIButton class]] && [keyPath isEqualToString:@"enabled"]) {
UIButton *button = object;
if (!button.enabled)
button.enabled = YES;
}
}
Как и в iOS 6, вместо UIButton кнопка выглядит как UINavigationButton (частный класс).
Я изменил приведенный выше пример, чтобы выглядеть так.
for (UIView *v in searchBar.subviews) {
if ([v isKindOfClass:[UIControl class]]) {
((UIControl *)v).enabled = YES;
}
}
Однако это, очевидно, хрупкое, так как мы обманываем внутренности. Он также может включать больше, чем кнопку, но он работает для меня, пока не будет найдено лучшее решение.
Мы должны попросить Apple разоблачить это.
Казалось, это сработало для меня (в viewDidLoad):
__unused UISearchDisplayController* searchDisplayController = [[UISearchDisplayController alloc] initWithSearchBar:self.searchBar contentsController:self];
Я понимаю, что я, вероятно, должен правильно использовать UISearchDisplayController, но это было простое исправление для моей текущей реализации.
Вы можете использовать API среды выполнения для доступа к кнопке отмены.
UIButton *btnCancel = [self.searchBar valueForKey:@"_cancelButton"];
[btnCancel setEnabled:YES];
Я расширил то, что другие здесь уже размещены, реализовав это как простую категорию в UISearchBar.
UISearchBar + alwaysEnableCancelButton.h
#import <UIKit/UIKit.h>
@interface UISearchBar (alwaysEnableCancelButton)
@end
UISearchBar + alwaysEnableCancelButton.m
#import "UISearchBar+alwaysEnableCancelButton.h"
@implementation UISearchBar (alwaysEnableCancelButton)
- (BOOL)resignFirstResponder
{
for (UIView *v in self.subviews) {
// Force the cancel button to stay enabled
if ([v isKindOfClass:[UIControl class]]) {
((UIControl *)v).enabled = YES;
}
// Dismiss the keyboard
if ([v isKindOfClass:[UITextField class]]) {
[(UITextField *)v resignFirstResponder];
}
}
return YES;
}
@end
Вот несколько более надежное решение, которое работает на iOS 7. Он будет рекурсивно проходить все подзоны панели поиска, чтобы убедиться, что он включает все UIControl
(включая кнопку "Отмена" ).
- (void)enableControlsInView:(UIView *)view
{
for (id subview in view.subviews) {
if ([subview isKindOfClass:[UIControl class]]) {
[subview setEnabled:YES];
}
[self enableControlsInView:subview];
}
}
Просто вызовите этот метод сразу после вызова [self.searchBar resignFirstResponder]
следующим образом:
[self enableControlsInView:self.searchBar];
Voila! Кнопка отмены остается включенной.
for (UIView *firstView in searchBar.subviews) {
for(UIView* view in firstView.subviews) {
if([view isKindOfClass:[UIButton class]]) {
UIButton* button = (UIButton*) view;
[button setEnabled:YES];
}
}
}
Я нашел другой подход для его работы в iOS 7.
То, что я пытаюсь, похоже на приложение Twitter iOS. Если вы нажмете на увеличительное стекло на вкладке Timelines, появится UISearchBar
с активированной кнопкой "Отмена", клавиатурой и последним экраном поиска. Прокрутите последний экран поиска, и он скрывает клавиатуру, но он удерживает кнопку "Отмена".
Это мой рабочий код:
UIView *searchBarSubview = self.searchBar.subviews[0];
NSArray *subviewCache = [searchBarSubview valueForKeyPath:@"subviewCache"];
if ([subviewCache[2] respondsToSelector:@selector(setEnabled:)]) {
[subviewCache[2] setValue:@YES forKeyPath:@"enabled"];
}
Я пришел к этому решению, установив точку останова в моем представлении таблицы scrollViewWillBeginDragging:
. Я заглянул в мой UISearchBar
и обнажил его подзаголовки. Он всегда имеет только один, который имеет тип UIView
(моя переменная searchBarSubview
).
Затем, что UIView
содержит NSArray
, называемый subviewCache
, и я заметил, что последний третий элемент имеет тип UINavigationButton
, а не публичный API. Поэтому я решил использовать кодировку с ключом. Я проверил, отвечает ли UINavigationButton
на setEnabled:
, и, к счастью, он это делает. Поэтому я установил свойство @YES
. Оказывается, что UINavigationButton
- кнопка Отмена.
Это обязательно сломается, если Apple решит изменить реализацию UISearchBar
внутренних, но какого черта. Он работает сейчас.
Версия SWIFT для ответа Дэвида Дугласа (проверена на iOS9)
func enableSearchCancelButton(searchBar: UISearchBar){
for view in searchBar.subviews {
for subview in view.subviews {
if let button = subview as? UIButton {
button.enabled = true
}
}
}
}
На основе smileyborg answer, просто поместите это в свой делегат searchBar:
- (void)searchBarTextDidEndEditing:(UISearchBar *)searchBar
{
dispatch_async(dispatch_get_main_queue(), ^{
__block __weak void (^weakEnsureCancelButtonRemainsEnabled)(UIView *);
void (^ensureCancelButtonRemainsEnabled)(UIView *);
weakEnsureCancelButtonRemainsEnabled = ensureCancelButtonRemainsEnabled = ^(UIView *view) {
for (UIView *subview in view.subviews) {
if ([subview isKindOfClass:[UIControl class]]) {
[(UIControl *)subview setEnabled:YES];
}
weakEnsureCancelButtonRemainsEnabled(subview);
}
};
ensureCancelButtonRemainsEnabled(searchBar);
});
}
Это решение хорошо работает на iOS 7 и выше.
Вы можете создать свой CustomSearchBar, наследующий от UISearchBar, и реализовать этот метод:
- (void)layoutSubviews {
[super layoutSubviews];
@try {
UIView *baseView = self.subviews[0];
for (UIView *possibleButton in baseView.subviews)
{
if ([possibleButton respondsToSelector:@selector(setEnabled:)]) {
[(UIControl *)possibleButton setEnabled:YES];
}
}
}
@catch (NSException *exception) {
NSLog(@"ERROR%@",exception);
}
}
Лучшее решение
[UIBarButtonItem appearanceWhenContainedIn:[UISearchBar class], nil].enabled = YES;
Для iOS 10, Swift 3:
for subView in self.movieSearchBar.subviews {
for view in subView.subviews {
if view.isKind(of:NSClassFromString("UIButton")!) {
let cancelButton = view as! UIButton
cancelButton.isEnabled = true
}
}
}
Лучший и простой метод:
[(UIButton *)[self.searchBar valueForKey:@"_cancelButton"] setEnabled:YES];
Для iOS 9/10 (проверено), Swift 3 (короче):
searchBar.subviews.flatMap({$0.subviews}).forEach({ ($0 as? UIButton)?.isEnabled = true })
Большинство размещенных решений не являются надежными и позволят отключить кнопку "Отмена" при различных обстоятельствах.
Я попытался реализовать решение, в котором всегда включена кнопка "Отмена", даже когда вы делаете более сложные вещи с помощью панели поиска. Это реализовано как пользовательский подкласс UISearchView в Swift 4. Он использует значение (forKey:) трюк, чтобы найти кнопку отмены и текстовое поле поиска, и прослушивает, когда поле поиска прекращает редактирование и снова включает кнопку отмены. Он также позволяет кнопку отмены при переключении флага showCancelButton.
Он содержит пару утверждений, предупреждающих вас, когда внутренние детали UISearchBar меняются и не позволяют работать.
import UIKit
final class CancelSearchBar: UISearchBar {
override init(frame: CGRect) {
super.init(frame: frame)
setup()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
setup()
}
private func setup() {
guard let searchField = value(forKey: "_searchField") as? UIControl else {
assertionFailure("UISearchBar internal implementation has changed, this code needs updating")
return
}
searchField.addTarget(self, action: #selector(enableSearchButton), for: .editingDidEnd)
}
override var showsCancelButton: Bool {
didSet { enableSearchButton() }
}
@objc private func enableSearchButton() {
guard showsCancelButton else { return }
guard let cancelButton = value(forKey: "_cancelButton") as? UIControl else {
assertionFailure("UISearchBar internal implementation has changed, this code needs updating")
return
}
cancelButton.isEnabled = true
}
}