自定义UISearchBar外观

作者: lexiaoyao20 | 来源:发表于2016-01-10 00:42 被阅读16714次

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

    图1:设计效果图

    从上图可以看出,我们要做的UISearchBar要有圆角,边框颜色,取消按钮颜色,背景透明等等。

    开始以为可能要自己写一个自定义的UISearchBar控件了,后面研究了一番,发现可以设定系统UISearchBar属性来更改,便把经验记录下来跟大家分享一下。

    首先,我们看下系统默认的SearchBar的样式,离我们的目标样式确实相差很大, 后面我会一步一步详细说明做成我们的目标样式。

    图2:UISearchBar默认样式

    1. 设置背景色

    我以白色的背景色为例,下面看看代码:

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

    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,会发现这样做是有问题的。

    图4:设置边框颜色和圆角

    嗯,离预期效果越来越近了!

    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];
        }
    }
    
    图5:设置按钮文字和颜色

    需要特别注意的是设置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];
        }
    }
    
    图6:最终对比效果图

    5. 如何设置搜索图标

    下面评论中有简友问我怎么更改默认的搜索图标,我查了下UISearchBar的API,发现有方法可以更改的。

    //5. 设置搜索Icon
        [self.customSearchBar setImage:[UIImage imageNamed:@"Search_Icon"]
                      forSearchBarIcon:UISearchBarIconSearch
                                 state:UIControlStateNormal];
    

    为了跟系统默认Icon的有个明显的对比,我特殊找了张绿色的搜索Icon,效果见下图:

    设置搜索Icon.png

    Tips: 还可以设置其他的Icon(如清除按钮图标),也是用上面的方法,具体要设置什么,可以去看看UISearchBarIcon这个枚举。

    2016-10-20新增

    6.实现类似微信的搜索框

    图8:提问.png

    这里有位简友问到怎么实现类似微信的搜索框,下面我将详细说说我的思路。

    首先,要告诉大家的是 UISearchBar 是一个由多个控件组合成的比较复杂的控件。用Reveal查看 UISearchBar 组成如下 :

    图9:UISearchBar组成图.png

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

    图10:UISearchBar思维导图.png

    UISearchBar 的主要部分是 UISearchBarTextFieldUISearchBarTextField 又包含好几个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;
    }
    

    好了,打完收工,最终效果图如下:

    record.gif

    完整代码在这里

    相关文章

      网友评论

      • 想象纸中:我是用你的方法 设置圆角。效果一直不理想。给人的感觉就是 上下两条边似乎不能被“切割边缘”的感觉?请教你这个问题。
        想象纸中:好的。先谢谢楼主!
        lexiaoyao20:@想象之中3 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/
        因为iOS11的SearchBar已经改了,你可以参考 一下这篇文章
      • 不泯iOS:你好,我想问一下,现在有什么办法可以谁让搜索栏上的搜索图标和文字居中呢
        lexiaoyao20:最近太忙了,一直没关注简书,也没时间更新博客,抱歉
        你看看这个是否满足你的要求: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/
      • junfly:你好,感觉可以价格IB版,storyboard上怎么更改。这样效率更高。。
      • 小李童学:难道没有人发现吗,原设计图没有左边🔍吗,然而这样写一定会有🔍,亲
        lexiaoyao20:要隐藏也是可以的,参考:https://stackoverflow.com/questions/3245342/removing-the-image-on-the-left-of-an-uisearchbar/11647556
      • jade_xiaohui:非常棒。正好用上了。
        lexiaoyao20:@墓后煮屎人 很高兴能够帮到你~
      • TinXie:幫頂呀!
        最完善的 教學!
      • 清無:searchBar.backgroundImage = UIImage()
        if let tf = searchBar.value(forKey: "searchField") as? UITextField{
        tf.subviews[0].corner(radius: 14)
        }
        这样才完美
      • 清無:这个还比较详细点
      • 洁简:在iOS11上有点不一样了
      • Ko_Neko:请问下有办法设置搜索栏(放大镜所在View)的背景吗? 现在一直是白色 想变为灰色
        lexiaoyao20:你这样试试:

        UITextField *searchField = [self.customSearchBar valueForKey:@"searchField"];
        if (searchField) {
        [searchField setBackgroundColor:[UIColor grayColor]];
        }
      • 范小兵:什么代码编辑器😳
        范小兵:@落羽生 我说的是你那张UISearchBar组成图的左边的图看着是编辑器里面的吧?
        lexiaoyao20:用的 Markdown 语法
      • Highgray:当用拼音输入法时,按到第二个字母,再删除一个,右边清除按钮和声音的按钮就重叠了.这个问题怎么解决啊 老板
        Highgray:@落羽生 管用! 楼主v5
        lexiaoyao20:把监控文本变化的那个代理方法改成下面这个:
        - (BOOL)searchBar:(UISearchBar *)searchBar shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text {
        self.voiceButton.hidden = searchBar.text.length + (text.length - range.length) > 0;
        return YES;
        }
      • CocoFei:我觉得自定义一个view,好像更简单吧!纯属感觉:blush:
      • 咔客:楼主 在吗? 用你的方法设置后 我想用键盘上面的搜索按钮作为事件响应 就会崩溃 类型就不对 有什么解决办法吗?
        lexiaoyao20:@咔客 发我qq邮箱吧:455295813@qq.com
        咔客:@落羽生 有邮件或者什么 我发下你
        lexiaoyao20:@咔客 有没有Demo可以看,光看你描述也没法知道问题所在
      • 一个努力的boy:这篇文章写的不错,对这个控件研究的很透彻
      • da4ab0a77bc6:自己写的程序是uisearchbar作为tableheaderview,求教怎么改变其中textfield与searchbar的边距
        lexiaoyao20:试试修改searchBar 的这个属性看看:searchTextPositionAdjustment,
        比如 self.searchBar.searchTextPositionAdjustment = UIOffsetMake(15, 0);
        表示在水平方向text离searchBar的距离为15个像素
      • beyourking:学习了 正在做搜索功能 :smile:
      • junfly:1024
      • b1a67a134e4d:在把voiceButton加在searchField中时,当searchField处于firstResponder时,貌似voiceButton不接收点击事件了.在把voiceButton加在customSearchBar中时就不影响接收点击事件,不知道是怎么回事,求大神指点
        lexiaoyao20:@zdbeyond 嗯嗯,不错
        b1a67a134e4d:多谢回复,可能不应该把button加在searchField中吧,加在searchBar中就可以接收点击事件,设置对应约束后在界面上看着跟加在searchField中一样一样的,嘿嘿
        lexiaoyao20:@zdbeyond 跟iOS事件响应链有关,searchField是第一响应者,在它内部点击,事件就没传递出去了,后续有时间再研究下怎么解决这个问题
      • 秋_明:特意滑到最下面来👍一个
        lexiaoyao20:@秋_明 受宠若惊:smiley:
      • 小魔仙儿:请问想在搜索栏添加个话筒按钮,当点击时候走语音功能。 跟微信一样 怎么搞呢?
        lexiaoyao20:@小魔仙儿 文章已更新,你看下第6节,另外,示例代码也已经更新:https://github.com/lexiaoyao20/CustomSearchBar
      • e4fa7826e618:用了设置icon位置,为什么还是无法把icon放到右边 求解答????
        e4fa7826e618:解决了亲
        lexiaoyao20:@businessios 你是怎么设置的?
      • 8cc60b1d01de:怎么去掉默认的图标图片呢,那个方法将图标为空也不想啊 :sweat:
        8cc60b1d01de:@豆花喵喵向前冲 也不行啊,打错了
      • 烟佛经:SearchBar.layer.borderColor和cornerRadius 会出什么问题啊
        lexiaoyao20:@烟佛经 外观会有问题,不是想要的效果
      • 583a28b22745:一直不知道怎么自定义,现在学习了
      • jane4321:很好,学习了。我还想问一个问题,怎么设置取消按钮的字体大小?
        jane4321:不错,已Star。 :grin:
        lexiaoyao20:@jane4321
        - (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哟 :smiley:
      • 穿山甲到底说了什么:问下楼主 ,为什么 当 searcBar 作为 self.navigationItem.titleView = searchBar的时候;
        [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]这个类型调用机制 不给力啊,能 给个解释方向吗,感激不尽!


        lexiaoyao20:@穿山甲到底说了什么 我用下面的方法是可以的
        [[UITextField appearanceWhenContainedIn:[UISearchBar class], nil] setDefaultTextAttributes:
        @{NSForegroundColorAttributeName:[UIColor redColor],
        NSFontAttributeName : [UIFont systemFontOfSize:25]}];

        我也没闹明白是为什么,我猜在NavigationBar上可能优先用DefaultTextAttributes效果吧。。
      • 33a02bf71691: self.customSearchBar.barTintColor = [UIColor blackcolor];背景色如果设置深颜色的就会变浅 毛玻璃效果? 有么有什么办法不让他变浅 而是真实的颜色呢
        lexiaoyao20:@重复昵称 能搞个Demo出来吗,我可以给你来调试一下
        33a02bf71691:@落羽生 还是不行 :sob:
        lexiaoyao20:@重复昵称
        你试下 self.customSearchBar.translucent = NO;
        如果还是不行,试试 self.customSearchBar.barStyle = UIBarStyleBlack;
        如果还不行,再告知我一声。。
      • 33a02bf71691:知道怎么改大小了 :relaxed:
        lexiaoyao20:@重复昵称 UIBarButtonItem的setTitleTextAttributes方法应该是可以设置字体大小的
      • 33a02bf71691:取消按钮的字体大小是不是不能改的
      • 33a02bf71691:真不错 :+1:
      • Rickie_Lambert: :+1: 楼主 真 棒, 对我 太有用了, 大赞 :+1: :+1: :+1: :+1: !!!
        lexiaoyao20:@Rickie_Lambert 很高兴能够帮到你:smile:
      • 陈鸡蛋:你好,知道怎么设置searchBar 的文字 左对齐吗? 默认是居中的 。找了好久没好到方法。。求助
        晓折:我也遇到了这个问题,现在的要求是,无论是不是第一响应者,提示文字都要一直靠左显示.我也试了很多方法了.
        lexiaoyao20:@Key_Chen 有一种方法就是去找产品经理谈,说服她用系统原生的效果就好了 :joy:
        lexiaoyao20:@Key_Chen 我试了很多种方法,貌似也不行。 searchBar.text = @"XXX",这样默认是左对齐的,你应该是要设置placeholder左对齐吧,UISearchBar对placeholder固定做了居中,实在不行的话,只能自己去写一个SearchBar了。
      • 我系哆啦:写的很详细,封装的也很好,看得出楼主写代码比较用心
        macfai:@落羽生 楼主真大神:+1::+1:
        lexiaoyao20:@我系哆啦 大家都能看懂的代码才是好代码:yum:
      • 程序H:请问为什么我点击取消没有效果呀
        lexiaoyao20:@人士浮屠 你需要实现UISearchBarDelegate代理方法:- (void)searchBarCancelButtonClicked:(UISearchBar *)searchBar; 具体你可以参考我文章最后发的那个Demo
      • 编号x71291:我想知道怎么改变searchBar的textfield的高度
        编号x71291:@落羽生 我也是暂时还没解决这个问题
        lexiaoyao20:@CatLoveDog 我今天试了下,貌似也没找到满意的解决方案。系统UISearchBar的高度是固定死的,如果真的要修改高度的话,可能要自己去重写了。 若你有好的解决方案,不妨告诉我一下。
        lexiaoyao20:@CatLoveDog 你看看这个对你有没有帮助:http://stackoverflow.com/questions/556814/changing-the-size-of-the-uisearchbar-textfield
        回头我在家里面再试一下~
      • 7babacee03ce: UITextField *searchField = [self.customSearchBar valueForKey:@"searchField"];
        调用KVC,有被拒的风险么?
        7babacee03ce:@落羽生 多谢 :smiley:
        lexiaoyao20:@ivars 没有的,具体原因可以参考这篇博客 http://blog.sunnyxx.com/2015/06/07/fullscreen-pop-gesture/ 里面的 关于私有API 那段
      • 再见远洋:多写点文章啊,期待你的干货
        再见远洋:@落羽生 嗯嗯,期待中
        lexiaoyao20:@再见远洋 多谢关注,一直想写,但是还没想到合适的主题。 这个周末尽量整一篇出来
      • flowerVV:非常感谢
      • dcdb9e0934f1:mark,很有用
        lexiaoyao20:@lalaluya :relieved:
      • 3d0eb8334cdd:能改变🔍这个图标么
        lexiaoyao20:@海底_捞月 这个是可以的

        //5. 设置搜索Icon
        [self.customSearchBar setImage:[UIImage imageNamed:@"Search_Icon"]
        forSearchBarIcon:UISearchBarIconSearch
        state:UIControlStateNormal];
      • 779b68e64744:写的很好,特别是用KVC获取私有属性,我经常这么干哈哈😄
      • 6605eae1686f:学习了
        lexiaoyao20:@征天紫龙 😋
      • 半尺尘:不错~
        lexiaoyao20:@半尺尘 谢谢关注:relaxed:

      本文标题:自定义UISearchBar外观

      本文链接:https://www.haomeiwen.com/subject/hdeahttx.html