Стилизация кнопки отмены в 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
Ответы
Ответ 1
То, что вы хотите сделать, довольно сложно. Нет встроенного крючка, чтобы попасть на кнопку отмены.
Однако есть несколько вариантов, если вы готовы открыть капот.
Во-первых, UISearchBar - это UIView, а кнопка "Отмена" также представляет собой представление, которое добавляется в панель поиска как подвью, как и следовало ожидать.
Я немного экспериментировал и могу сказать, что, когда кнопка находится на экране, она имеет размер 48,30.
Итак, в viewWillAppear вы можете сделать что-то вроде этого:
-
Найдите изображение кнопки отмены в [searchBar subviews], ища номер с размером 48,30. (Кажется, только один - это может измениться...) Вы можете быть вдвойне осторожны и искать ту, которая находится примерно в правильном положении (отличается пейзажем и портретом).
-
Добавьте подзаголовок к кнопке отмены.
-
Подвью должен быть UIControl (чтобы вы могли установить enabled = NO, чтобы убедиться, что события касания доходят до кнопки фактической отмены)
-
Он должен иметь правильный цвет и закругленные углы; вам нужно будет вымыть размер по причинам, которые я еще не понял (55,30, похоже, работает)
-
Это будет работать, если searchBar.showsCancelButton всегда ДА; если вы хотите, чтобы он исчез, когда вы не редактировали строку поиска, вам нужно будет найти крючок для добавления наложения каждый раз, когда появится кнопка отмены.
-
Как вы можете видеть, это какое-то уродливое мастерство. Сделайте это широко открытыми глазами.
Ответ 2
Вы можете использовать UIAppearance
, чтобы стилизовать кнопку отмены без повторения подпунктов UISearchBar
, но заголовок UIButton
в настоящее время не имеет каких-либо методов, аннотированных с помощью UI_APPEARANCE_SELECTOR
.
РЕДАКТИРОВАТЬ: разверните подпрограммы до тех пор, пока вы не получите эту кнопку отмены
Но это обычно возвращает nil до
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
}
}
Ответ 3
Измените заголовок кнопки "Отмена":
[[UIButton appearanceWhenContainedIn:[UISearchBar class], nil] setTitle:@"newTitle" forState:UIControlStateNormal];
Быстрый эквивалент:
let cancelButton = UIButton.appearance(whenContainedInInstancesOf: [UISearchBar.self])
cancelButton?.setTitle("cancel".localized, for: .normal)
Ответ 4
В 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
manifestnce. Попробуйте UIBarButtonItem
.
Ответ 5
Хотя это может быть не совсем актуально для исходного вопроса, решение по-прежнему применимо в более широком смысле, чтобы попытаться настроить кнопку "Отмена" в 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];
}
Как ни странно, мне не нужно было ничего делать, чтобы скрыть кнопку отмены, поскольку она скрыта по умолчанию при выходе из режима поиска.
Ответ 6
Вы можете найти кнопку отмены, перейдя по подзонам панели поиска и проверив тип класса (вместо размера):
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
Если вы хотите настроить кнопку отмены на 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;
}
}
Ответ 8
Пользовательский метод 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];
}
}
Ответ 9
Я дам подробный ответ на технику 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:...];
Это позволит вам настроить кнопку "Отмена" столько, сколько захотите.
Ответ 10
После того, как вы инициализировали свой 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
}
}
Отличная работа! Особенно градиент:)
Ответ 11
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
}
}
Ответ 12
Прежде всего, я хотел бы поблагодарить @Eliott из этого fooobar.com/questions/196858/...
Мне пришлось внести несколько корректировок для его ответа на работу в моих спецификациях, которые идут ниже.
Пожалуйста, я прошу 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)
}
}
}
Ответ 13
Ну, вот функция, которая может изменить метку кнопки "Отмена". Измените его, если хотите.
Использование:
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];
}
}
}
Ответ 14
- (void) searchBarTextDidBeginEditing:(UISearchBar *)theSearchBar
{
NSArray *arr = [theSearchBar subviews];
UIButton *cancelButton = [arr objectAtIndex:3];
[cancelButton setTitle:@"yourtitle" forState:UIControlStateNormal];
}
Просто возьмите журнал arr amd и посмотрите, на чём находится индексный контроль. Таким же образом u может установить свойства UITextField
:
NSArray *arr = [searchbar subviews];
UITextField *searchfield = [arr objectAtIndex:2];
[searchfield setTextAlignment:UITextAlignmentRight];
Ответ 15
У меня есть много элементов 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
Ответ 16
глупый путь
for(id cc in [SearchBar subviews])
{
if([cc isKindOfClass:[UIButton class]])
{
UIButton *btn = (UIButton *)cc;
......
Do whatever you want
.......
}
}
Ответ 17
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
}
}
Ответ 18
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];
Ответ 19
Для iOS 11 и Swift 4.
Создайте подкласс 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)
}
}
Ответ 20
Для iOS 10 и выше, используйте следующий метод
[[UIBarButtonItem appearanceWhenContainedInInstancesOfClasses:@[[UISearchBar class]]] setTintColor:[UIColor blackColor]];