1、searchBar
本例子实现布局:上面是一个navigationController,接下来一个searchBar,下面是tableView
searchBar这个控件就用来搜索tableView上的数据
[[UISearchDisplayControlleralloc] initWithSearchBar:searchBarcontentsController:self];
UISearchDisplayController这个控件很强大,它初始化是基于searchBar的,里面有些效果很不错,apple都封装好了,并且可以很好的支持实时搜索,即我们只需要将搜索出来的数据重新赋给array(这个array用来存储tableView数据),不需要reloadData,就会自动出来
其实reloadData也没用,为什么呢?因为搜索出来的结果显示在tableView上,该tableView并不是当前布局的那个tableView,而是另外一个,我猜测应该是UISearchDisplayController里面自带的,所以不要混淆了
特别是在tableView代理方法里,有时候需要判断代理方法传入的tableView是否为当前布局的tableView,因为也有可能是UISearchDisplayController里自带的,它们同样会触发代理方法
当点击searchBar时,它会自动上移并且遮住navigationController
经过测试,如果上面是navigationBar,则searchBar不会移动,但如果是UINavigationController自带过来的,则会上移覆盖
往往有的时候都是UINavigationController自带过来的,如果使用UISearchDisplayController,searchBar就会自动覆盖,这个情况我试了很多次,包括新创建了一个navigationBar盖在上面,但效果依然不好,对于这种情况,基于我目前的技术,只能舍弃UISearchDisplayController,单纯的用UISearchBar了,虽然效果差了一些,但需要实现的功能照样可以,比如实时搜索,除了重新赋值给array外,额外的操作就是需要reloadData了。
有时候点击searchBar时,右侧可能没有出现‘cancel/取消’按钮,这时需要调用下面的方法
- (void)setShowsCancelButton:(BOOL)showsCancelButton animated:(BOOL)animated
相信看方法名字就知道是做什么的了
效果如图:
可以根据输入的关键字,在TableView中显示符合的数据。
图中分组显示和索引效果,前面的博文已经记录,不再赘述。下面的例子是基于前文的基础上修改的,所以文件名啥的,请参考前文。
第一步是在TableView上方添加一个Search Bar,这里有一点需要注意,必须先把TableView拖下来,留下空间放Search Bar,不要在Table View占满屏幕的情况下把Search Bar拖到Table View顶部。区别在于,使用后面的方法,Search Bar是作为Table View的Header部分添加的,而前面的方法,Search Bar是独立的。在添加索引功能时,如果作为Table View的Header添加,右侧的索引会遮住Search Bar的右边部分。Search Bar几个常用属性:
Placeholder是提示,就是hint属性,Corretion是自动修正,一般设为NO,即不修正,Show Cancel Button是显示取消按钮,我这里勾选。选中Search Bar的情况下切换到Connections Inspector面板,delegate与File’s Owner建立连接(我们会在ViewController中支持UISearchBarDelegate协议)。与前面几篇文章的例子相同,ViewController文件名为PDViewController.h和PDViewController.m。
第二步,添加Table View和Search Bar的Outlet.按住Control键,分别拖动Table View和Search Bar到PDViewController.h,添加Outlet
第三步,就是PDViewController代码:
PDViewController.h:
15#import
@interface PDViewController : UIViewController
@property (strong,nonatomic) NSDictionary *names;
@property (strong,nonatomic) NSMutableDictionary *mutableNames;
@property (strong,nonatomic)NSMutableArray *mutableKeys;
//可变字典和可变数组,用于存储显示的数据,而不可变的字典用于存储从文件中读取的数据
@property (strong, nonatomic) IBOutlet UITableView *table;
@property (strong, nonatomic) IBOutlet UISearchBar *search;
-(void)resetSearch;
//重置搜索,即恢复到没有输入关键字的状态
-(void)handleSearchForTerm:(NSString *)searchTerm;
//处理搜索,即把不包含searchTerm的值从可变数组中删除
@end
PDViewController.m:
198#import "PDViewController.h"
#import "NSDictionary+MutableDeepCopy.h"
@implementation PDViewController
@synthesize names=_names;
@synthesize mutableKeys=_mutableKeys;
@synthesize table = _table;
@synthesize search = _search;
@synthesize mutableNames=_mutableNames;
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Release any cached data, images, etc that aren't in use.
}
#pragma mark - View lifecycle
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
NSString *path=[[NSBundle mainBundle] pathForResource:@"sortednames" ofType:@"plist"];
//取得sortednames.plist绝对路径
//sortednames.plist本身是一个NSDictionary,以键-值的形式存储字符串数组
NSDictionary *dict=[[NSDictionary alloc] initWithContentsOfFile:path];
//转换成NSDictionary对象
self.names=dict;
[self resetSearch];
//重置
[_table reloadData];
//重新载入数据
}
- (void)viewDidUnload
{
[self setTable:nil];
[self setSearch:nil];
[super viewDidUnload];
self.names=nil;
self.mutableKeys=nil;
self.mutableNames=nil;
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
// Return YES for supported orientations
return (interfaceOrientation != UIInterfaceOrientationPortraitUpsideDown);
}
-(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
//返回分组数量,即Array的数量
return [_mutableKeys count];
//
}
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
if ([_mutableKeys count]==0) {
return 0;
}
NSString *key=[_mutableKeys objectAtIndex:section];
NSArray *nameSection=[_mutableNames objectForKey:key];
return [nameSection count];
//返回Array的大小
}
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSUInteger section=[indexPath section];
//分组号
NSUInteger rowNumber=[indexPath row];
//行号
//即返回第section组,rowNumber行的UITableViewCell
NSString *key=[_mutableKeys objectAtIndex:section];
//取得第section组array的key
NSArray *nameSection=[_mutableNames objectForKey:key];
//通过key,取得Array
static NSString * tableIdentifier=@"CellFromNib";
UITableViewCell *cell=[tableView dequeueReusableCellWithIdentifier:tableIdentifier];
if(cell==nil)
{
cell=[[UITableViewCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:tableIdentifier];
}
cell.textLabel.text=[nameSection objectAtIndex:rowNumber];
//从数组中读取字符串,设置text
return cell;
}
-(NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
{
if ([_mutableKeys count]==0) {
return 0;
}
NSString *key=[_mutableKeys objectAtIndex:section];
return key;
}
-(NSArray *)sectionIndexTitlesForTableView:(UITableView *)tableView
{
return _mutableKeys;
//通过key来索引
}
-(void)resetSearch
{//重置搜索
_mutableNames=[_names mutableDeepCopy];
//使用mutableDeepCopy方法深复制
NSMutableArray *keyarr=[NSMutableArray new];
[keyarr addObjectsFromArray:[[_names allKeys] sortedArrayUsingSelector:@selector(compare:)]];
//读取键,排序后存放可变数组
_mutableKeys=keyarr;
}
-(void)handleSearchForTerm:(NSString *)searchTerm
{//处理搜索
NSMutableArray *sectionToRemove=[NSMutableArray new];
//分组待删除列表
[self resetSearch];
//先重置
for(NSString *key in _mutableKeys)
{//循环读取所有的数组
NSMutableArray *array=[_mutableNames valueForKey:key];
NSMutableArray *toRemove=[NSMutableArray new];
//待删除列表
for(NSString *name in array)
{//数组内的元素循环对比
if([name rangeOfString:searchTerm options:NSCaseInsensitiveSearch].location==NSNotFound)
{
//rangeOfString方法是返回NSRange对象(包含位置索引和长度信息)
//NSCaseInsensitiveSearch是忽略大小写
//这里的代码会在name中找不到searchTerm时执行
[toRemove addObject:name];
//找不到,把name添加到待删除列表
}
}
if ([array count]==[toRemove count]) {
[sectionToRemove addObject:key];
//如果待删除的总数和数组元素总数相同,把该分组的key加入待删除列表,即不显示该分组
}
[array removeObjectsInArray:toRemove];
//删除数组待删除元素
}
[_mutableKeys removeObjectsInArray:sectionToRemove];
//能过待删除的key数组删除数组
[_table reloadData];
//重载数据
}
-(NSIndexPath *)tableView:(UITableView *)tableView willSelectRowAtIndexPath:(NSIndexPath *)indexPath
{//TableView的项被选择前触发
[_search resignFirstResponder];
//搜索条释放焦点,隐藏软键盘
return indexPath;
}
-(void)searchBarSearchButtonClicked:(UISearchBar *)searchBar
{//按软键盘右下角的搜索按钮时触发
NSString *searchTerm=[searchBar text];
//读取被输入的关键字
[self handleSearchForTerm:searchTerm];
//根据关键字,进行处理
[_search resignFirstResponder];
//隐藏软键盘
}
-(void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText
{//搜索条输入文字修改时触发
if([searchText length]==0)
{//如果无文字输入
[self resetSearch];
[_table reloadData];
return;
}
[self handleSearchForTerm:searchText];
//有文字输入就把关键字传给handleSearchForTerm处理
}
-(void)searchBarCancelButtonClicked:(UISearchBar *)searchBar
{//取消按钮被按下时触发
[self resetSearch];
//重置
searchBar.text=@"";
//输入框清空
[_table reloadData];
[_search resignFirstResponder];
//重新载入数据,隐藏软键盘
}
@end
3、Search Bar and Search DisplayController的实现
新建Navigation-based Project。打开.xib文件,拖一个Search Bar and Search DisplayController 对象到Table View对象上方,如下图所示,选中File’s Owner ,打开Connections面板:
现在我们来创建Search Bar和SearchDisplay Controller的出口。打开Assistant Editor,按住ctrl键,将SearchDisplay Controller拖到ViewController 的头文件中。创建一个名为searchDisplayController的出口,然后点Connect。
同样的方法为Search Bar创建连接。现在ViewController的头文件看起来像这样:
#import
@interface RootViewController : UITableViewController {
UISearchDisplayController *searchDisplayController; UISearchDisplayController *searchBar;
NSArray *allItems;
NSArray *searchResults;
}
@property (nonatomic, retain) IBOutlet UISearchDisplayController *searchDisplayController;
@property (nonatomic, retain) IBOutlet UISearchDisplayController *searchBar;
@property (nonatomic, copy) NSArray *allItems;
@property (nonatomic, copy) NSArray *searchResults;
@end
你可能注意到,我初始化了两个NSArray。一个用于作为数据源,一个用于保存查找结果。在本文中,我使用字符串数组作为数据源。继续编辑.m文件前,别忘了synthesize相关属性:
@synthesize searchDisplayController;
@synthesize searchBar;
@synthesize allItems;
@synthesize searchResults;
在viewDidLoad 方法中,我们构造了我们的字符串数组:
- (void)viewDidLoad {
[super viewDidLoad];
// [self.tableView reloadData];
self.tableView.scrollEnabled = YES;
NSArray *items = [[NSArray alloc] initWithObjects: @"Code Geass", @"Asura Cryin'", @"Voltes V", @"Mazinger Z", @"Daimos", nil];
self.allItems = items;
[items release];
[self.tableView reloadData];
}
在Table View的返回TableView行数的方法中,我们先判断当前Table View是否是searchDisplayController的查找结果表格还是数据源本来的表格,然后返回对应的行数:
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
NSInteger rows = 0;
if ([tableView isEqual:self.searchDisplayController.searchResultsTableView]){
rows = [self.searchResults count];
}else{
rows = [self.allItems count];
}
return rows;
}
在tableView:cellForRowAtIndexPath:方法里,我们需要做同样的事:
// Customize the appearance of table view cells.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = @"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
}
/* Configure the cell. */
if ([tableView isEqual:self.searchDisplayController.searchResultsTableView]){
cell.textLabel.text = [self.searchResults objectAtIndex:indexPath.row];
}else{
cell.textLabel.text = [self.allItems objectAtIndex:indexPath.row];
}
return cell;
}
现在来实现当搜索文本改变时的回调函数。这个方法使用谓词进行比较,并讲匹配结果赋给searchResults数组:
- (void)filterContentForSearchText:(NSString*)searchText scope:(NSString*)scope {
NSPredicate *resultPredicate = [NSPredicate predicateWithFormat:@"SELF contains[cd] %@", searchText];
self.searchResults = [self.allItems filteredArrayUsingPredicate:resultPredicate];
}
接下来是UISearchDisplayController的委托方法,负责响应搜索事件:
#pragma mark - UISearchDisplayController delegate methods
-(BOOL)searchDisplayController:(UISearchDisplayController *)controller shouldReloadTableForSearchString:(NSString *)searchString {
[self filterContentForSearchText:searchString scope:[[self.searchDisplayController.searchBar scopeButtonTitles] objectAtIndex:[self.searchDisplayController.searchBar selectedScopeButtonIndex]]];
return YES;
}
- (BOOL)searchDisplayController:(UISearchDisplayController *)controller shouldReloadTableForSearchScope:(NSInteger)searchOption {
[self filterContentForSearchText:[self.searchDisplayController.searchBar text] scope:[[self.searchDisplayController.searchBar scopeButtonTitles] objectAtIndex:searchOption]];
return YES;
}
运行工程,当你在搜索栏中点击及输入文本时,如下图所示:
4、UISearchBar的使用以及下拉列表框的实现
在IOS混饭吃的同志们都很清楚,搜索框在移动开发应用中的地位。今天我们就结合下拉列表框的实现来聊聊UISearchBar的使用。本人新入行的菜鸟一个,不足之处请多多指教。直接上代码。
UISearchBar控件的声明:(在控制器DownListViewController中)
@property (nonatomic,retain) UISearchBar* searchBar;
控件的初始化:
_searchBar = [[UISearchBar alloc]initWithFrame:CGRectMake(0, 0, 320, 40)];
_searchBar.placeholder = @"test"; //设置占位符
_searchBar.delegate =self; //设置控件代理
当然,做完这些工作之后,我们还要在将控件添加到父视图之上,也可以把他设置成UITableView的tableHeaderView属性值,由于大家需求不一,这里就不再给出代码。
前面,我们设置了控件的代理,当然我们必须让控制器(DownListViewController)的 .h 文件实现 UISearchBarDelegate 协议,然后我们继续, 我们要在 .m 文件中实现协议方法:
#pragma mark -
#pragma mark UISearchBarDelegate
//搜索框中的内容发生改变时 回调(即要搜索的内容改变)
- (void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText{
NSLog(@"changed");
if (_searchBar.text.length == 0) {
[self setSearchControllerHidden:YES]; //控制下拉列表的隐现
}else{
[self setSearchControllerHidden:NO];
}
}
- (BOOL)searchBarShouldBeginEditing:(UISearchBar *)searchBar {
searchBar.showsCancelButton =YES;
for(id cc in [searchBar subviews])
{
if([cc isKindOfClass:[UIButton class]])
{
UIButton *btn = (UIButton *)cc;
[btn setTitle:@"取消" forState:UIControlStateNormal];
}
}
NSLog(@"shuould begin");
return YES;
}
- (void)searchBarTextDidBeginEditing:(UISearchBar *)searchBar {
searchBar.text = @"";
NSLog(@"did begin");
}
- (void)searchBarTextDidEndEditing:(UISearchBar *)searchBar {
NSLog(@"did end");
searchBar.showsCancelButton =NO;
}
- (void)searchBarSearchButtonClicked:(UISearchBar *)searchBar {
NSLog(@"search clicked");
}
//点击搜索框上的 取消按钮时 调用
- (void)searchBarCancelButtonClicked:(UISearchBar *)searchBar {
NSLog(@"cancle clicked");
_searchBar.text = @"";
[_searchBar resignFirstResponder];
[self setSearchControllerHidden:YES];
}
至此,搜索框的实现就搞定了,怎么样简单吧。下面我们来讲讲下拉列表框的实现,先说说他的实现原理或者是思路吧。下拉列表框我们用一个控制器来实现,我们新建一个控制器SearchViewController.
@interface SearchViewController :UITableViewController
@end
在 .m 文件中,我们实现该控制器
- (id)initWithStyle:(UITableViewStyle)style
{
self = [super initWithStyle:style];
if (self) {
// Custom initialization
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
self.tableView.layer.borderWidth =1;
self.tableView.layer.borderColor = [[UIColor blackColor] CGColor];
}
然后实现控制器的数据源,
#pragma mark -
#pragma mark Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
// 返回列表框的下拉列表的数量
return 3;
}
// Customize the appearance of table view cells.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = @"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] ;
}
// Configure the cell...
NSUIntegerrow = [indexPath row];
cell.textLabel.text = @"down list";
return cell;
}
这样列表框的控制器就实现了。接下来我们就来看看怎么让出现、隐藏。这点我们利用UIView的动画效果来实现,我们在DownListViewController控制器中 增加一个方法:
- (void) setSearchControllerHidden:(BOOL)hidden {
NSIntegerheight =hidden ? 0: 180;
[UIView beginAnimations:nil context:nil];
[UIView setAnimationDuration:0.2];
[_searchController.view setFrame:CGRectMake(30, 36, 200, height)];
[UIView commitAnimations];
}
我们只需调用该方法就可以了。现在我们看看DownListViewController的布局方法
- (void)viewDidLoad
{
[super viewDidLoad];
_searchBar = [[UISearchBar alloc]initWithFrame:CGRectMake(0, 0, 320, 40)];
_searchBar.placeholder = @"test";
_searchBar.delegate =self;
_tableview = [[UITableView alloc]initWithFrame:self.view.bounds style:UITableViewStylePlain];
_tableview.dataSource =self;
_tableview.tableHeaderView =_searchBar;
_searchController = [[SearchViewController alloc] initWithStyle:UITableViewStylePlain];
[_searchController.view setFrame:CGRectMake(30, 40, 200, 0)];
[self.view addSubview:_tableview];
[self.view addSubview:_searchController.view];
}