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

Как включить кнопку отмены с помощью UISearchBar?

В приложении для контактов на iPhone, если вы вводите поисковый запрос, нажмите кнопку "Поиск", клавиатура скрыта, НО кнопка отмены все еще включена. В моем приложении кнопка отмены отключается, когда я вызываю resignFirstResponder.

Кто-нибудь знает, как скрыть клавиатуру, сохраняя кнопку отмены в разрешенном состоянии?

Я использую следующий код:

- (void)searchBarSearchButtonClicked:(UISearchBar *)searchBar
{
    [searchBar resignFirstResponder];
}

Клавиатура выдвигается из вида, но кнопка "Отмена" справа от текстового поля поиска отключена, поэтому я не могу отменить поиск. Приложение контактов поддерживает кнопку отмены в разрешенном состоянии.

Я думаю, может быть, одним из решений является погружение в объект searchBar и вызов resignFirstResponder в реальном текстовом поле, а не в строке поиска.

Любой вход оценивается.

4b9b3361

Ответ 1

попробуйте это

for(id subview in [yourSearchBar subviews])
{
    if ([subview isKindOfClass:[UIButton class]]) {
        [subview setEnabled:YES];
    }
}

Ответ 2

Этот метод работал в 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].)

Ответ 3

Принятое решение не будет работать, когда вы начнете прокручивать таблицу, а не нажимаете кнопку "Поиск". В этом случае кнопка "Отмена" будет отключена.

Это мое решение, которое повторно включает кнопку "Отмена" при каждом отключении с помощью 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;
    }
}

Ответ 4

Как и в iOS 6, вместо UIButton кнопка выглядит как UINavigationButton (частный класс).

Я изменил приведенный выше пример, чтобы выглядеть так.

for (UIView *v in searchBar.subviews) {
    if ([v isKindOfClass:[UIControl class]]) {
        ((UIControl *)v).enabled = YES;
    }
}

Однако это, очевидно, хрупкое, так как мы обманываем внутренности. Он также может включать больше, чем кнопку, но он работает для меня, пока не будет найдено лучшее решение.

Мы должны попросить Apple разоблачить это.

Ответ 5

Казалось, это сработало для меня (в viewDidLoad):

__unused UISearchDisplayController* searchDisplayController = [[UISearchDisplayController alloc] initWithSearchBar:self.searchBar contentsController:self];

Я понимаю, что я, вероятно, должен правильно использовать UISearchDisplayController, но это было простое исправление для моей текущей реализации.

Ответ 6

Вы можете использовать API среды выполнения для доступа к кнопке отмены.

UIButton *btnCancel = [self.searchBar valueForKey:@"_cancelButton"];
[btnCancel setEnabled:YES];

Ответ 7

Я расширил то, что другие здесь уже размещены, реализовав это как простую категорию в 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

Ответ 8

Вот несколько более надежное решение, которое работает на 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! Кнопка отмены остается включенной.

Ответ 9

for (UIView *firstView in searchBar.subviews) {
    for(UIView* view in firstView.subviews) {
        if([view isKindOfClass:[UIButton class]]) {
             UIButton* button = (UIButton*) view;
             [button setEnabled:YES];
        }
    }
}

Ответ 10

Я нашел другой подход для его работы в 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).

enter image description here

Затем, что UIView содержит NSArray, называемый subviewCache, и я заметил, что последний третий элемент имеет тип UINavigationButton, а не публичный API. Поэтому я решил использовать кодировку с ключом. Я проверил, отвечает ли UINavigationButton на setEnabled:, и, к счастью, он это делает. Поэтому я установил свойство @YES. Оказывается, что UINavigationButton - кнопка Отмена.

Это обязательно сломается, если Apple решит изменить реализацию UISearchBar внутренних, но какого черта. Он работает сейчас.

Ответ 11

Версия 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
            }
        }
    }
}

Ответ 12

На основе 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 и выше.

Ответ 13

Вы можете создать свой 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);
    }
}

Ответ 14

Лучшее решение

[UIBarButtonItem appearanceWhenContainedIn:[UISearchBar class], nil].enabled = YES;

Ответ 15

Для 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
        }
    }
}

Ответ 16

Лучший и простой метод:

[(UIButton *)[self.searchBar valueForKey:@"_cancelButton"] setEnabled:YES];

Ответ 17

Для iOS 9/10 (проверено), Swift 3 (короче):

searchBar.subviews.flatMap({$0.subviews}).forEach({ ($0 as? UIButton)?.isEnabled = true })

Ответ 18

Большинство размещенных решений не являются надежными и позволят отключить кнопку "Отмена" при различных обстоятельствах.

Я попытался реализовать решение, в котором всегда включена кнопка "Отмена", даже когда вы делаете более сложные вещи с помощью панели поиска. Это реализовано как пользовательский подкласс 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
    }
}