【一步一步学IOS5 】 在表视图中添加搜索栏

系统 1939 0

下面,我们来演示一下如何在Tab Bar项目基础上添加一个搜索栏。通过搜索栏,App可以让用户指定搜索条件后,搜索菜单列表。

 

1.理解搜索栏显示控制器(Search Display Controller)

 

你可以使用搜索显示控制器(如 UISearchDisplayController 类)管理App中的搜索功能。搜索显示控制器管理搜索栏(search bar)和表视图(table view)的显示,表视图复杂显示搜索结果。

 

当用户开始搜索时,搜索显示控制器将在原始的视图之上,叠加搜索界面,并显示搜索结果。有趣的是,在表视图中显示的结果是有搜索显示控制器生成的。

 

和其它的视图控制器一样,你可以选择编程创建搜索控制器,或者使用Storyboard简单添加搜索显示控制器到App中,我们采用后者。

 

2.在Storyboard 中添加搜索显示控制器

 

在Storyboard 编程界面,拖拉Search Bar and Search Display Controller 对象到 Recipe Book 视图控制器的导航条下面。如果操作正确,你应该看到的如下所示的界面:

 


【一步一步学IOS5 】 在表视图中添加搜索栏

 

 在继续之前,我们尝试运行下App,界面效果如下。在没有编写任何新的代码之前,你已经有一个搜索栏。轻拍搜索栏,将显示搜索界面。但是,搜索并没有显示正确的搜索结果。


【一步一步学IOS5 】 在表视图中添加搜索栏

 

3.搜索结果显示原理解析?

 

在前面提到过,搜索结果显示搜索显示控制器(Search Display Controller)生成的表视图中,

 

我们在开发视图App时,我们实现了UITableViewDataSource协议,告诉表视图有多少条数据行显示,以及每一行的数据。

 

和UITableView 对象一样,搜索显示控制器生成的表视图采用相同的方法,采用委托的方式,让搜索栏和搜索结果交互。

 

 

一般而言,原始视图控制器作为搜索结果数据源和委托的源对象,我们不必手动连接数据源(DataSource)和委托(Delegate)到视图控制器上,

 

当我们插入搜索栏到Recipe Book 视图控制器中时,将自动建立搜索显示控制器(Search Display Controller)的连接。鼠标右键,点击搜索显示控制器(Search Display Controller)显示连接信息。

 


【一步一步学IOS5 】 在表视图中添加搜索栏

两个表视图(Recipe Book 视图控制器中的表视图  和 搜索结果表视图)共享相同的视图控制器,负责数据填充。在显示表数据时,都会调用到

 

 

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{}

 

 

- (UITableViewCell *)tableView:(UITableView *)tableView

cellForRowAtIndexPath:(NSIndexPath *)indexPath {}

 

4.实现搜索过滤器

 

显然,为了实现搜索功能,我们必须实现如下任务:

 

1.实现方法过滤菜单名称,返回正确的搜索结果

2.更改数据源方法,区分不同的表视图。如果传入的tableView 是 Recipe Book 视图控制器的表视图,则显示所有的菜单列表,如果传入的是搜索结果表视图,则仅仅显示搜索结果。

 

首先,我们演示如何时间过滤器,这里,我们已经有一个数组存放所有的菜单列表了,我们需要创建另外一个数组存放搜索结果 - 命名为searchResults 数组。

 

 

@implementation RecipeBookViewController

{

    NSArray *recipes;

    NSArray *searchResults;

}



接着,添加一个新的方法负责处理搜索过滤功能,过滤功能是iOS App 中常见的任务。过滤菜单列表的直接方法是循环所有名称,使用if语句过滤结果,这样是实现并没有任何错误。但是,iOS SDK 提供了一个更好的方法 - Predicate 负责搜索查询。

通过使用NSPredicate (是Predicate对象的表现形式),可以简化代码。通过仅仅2行代码,就可以搜索所有的菜单列表,返回匹配的结果:

- ( void )filterContentForSearchText:( NSString *)searchText scope:( NSString *)scope

{

    NSPredicate *resultPredicate = [ NSPredicate predicateWithFormat : @"SELF contains[cd] %@" ,searchText];

    searchResults = [ recipes filteredArrayUsingPredicate :resultPredicate];

}

 

基本上,一个Predicate,返回Boolearn值(true或false).你可以NSPredicate格式指定查询条件,然后使用NSPredicate对象过滤数组中的数据。NSArray提供了filteredArrayUsingPredicate:方法,该方法返回一个新的数组,数组包含了匹配制定的Predicate的对象。Predicate 中 SELF 关键字 - SELF contains[cd]%@ 指向比较对象(如菜单名称)。

 

操作符[cd]表示比较操作 - case 和 diacritic 不敏感。

 

 

 

5.实现搜索显示控制器(Search Display Controller)委托

 

现在,我们已经创建了处理数据过滤的方法,但是如何调用该方法呢?

 

显然,在用户输入搜索条件时,调用filterContentForSearchText: 方法。

 

UISearchDisplayController 类提供了 shouldReloadTableForSearchString: 方法,在搜索文本更改时,该方法会自动调用,因此,在RecipeBookViewController.m 文件添加如下方法:

 

 

- ( BOOL )searchDisplayController:( UISearchDisplayController *)controller shouldReloadTableForSearchString:( NSString *)searchString

{

    [ self filterContentForSearchText :searchString scope :[[ self . searchDisplayController . searchBar scopeButtonTitles ] objectAtIndex :[ self . searchDisplayController . searchBar selectedScopeButtonIndex ]]];

    return YES ;

}

 

 

6.在searchResultsTableView 显示搜索结果

 

在前面解释过,我们需要修改Data Source 方法,区分不同的表视图(如Recipe Book 视图控制器中的表视图  和 搜索结果表视图)。

 

区分表视图是相当简单的。

 

我们简单标记tableView 对象和 searchDisplayController的 searchResultsTableView. 

 

如果相同,则显示搜索结果。

 

代码修改如下:

 

 

- ( NSInteger )tableView:( UITableView *)tableView numberOfRowsInSection:( NSInteger )section

{

    if (tableView == self . searchDisplayController . searchResultsTableView ) {

        return [ searchResults count ];

    } else {

        return [ recipes count ];

    }

}

 

 

 

 

- ( UITableViewCell *)tableView:( UITableView *)tableView cellForRowAtIndexPath:( NSIndexPath *)indexPath

{

    static NSString *simpleTableIdentifier = @"RecipeCell" ;

 

    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier :simpleTableIdentifier];

 

    if (cell == nil ) {

        cell = [[ UITableViewCell alloc ] initWithStyle : UITableViewCellStyleDefault reuseIdentifier :simpleTableIdentifier];

    }

 

    if (tableView == self . searchDisplayController . searchResultsTableView ) {

        cell. textLabel . text = [ searchResults objectAtIndex :indexPath. row ];

    } else {

        cell. textLabel . text = [ recipes objectAtIndex :indexPath. row ];

 

    }

 

    return cell;

}




7.再次运行App

当完成上述更新之后,再次运行App,搜索栏效果如下


8.处理搜索结果中的行选择

尽管搜索功能正常了,但是它并没有对行选择进行处理。我们希望它和菜单表视图一样功能,

当用户轻拍任一搜索记录时,将切换到详细视图,显示所选择的菜单名称。

之前,我们使用联线(Segue)连接单元格和详细视图

现在,我们需要在Storyboard 中创建另外一个联线,定义搜索结果和详细视图之间的切换。

问题是我们不能这样操作,搜索结果表视图是搜索显示控制器(Search Display Controller)的一个私有变量,
它不可能使用Storyboard 处理搜索结果的行选择。

然而,搜索结果控制器可以让你使用委托(Delegate),和搜索结果表视图的用户选择来交互。当用户选择一行时,将调用
didSelectRowAtIndexPath: 方法。

因此,我们需要实现如下方法:

- ( void )tableView:( UITableView *)tableView didSelectRowAtIndexPath:( NSIndexPath *)indexPath

{

    if (tableView == self . searchDisplayController . searchResultsTableView ) {

        [ self performSegueWithIdentifier : @"showRecipeDetail" sender : self ];

    }

}

 

我们简单的调用preformSegueWithIdentifier: 方法,手动触发showRecipeDetail 联线。

 

在继续编写代码之前,我们再次运行App。在你选择任一搜索结果记录时,App显示详细视图,并带有菜单名称,

 

但是,菜单名称并不总是正确的。

 

参考prepareForSegue: 方法,我们使用indexPathForSelectedRow 方法检索所选indexPath属性值。

 

前面提到过,搜索结果显示在一个独立的表视图中,但是,在之前的prepareForSegue:方法中,我们总是从Recipe Book

 

视图控制器的表视图中检索所选中的记录行。

 

这就是为什么我们在详细视图中获得错误的菜单名称。为了取得搜索结果中正确的选择,我们需要修改prepareForSegue:方法:

 

 

- ( void )prepareForSegue:( UIStoryboardSegue *)segue sender:( id )sender

{

    if ([segue. identifier isEqualToString : @"showRecipeDetail" ]) {

 

        NSIndexPath *indexPath = nil ;

 

        RecipeDetailViewController *destViewController = segue. destinationViewController ;

        if ([ self . searchDisplayController isActive ]) {

            indexPath = [ self . searchDisplayController . searchResultsTableView indexPathForSelectedRow ];

            destViewController. recipeName = [ searchResults objectAtIndex :indexPath. row ];

        } else {

            indexPath = [ self . tableView indexPathForSelectedRow ];

            destViewController. recipeName = [ recipes objectAtIndex :indexPath. row ];

        }

 

    }

}


我们首先判断用户是否使用搜索功能。当使用搜索功能时,我们从searchResultTableView 中检索indexPath,这个是搜索结果的表视图。否则,我们仍然从Recipe Book 视图控制器中的表视图获取indexPath 属性值。

好啦,再次运行App。搜索功能正常了。

【一步一步学IOS5 】 在表视图中添加搜索栏
 

 

 

【一步一步学IOS5 】 在表视图中添加搜索栏


更多文章、技术交流、商务合作、联系博主

微信扫码或搜索:z360901061

微信扫一扫加我为好友

QQ号联系: 360901061

您的支持是博主写作最大的动力,如果您喜欢我的文章,感觉我的文章对您有帮助,请用微信扫描下面二维码支持博主2元、5元、10元、20元等您想捐的金额吧,狠狠点击下面给点支持吧,站长非常感激您!手机微信长按不能支付解决办法:请将微信支付二维码保存到相册,切换到微信,然后点击微信右上角扫一扫功能,选择支付二维码完成支付。

【本文对您有帮助就好】

您的支持是博主写作最大的动力,如果您喜欢我的文章,感觉我的文章对您有帮助,请用微信扫描上面二维码支持博主2元、5元、10元、自定义金额等您想捐的金额吧,站长会非常 感谢您的哦!!!

发表我的评论
最新评论 总共0条评论