当前位置:首页 > 通信资讯 > 正文

系统原生的uisearchbar在ios 11经历了一次变革,高度由原来的44变成了56 (使用默认高度的估计都被坑了),样式也发生了些微的变化,比如在未输入状态下圆角变化,放大镜图标和文本的文字不再居中而是靠左了。具体看图

ios 自定义navigationBar(ios定制uisearchbar导航栏同步ios1","p":true,"g":[{"type":"sug","sa":"s_1","q":"ios 自定义navigationBar"}],"slid":"270489584322374",&quo

一些主流app也常见在导航栏嵌入searchbar,以网易云音乐和知乎为例,左边是主页,右边是搜索页面 (注意光标)。

ios 自定义navigationBar(ios定制uisearchbar导航栏同步ios1","p":true,"g":[{"type":"sug","sa":"s_1","q":"ios 自定义navigationBar"}],"slid":"270489584322374",&quoios 自定义navigationBar(ios定制uisearchbar导航栏同步ios1","p":true,"g":[{"type":"sug","sa":"s_1","q":"ios 自定义navigationBar"}],"slid":"270489584322374",&quo

实现思路与案例

核心思想是设置导航栏的titleview和左右的barbuttonitem。主要有3种方式

  1. 首页导航栏的titleview使用button来实现,搜索页面使用searchbar。
  2. 首页和搜索页面导航栏的titleview都是用searchbar,searchbar的样式针对两个页面做不同的修改。这种方式可以重用我们定制的searchbar,减少冗余。
  3. 首页导航栏titleview使用button来实现,搜索页面的使用textfield。这种方式更彻底,更灵活,相对也更复杂一些。

为什么上面的titleview说是button不是其他的?其他的当然也可以实现。button自带imageview和titlelabel,只需要设置偏移量更容易达到我们想要的,而且视图层级更少,在流畅性方面更有保证些。

案例

ios 自定义navigationBar(ios定制uisearchbar导航栏同步ios1","p":true,"g":[{"type":"sug","sa":"s_1","q":"ios 自定义navigationBar"}],"slid":"270489584322374",&quo

ios 自定义navigationBar(ios定制uisearchbar导航栏同步ios1","p":true,"g":[{"type":"sug","sa":"s_1","q":"ios 自定义navigationBar"}],"slid":"270489584322374",&quo

网易云音乐首页和搜索页面的导航栏视图层级,titleview都使用mcsearchbar来实现,并且设置了导航栏左右两边的按钮 。这类似上文所说的第二种思路。

ios 自定义navigationBar(ios定制uisearchbar导航栏同步ios1","p":true,"g":[{"type":"sug","sa":"s_1","q":"ios 自定义navigationBar"}],"slid":"270489584322374",&quo

ios 自定义navigationBar(ios定制uisearchbar导航栏同步ios1","p":true,"g":[{"type":"sug","sa":"s_1","q":"ios 自定义navigationBar"}],"slid":"270489584322374",&quo

图中可以清楚看到知乎首页导航栏由2个button组成,搜索页面使用了textfield,这类似上文提到的第三种思路。

实战

通过自定义searchbar实现一个如下样式的导航栏

ios 自定义navigationBar(ios定制uisearchbar导航栏同步ios1","p":true,"g":[{"type":"sug","sa":"s_1","q":"ios 自定义navigationBar"}],"slid":"270489584322374",&quo

先自定义一个uisearchbar的初始化方法,观察一下首页和搜索页的异同,像searchfield的大小背景色是一致的,可以这部分可以直接给定,而placeholder是不一样的,所以应该在调用的时候提供。以此类推,新建一个ohsearchbar类,一个初始化方法

?
1 2 3 4 5 6 7 8 9 10 11 12 - (instancetype)initwithframe:(cgrect)frame placeholder:(nsstring *)placeholder textfieldleftview:(uiimageview *)leftview showcancelbutton:(bool)showcancelbutton tintcolor:(uicolor *)tintcolor { if (self = [super initwithframe:frame]) { self.frame = frame; self.tintcolor = tintcolor; //光标颜色 self.bartintcolor = [uicolor whitecolor]; self.placeholder = placeholder; self.showscancelbutton = showcancelbutton; self.leftview = leftview; // 用来代替左边的放大镜 [self setimage:[uiimage imagenamed:@"clear"] forsearchbaricon:uisearchbariconclear state:uicontrolstatenormal]; // 替换输入过程中右侧的clearicon } return self; }

新建一个首页ohhomeviewcontroller,设置导航栏的titleview和rightbarbutton

?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 // navigation buttom uibutton *messagebutton = [uibutton buttonwithtype:uibuttontypesystem]; [messagebutton setimage:[uiimage imagenamed:@"msg"] forstate:uicontrolstatenormal]; messagebutton.bounds = cgrectmake(0, 0, 30, 30); uibarbuttonitem *messagebarbutton = [[uibarbuttonitem alloc] initwithcustomview:messagebutton]; self.navigationitem.rightbarbuttonitem = messagebarbutton; // search bar uiimageview *leftview = [[uiimageview alloc] initwithimage:[uiimage imagenamed:@"scan"]]; leftview.bounds = cgrectmake(0, 0, 24, 24); self.ohsearchbar = [[ohsearchbar alloc] initwithframe:cgrectmake(0, 0, screen_width, 44) placeholder:@"点击我跳转" textfieldleftview:leftview showcancelbutton:no tintcolor:[uicolor clearcolor]]; self.navigationitem.titleview = self.ohsearchbar;

让我们来看下效果,左边为ios 9,右边ios 11

ios 自定义navigationBar(ios定制uisearchbar导航栏同步ios1","p":true,"g":[{"type":"sug","sa":"s_1","q":"ios 自定义navigationBar"}],"slid":"270489584322374",&quo

这时候可以看到几处差异

  1. searchbar的高度
  2. searchbar的textfield的放大镜和文字位置
  3. textfield的圆角不一致
  4. 更细心的还会发现,textfield的位置不一致

解决方法: 第一和第二个问题,判断设备是否是ios 11,若是则设置其高度,不是则让其placeholder居左。关键代码如下

?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 if ([[uidevice currentdevice] systemversion].doublevalue >= 11.0) { [[self.heightanchor constraintequaltoconstant:44.0] setactive:yes]; } else { [self setleftplaceholder]; } - (void)setleftplaceholder { sel centerselector = nsselectorfromstring([nsstring stringwithformat:@"%@%@", @"setcenter", @"placeholder:"]); if ([self respondstoselector:centerselector]) { bool centeredplaceholder = no; nsmethodsignature *signature = [[uisearchbar class] instancemethodsignatureforselector:centerselector]; nsinvocation *invocation = [nsinvocation invocationwithmethodsignature:signature]; [invocation settarget:self]; [invocation setselector:centerselector]; [invocation setargument:¢eredplaceholder atindex:2]; [invocation invoke]; } }

对于第三和第四个问题,用kvc获取textfield,并对其进行定制。令textfield位置、大小、圆角一致。

?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 - (void)layoutsubviews{ [super layoutsubviews]; // search field uitextfield *searchfield = [self valueforkey:@"searchfield"]; searchfield.backgroundcolor = dark_blue_color; searchfield.textcolor = [uicolor whitecolor]; searchfield.font = [uifont systemfontofsize:16]; searchfield.leftview = self.leftview; searchfield.frame = cgrectmake(0, 8, screen_width, 28); searchfield.layer.cornerradius = 5; searchfield.layer.maskstobounds = yes; [searchfield setvalue:[uicolor whitecolor] forkeypath:@"placeholderlabel.textcolor"]; [self setvalue:searchfield forkey:@"searchfield"]; self.searchtextpositionadjustment = (uioffset){10, 0}; // 光标偏移量 }

同样的,先看下运行效果

ios 自定义navigationBar(ios定制uisearchbar导航栏同步ios1","p":true,"g":[{"type":"sug","sa":"s_1","q":"ios 自定义navigationBar"}],"slid":"270489584322374",&quo

原本以为这下是没什么问题的,结果简直是坑

ios 自定义navigationBar(ios定制uisearchbar导航栏同步ios1","p":true,"g":[{"type":"sug","sa":"s_1","q":"ios 自定义navigationBar"}],"slid":"270489584322374",&quo

textfild的长度、位置、圆角都不一样 解释下这里出现的问题

观察上方图片上方的searchbar,会发现textfield左边是圆角,右边是直角,说明是被截取的。导航栏titleview的范围就划分到了那个部分,而下边的searchbar连rightbarbutton都不放过,直接抢占了位置。推测这是由于ios 11导航栏视图层级变化产生的,可以这里了解下 www.jianshu.com/p/352f101d6… ,或者自行科普,不详细展开。所以对于searchbar的size设置要小心了,尽量控制在合适的范围。

textfield的圆角是不一致的,自定义圆角大小时,取消其本身的圆角样式

?
1 searchfield.borderstyle = uitextborderstylenone;

查看视图层级会发现,ios 11以下,设置titleview,x的默认坐标居然是12,而ios 11是0。所以设置textfield的x坐标的话,在ios 11下必须多出12才会是一致的位置。

ios 自定义navigationBar(ios定制uisearchbar导航栏同步ios1","p":true,"g":[{"type":"sug","sa":"s_1","q":"ios 自定义navigationBar"}],"slid":"270489584322374",&quo

ios 自定义navigationBar(ios定制uisearchbar导航栏同步ios1","p":true,"g":[{"type":"sug","sa":"s_1","q":"ios 自定义navigationBar"}],"slid":"270489584322374",&quo

修改代码上面的代码

?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 - (void)layoutsubviews{ [super layoutsubviews]; // search field uitextfield *searchfield = [self valueforkey:@"searchfield"]; searchfield.backgroundcolor = dark_blue_color; searchfield.textcolor = [uicolor whitecolor]; searchfield.font = [uifont systemfontofsize:16]; searchfield.leftview = self.leftview; if (@available(ios 11.0, *)) { // 查看视图层级,在ios 11之前searchbar的x是12 searchfield.frame = cgrectmake(12, 8, screen_width*0.8, 28); } else { searchfield.frame = cgrectmake(0, 8, screen_width*0.8, 28); } searchfield.borderstyle = uitextborderstylenone; searchfield.layer.cornerradius = 5; searchfield.layer.maskstobounds = yes; [searchfield setvalue:[uicolor whitecolor] forkeypath:@"placeholderlabel.textcolor"]; [self setvalue:searchfield forkey:@"searchfield"]; self.searchtextpositionadjustment = (uioffset){10, 0}; // 光标偏移量 }

这时候就是我们想要的结果了。

首页暂时告一段落,接着开始我们的搜索页面。与首页不同的是需要searchbar与searchcontroller配合使用。新建一个ohsearchcontroller类 添加一个属性

?
1 @property (nonatomic, strong) ohsearchbar *ohsearchbar;

初始化代码

?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 - (instancetype)initwithsearchresultscontroller:(uiviewcontroller *)searchresultscontroller searchbarframe:(cgrect)searchbarframe placeholder:(nsstring *)placeholder textfieldleftview:(uiimageview *)leftview showcancelbutton:(bool)showcancelbutton bartintcolor:(uicolor *)bartintcolor{ if (self = [super initwithsearchresultscontroller:searchresultscontroller]) { self.ohsearchbar = [[ohsearchbar alloc] initwithframe:searchbarframe placeholder:placeholder textfieldleftview:leftview showcancelbutton:yes tintcolor:bartintcolor]; uibutton *button = [self.ohsearchbar valueforkey:@"cancelbutton"]; button.tintcolor = [uicolor whitecolor]; [button settitle:@"取消" forstate:uicontrolstatenormal]; [self.ohsearchbar setvalue:button forkey:@"cancelbutton"]; } return self; }

接着是我们的视图控制器ohsearchviewcontroller

?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 uiimageview *leftview = [[uiimageview alloc] initwithimage:[uiimage imagenamed:@"search"]]; leftview.bounds = cgrectmake(0, 0, 24, 24); self.ohsearchcontroller = [[ohsearchcontroller alloc] initwithsearchresultscontroller:self searchbarframe:cgrectmake(0, 0, screen_width, 44) placeholder:@"请输入搜索内容进行搜索" textfieldleftview:leftview showcancelbutton:yes bartintcolor:base_blue_color]; [self.ohsearchcontroller.ohsearchbar becomefirstresponder]; self.ohsearchcontroller.ohsearchbar.delegate = self; [self.ohsearchcontroller.ohsearchbar setleftplaceholder]; self.navigationitem.titleview = self.ohsearchcontroller.ohsearchbar; self.navigationitem.hidesbackbutton = yes;

完成这一步后到了交互环节了,点击首页的searchbar跳转搜索页面,点击搜索页面的取消按钮返回到首页。 首页设置searchbar的代理,并完成一下代理方法

?
1 2 3 4 5 - (bool)searchbarshouldbeginediting:(uisearchbar *)searchbar { ohsearchviewcontroller *ohsearchviewcontroller = [[ohsearchviewcontroller alloc] init]; [self.navigationcontroller pushviewcontroller:ohsearchviewcontroller animated:no]; return yes; }

搜索页设置searchbar的代理,并完成一下代理方法

?
1 2 3 4 5 6 7 8 9 10 - (void)searchbarcancelbuttonclicked:(uisearchbar *)searchbar { [self.navigationcontroller popviewcontrolleranimated:no]; } - (void)searchbarsearchbuttonclicked:(uisearchbar *)searchbar { [self.ohsearchcontroller.ohsearchbar resignfirstresponder]; // 让取消按钮一直处于激活状态 uibutton *cancelbtn = [searchbar valueforkey:@"cancelbutton"]; cancelbtn.enabled = yes; }

这时候问题又出现了,点击搜索页面的取消按钮,没有跳回首页而是还在这个页面。但是可以看到屏幕的闪动。通过打印消息发现,点了取消按钮,执行了首页的- (bool)searchbarshouldbeginediting:(uisearchbar *)searchbar方法。 仔细推敲之后想明白了原因是没有取消第一响应者,加上导航栏的交互机制,pop到上个页面的时候并不会进行页面刷新导致了这个问题。 解决办法在首页要push搜索页面的时候取消第一响应者

?
1 2 3 - (void)viewwilldisappear:(bool)animated { [self.ohsearchbar resignfirstresponder]; }

到此,便大功告成了。可以看下源码加深理解。希望对大家的学习有所帮助,也希望大家多多支持服务器之家。

原文链接:https://juejin.im/post/5a367c9f6fb9a045186ad15b

如果您对该产品感兴趣,请填写办理(客服微信:xiaoxiongyidong)

为您推荐:

发表评论

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。