Стилизация кнопки отмены в UISearchBar - PullRequest
27 голосов
/ 29 июля 2009

У меня есть UISearchBar с кнопкой отмены (отображается с помощью -(void)setShowsCancelButton:animated). Я изменил tintColor панели поиска следующим образом, пытаясь получить сероватую панель поиска:

UISearchBar *searchBar = [[UISearchBar alloc] initWithFrame:CGRectMake(0, 0, 320, 40)];
searchBar.tintColor = [UIColor colorWithWhite:0.8 alpha:1.0];

Вот как это выглядит сейчас - обратите внимание, что кнопка отмены также серая: http://twitpic.com/c0hte

Есть ли способ отдельно установить цвет кнопки отмены, чтобы она выглядела примерно так: http://twitpic.com/c0i6q

Ответы [ 19 ]

26 голосов
/ 11 апреля 2012

Вы можете использовать UIAppearance для стилизации кнопки отмены без итерации подпредставлений UISearchBar, но заголовок UIButton в настоящее время не имеет методов, помеченных UI_APPEARANCE_SELECTOR.

РЕДАКТИРОВАТЬ: развернуть подпредставления, пока вы не получите эту кнопку отмены

Но это обычно возвращает ноль до searchBar.setShowsCancelButton(true, animated: true) называется.

extension UISearchBar {

var cancelButton : UIButton? {
    if let view = self.subviews.first {
        for subView in view.subviews {
            if let cancelButton = subView as? UIButton {
                return cancelButton
            }
        }
    }
    return nil
}
}
20 голосов
/ 21 декабря 2012

В iOS 5.0+ вы можете использовать прокси appearnce.

До того, как отобразится строка поиска .:

UIBarButtonItem *searchBarButton = [UIBarButtonItem appearanceWhenContainedIn:[UISearchBar class], nil];
[searchBarButton setBackgroundImage:myCancelButtonImageNormal forState:UIControlStateNormal barMetrics:UIBarMetricsDefault];
[searchBarButton setBackgroundImage:myCancelButtonImageHighlighted forState:UIControlStateHighlighted barMetrics:UIBarMetricsDefault];
[searchBarButton setTitleTextAttributes:barButtonTitleTextAttributesNormal forState:UIControlStateNormal];
[searchBarButton setTitleTextAttributes:barButtonTitleTextAttributesHighlighted forState:UIControlStateHighlighted];

Если вы используете [UIButton appearanceWhenContainedIn:[UISearchBar class], nil], это повлияет на другие кнопки (например, кнопку очистки). Итак, вам лучше не использовать внешний вид UIButton. Попробуйте UIBarButtonItem.

19 голосов
/ 01 октября 2012

Изменить название кнопки «Отмена»:

[[UIButton appearanceWhenContainedIn:[UISearchBar class], nil] setTitle:@"newTitle" forState:UIControlStateNormal];

Свифт эквивалент:

   let cancelButton = UIButton.appearance(whenContainedInInstancesOf: [UISearchBar.self])
   cancelButton?.setTitle("cancel".localized, for: .normal)
17 голосов
/ 03 октября 2010

Хотя это может не совсем соответствовать исходному вопросу, решение все же применимо в более широком смысле, если попытаться настроить кнопку «Отмена» в UISearchBar. Думаю, это поможет другим, кто застрял в таком сценарии.

Моя ситуация состояла в том, чтобы изменить заголовок кнопки отмены, но с поворотом, при котором я не хотел показывать кнопку отмены по умолчанию, а только хотел, чтобы она отображалась, когда пользователь входит в режим поиска (нажав внутри поиск текстового поля). В этот момент я хотел, чтобы кнопка отмены содержала надпись «Готово» («Отмена» придавала другому значению мой экран, отсюда и настройка).

Тем не менее, вот что я сделал (комбинация решений Caelavel и Arenim):

Субклассированный UISearchBar как MyUISearchBar с этими двумя методами:

-(void) setCloseButtonTitle: (NSString *) title forState: (UIControlState)state
{
    [self setTitle: title forState: state forView:self];
}

-(void) setTitle: (NSString *) title forState: (UIControlState)state forView: (UIView *)view
{
    UIButton *cancelButton = nil;
    for(UIView *subView in view.subviews){
        if([subView isKindOfClass:UIButton.class])
        {
            cancelButton = (UIButton*)subView;
        }
        else
        {
            [self setTitle:title forState:state forView:subView];
        }
    }

    if (cancelButton)
        [cancelButton setTitle:title forState:state];

}

А в viewcontroller, который использует эту панель поиска, следующий фрагмент кода заботится о том, чтобы показать кнопку отмены и настроить ее заголовок:

- (void)searchBarTextDidBeginEditing:(UISearchBar *)searchBar
{
    MyUISearchBar *sBar = (MyUISearchBar *)searchBar;
    [sBar setShowsCancelButton:YES];
    [sBar setCloseButtonTitle:@"Done" forState:UIControlStateNormal];   
}

Как ни странно, мне не пришлось ничего делать, чтобы скрыть кнопку отмены, так как она скрыта по умолчанию при выходе из режима поиска.

13 голосов
/ 29 июля 2009

То, что вы хотите сделать, довольно сложно. На кнопке отмены нет встроенного крючка.

Однако, есть несколько вариантов, если вы хотите, чтобы Джимми открыл капот.

Во-первых, UISearchBar - это UIView, а кнопка Отмена - это также представление, которое добавляется в строку поиска в качестве подпредставления, как и следовало ожидать.

Я немного поэкспериментировал и могу сказать, что когда кнопка находится на экране, она имеет размер 48,30.

Так что в viewWillAppear вы можете сделать что-то вроде этого:

  1. Найдите вид кнопки отмены в [подвиде searchBar], отыскив один с размером 48,30. (Кажется, только один - это может измениться ...) Вы можете быть вдвойне осторожны и искать тот, который находится приблизительно в правильном положении (отличается в альбомной и портретной ориентации).

  2. Добавить подпредставление для кнопки отмены.

  3. Подвидом должен быть UIControl (чтобы можно было включить enabled = NO, чтобы убедиться, что сенсорные события доходят до фактической кнопки отмены)

  4. Нужно иметь правильный цвет и закругленные углы; вам нужно будет придумать размер по причинам, которые я еще не понимаю (55,30, кажется, работает)

  5. Это будет работать, если searchBar.showsCancelButton всегда YES; если вы хотите, чтобы оно исчезло, если не редактировать строку поиска, вам нужно будет найти ловушку для добавления наложения при каждом появлении кнопки отмены.

  6. Как видите, это некрасивое возиться. Сделайте это с широко открытыми глазами.

7 голосов
/ 12 мая 2010

Вы можете найти кнопку отмены, просматривая подпредставления панели поиска и проверяя тип класса (вместо размера):

UIButton *cancelButton = nil;
for(UIView *subView in yourSearchBar.subviews){
    if([subView isKindOfClass:UIButton.class]){
    cancelButton = (UIButton*)subView;
    }
}

А затем измените цвет оттенка:

[cancelButton setTintColor:[UIColor colorWithRed:145.0/255.0 green:159.0/255.0 blue:179.0/255.0 alpha:1.0]];
7 голосов
/ 03 марта 2011

Если вы хотите настроить кнопку отмены на UISearchBar, вы должны получить объект UIButton из объекта UISearchBar. Пример ниже

UISearchBar *s_bar = [[UISearchBar alloc] initWithFrame:CGRectMake(50,20,300,30)];
s_bar.delegate = self;
s_bar.barStyle = UIBarStyleDefault;
s_bar.showsCancelButton = YES;
UIButton *cancelButton;
for (id button in s_bar.subviews)
{
    if ([button isKindOfClass:[UIButton class]])
    {
        cancelButton=(UIButton*)button;
        break;
    }
}
4 голосов
/ 11 сентября 2013

Я дам подробный ответ относительно техники UIAppearance. Во-первых, вы должны понимать, что кнопка отмены является частной кнопкой UINavigationButton: UIButton. После некоторой проверки кажется, что UINavigationButton ответит на эти селекторы UIAppearance:

// inherited from UINavigationButton
@selector(setTintColor:)
@selector(setBackgroundImage:forState:style:barMetrics:)
@selector(setBackgroundImage:forState:barMetrics:)
@selector(setTitleTextAttributes:forState:)
@selector(setBackgroundVerticalPositionAdjustment:forBarMetrics:)
@selector(setTitlePositionAdjustment:forBarMetrics:)
@selector(setBackButtonBackgroundImage:forState:barMetrics:)
@selector(setBackButtonTitlePositionAdjustment:forBarMetrics:)
@selector(setBackButtonBackgroundVerticalPositionAdjustment:forBarMetrics:)

// inherited from UIButton
@selector(setTitle:forState:)

По совпадению, эти селекторы совпадают с селекторами UIBarButtonItem. Смысл в том, чтобы использовать два отдельных UIAppearance для обработки частного класса UINavigationButton.

/* dual appearance technique by Cœur to customize a UINavigationButton */
Class barClass = [UISearchBar self];

UIBarButtonItem<UIAppearance> *barButtonItemAppearanceInBar = [UIBarButtonItem appearanceWhenContainedIn:barClass, nil];
[barButtonItemAppearanceInBar setTintColor:...];
[barButtonItemAppearanceInBar setBackgroundImage:... forState:... style:... barMetrics:...];
[barButtonItemAppearanceInBar setBackgroundImage:... forState:... barMetrics:...];
[barButtonItemAppearanceInBar setTitleTextAttributes:... forState:...];
[barButtonItemAppearanceInBar setBackgroundVerticalPositionAdjustment:... forBarMetrics:...];
[barButtonItemAppearanceInBar setTitlePositionAdjustment:... forBarMetrics:...];
// only for a backButton in an UINavigationBar, not for a cancelButton in an UISearchBar
//[barButtonItemAppearanceInBar setBackButtonBackgroundImage:... forState:... barMetrics:...];
//[barButtonItemAppearanceInBar setBackButtonTitlePositionAdjustment:... forBarMetrics:...];
//[barButtonItemAppearanceInBar setBackButtonBackgroundVerticalPositionAdjustment:... forBarMetrics:...];

UIButton<UIAppearance> *buttonAppearanceInBar = [UIButton appearanceWhenContainedIn:barClass, nil];
// warning: doesn't work for iOS7+
[buttonAppearanceInBar setTitle:... forState:...];

Это позволит вам настроить кнопку «Отмена» так, как вы хотите.

4 голосов
/ 21 сентября 2011

Пользовательский UISearchBar и метод переопределения -addSubview:

- (void) addSubview:(UIView *)view {
    [super addSubview:view];

    if ([view isKindOfClass:UIButton.class]) {
        UIButton *cancelButton = (UIButton *)view;
        [cancelButton setBackgroundImage:[UIImage imageNamed:@"xxxx.png"] forState:UIControlStateNormal];
        [cancelButton setBackgroundImage:[UIImage imageNamed:@"yyyy.png"] forState:UIControlStateHighlighted];
    }
}
1 голос
/ 05 января 2017

Прежде всего, я хотел бы поблагодарить @Eliott от этого https://stackoverflow.com/a/37381821/1473144

Мне пришлось внести несколько корректировок, чтобы его ответ работал в моих спецификациях, которые приведены ниже. Пожалуйста, прошу ОП обновить принятый ответ, так как он ОЧЕНЬ устарел.

Swift 3, iOS 10 и Xcode 8.2.1

searchBar.showsCancelButton = true
var cancelButton: UIButton
let topView: UIView = self.searchBar.subviews[0] as UIView
for subView in topView.subviews {
    if let pvtClass = NSClassFromString("UINavigationButton") {
        if subView.isKind(of: pvtClass) {
            cancelButton = subView as! UIButton

            cancelButton.setTitle("", for: .normal)
            cancelButton.tintColor = UIColor.black
            cancelButton.setImage(#imageLiteral(resourceName: "searchX"), for: .normal)
        }
    }

}
...