最近,在项目过程中遇到要自定义SearchBar的外观,虽然自己觉得用系统默认的外观就行了,不过UI设计师要求不用系统的默认样式,要跟app主题保持一致。

从上图可以看出,我们要做的UISearchBar要有圆角,边框颜色,取消按钮颜色,背景透明等等。
开始以为可能要自己写一个自定义的UISearchBar控件了,后面研究了一番,发现可以设定系统UISearchBar属性来更改,便把经验记录下来跟大家分享一下。
首先,我们看下系统默认的SearchBar的样式,离我们的目标样式确实相差很大, 后面我会一步一步详细说明做成我们的目标样式。

1. 设置背景色
我以白色的背景色为例,下面看看代码:
//1. 设置背景颜色
//设置背景图是为了去掉上下黑线
self.customSearchBar.backgroundImage = [[UIImage alloc] init];
// 设置SearchBar的颜色主题为白色
self.customSearchBar.barTintColor = [UIColor whiteColor];

2. 设置边框颜色和圆角
//2. 设置圆角和边框颜色
UITextField *searchField = [self.customSearchBar valueForKey:@"searchField"];
if (searchField) {
[searchField setBackgroundColor:[UIColor whiteColor]];
searchField.layer.cornerRadius = 14.0f;
searchField.layer.borderColor = [UIColor colorWithRed:247/255.0 green:75/255.0 blue:31/255.0 alpha:1].CGColor;
searchField.layer.borderWidth = 1;
searchField.layer.masksToBounds = YES;
}
这段代码有个特别的地方就是通过KVC获得到UISearchBar的私有变量
searchField(类型为UITextField),设置SearchBar的边框颜色和圆角实际上也就变成了设置searchField的边框颜色和圆角,你可以试试直接设置SearchBar.layer.borderColor和cornerRadius,会发现这样做是有问题的。

嗯,离预期效果越来越近了!
3. 设置按钮(取消按钮)的文字和文字颜色
//3. 设置按钮文字和颜色
[self.customSearchBar fm_setCancelButtonTitle:@"取消"];
self.customSearchBar.tintColor = [UIColor colorWithRed:86/255.0 green:179/255.0 blue:11/255.0 alpha:1];
//修正光标颜色
[searchField setTintColor:[UIColor blackColor]];
//其中fm_setCancelButtonTitle是我写的UISearchBar一个分类的方法
- (void)fm_setCancelButtonTitle:(NSString *)title {
if (IS_IOS9) {
[[UIBarButtonItem appearanceWhenContainedInInstancesOfClasses:@[[UISearchBar class]]] setTitle:title];
}else {
[[UIBarButtonItem appearanceWhenContainedIn:[UISearchBar class], nil] setTitle:title];
}
}

需要特别注意的是设置searchBar的tintColor会使输入框的光标颜色改变,可以通过设置searchField的tintColor来修正。
4. 设置输入框的文字颜色和字体
//4. 设置输入框文字颜色和字体
[self.customSearchBar fm_setTextColor:[UIColor blackColor]];
[self.customSearchBar fm_setTextFont:[UIFont systemFontOfSize:14]];
//下面两个方法是UISearchBar分类代码
- (void)fm_setTextColor:(UIColor *)textColor {
if (IS_IOS9) {
[UITextField appearanceWhenContainedInInstancesOfClasses:@[[UISearchBar class]]].textColor = textColor;
}else {
[[UITextField appearanceWhenContainedIn:[UISearchBar class], nil] setTextColor:textColor];
}
}
- (void)fm_setCancelButtonTitle:(NSString *)title {
if (IS_IOS9) {
[[UIBarButtonItem appearanceWhenContainedInInstancesOfClasses:@[[UISearchBar class]]] setTitle:title];
}else {
[[UIBarButtonItem appearanceWhenContainedIn:[UISearchBar class], nil] setTitle:title];
}
}

5. 如何设置搜索图标
下面评论中有简友问我怎么更改默认的搜索图标,我查了下UISearchBar的API,发现有方法可以更改的。
//5. 设置搜索Icon
[self.customSearchBar setImage:[UIImage imageNamed:@"Search_Icon"]
forSearchBarIcon:UISearchBarIconSearch
state:UIControlStateNormal];
为了跟系统默认Icon的有个明显的对比,我特殊找了张绿色的搜索Icon,效果见下图:

Tips: 还可以设置其他的Icon(如清除按钮图标),也是用上面的方法,具体要设置什么,可以去看看UISearchBarIcon这个枚举。
2016-10-20新增
6.实现类似微信的搜索框

这里有位简友问到怎么实现类似微信的搜索框,下面我将详细说说我的思路。
首先,要告诉大家的是 UISearchBar
是一个由多个控件组合成的比较复杂的控件。用Reveal查看 UISearchBar
组成如下 :

从上图可以看出UISearch的组成结构,下面我总结了一张思维导图,更加的清晰直观:

UISearchBar
的主要部分是 UISearchBarTextField
,UISearchBarTextField
又包含好几个subView,其中那个 UIButton
指的是清除按钮(输入文字时会出现)。
好了,上面说了一堆,其实我主要是想表达:既然 UISearchBar
是由这么多个子控件组成,我再往里面加一个按钮又何妨?
最终解决思路就是这样了:在 UISearchBarTextField
添加一个 UIButton
,我暂且将它叫做 voiceButton
,然后 voiceButton
设置好自动布局以及点击事件处理(点击按钮后,显示录音界面...),最后还要监控文本的变化,有输入了文本时,voiceButton
隐藏,没有文本时, 显示 voiceButton
。
所有代码如下:
//6. 实现类似微信的搜索框
UIButton *voiceButton = [UIButton buttonWithType:UIButtonTypeCustom];
[voiceButton setImage:[UIImage imageNamed:@"Voice_button_icon"] forState:UIControlStateNormal];
[voiceButton addTarget:self action:@selector(tapVoiceButton:) forControlEvents:UIControlEventTouchUpInside];
[searchField addSubview:voiceButton];
self.voiceButton = voiceButton;
//Autolayout
voiceButton.translatesAutoresizingMaskIntoConstraints = NO;
NSDictionary *views = NSDictionaryOfVariableBindings(voiceButton);
//设置水平方向约束
[searchField addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"[voiceButton(21)]-|" options:NSLayoutFormatAlignAllRight | NSLayoutFormatAlignAllLeft metrics:nil views:views]];
//设置高度约束
[searchField addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:[voiceButton(21)]" options:NSLayoutFormatAlignAllTop | NSLayoutFormatAlignAllBottom metrics:nil views:views]];
//设置垂直方向居中约束
[searchField addConstraint:[NSLayoutConstraint constraintWithItem:voiceButton attribute:NSLayoutAttributeCenterY relatedBy:NSLayoutRelationEqual toItem:searchField attribute:NSLayoutAttributeCenterY multiplier:1 constant:0]];
//按钮触摸事件
- (IBAction)tapVoiceButton:(id)sender {
NSLog(@"Tap voiceButton");
}
//监控文本变化
- (void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText {
self.voiceButton.hidden = searchText.length > 0;
}
好了,打完收工,最终效果图如下:

完整代码在这里。
网友评论
因为iOS11的SearchBar已经改了,你可以参考 一下这篇文章
你看看这个是否满足你的要求:http://hasjoh.cc/2017/12/11/%E5%AE%9A%E5%88%B6UISearchBar%E5%AF%BC%E8%88%AA%E6%A0%8F-%E5%90%8C%E6%AD%A5iOS-11/
最完善的 教學!
if let tf = searchBar.value(forKey: "searchField") as? UITextField{
tf.subviews[0].corner(radius: 14)
}
这样才完美
UITextField *searchField = [self.customSearchBar valueForKey:@"searchField"];
if (searchField) {
[searchField setBackgroundColor:[UIColor grayColor]];
}
- (BOOL)searchBar:(UISearchBar *)searchBar shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text {
self.voiceButton.hidden = searchBar.text.length + (text.length - range.length) > 0;
return YES;
}
比如 self.searchBar.searchTextPositionAdjustment = UIOffsetMake(15, 0);
表示在水平方向text离searchBar的距离为15个像素
- (void)fm_setCancelButtonFont:(UIFont *)font {
NSDictionary *textAttr = @{NSFontAttributeName : font};
if (IS_IOS9) {
[[UIBarButtonItem appearanceWhenContainedInInstancesOfClasses:@[[UISearchBar class]]] setTitleTextAttributes:textAttr forState:UIControlStateNormal];
}else {
[[UIBarButtonItem appearanceWhenContainedIn:[UISearchBar class], nil] setTitleTextAttributes:textAttr forState:UIControlStateNormal];
}
}
详细Demo可以看这里:https://github.com/lexiaoyao20/CustomSearchBar
如果觉得不错记得给个Star哟
[UITextField appearanceWhenContainedInInstancesOfClasses:@[[UISearchBar class]]].textColor = [UIColor redColor];
[[UITextField appearanceWhenContainedInInstancesOfClasses:@[[UISearchBar class]]] setFont:[UIFont systemFontOfSize:10]];都会失效,都会变成默认的黑色和默认字体,只有取消按钮title可以通过 [[UIBarButtonItem appearanceWhenContainedInInstancesOfClasses:@[[UISearchBar class]]] setTitle:title];
修改成功 正常我尝试了下将searchBar 包装在一个UIVIew *wrapView里面,还是没用,就是这样包装:
UIView *wrapView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 375 - 2 * 44 - 2 * 15, 44)];
[wrapView addSubview:self.customSearchBar];
self.navigationItem.titleView = wrapView;
最后 我猜测 估计是 哪里出现了问题,但是 用KVC直接取出 searchBar的searchField,然后设置
searchField.textColor = [UIColor redColor];
searchField.font = [UIFont systemFontOfSize:17];
却能更改字体和颜色成功,一点问题都没有,!!我很费解,那个[UIAppearance xxx]这个类型调用机制 不给力啊,能 给个解释方向吗,感激不尽!
[[UITextField appearanceWhenContainedIn:[UISearchBar class], nil] setDefaultTextAttributes:
@{NSForegroundColorAttributeName:[UIColor redColor],
NSFontAttributeName : [UIFont systemFontOfSize:25]}];
我也没闹明白是为什么,我猜在NavigationBar上可能优先用DefaultTextAttributes效果吧。。
你试下 self.customSearchBar.translucent = NO;
如果还是不行,试试 self.customSearchBar.barStyle = UIBarStyleBlack;
如果还不行,再告知我一声。。
回头我在家里面再试一下~
调用KVC,有被拒的风险么?
//5. 设置搜索Icon
[self.customSearchBar setImage:[UIImage imageNamed:@"Search_Icon"]
forSearchBarIcon:UISearchBarIconSearch
state:UIControlStateNormal];