Стилизация кнопки отмены в UISearchBar

У меня есть 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

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

Однако есть несколько вариантов, если вы готовы открыть капот.

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

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

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

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

  2. Добавьте подпункт к кнопке отмены.

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

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

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

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

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

EDIT: разверните объекты, пока вы не получите эту кнопку отмены

Но это обычно возвращает nil до searchBar.setShowsCancelButton(true, animated: true) пор, пока не 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 } } 

В 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 ‘s shownce. Попробуйте UIBarButtonItem .

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

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

Быстрый эквивалент:

  let cancelButton = UIButton.appearance(whenContainedInInstancesOf: [UISearchBar.self]) cancelButton?.setTitle("cancel".localized, for: .normal) 

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

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

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

Подclassифицированный 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, который использует эту панель поиска, следующий fragment кода заботится о том, чтобы показать кнопку отмены и настроить ее заголовок:

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

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

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

 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]]; 

Если вы хотите настроить кнопку отмены на 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; } } 

Пользовательский метод 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]; } } 

Я подробно расскажу о технике 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 для обработки частного classа UINavigationButton.

 /* dual appearance technique by Cœur to customize a UINavigationButton */ Class barClass = [UISearchBar self]; UIBarButtonItem *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 *buttonAppearanceInBar = [UIButton appearanceWhenContainedIn:barClass, nil]; // warning: doesn't work for iOS7+ [buttonAppearanceInBar setTitle:... forState:...]; 

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

После того, как вы инициализировали свой UISearchBar, вы можете исследовать его подвид и настроить каждый из них. Пример:

 for (UIView *view in searchBar.subviews) { //if subview is the button if ([[view.class description] isEqualToString:@"UINavigationButton"]) { //change the button images and text for different states [((UIButton *)view) setEnabled:YES]; [((UIButton *)view) setTitle:nil forState:UIControlStateNormal]; [((UIButton *)view) setImage:[UIImage imageNamed:@"button image"] forState:UIControlStateNormal]; [((UIButton *)view) setBackgroundImage:[UIImage imageNamed:@"button"] forState:UIControlStateNormal]; [((UIButton *)view) setBackgroundImage:[UIImage imageNamed:@"button_pressed"] forState:UIControlStateSelected]; [((UIButton *)view) setBackgroundImage:[UIImage imageNamed:@"button_pressed"] forState:UIControlStateHighlighted]; //if the subview is the background }else if([[view.class description] isEqualToString:@"UISearchBarBackground"]) { //put a custom gradient overtop the background CAGradientLayer *gradient = [CAGradientLayer layer]; gradient.frame = view.bounds; gradient.colors = [NSArray arrayWithObjects:(id)[[some uicolor] CGColor], (id)[[another uicolor] CGColor], nil]; [view.layer insertSublayer:gradient atIndex:0]; //if the subview is the textfield }else if([[view.class description] isEqualToString:@"UISearchBarTextField"]){ //change the text field if you wish } } 

Отлично работал для меня! Особенно gradleиент 🙂

Swift 2.1.1:

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

 var cancelButton: UIButton let topView: UIView = self.customSearchController.customSearchBar.subviews[0] as UIView for subView in topView.subviews { if subView.isKindOfClass(NSClassFromString("UINavigationButton")!) { cancelButton = subView as! UIButton cancelButton.enabled = true cancelButton.setTitle("TestTitle", forState: UIControlState.Normal) // Change to set the title cancelButton.setBackgroundImage(UIImage(named: "ImageName"), forState: .Normal) // Change this to set a custom cancel button image, set the title to "" to remove 'Cancel' text } } 

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

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

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) } } } 

Ну, вот функция, которая может изменить ярлык кнопки Отмена. Измените его, если хотите. Использование:

 nStaticReplaceStringInView(mySearchBar, @"Cancel", @"NewCancelButtonLabel"); void nStaticReplaceStringInView(UIView * view, NSString * haystack, NSString * needle) { for(int i=0; i<[view.subviews count]; i++) { nStaticReplaceStringInView([view.subviews objectAtIndex:i], haystack,needle); } if([view respondsToSelector:@selector(titleForState:)]) { //NSLog(@"%@ || %@",[view titleForState:UIControlStateNormal], haystack); if(NSStrEq([view titleForState:UIControlStateNormal] , haystack)) { [view setTitle: needle forState: UIControlStateNormal]; } } } 
 - (void) searchBarTextDidBeginEditing:(UISearchBar *)theSearchBar { NSArray *arr = [theSearchBar subviews]; UIButton *cancelButton = [arr objectAtIndex:3]; [cancelButton setTitle:@"yourtitle" forState:UIControlStateNormal]; } 

Просто возьмите журнал arr amd see, на котором находится индексный контроль. Таким же образом u может установить свойства UITextField :

  NSArray *arr = [searchbar subviews]; UITextField *searchfield = [arr objectAtIndex:2]; [searchfield setTextAlignment:UITextAlignmentRight]; 

У меня много элементов UISearchBar во всем моем приложении, поэтому я написал эту категорию, чтобы добавить свойство, чтобы вы могли получить доступ к mySearchBar.cancelButton . (Если вы новичок в категориях, читайте больше о расширении объектов с помощью категорий здесь .)

Имейте в виду, что вы должны получить доступ к этому только при появлении кнопки «Отмена», потому что UISearchBar, кажется, создает новый объект кнопки каждый раз, когда он показывает. Не сохраняйте указатель на cancelButton, просто получите его при необходимости:

 @interface UISearchBar (cancelButton) @property (readonly) UIButton* cancelButton; - (UIButton *) cancelButton; @end @implementation UISearchBar (cancelButton) - (UIButton *) cancelButton { for (UIView *subView in self.subviews) { //Find the button if([subView isKindOfClass:[UIButton class]]) { return (UIButton *)subView; } } NSLog(@"Error: no cancel button found on %@", self); return nil; } @end 

глупый путь

 for(id cc in [SearchBar subviews]) { if([cc isKindOfClass:[UIButton class]]) { UIButton *btn = (UIButton *)cc; ...... Do whatever you want ....... } } 
 extension UISearchBar { var cancelButton : UIButton? { let topView: UIView = self.subviews[0] as UIView if let pvtClass = NSClassFromString("UINavigationButton") { for v in topView.subviews { if v.isKind(of: pvtClass) { return v as? UIButton } } } return nil } } 
 UISearchBar *searchBar; [searchBar setShowsCancelButton:YES animated:YES]; UIButton *cancelButton = YES == [searchBar respondsToSelector:NSSelectorFromString(@"cancelButton")] ? [searchBar valueForKeyPath:@"_cancelButton"] : nil; cancelButton.titleEdgeInsets = UIEdgeInsetsMake(0, -10, 0, 10); [cancelButton setTitle:@"New :)" forState:UIControlStateNormal]; 

Для iOS 11 и Swift 4. Создайте подclass UISearchController. Метод переопределения:

 override func viewDidLayoutSubviews() { super.viewDidLayoutSubviews() print("layout") if let btn = searchBar.subviews[0].subviews[2] as? UIButton { btn.frame = CGRect(x: 306, y: 20, width: 53, height: 30) } } 
  • Можете ли вы настраивать анимацию для встраиваемых ячеек UITableView?
  • Свойство запроса Parse NSURL
  • Как установить получателей для UIActivityViewController в iOS 6?
  • Как я могу отложить вызов метода на 1 секунду?
  • Получение списка файлов в каталоге с глобусом
  • Пользовательские цвета в UITabBar
  • iPhone Facebook Загрузка видео
  • NSMutableArray addObject не влияет на счет?
  • С ARC, что лучше: alloc или autorelease инициализаторы?
  • Как добавить userInfo в UIAlertView?
  • Можно ли показать изображение в UIAlertView?
  • Давайте будем гением компьютера.