在 UISearchBar 中设置取消按钮的样式
Posted
技术标签:
【中文标题】在 UISearchBar 中设置取消按钮的样式【英文标题】:Styling the cancel button in a UISearchBar 【发布时间】:2009-07-29 13:10:53 【问题描述】:我有一个带有取消按钮的 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】:您可以使用UIAppearance
来设置取消按钮的样式,而无需迭代UISearchBar
的子视图,但the UIButton
header does not currently have any methods annotated with 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
【讨论】:
[[UIButton appearanceWhenContainedIn:[MySearchBar class], nil] setBackgroundImage:... forState:UIControlStateNormal]; 有趣的是,取消按钮声明它是 UISearchBar 标题中的 UIButton,但我的 UIBarButton 外观代理覆盖了样式。所以我最终设计了我的取消按钮[[UIBarButtonItem appearanceWhenContainedIn: [UISearchBar class], [ContainingClass class], nil] setTintColor:UIColorFromRGB(0xc5c2b6)];
这是 ios 5+ 的最佳答案。鸡的评论应该在自己的答案中。
请注意,使用 [[UIButton appearance... 将编辑用于擦除您键入的内容的 X 按钮。改用 [[UIBarButtonItem 外观。
@JasonZhao UIBarButtonItem 有不同的背景图片外观选择器,setBackgroundImage:forState:barMetrics:【参考方案2】:
在 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
。
【讨论】:
这才是真正解决问题的方法!谢谢!【参考方案3】:更改“取消”按钮的标题:
[[UIButton appearanceWhenContainedIn:[UISearchBar class], nil] setTitle:@"newTitle" forState:UIControlStateNormal];
Swift 等效项:
let cancelButton = UIButton.appearance(whenContainedInInstancesOf: [UISearchBar.self])
cancelButton?.setTitle("cancel".localized, for: .normal)
【讨论】:
从 iOS5 开始,这是最简单的方法。谢谢 Neru。【参考方案4】:尽管这可能与原始问题不完全相关,但在尝试自定义 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];
在使用此搜索栏的视图控制器中,以下代码负责显示取消按钮并自定义其标题:
- (void)searchBarTextDidBeginEditing:(UISearchBar *)searchBar
MyUISearchBar *sBar = (MyUISearchBar *)searchBar;
[sBar setShowsCancelButton:YES];
[sBar setCloseButtonTitle:@"Done" forState:UIControlStateNormal];
奇怪的是,我不需要做任何事情来隐藏取消按钮,因为默认情况下,当退出搜索模式时它是隐藏的。
【讨论】:
【参考方案5】:你想做的事情很艰难。取消按钮没有内置的钩子。
但是,如果您愿意打开引擎盖,有几个选择。
首先,UISearchBar 是一个 UIView,Cancel 按钮也是一个视图,正如你所期望的那样,它作为子视图添加到搜索栏中。
我做了一点实验,可以告诉你,当按钮在屏幕上时,它的大小为 48,30。
所以在 viewWillAppear 中,你可以这样做:
在 [searchBar 子视图] 中查找大小为 48,30 的取消按钮视图。 (似乎只有一个——这可能会改变...)您可以加倍小心,寻找一个位置大致正确的(横向和纵向不同)。
向取消按钮添加子视图。
子视图应该是一个 UIControl(这样您就可以设置 enabled = NO,以确保触摸事件到达实际的取消按钮)
它需要有正确的颜色和圆角;由于我还不明白的原因,您需要捏造尺寸(55,30 似乎有效)
如果 searchBar.showsCancelButton 始终为 YES,这将起作用;如果您希望它在不编辑搜索字符串时消失,则需要在每次出现取消按钮时找到一个挂钩来添加叠加层。
如您所见,这是一些丑陋的修补。睁大眼睛做。
【讨论】:
我希望它不会变成这样 :) 它确实按照您描述的方式工作(除了取消按钮的大小对我来说是 55x30)。仍然需要尝试使外观正确,但除了这种方法的丑陋之外,它可能有效。非常感谢您的帮助! 嗯!我想做的就是更改 Scope Button 栏的 tintColor。也许类似的东西也会让我看到这种观点......但它是如此脆弱。耶奇。决策决策。 jandrea -- 提出你自己的问题并指出我的问题 -- 我将尝试解释如何做类似的事情。 @Dermot 然后使用 [subview isKindOfClass:[UIButton class]] 找到取消按钮 如果您的目标是 iOS 5+,请考虑小鸡和唐一鸣给出的答案(在此页面上搜索这些名称以找到它们)。【参考方案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]];
【讨论】:
这给出了一个警告,即 cancelButton 可能不会响应 setTintColor,因为这是一个未记录的 API。这也可能导致您的应用因使用未记录的 API 而被 Apple 拒绝。 不要忘记 if 语句中的中断 subViews 只包含 UISearchBarBackground 和 UISearchBarTextField 类型的视图,不包含 UIButton 类型的视图。我正在使用 iOS5。【参考方案7】:如果您想在UISearchBar
上配置取消按钮,您应该从您的UISearchBar
对象中获取UIButton
对象。下面的例子
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;
【讨论】:
这行得通,但只有在取消按钮可见时才能这样做。 对我来说,如果永远不会返回 true。我在这里只得到 UISearchBarBackground UISearchBarTextField 类型的视图。这是我的代码。 for (UIView *subview in [searchField subviews]) NSLog([NSString stringWithFormat:@"%@", [subview class]); if([subview isKindOfClass: [UIButton class]]) //做点什么。 //end if //end for 但这 if 永远不会返回 true。这些 NSLog 打印 UISearchBarBackground 和 UISearchBarTextField ...【参考方案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:...];
这将使您可以根据需要自定义“取消”按钮。
【讨论】:
要在 iOS7+ 上更改标题,您应该为您的应用使用本地化(具有文件夹 de.lproj、fr.lproj、...)【参考方案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】:斯威夫特 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 来自https://***.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)
【讨论】:
【参考方案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 并查看索引控制所在的位置。同样你可以设置
UITextField
属性:
NSArray *arr = [searchbar subviews];
UITextField *searchfield = [arr objectAtIndex:2];
[searchfield setTextAlignment:UITextAlignmentRight];
【讨论】:
【参考方案15】:我的应用程序中有很多 UISearchBar 项目,所以我编写了这个类别来添加一个属性,以便您可以访问mySearchBar.cancelButton
。 (如果您不熟悉类别,read more about extending objects with Categories here。)
请记住,您应该仅在“取消”按钮可见时访问它,因为 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]];
【讨论】:
【参考方案21】:设置cancelButton
样式的最佳方式是不使用UIAppearance
,就像这样它适用于Swift5
iOS13
,它也最适合与UISearchResultController.searchBar
一起使用
extension UISearchBar
func changeSearchBarAppearance(appearance: MyAppearance)
self.barTintColor = appearance.searchbar.barTintColor
self.tintColor = appearance.searchbar.tintColor
if let textField = self.subviews.first?.subviews.last?.subviews.first
textField.tintColor = .black
设置serachBar
tintColor
将设置包括cancelButton
在内的所有项目的tintColor
但这样searchField
中的闪光灯也将设置为相同的tintColor
所以找到textfield
并设置其tintColor
将解决闪光灯问题
【讨论】:
以上是关于在 UISearchBar 中设置取消按钮的样式的主要内容,如果未能解决你的问题,请参考以下文章
如果在 viewDidAppear 之前显示,UISearchBar 取消按钮没有样式