UISearchBar

转载:http://blog.csdn.net/pingshw/article/details/13999797

非常菜的iOS菜鸟一枚~~~~~最近做项目里需要一个搜索框,UI设计的是使用自定义的外观,所以需要修改很多内置的东西,写代码和调试的过程搜了很多资料,也在stack上问了很多问题,发现关于UISearchBar的资料少得可怜,尤其是iOS7里search bar的资料更是几近没有,所以我整理了一下我这段时间的研究出来的东西,希望能帮助大家自定义search bar,也方便日后自己查看。备注:因为我用到的是iOS默认的搜索框交互模式,即搜索框获取焦点时,输入框变窄,显示取消按钮,失去焦点时输入框变宽,取消按钮隐藏,所以下面的介绍全部基于这一情况。内容比较多,慢慢整理。


1.UISearchBar结构

     网上关于iOS6及以下版本的search bar结构分析很多,但是,iOS7 search bar结构修改较大,下面截图说明。iOS6下UISearchBar的subviews数组如下,失去焦点时有两个对象,分别对应:背景图、搜索输入框,获取焦点时有三个对象,分别对应:背景图、搜索输入框、取消按钮。 

    
         图1 iOS6下搜索框失去焦点时subviews                 图2 iOS6下搜索框获取焦点时subviews

而iOS7下,UISearchBar的subviews数组如下,只有一个对象,类型是UIView,我继续挖掘了这个UIView的subviews,

图3 iOS7下搜索框的subviews

subviewContainerView就是刚刚提到的search bar 的subviews数组中的那个UIView(即上图中的0x08a83400),它的subviews如下,和iOS6不同的是,图4是search bar从未获得过焦点情况下的截图,而图5是获得过一次焦点以后的截图(无论当下它是否是第一响应者),也就是说,search bar初始化时,不会创建取消按钮,当它第一次获取焦点时,会创建取消按钮,并存入subviewContainerView的subviews数组,这之后,即使search bar失去焦点,取消按钮需要隐藏,也不会remove取消按钮。iOS7速度上比iOS6快很多,不知道和这些改变有没有关系。
 
图4                                                                      图5
总结:

iOS6下,UISearchBar的结构是:

不是第一响应者状态:                                    第一响应者状态:

                                                               


iOS7下,UISearchBar的结构是:

             首次获得焦点前:                                  首次获得焦点之后(无论是否是第一响应者):

 

2.自定义外观--输入框、取消按钮、背景

     知道UISearchBar结构后,就可以按照结构来修改UISearchBar各个部分,定制自己的搜索框外观。首先获取subviews:

  1. float version = [[[UIDevice currentDevice] systemVersion] floatValue];  
  2. NSArray *subviewContainer = version>7.0 ? ((UIView *)self.subviews[0]).subviews : self.subviews;  

然后依次读取该数组中的元素subview,修改它的颜色、frame等即可:

  1.         if ([subview isKindOfClass:NSClassFromString(@"UISearchBarBackground") ] ){   
  2. //subview是背景图  
  3.         }   
  4.         if ([subview isKindOfClass:NSClassFromString(@" UISearchBarTextField") ] ){  
  5. //subview是输入框  
  6.         }   
  7.         if ([subview isKindOfClass:NSClassFromString(@"UINavigationButton") ] ){  
  8. //subview是取消按钮  
  9.         }  


iOS6里,背景和输入框一直存在,所以关于它们的颜色、字体等修改可以在init函数里实现,而它们的frame每次绘制均会重置,所以需要在layoutSubviews里修改,而且输入框有时宽有时窄需要自己判断。而取消按钮在初始化时并不会创建,它会在需要显示时创建,需要隐藏时remove掉,所以,每次绘制的取消按钮其实都是一个全新的按钮,有关它的设置需要全部在layoutSubviews里完成。


     iOS7里,背景框和输入框情况和iOS6中一样,需要特别留意的是取消按钮,第一部分讲过了,iOS7里,取消按钮会在第一次需要显示时创建,创建后一直存在,并未被remove掉,我打印了它的属性发现,它的hidden始终为NO,而且它位于subviews最后一个,应该在最上层,那么它为什么可以藏起来呢?打印这个对象发现,它的frame在未显示时是origin.x是大于320的,原来被藏到屏幕外去了。所以,iOS7时可以通过检测取消按钮被重置后的默认x坐标来判断它是显示还是隐藏,如果需要显示,再修改为你想要的frame。
     iOS7里,你会发现用上面的方法修改subview的title外观等并未成功,apple的文档建议利用Appearance来修改:
     

  1. [[UIBarButtonItem appearanceWhenContainedIn:[UISearchBarclass],nil] set……];  

不过这个方法又修改不了背景图片和title文字……而取消按钮初始化时也不会被创建……所以这几个还是在layoutSubviews实现……

输入框在获取焦点时,是窄的,失去焦点时变宽,所以修改frame需要判断它是哪种状态。我是通过判断取消按钮是否存在或是否存在在屏幕里来设置的,好像有点绕弯,用UITextField的isEditing或者isFirstResponder就可以,其实iOS6里确实这样就可以,但是我遇到了一个变态的情况…… 我的搜索框是放在一个view controller里的,这个view controller是tab bar三个view controller中的一个……问题来了,当我点击了搜索框,却未输入任何东西的情况下,我切换tab bar,然后再切换回来,这个时候输入框不是第一响应者也不在编辑,所以输入框是宽的……而且因为我偷懒,输入框不是第一响应者也不在编辑的时候我就让取消按钮按默认frame显示了,反正它在界面外,但是这时候这个按钮却意料外的在界面内,frame还很不符合要求……我查看了一下,是因为在切回来的时候,iOS7里又调用了一次layoutSubviews函数,而这个时候又不符合输入框是第一响应者或在编辑这一条件,所以悲剧了……我也没明白具体是哪个事件或者哪个属性决定取消按钮隐藏或者显示,总之不是UITextField的isEditing和isFirstResponder,所以我就改用这个取消按钮的x坐标来判断了,有点绕,但是不出错。iOS6里切回来也会看到输入框并不是第一响应者,没在编辑,不出错的原因在于,layoutSubivew压根没有重新调用……而且iOS6里我用subviews count判断,反正取消按钮存在,我就让输入框变小,也不会出错。如果哪位大神知道这里面具体是怎么隐藏取消按钮的,还望指点!

总结:
UISearchBar三部分:
1.背景和输入框的外观在init修改,尺寸在layoutSubviews实现,输入框尺寸在iOS6里通过subviews count判断,iOS7通过取消按钮frame.origin.x判断;
2.取消按钮,iOS6里在subviews count是3的时候,获取lastObject并修改它的外观和尺寸,iOS7里,部分外观利用Appearance在init修改,部分在layoutSubviews修改,尺寸在layoutSubviews根据其每次被绘制前的默认x坐标判断是否修改。


3.搜索

     iOS自带一个控件和搜索框配套使用UISearchDisplayController,初始化时,需要指明它的search bar是哪个,它的searchContentsController是哪个。刚刚才发现,如果只添加search bar,不设置它的UISearchDisplayController,search bar使用非常不正常,所以,一定要设置UISearchDisplayController。

  1. [[ UISearchDisplayController  alloc ]initWithSearchBar:searchBar contentsController:controller];  

controller是要搜索的view controller,其实就是它和search bar所在的那个view controller。搜索结果会显示在 searchResultsTableView中,它是一个UITableView。
     然后实现它的委托: id < UISearchDisplayDelegate > delegate id < UITableViewDataSource > searchResultsDataSource、 id < UITableViewDelegate > searchResultsDelegate第一个负责搜索, 后两个负责搜索结果的显示。
     UISearchDisplayDelegate 里有很多可选择的函数,会在不同的时候自动调用,为了实时显示搜索结果,采用了
  1. - (BOOL) searchDisplayController:(UISearchDisplayController *)controller shouldReloadTableForSearchString:(NSString *)searchString;  

输入框内文字一有变动,就会自动调用。然后可以根据输入框里的文字 searchBar . text ,调用自己的具体搜索函数,返回一个数组,reload searchResultsTableView就可以了,这个数组就是搜索结果table view的数据源。如果搜索结果为空,那么就遍历searchResultsTableView的subviews,找到那个类型时UILabel的subview,修改它就好了,如果搜索过程比较慢,也可以让它在搜索结果显示前提示“搜索中”。
     因为我们的搜索比较费时,所以采用了多线程处理,最后在显示前检测了一下这个结果的搜索关键字和当前的搜索框里的文本是否一致,如果一直再显示。


4.搜索结果高亮显示

    和普通的UITableView一样,搜索结果列表的cell也可以自己定制,用 CATextLayer和NSMutableAttributedString 高亮显示搜索关键字。如果一个cell同时有UILabel和CATextLayer,那么在reloadData的时候会很明显的发现,label会一瞬间刷新为新的值,layer会有个渐变的过程,而且这个情况在cell复用时没什么影响,还不明白为什么。
     retina屏里CATextLayer会出现字体模糊,设置
  1. layer.contentsScale = [[UIScreen mainScreen]scale];  

即可,这是retina屏幕分辨率和尺寸的比例问题。其实search bar取消按钮也会有模糊,那是因为shadow offset的原因,设为0就ok了。



5.点击search bar下面的阴影--灰色半透明背景修改

     我不太清楚这个部分到底该叫什么名字,就是如图7中灰色的部分,这一部分是半透明的,把除了键盘和搜索框的其余部分全部遮盖起来了,也就是告诉用户,这一部分暂时不能操作。这一部分应该时接到上面的search bar的底部,然后输入框是第一响应者,但是没有输入任何文字时,它存在,如果输入文字了,那么显示的就是searchResultsTableView了。点击这一部分,和点击取消按钮一样,退出搜索框,键盘收起。研究这个的起因是UI要求修改它的颜色,最后被逼无奈找见它是因为iOS7……
     我们的搜索框是比默认高度矮的,只修改三个子view的frame会导致search bar底部和这个灰色view的顶部有一段距离,灰色的背景不能完全盖住后面的view。其实,在iOS6里解决这个问题比较简单,你只需要修改这个search bar的frame 高度就好。所以寻觅这个view的计划就被我搁浅了。但是iOS7里,悲剧了,不管你是修改search bar自己的高度,还是search bar subviews[0]的高度,这个部分都还是盖不住。所以我从search bar开始,逐个往上研究。假设search bar所在的view controller叫做vc1,我想search bar是在vc1里的,这个灰色的背景也该在vc1里面吧,所以我查看了这个vc1的subviews,发现里面有一个类型是 UISearchDisplayControllerContainerView 的subview,望文生义,搜索-显示-控制器-容器-视图,那就应该是这个灰色view了,是不是呢?这个view的y坐标为0,我将它设置为 我的search bar的高度 - 默认搜索框的高度44,这个view就向上移动了。所以这个灰色的view就是 UISearchDisplayControllerContainerView
     iOS6和iOS7在这个问题上,同样处理不一样,在iOS7里,这个view类型是 UISearchDisplayControllerContainerView ,这个类型的view一般在一个controller里就只有一个,所以可以放心地直接用if语句把这个类型的subview挑出来,修改subview就好了,但是,iOS6里,这个view的类型是UIControl,这个类型的view就不敢保证只用一个了,比如我的界面里除了这个灰色的view,还有两个也是这个类型的view或者这个类型派生的其他类型的view,所以,直接if挑出来很有可能把别的view也摘出来了。不过好在这个灰色view不管是在iOS6里还是iOS7里,都是在显示的时候添加在search bar的view controller的最顶端,不显示时remove掉,所以可以判断search bar所在view controller的subviews的长度来确定这个view存在否,如果存在,即为subviews的最后一个subview,修改它就可以。
     既然找到这个view了,是不是修改这个view的backgroundColor就可以达到UI的要求了呢?尝试了下,iOS6确实是这样滴!但是……iOS7里又悲剧了……iOS7下,这个view有3个subview,结构如图8所示。


                       图7                                                                                                                  图8                                                                   

subview1和整个window一样大,subview2在search bar默认位置是320X44(search bar默认大小)大小,subview3是紧接着subview2的纵坐标,然后到window底部的一个view。所以以3.5屏为例,上面修改这个view的frame的效果其实是:

假如search bar 的frame是(0,100,320,24),那么view的frame是(0,0,320,480), subview1的frame是(0,0,320,480),subview2 (0,100,320,44),subview3(0,144,320,336)。subview3就是我们想要修改的那个灰色背景,从它们的frame可以看出y坐标125-144中间空了一段。上面修改view的frame的纵坐标为我的search bar的高度 - 默认搜索框的高度44,即24-44=-20,总高度加上20,其实是将整个view向上拉伸20,subview3自然也就向上移了20.修改颜色的话,修改subview3的就好啦。其实iOS6下,这个view就是subview3。

不过不管在iOS6还是7,这个灰色背景默认有一个灰色的附加值,所以你将其设置为红色背景,会看起来像红色底上加一个灰色的膜。

总结:

  • iOS6下,修改灰色半透明背景的高度只需要修改search bar的frame即可,修改颜色通过修改点击search bar时它所在的controller新添加的那个view的颜色来实现。
  • iOS7下,修改高度通过修改点击search bar时它所在的controller新添加的那个view的y坐标来实现,颜色通过修改这个view的第三个subview的颜色来实现。
实现:
通用一点,我是不修改这个search bar的frame,自定义外观时只修改三个subview的frame。无论是iOS6,还是iOS7,(1)获取search bar所在controller在search bar获取焦点时新添加的这个view,(2)修改这个view的frame以修改高度,(3)然后判断这个view的subview有几个,(4)0个,就直接修改view的颜色以修改灰色为想要的颜色,3个就修改第三个subview的颜色。

转载:http://blog.sina.com.cn/s/blog_7b9d64af0101dfg8.html

UISearchBar控件就是要为你完成搜索功能的一个专用控件。它集成了很多你意想不到的功能和特点!


首先,还是来普及一下UISearchBar控件API相关的属性和方法吧!


UISearchBar属性相关


_searchBar = [[UISearchBar alloc] initWithFrame:CGRectZero];// 初始化,不解释

    [self.searchBar setPlaceholder:@"Search"];// 搜索框的占位符

    [self.searchBar setPrompt:@"Prompt"];// 顶部提示文本,相当于控件的Title

    [self.searchBar setBarStyle:UIBarMetricsDefault];// 搜索框样式

    [self.searchBar setTintColor:[UIColor blackColor]];// 搜索框的颜色,当设置此属性时,barStyle将失效

    [self.searchBar setTranslucent:YES];// 设置是否透明

    [self.searchBar setBackgroundImage:[UIImage imageNamed:@"image0"]];// 设置背景图片

    [self.searchBar setSearchFieldBackgroundImage:[UIImage imageNamed:@"image3"]forState:UIControlStateNormal];// 设置搜索框中文本框的背景

    [self.searchBar setSearchFieldBackgroundImage:[UIImage imageNamed:@"image0"]forState:UIControlStateHighlighted];

    [self.searchBar setSearchFieldBackgroundPositionAdjustment:UIOffsetMake(30,30)];// 设置搜索框中文本框的背景的偏移量

    

    [self.searchBar setSearchResultsButtonSelected:NO];// 设置搜索结果按钮是否选中

    [self.searchBar setShowsSearchResultsButton:YES];// 是否显示搜索结果按钮

    

    [self.searchBar setSearchTextPositionAdjustment:UIOffsetMake(30, 0)];// 设置搜索框中文本框的文本偏移量

    

    

    

    [self.searchBar setInputAccessoryView:_btnHide];// 提供一个遮盖视图

    [self.searchBar setKeyboardType:UIKeyboardTypeEmailAddress];// 设置键盘样式

    

    // 设置搜索框下边的分栏条

    [self.searchBar setShowsScopeBar:YES];// 是否显示分栏条

    [self.searchBar setScopeButtonTitles:[NSArrayarrayWithObjects:@"Singer",@"Song",@"Album", nil]];// 分栏条,栏目

    [self.searchBar setScopeBarBackgroundImage:[UIImage imageNamed:@"image3"]];//分栏条的背景颜色

    [self.searchBar setSelectedScopeButtonIndex:1];// 分栏条默认选中的按钮的下标

    

    

    [self.searchBar setShowsBookmarkButton:YES];// 是否显示右侧的书图标

    

    [self.searchBar setShowsCancelButton:YES];// 是否显示取消按钮

    [self.searchBar setShowsCancelButton:YES animated:YES];

    

    // 是否提供自动修正功能(这个方法一般都不用的)

    [self.searchBar setSpellCheckingType:UITextSpellCheckingTypeYes];// 设置自动检查的类型

    [self.searchBar setAutocorrectionType:UITextAutocorrectionTypeDefault];// 是否提供自动修正功能,一般设置为UITextAutocorrectionTypeDefault

    

    self.searchBar.delegate = self;// 设置代理

    [self.searchBar sizeToFit];

    myTableView.contentInset =UIEdgeInsetsMake(CGRectGetHeight(self.searchBar.bounds), 0, 0, 0);


    

    [self.view addSubview:myTableView];

    

    [myTableView addSubview:self.searchBar];



这么多属性,其实看起来多,你实际去操作事件一下,就发现很简单的!

绝大多部分都是定义一些外观的东西!了解了各个属性,一定能满足你设计出你想要的外观效果!!


然后,解释一下,我个人觉的比较有趣和重要的属性!


1.@property (nonatomic, readwrite, retain) UIView *inputAccessoryView;属性


例如:


[self.searchBar setInputAccessoryView:your_View];// 提供一个遮盖视图


当处于UISearchBar焦点状态下(输入框正要输入内容时),会有一个遮盖视图。


你翻看一下,iPhone手机上的电话本搜索功能。那个遮盖视图就是一个半透明的黑色View。




查看了一下API,是iOS 6.0 以及以后,新加入的!

那么就意味这 iOS 6.0 之前的系统是不兼容的。那么怎么才能达到这个类似的效果呢?

变通一下,其实,很简单:仍然设置一个按钮,初始状态下,该UIButton控件透明度设置为0;并且在控件取得焦点时,设置透明度为1。

小技巧:如果要设置这个属性,那么,就最好定义一个UIButton控件,这样,当点击该遮盖层的话,可以利用按钮事件,

设置:[self.searchBar resignFirstResponder];让搜索框放弃第一焦点。(iPhone电话薄也是这么做的,感觉很人性化)。


迷惑:还有一个小的问题:当我让UISearchBar显示取消按钮时,当我让UISearchBar失去焦点时,我的取消按钮也不能点击了。衰啊。

看了一下iPhone电话薄的UISearchBar,竟然可以也,找了很久,都不知道是怎么回事,大概苹果又开始玩私有API了吧。

解决方法:很暴力,但是很好用!在UISearchBar上原来取消按钮的位置上覆盖一个UIButton,设置成一样的。呵呵。可以了。


类似如下:


// 遮盖层

_btnAccessoryView=[[UIButton alloc] initWithFrame:CGRectMake(0, 44, BOUNDS_WIDTH,BOUNDS_HEIGHT)];

[_btnAccessoryView setBackgroundColor:[UIColor blackColor]];

[_btnAccessoryView setAlpha:0.0f];

[_btnAccessoryView addTarget:self action:@selector(ClickControlAction:)forControlEvents:UIControlEventTouchUpInside];

[self.view addSubview:_btnAccessoryView];



// 遮罩层(按钮)-点击处理事件

- (void) ClickControlAction:(id)sender{

    NSLog(@"handleTaps");

    

    [self controlAccessoryView:0];


}



// 控制遮罩层的透明度

- (void)controlAccessoryView:(float)alphaValue{

    

    [UIView animateWithDuration:0.2 animations:^{

        //动画代码

        [self.btnAccessoryView setAlpha:alphaValue];

    }completion:^(BOOL finished){

        if (alphaValue<=0) {

            [self.searchBar resignFirstResponder];

            [self.searchBar setShowsCancelButton:NO animated:YES];

            [self.navigationController setNavigationBarHidden:NO animated:YES];


        }

        

    }];

}


2.@property(nonatomic,assign) id<</b>UISearchBarDelegate> delegate;属性


例如:


self.searchBar.delegate = self;


说到这个属性,就是设置委托了。


UISearchBarDelegate委托定义了很多关于,搜索框的一些操作数据的协议方法!


先来个,特写,把x协议的家庭成员列出来:


@protocol UISearchBarDelegate


@optional


- (BOOL)searchBarShouldBeginEditing:(UISearchBar *)searchBar;

- (void)searchBarTextDidBeginEditing:(UISearchBar *)searchBar;

- (BOOL)searchBarShouldEndEditing:(UISearchBar *)searchBar;

- (void)searchBarTextDidEndEditing:(UISearchBar *)searchBar;

- (void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText;

- (BOOL)searchBar:(UISearchBar *)searchBar shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text; 

- (void)searchBarSearchButtonClicked:(UISearchBar *)searchBar;

- (void)searchBarBookmarkButtonClicked:(UISearchBar *)searchBar;

- (void)searchBarCancelButtonClicked:(UISearchBar *) searchBar;

- (void)searchBarResultsListButtonClicked:(UISearchBar *)searchBar;


- (void)searchBar:(UISearchBar *)searchBar selectedScopeButtonIndexDidChange:(NSInteger)selectedScope;


@end


这不需要解释吧,看方法名称就能了解!


我们来看一看,常用的委托方法吧。


#pragma mark - UISearchBarDelegate 协议


// UISearchBar得到焦点并开始编辑时,执行该方法

- (BOOL)searchBarShouldBeginEditing:(UISearchBar *)searchBar{

    [self.searchBar setShowsCancelButton:YES animated:YES];

    [self.navigationController setNavigationBarHidden:YES animated:YES];

    [self controlAccessoryView:0.9];// 显示遮盖层。

    return YES;


}


// 取消按钮被按下时,执行的方法

- (void)searchBarCancelButtonClicked:(UISearchBar *)searchBar{

    [self.searchBar resignFirstResponder];

    [self.searchBar setShowsCancelButton:NO animated:YES];

    [liveViewAreaTable searchDataBySearchString:nil];// 搜索tableView数据

    [self.navigationController setNavigationBarHidden:NO animated:YES];

    [self controlAccessoryView:0];// 隐藏遮盖层。

    

}


// 键盘中,搜索按钮被按下,执行的方法

- (void)searchBarSearchButtonClicked:(UISearchBar *)searchBar{

    NSLog(@"---%@",searchBar.text);

    [self.searchBar resignFirstResponder];// 放弃第一响应者

    [liveViewAreaTable searchDataBySearchString:searchBar.text];

    [self.navigationController setNavigationBarHidden:NO animated:YES];

    [self controlAccessoryView:0];// 隐藏遮盖层。


}


// 当搜索内容变化时,执行该方法。很有用,可以实现时实搜索

- (void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText;{

    NSLog(@"textDidChange---%@",searchBar.text);

    [liveViewAreaTable searchDataBySearchString:searchBar.text];// 搜索tableView数据

    [self controlAccessoryView:0];// 隐藏遮盖层。


}


3.遍历UISearchBar控件的子控件,这样可以针对不同的子视图来设置外观了。


for(id subView in [self.searchBar subviews]){

        if([subView isKindOfClass:[UIButton class]]){

            UIButton *btn = (UIButton *)subView;

            [btn setTitle:@"取消"  forState:UIControlStateNormal];

        }  

    }


  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值