UISearchDisplayController的用法

前记

  UISearchDisplayController是iOS开发中专门为UITableView提供搜索而封装的一个类,平时开发的时候也没怎么用到,这次趁着项目开发中用到它,做了下笔记,方便下次再次用到的时候查找方便。
  

UISearchDisplayController介绍

  UISearchDisplayController是用来提供搜索展示的,兼容3.0~8.0,8.0之后就过期了,被UISearchController所取代,这次主要讲如何使用UISearchDisplayController以及在使用中碰到的一些问题,先来看下项目中结合UISearchDisplayController做搜索是一个怎样的效果,先上图!
  
整体的动画效果很流畅,如果是我们自己来做这个动画,估计也不容易,接下来我们来看看项目中如何使用UISearchDisplayController吧!

UISearchDisplayController使用

  UISearchDisplayController的使用场景是UITableView+UISearchBar+带有UINavigationController结构的UIViewController,这些东西如何创建我就不啰嗦了,直奔主题吧!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
  - (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = [UIColor whiteColor];

self.title = @"Apple";

_contentArray = [[NSMutableArray alloc] initWithObjects:@"iPhone",@"iPod",@"iPod touch",@"iPad",@"iPad mini",@"iMac",@"Mac Pro",@"MacBook Air",@"MacBook Pro", nil];

_contentTableView = [[UITableView alloc] initWithFrame:self.view.bounds style:UITableViewStylePlain];
_contentTableView.dataSource = self;
_contentTableView.delegate = self;
[self.view addSubview:_contentTableView];
[self addSearchBar];
}

- (void)addSearchBar {
_searchBar = [[UISearchBar alloc] initWithFrame:CGRectMake(0, 0, self.view.frame.size.width, 44)];
// _searchBar.scopeButtonTitles = [NSArray arrayWithObjects:@"All",@"Device",@"Potable", nil];
_contentTableView.tableHeaderView = _searchBar;
// [_contentTableView.tableHeaderView addSubview:_searchBar];
_searchDisplayController = [[UISearchDisplayController alloc] initWithSearchBar:_searchBar contentsController:self];
_searchDisplayController.searchResultsDataSource = self;
_searchDisplayController.searchResultsDelegate = self;
_searchDisplayController.delegate = self;
}

#pragma mark - UITableViewDataSource

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
//需要判断该tableView是界面展示的tableView还是搜索结果的tableView
if (tableView == _contentTableView) {
return [_contentArray count];
} else {
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"self contains [cd] %@",_searchDisplayController.searchBar.text];
_filterData = [[NSArray alloc] initWithArray:[_contentArray filteredArrayUsingPredicate:predicate]];
return _filterData.count;
}

}


- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *cellStr = @"cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellStr];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellStr];
}
if (tableView == _contentTableView) {
cell.textLabel.text = [_contentArray objectAtIndex:[indexPath row]];
} else {
cell.textLabel.text = [_filterData objectAtIndex:[indexPath row]];
}
return cell;

}

简单的代码就能使用UISearchDisplayController,代码里面用到了NSPredicate,这个有点类似正则表达,是用来过滤结果使用的,具体的介绍请移步到此NSPredicate介绍,接下来我们来玩点高级的。

UISearchDisplayController高级用法

使用自定义的搜索界面

  如果觉得系统提供的搜索结果界面无法满足项目需求,需要自定义的时候,我们就需要使用到UISearchDisplayController的delegate了,它提供了很多了委托方法,请自行查看UISearchDisplayDelegate,这里就不一一列举了,在自定义搜索界面的时候,我用到了其中3个回调,暂时能满足需求了

1
2
3
4
5
6
//搜索动画即将开始的时候回调
- (void) searchDisplayControllerWillBeginSearch:(UISearchDisplayController *)controller NS_DEPRECATED_IOS(3_0,8_0);
//搜索动画即将结束的时候回调
- (void) searchDisplayControllerWillEndSearch:(UISearchDisplayController *)controller NS_DEPRECATED_IOS(3_0,8_0);
//显示搜索结果的回调
- (void)searchDisplayController:(UISearchDisplayController *)controller didShowSearchResultsTableView:(UITableView *)tableView NS_DEPRECATED_IOS(3_0,8_0);

具体的实现方案如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
- (void) searchDisplayControllerWillBeginSearch:(UISearchDisplayController *)controller NS_DEPRECATED_IOS(3_0,8_0) {
_searchResultController = [[SearchResultViewController alloc] init];
[controller.searchContentsController addChildViewController:_searchResultController];

[controller.searchContentsController.view addSubview:_searchResultController.view];
[controller.searchContentsController.view bringSubviewToFront:_searchResultController.view];
}

- (void) searchDisplayControllerWillEndSearch:(UISearchDisplayController *)controller NS_DEPRECATED_IOS(3_0,8_0) {
if (_searchResultController) {
[_searchResultController.view removeFromSuperview];

[_searchResultController removeFromParentViewController];
_searchResultController = nil;
}
}

- (void)searchDisplayController:(UISearchDisplayController *)controller didShowSearchResultsTableView:(UITableView *)tableView NS_DEPRECATED_IOS(3_0,8_0) {
tableView.hidden = YES;

}

上面这些代码就能实现去掉系统自带的搜索结果界面,而使用自定义的搜索界面了。这代码有很多优化的地方,我这边只是介绍实现方案,具体项目具体处理,优化方案就不再这里讨论了。

去掉半透明遮罩

  如果上面我们使用了自定义的搜索界面,但是有个很可恶的东西,就是在还没搜索前有个半透明的遮罩(这个半透明遮罩有人觉得好,有人觉得不好,主要是看项目需求,我的项目里面不需要用它,所以要把它去掉),界面上不是特别美观,所以就要想方设法把它去掉。

  一开始本来还想通过寻找委托函数来实现该方案,但是都没能找到一个适合的(也许我找的有问题,如果能通过委托函数来实现的,麻烦告知一下,我也学习下),最后发现UISearchDisplayController有个这样的函数

1
- (void)setActive:(BOOL)visible animated:(BOOL)animated;       // animate the view controller for searching

它是在用户点击UISearchBar做收起动画的时候以及用户点击Cancel按钮的时候都会触发的,我们可以直接在该函数里面实现我们去掉半透明遮罩的功能,具体方案如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
- (void)setActive:(BOOL)visible animated:(BOOL)animated {
if(self.active == visible) {
return;
}
[super setActive:visible animated:animated];
NSArray *subViews = self.searchContentsController.view.subviews;
if ([[[UIDevice currentDevice] systemVersion] floatValue] >= 7.0f) {
for (UIView *view in subViews) {
if ([view isKindOfClass:NSClassFromString(@"UISearchDisplayControllerContainerView")]) {
NSArray *sub = view.subviews;
((UIView*)sub[2]).hidden = YES;
}
}
} else {
[[subViews lastObject] removeFromSuperview];
}
}

实现后再次点击UISearchBar的时候:

半透明遮罩消失了,大功告成!

  最后整个工程的代码已经上传到Github上,大家可以到这里下载,有任何问题,欢迎讨论。