您的位置:首页 > 移动开发 > IOS开发

iOS 搜索功能的实现

2014-05-19 11:06 246 查看
开源中国社区团队基于开源项目 GitLab 开发了一款和GitHub一样的在线代码托管平台 Git @ OSC。并且开源客户端的源码在GitHub上不做更新,迁移到Git @OSC上了,欲了解更多请访问Git @ OSC官网http://git.oschina.net

客户端最新源码下载地址:http://git.oschina.net/oschina/iphone-app

回归正题,今天分析的是开源中国iOS客户端搜索功能涉及道一些知识,XML解析和动态加载表视图单元格;

在软件首页右上角有一个搜索按钮,点击进入搜索界面,当搜索的内容很多时我们下拉点击 “下面20项。。。”可以在加载20项,这些数据如何填充到表视图之中?









负责搜索功能的是search下的searchView类,xib控件已经已经做好布局,首先说下SearchView.h文件属性成员代表的作用;

[cpp] view
plaincopy

#import <UIKit/UIKit.h>

#import "SearchResult.h"

#import "MBProgressHUD.h"

@interface SearchView : UIViewController<UITableViewDelegate,UITableViewDataSource,UISearchBarDelegate>

{

// 可变数组存放解析的数据

NSMutableArray * results;

// 搜索的时候判断是否正在加载数据

BOOL isLoading;

// 判断数据是否加载完毕

BOOL isLoadOver;

// 记录表视图单元格应该加载数据总条数

int allCount;

}

@property (strong, nonatomic) IBOutlet UISegmentedControl *segmentSearch;

@property (strong, nonatomic) IBOutlet UITableView *tableResult;

@property (strong, nonatomic) IBOutlet UISearchBar *_searchBar;

//根据搜索关键字在不同分类中进行搜索

- (IBAction)segementChanged:(id)sender;

//搜索

-(void)doSearch;

//清空上次搜索记录

-(void)clear;

OK现在挺进SearchView.m文件,如果搜索的内容不为空开始 dosearch方法,dosearch方法中使用了ASNetwork类库封装的post网络请求方法(关于AFNetwork post get请求方法请看http://blog.csdn.net/duxinfeng2010/article/details/8620901

[cpp] view
plaincopy

- (void)postPath:(NSString *)path

parameters:(NSDictionary *)parameters

success:(void (^)(AFHTTPRequestOperation *operation, id responseObject))success

failure:(void (^)(AFHTTPRequestOperation *operation, NSError *error))failure

post请求无法获取它的url,但是可以取出请求成功返回来的数据,比如搜索iOS 返回的xml

[html] view
plaincopy

<?xml version="1.0" encoding="UTF-8"?>

<oschina>

<pagesize>20</pagesize>

<results>

<result>

<objid>18397</objid>

<type>software</type>

<title><![CDATA[iOS应用开发模板 iOS Boilerplate]]></title>

<url><![CDATA[http://www.oschina.net/p/ios-boilerplate]]></url>

<pubDate></pubDate>

<author></author>

</result>

<result>

<objid>18977</objid>

<type>software</type>

<title><![CDATA[ ios-static-libraries]]></title>

<url><![CDATA[http://www.oschina.net/p/ios-static-libraries]]></url>

<pubDate></pubDate>

<author></author>

</result>

<result>

<objid>23309</objid>

<type>software</type>

<title><![CDATA[ 仿陌陌的ios客户端]]></title>

<url><![CDATA[http://www.oschina.net/p/momo-ios-app]]></url>

<pubDate></pubDate>

<author></author>

</result>

<result>

<objid>22121</objid>

<type>software</type>

<title><![CDATA[iOS任务管理器 cheddar-ios]]></title>

<url><![CDATA[http://www.oschina.net/p/cheddar-ios]]></url>

<pubDate></pubDate>

<author></author>

</result>

<result>

<objid>22900</objid>

<type>software</type>

<title><![CDATA[白宫网站 iOS 客户端 wh-app-ios]]></title>

<url><![CDATA[http://www.oschina.net/p/wh-app-ios]]></url>

<pubDate></pubDate>

<author></author>

</result>

<result>

<objid>17045</objid>

<type>software</type>

<title><![CDATA[iPhone操作系统 iOS]]></title>

<url><![CDATA[http://www.oschina.net/p/ios]]></url>

<pubDate></pubDate>

<author></author>

</result>

<result>

<objid>25685</objid>

<type>software</type>

<title><![CDATA[iOS 弹出菜单 MLPPopupMenu]]></title>

<url><![CDATA[http://www.oschina.net/p/mlppopupmenu]]></url>

<pubDate></pubDate>

<author></author>

</result>

<result>

<objid>22803</objid>

<type>software</type>

<title><![CDATA[iOS日历控件 PMCalendar]]></title>

<url><![CDATA[http://www.oschina.net/p/pmcalendar]]></url>

<pubDate></pubDate>

<author></author>

</result>

<result>

<objid>24390</objid>

<type>software</type>

<title><![CDATA[iOS 功能测试框架 calabash-ios]]></title>

<url><![CDATA[http://www.oschina.net/p/calabash-ios]]></url>

<pubDate></pubDate>

<author></author>

</result>

<result>

<objid>24665</objid>

<type>software</type>

<title><![CDATA[ iOS-Tree-Component]]></title>

<url><![CDATA[http://www.oschina.net/p/ios-tree-component]]></url>

<pubDate></pubDate>

<author></author>

</result>

<result>

<objid>22217</objid>

<type>software</type>

<title><![CDATA[ ios-calendar]]></title>

<url><![CDATA[http://www.oschina.net/p/ios-calendar]]></url>

<pubDate></pubDate>

<author></author>

</result>

<result>

<objid>22380</objid>

<type>software</type>

<title><![CDATA[ PaperFold-for-iOS]]></title>

<url><![CDATA[http://www.oschina.net/p/paperfold-for-ios]]></url>

<pubDate></pubDate>

<author></author>

</result>

<result>

<objid>21763</objid>

<type>software</type>

<title><![CDATA[ drupal-ios-sdk]]></title>

<url><![CDATA[http://www.oschina.net/p/drupal-ios-sdk]]></url>

<pubDate></pubDate>

<author></author>

</result>

<result>

<objid>19628</objid>

<type>software</type>

<title><![CDATA[iOS开发基础工具包 BaseAppKit]]></title>

<url><![CDATA[http://www.oschina.net/p/baseappkit]]></url>

<pubDate></pubDate>

<author></author>

</result>

<result>

<objid>20637</objid>

<type>software</type>

<title><![CDATA[iOS消息提醒库 TBHintView]]></title>

<url><![CDATA[http://www.oschina.net/p/tbhintview]]></url>

<pubDate></pubDate>

<author></author>

</result>

<result>

<objid>21246</objid>

<type>software</type>

<title><![CDATA[iOS 弹出式菜单 MGTileMenu]]></title>

<url><![CDATA[http://www.oschina.net/p/mgtilemenu]]></url>

<pubDate></pubDate>

<author></author>

</result>

<result>

<objid>23498</objid>

<type>software</type>

<title><![CDATA[iOS 的 Canvas 和 Audio 实现 Ejecta]]></title>

<url><![CDATA[http://www.oschina.net/p/ejecta]]></url>

<pubDate></pubDate>

<author></author>

</result>

<result>

<objid>23968</objid>

<type>software</type>

<title><![CDATA[样式化 iOS 应用 NUI]]></title>

<url><![CDATA[http://www.oschina.net/p/nui]]></url>

<pubDate></pubDate>

<author></author>

</result>

<result>

<objid>20730</objid>

<type>software</type>

<title><![CDATA[iOS/Android 矢量图形框架 TouchVG]]></title>

<url><![CDATA[http://www.oschina.net/p/touchvg]]></url>

<pubDate></pubDate>

<author></author>

</result>

<result>

<objid>22356</objid>

<type>software</type>

<title><![CDATA[iOS日历控件 MACalendarUI]]></title>

<url><![CDATA[http://www.oschina.net/p/macalendarui]]></url>

<pubDate></pubDate>

<author></author>

</result>

</results>

<notice>

<atmeCount>0</atmeCount>

<msgCount>0</msgCount>

<reviewCount>0</reviewCount>

<newFansCount>0</newFansCount>

</notice>

</oschina>

<!-- Generated by OsChina.NET (init:0[ms],page:13[ms],ip:61.163.231.198) -->

要解析xm里数据必须熟悉xml文件各个节点之间关系

根节点oschina,它的子节点pagesize返回本次加载了几条数据,子节点results,results的子节点下20条result节点,我们主要获取result内容。然后就是最后面的子节点notice节点,存放用户的一些信息如动弹情况、收到消息、回复、粉丝;在post请求中用到了一个异常处理语句 @try @catch @finally

@try

{

//执行的代码,其中可能有异常。一旦发现异常,则立即跳到catch执行。否则不会执行catch里面的内容

}

@catch

{

//除非try里面执行代码发生了异常,否则这里的代码不会执行

}

@finally

{

//不管什么情况都会执行,包括try catch 里面用了return ,可以理解为只要执行了try或者catch,就一定会执行 finally

}


给dosearch添加了一些注释

[cpp] view
plaincopy

-(void)doSearch

{

// 标记,表示正在加载数据中

isLoading = YES;

NSString * catalog;

// switch语句中根据Segment按钮集合中按钮索引,判断搜索哪一类内容,为下面的搜索API传参

switch (self.segmentSearch.selectedSegmentIndex) {

case 0:

catalog = @"software";

break;

case 1:

catalog = @"post";

break;

case 2:

catalog = @"blog";

break;

case 3:

catalog = @"news";

break;

}

//使用AFNetWork使用post方式从网络请求数据

[[AFOSCClient sharedClient] postPath:api_search_list parameters:[NSDictionary dictionaryWithObjectsAndKeys:_searchBar.text,@"content",catalog,@"catalog",[NSString stringWithFormat:@"%d", allCount/20],@"pageIndex",@"20",@"pageSize", nil] success:^(AFHTTPRequestOperation *operation, id responseObject) {

// 取消searchBar的第一响应对象,键盘消失

[self._searchBar resignFirstResponder];

// 在没有内容之前tableView是没有任何内容的,所以隐藏掉

self.tableResult.hidden = NO;

// 根据请求回来的数据判断当前用户释放登陆,需要获取用户一些信息

[Tool getOSCNotice2:operation.responseString];

// 上面属于请求数据是不回加载到视图控制器上,所以标记属性为NO

isLoading = NO;

// 再次从xml文件中请求数据,获取当前加载数据条数,数量

int count = [Tool isListOver2:operation.responseString];

allCount += count;

// 将请求的xml内容给NSString对象

NSString *response = operation.responseString;

NSLog(@"response =%@",response);

@try {

// 开始解析需要显示到表视图单元格对象中的数据

TBXML *xml = [[TBXML alloc] initWithXMLString:response error:nil];

TBXMLElement *root = xml.rootXMLElement;

// 从次根节点获取根节点下内容

TBXMLElement *_results = [TBXML childElementNamed:@"results" parentElement:root];

if (!_results) {

isLoadOver = YES;

[self.tableResult reloadData];

return;

}

// 获取result节点下内容

TBXMLElement *first = [TBXML childElementNamed:@"result" parentElement:_results];

if (!first) {

isLoadOver = YES;

[self.tableResult reloadData];

return;

}

// 取出result节点下的节点

NSMutableArray * newResults = [[NSMutableArray alloc] initWithCapacity:20];

TBXMLElement *objid = [TBXML childElementNamed:@"objid" parentElement:first];

TBXMLElement *type = [TBXML childElementNamed:@"type" parentElement:first];

TBXMLElement *title = [TBXML childElementNamed:@"title" parentElement:first];

TBXMLElement *url = [TBXML childElementNamed:@"url" parentElement:first];

TBXMLElement *pubDate = [TBXML childElementNamed:@"pubDate" parentElement:first];

NSString * pubDateStr = [TBXML textForElement:pubDate];

TBXMLElement *author = [TBXML childElementNamed:@"author" parentElement:first];

// 取出节点中的值,赋给一个SearchResult对象属性

SearchResult * s = [[SearchResult alloc] initWithParameters:[[TBXML textForElement:objid] intValue] andType:[[TBXML textForElement:type] intValue] andTitle:[TBXML textForElement:title] andUrl:[TBXML textForElement:url] andPubDate:[pubDateStr isEqualToString:@""] ? @"" : [Tool intervalSinceNow:pubDateStr] andAuthor:[TBXML textForElement:author]];

// 将获取对象添加到可变数组

if (![Tool isRepeatSearch:results andResult:s]) {

[newResults addObject:s];

}

// 在循环之中 寻找下一个节点 直至找完

while (first) {

first = [TBXML nextSiblingNamed:@"result" searchFromElement:first];

if (first) {

objid = [TBXML childElementNamed:@"objid" parentElement:first];

type = [TBXML childElementNamed:@"type" parentElement:first];

title = [TBXML childElementNamed:@"title" parentElement:first];

url = [TBXML childElementNamed:@"url" parentElement:first];

pubDate = [TBXML childElementNamed:@"pubDate" parentElement:first];

author = [TBXML childElementNamed:@"author" parentElement:first];

s = [[SearchResult alloc] initWithParameters:[[TBXML textForElement:objid] intValue] andType:[[TBXML textForElement:type] intValue] andTitle:[TBXML textForElement:title] andUrl:[TBXML textForElement:url] andPubDate:[Tool intervalSinceNow:[TBXML textForElement:pubDate]] andAuthor:[TBXML textForElement:author]];

//

if (![Tool isRepeatSearch:results andResult:s]) {

[newResults addObject:s];

}

}

// first = NULL 直接跳出

else

{

break;

}

}

// 如果搜索结果数据小雨20条,表示一个页面就可以加载完毕

if (newResults.count < 20) {

isLoadOver = YES;

}

// 将解析数据添加道results之中

[results addObjectsFromArray:newResults];

// 刷新表示图内容

[self.tableResult reloadData];

}

@catch (NSException *exception) {

[NdUncaughtExceptionHandler TakeException:exception];

}

@finally {

}

// 请求失败

} failure:^(AFHTTPRequestOperation *operation, NSError *error) {

[Tool ToastNotification:@"网络连接故障" andView:self.view andLoading:NO andIsBottom:NO];

}];

// 刷新表视图单元内容

[self.tableResult reloadData];

}

在 [Tool getOSCNotice2:operation.responseString];解析的登陆用户一些信息

// Tool类中

[cpp] view
plaincopy

+ (OSCNotice *)getOSCNotice2:(NSString *)response

{

TBXML *xml = [[TBXML alloc] initWithXMLString:response error:nil];

TBXMLElement *root = xml.rootXMLElement;

if (!root) {

return nil;

}

TBXMLElement *notice = [TBXML childElementNamed:@"notice" parentElement:root];

if (!notice) {

[Config Instance].isLogin = NO;

[[NSNotificationCenter defaultCenter] postNotificationName:@"login" object:@"0"];

return nil;

}

else

{

[[NSNotificationCenter defaultCenter] postNotificationName:@"login" object:@"1"];

[Config Instance].isLogin = YES;

}

TBXMLElement *atme = [TBXML childElementNamed:@"atmeCount" parentElement:notice];

TBXMLElement *msg = [TBXML childElementNamed:@"msgCount" parentElement:notice];

TBXMLElement *review = [TBXML childElementNamed:@"reviewCount" parentElement:notice];

TBXMLElement *newFans = [TBXML childElementNamed:@"newFansCount" parentElement:notice];

OSCNotice *oc = [[OSCNotice alloc] initWithParameters:[[TBXML textForElement:atme] intValue] andMsg:[[TBXML textForElement:msg] intValue] andReview:[[TBXML textForElement:review] intValue] andFans:[[TBXML textForElement:newFans] intValue]];

[[NSNotificationCenter defaultCenter] postNotificationName:Notification_NoticeUpdate object:oc];

return oc;

}

[Tool isListOver2:operation.responseString]; 用于也是解析数据,获取返回的数据条数,告诉table将要显示多少行cell,当把cell加载到最后的时候获取下面20项,或跟多,然后把这些数据存放道allcount里面,所以就有allCount += count

Tool类中,解析返回一个数据 pagesize,显示多少行

[cpp] view
plaincopy

+ (int)isListOver2:(NSString *)response

{

TBXML *xml = [[TBXML alloc] initWithXMLString:response error:nil];

TBXMLElement *root = xml.rootXMLElement;

TBXMLElement *pageSize = [TBXML childElementNamed:@"pagesize" parentElement:root];

int size = [[TBXML textForElement:pageSize] intValue];

return size;

}

然后进入到try中又一次解析获取result里面数据,这里有请求了一次数据,有解析了一边,感觉这里处理的不是很好,同一个返回数据请求了三次,如果用户用的不是wifi就可能耗费流量浪费电量;

剩下的就是表示图加载数据了

[cpp] view
plaincopy

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

{

// 如何加载完成,返回数据为空,返回1,这个单元格是显示一个提示,“查无结果” 如果返回不为空,返回results.count + 1 个显示结果,最后加 1 ,是显示加载数据超过20条的时候 点击 “下面20项”时加载更多数据

if (isLoadOver) {

return results.count == 0 ? 1 : results.count;

}

else

return results.count + 1;

}

//处理cell的行高

-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath

{

if (isLoadOver) {

return results.count == 0 ? 62 : 50;

}

else

{

return indexPath.row < results.count ? 50 : 62;

}

}

//处理tableView背景色

-(void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath

{

cell.backgroundColor = [Tool getCellBackgroundColor];

}

//定制单元格的显示内容

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

{

if (results.count > 0)

{

if (indexPath.row < results.count)

{

UITableViewCell * cell = [tableView dequeueReusableCellWithIdentifier:NormalCellIdentifier];

if (!cell) {

cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:NormalCellIdentifier];

}

SearchResult * s = [results objectAtIndex:indexPath.row];

cell.textLabel.font = [UIFont boldSystemFontOfSize:15.0];

cell.textLabel.text = s.title;

if (self.segmentSearch.selectedSegmentIndex != 0)

{

cell.detailTextLabel.text = [NSString stringWithFormat:@"%@ 发表于 %@", s.author, s.pubDate];

}

else

{

cell.detailTextLabel.text = @"";

}

cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;

return cell;

}

else

{

return [[DataSingleton Instance] getLoadMoreCell:tableView andIsLoadOver:isLoadOver andLoadOverString:@"搜索完毕" andLoadingString:(isLoading ? loadingTip : loadNext20Tip) andIsLoading:isLoading];

}

}

// 如果搜索返回的数据为空 提示 查无结果

else

{

return [[DataSingleton Instance] getLoadMoreCell:tableView andIsLoadOver:isLoadOver andLoadOverString:@"查无结果" andLoadingString:(isLoading ? loadingTip : loadNext20Tip) andIsLoading:isLoading];

}

}

//选中某一行的时候显示该条信息的详细内容

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

{

[self._searchBar resignFirstResponder];

[tableView deselectRowAtIndexPath:indexPath animated:YES];

int row = indexPath.row;

if (row >= results.count)

{

if (!isLoading && !isLoadOver)

{

[self performSelector:@selector(doSearch)];

}

}

else

{

SearchResult * s = [results objectAtIndex:row];

if (s)

{

[Tool analysis:s.url andNavController:self.navigationController];

}

}

}

打开某一条信息,并查看其详细信息调用 analysis: andNavController:,该方法里针对传入URL,如果是站外连接 比如某个软件官网,直接跳转到该软件的官网上,如果是开源中国社区站内连接,就可能需要加载一些这条信息的评论详情如果检测道用户登陆给予用户品论权限和分享功能;具体实现如下

[cpp] view
plaincopy

+ (BOOL)analysis:(NSString *)url andNavController:(UINavigationController *)navController

{

NSString *search = @"oschina.net";

//判断是否包含 oschina.net 来确定是不是站内链接

NSRange rng = [url rangeOfString:search];

if (rng.length <= 0) {

[[UIApplication sharedApplication] openURL:[NSURL URLWithString:url]];

return NO;

}

//站内链接

else

{

url = [url substringFromIndex:7];

NSString *prefix = [url substringToIndex:3];

//此情况为 博客,动弹,个人专页

if ([prefix isEqualToString:@"my."])

{

NSArray *array = [url componentsSeparatedByString:@"/"];

//个人专页 用户名形式

if ([array count] <= 2) {

[Tool pushUserDetailWithName:[array objectAtIndex:1] andNavController:navController];

return YES;

}

//个人专页 uid形式

else if([array count] <= 3)

{

if ([[array objectAtIndex:1] isEqualToString:@"u"]) {

[Tool pushUserDetail:[[array objectAtIndex:2] intValue] andNavController:navController];

return YES;

}

}

else if([array count] <= 4)

{

NSString *type = [array objectAtIndex:2];

if ([type isEqualToString:@"blog"]) {

News *n = [[News alloc] init];

n.newsType = 3;

n.attachment = [array objectAtIndex:3];

[Tool pushNewsDetail:n andNavController:navController andIsNextPage:NO];

return YES;

}

else if([type isEqualToString:@"tweet"]){

Tweet *t = [[Tweet alloc] init];

t._id = [[array objectAtIndex:3] intValue];

[Tool pushTweetDetail:t andNavController:navController];

return YES;

}

}

else if(array.count <= 5)

{

NSString *type = [array objectAtIndex:3];

if ([type isEqualToString:@"blog"]) {

News *n = [[News alloc] init];

n.newsType = 3;

n.attachment = [array objectAtIndex:4];

[Tool pushNewsDetail:n andNavController:navController andIsNextPage:NO];

return YES;

}

}

}

//此情况为 新闻,软件,问答

else if([prefix isEqualToString:@"www"])

{

NSArray *array = [url componentsSeparatedByString:@"/"];

int count = [array count];

if (count>=3) {

NSString *type = [array objectAtIndex:1];

if ([type isEqualToString:@"news"]) {

int newsid = [[array objectAtIndex:2] intValue];

News *n = [[News alloc] init];

n.newsType = 0;

n._id = newsid;

[Tool pushNewsDetail:n andNavController:navController andIsNextPage:YES];

return YES;

}

else if([type isEqualToString:@"p"]){

News *n = [[News alloc] init];

n.newsType = 1;

n.attachment = [array objectAtIndex:2];

[Tool pushNewsDetail:n andNavController:navController andIsNextPage:NO];

return YES;

}

else if([type isEqualToString:@"question"]){

if (count == 3) {

NSArray *array2 = [[array objectAtIndex:2] componentsSeparatedByString:@"_"];

if ([array2 count] >= 2) {

int _id = [[array2 objectAtIndex:1] intValue];

Post *p = [[Post alloc] init];

p._id = _id;

[Tool pushPostDetail:p andNavController:navController];

return YES;

}

}

else if(count >= 4)

{

// NSString *tag = [array objectAtIndex:3];

NSString *tag = @"";

if (array.count == 4) {

tag = [array objectAtIndex:3];

}

else

{

for (int i=3; i<count-1; i++) {

tag = [NSString stringWithFormat:@"%@/%@", [array objectAtIndex:i],[array objectAtIndex:i+1]];

}

}

NSString *tag2 = [tag stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];

PostsView *pview = [PostsView new];

pview.tag = tag;

pview.navigationItem.title = [NSString stringWithFormat:@"%@", tag2];

[navController pushViewController:pview animated:YES];

return YES;

}

}

}

}

// 根据一个URL打开一个网页

[[UIApplication sharedApplication] openURL:[NSURL URLWithString:[NSString stringWithFormat:@"http://%@", url]]];

return NO;

}

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  ios开发