请看图先 这是要的效果demo
下面是我写的demo
看到这效果,应该都能想到用UISearchController,但是这货是iOS8才出的,只能去找找老的UISearchDisplayController
简单分析下两个控件
1.UISearchDisplayController
Available in iOS 3.0 and later Deprecated in iOS 8.0 虽然被废弃了,但是如果需要支持7.0的话也只能勉强用用其实他不是一个控制器,他只是一个继承于NSObject的一个工具类,你需要自己实例化一个UISearchBar的控件传这个类,而且他内部已经给封装好了一个UISearchResultTableView,用于在开始搜索的时候创建并展示搜索结果数据的。
A search display controller manages the display of a search bar, along with a table view that displays search results.
2.UISearchController (自定义内部控件点击打开链接)
Available in iOS 8.0 and later 每一个searchController都内置一个searchBar,用的时候就必须和你的MainUI关联起来,例如你的基本UI是一个tableView,你可以把你的searchBar添加给tableHeaderView属性,那么搜索结果页面是可以自定义的,可以自己设计一个controller,然后用这个法:initWithSearchResultsController
进行关联,当你的searchbar被触发的时候,内部会自动调用你刚才关联的自定义搜索结果页面,代表要开始搜索了,那么这两者之间
就需要实现searchResultsUpdater协议,可以让搜索栏通知到MainUI,就可以实时传参刷新搜索结果页面。
基本的初始化如下,由于我们主要介绍UISearchDisplayController,所以UISearchController简单带过了
下面是基本的初始化方法
// Create the search results controller and store a reference to it.
MySearchResultsController* resultsController = [[MySearchResultsController alloc] init];
self.searchController = [[UISearchController alloc] initWithSearchResultsController:resultsController];
// Use the current view controller to update the search results.
self.searchController.searchResultsUpdater = self;
// Install the search bar as the table header.
self.tableView.tableHeaderView = self.searchController.searchBar;
// It is usually good to set the presentation context.
self.definesPresentationContext = YES;
我感觉为什么UISearDisplayController被UISearchController替代了,从结构上来说,前者已经把搜索结果页面给封装成了固定的UITableVIew了,这不坑爹了么,只能单纯的展示列表了,根本没有自定义空间,如果你要强制修改他的属性,这更麻烦了,还要涉及到runtime,但是人家向下支持啊,没办法啊。UISearchController就先进了,他内部封装好了UISearchBar,而且他的搜索结果页面是自定义的,只要实现代理方法就可以进行搜索了,这也符合现在的开发逻辑,但是最低支持iOS 8.0啊。鱼和熊掌不可兼得啊,所以我暂时用UISearchDisplayController给大家稍微分析下逻辑。
Demo分析开始
第一步
各位看到上面的录屏,刚进来的界面是一个自己创建控制器,这里有两个方法,第一个你可以继承UITableVIewController,或者像我一样创建一个空的VC,然后自己添加一个TableVIew进去即可。那么自然需要几个属性来加载数据
@property (weak, nonatomic) IBOutlet UITableView *tableView;
// 搜索界面上面展示热门搜索,下面展示历史搜索
@property (nonatomic,strong) NSMutableArray *hotDataSource;
@property (nonatomic,strong) NSMutableArray *historyDateSource;
// 搜索结果界面上面相关标签,下面展示相关文章
@property (nonatomic,strong) NSMutableArray *resultTagDataSource;
@property (nonatomic,strong) NSMutableArray *resultArticleDataSource;
@property (nonatomic,strong) UISearchBar *searchBar;
@property (nonatomic,strong) UISearchDisplayController *displayController; // 搜索用的类
先说下我如何加载数据进去,首先普通的主界面(搜索之前)他是一个TableView,分两个Section,这两个我都用collection做cell加载数据,那么搜索页面(搜索之后)他本身就是个内置的tableView,我也分两个Section,第一段用SKTagVIew来加载标签数据,需要标签布局流自适应的请看我写的另一个(传送门),第二段就是普通的cell了。
第二步
在需要的tableView里面注册对应的Cell
如何区分这两个tableView呢??
self.tableView 这个指的就是主界面的tableView
self.displayController.searchResultsTableView 这个就是搜索结果的tableView
我们只需要在实现的方法里面进行区分就好了主页面在ViewDidload注册cell
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view from its nib.
// self.automaticallyAdjustsScrollViewInsets = NO;
[self.tableView registerNib:[UINib nibWithNibName:identify1 bundle:nil] forCellReuseIdentifier:identify1];
[self.tableView registerNib:[UINib nibWithNibName:identify4 bundle:nil] forCellReuseIdentifier:identify4];
搜索结果页面在以下代理方法里面注册cell
- (void)searchDisplayController:(UISearchDisplayController *)controller didLoadSearchResultsTableView:(UITableView *)tableView {
NSLog(@"did load table");
[tableView registerNib:[UINib nibWithNibName:identify2 bundle:nil] forCellReuseIdentifier:identify2];
[tableView registerNib:[UINib nibWithNibName:identify3 bundle:nil] forCellReuseIdentifier:identify3];
[tableView registerNib:[UINib nibWithNibName:identify4 bundle:nil] forCellReuseIdentifier:identify4];
}
第三步
初始化UISearchBar以及相关的工具类
// 初始化UISearchBar
self.searchBar = [[UISearchBar alloc]initWithFrame:CGRectMake(0, 0, 375, 44)];
// 设置placeholder
[self.searchBar setPlaceholder:@"搜索"];
// 是否在编辑的时候需要cancel按钮
self.searchBar.showsCancelButton = NO;
// 把键盘的returnkey换成search
self.searchBar.returnKeyType = UIReturnKeySearch;
// 设置代理
self.searchBar.delegate = self;
self.searchBar.backgroundColor = [UIColor colorWithRed:246/255.0 green:246/255.0 blue:246/255.0 alpha:1];
self.searchBar.backgroundImage = [UIImage new];
// 把searchbar里面的textfield拿出来修改属性,原生的太丑了,黑黑的一片
UITextField *searchBarTextField = [self.searchBar valueForKey:@"_searchField"];
if (searchBarTextField)
{
[searchBarTextField setBackgroundColor:[UIColor whiteColor]];
[searchBarTextField setBorderStyle:UITextBorderStyleRoundedRect];
searchBarTextField.layer.cornerRadius = 5.0f;
searchBarTextField.layer.borderColor = [UIColor colorWithRed:204/255.0 green:204/255.0 blue:204/255.0 alpha:1].CGColor;
searchBarTextField.layer.borderWidth = 0.5f;
}
// 别忘了把设置好的Searchbar放到UItableVIew的头部去
self.tableView.tableHeaderView = self.searchBar;
// 实例化控制器的类
UISearchDisplayController *searchDisplayController = [[UISearchDisplayController alloc] initWithSearchBar:self.searchBar contentsController:self];
// 搜索类的代理
searchDisplayController.delegate = self;
// 搜索类内部UItableVIew的代理
searchDisplayController.searchResultsDataSource = self;
searchDisplayController.searchResultsDelegate = self;
// [searchDisplayController setActive:YES animated:YES];
self.displayController = searchDisplayController;
第四步
实现TableView的代理方法,这里需要注意的是区分加载哪个Tableview,这里只是介绍如何加载不同的cell,其他代理方法区分也是类似的
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
if (tableView == self.tableView)
{
HotTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:identify1 forIndexPath:indexPath];
[self configCell:cell indexpath:indexPath tableView:tableView];
return cell;
}
else
{
NSString *identyfy = nil;
if (indexPath.section == 0) {
identyfy = identify2;
}
else
{
identyfy = identify3;
}
HotTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:identyfy forIndexPath:indexPath];
[self configCell:cell indexpath:indexPath tableView:tableView];
return cell;
}
}
- (void)configCell:(HotTableViewCell *)cell indexpath:(NSIndexPath *)indexpath tableView:(UITableView *)tableView
{
if (tableView == self.tableView) {
if (indexpath.section == 0) {
cell.dataLists = self.hotDataSource;
cell.whichSection = 0;
}else
{
cell.dataLists = self.historyDateSource;
cell.whichSection = 1;
}
[cell.collectionView reloadData];
// CGRect rec = cell.collectionView.frame;
// rec.size.width = [UIScreen mainScreen].bounds.size.width;
// cell.collectionView.frame = rec;
CGSize size = cell.collectionView.collectionViewLayout.collectionViewContentSize;
cell.colletionViewHeight.constant = size.height;
}
else
{
// 第0段的时候是加载SKTagview
if (indexpath.section == 0)
{
__weak typeof(self)weakSelf = self;
[cell.tagListView removeAllTags];
cell.tagListView.preferredMaxLayoutWidth = [UIScreen mainScreen].bounds.size.width;
cell.tagListView.padding = UIEdgeInsetsMake(15, 10, 2, 10);
cell.tagListView.interitemSpacing = 20;
cell.tagListView.lineSpacing = 10;
[self.resultTagDataSource enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop)
{
SKTag *tag = [SKTag tagWithText:[NSString stringWithFormat:@"#%@",weakSelf.resultTagDataSource[idx]]];
// tag.padding = UIEdgeInsetsMake(3, 5, 3, 5);
tag.font = [UIFont systemFontOfSize:13.0];
// tag.borderWidth = 0.5f;
tag.bgColor = [UIColor whiteColor];
tag.cornerRadius = 3;
// tag.borderColor = RGBA(191, 191, 191, 1);
tag.textColor = [UIColor redColor];
tag.padding = UIEdgeInsetsMake(10, 5, 10, 5);
tag.enable = YES;
[cell.tagListView addTag:tag];
}];
cell.tagListView.didTapTagAtIndex = ^(NSUInteger index)
{
};
}
else // 第1段就加载美女图片
{
[cell.articleImage sd_setImageWithURL:[NSURL URLWithString:self.resultArticleDataSource[indexpath.row]] placeholderImage:nil completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, NSURL *imageURL) {
if (image && cacheType == SDImageCacheTypeNone) {
cell.articleImage.alpha = 0;
[UIView animateWithDuration:1.0 animations:^{
cell.articleImage.alpha = 1.0f;
}];
}
else
{
cell.articleImage.alpha = 1.0f;
}
}];
}
}
}
第五步
实现UISearchDisplayController的代理方法,这里我加了打印log以及注释,主要的方法里面一个是筛选本地数据进行搜索,我这里是模拟的网络加载数据
#pragma mark UISearchDisplayDelegate
//===============================================
- (void)searchDisplayControllerWillBeginSearch:(UISearchDisplayController *)controller {
NSLog(@"will begin search");
}
- (void)searchDisplayControllerDidBeginSearch:(UISearchDisplayController *)controller {
NSLog(@"did begin search");
}
- (void)searchDisplayControllerWillEndSearch:(UISearchDisplayController *)controller {
NSLog(@"will end search");
}
- (void)searchDisplayControllerDidEndSearch:(UISearchDisplayController *)controller {
NSLog(@"did end search");
}
- (void)searchDisplayController:(UISearchDisplayController *)controller didLoadSearchResultsTableView:(UITableView *)tableView {
NSLog(@"did load table");
[tableView registerNib:[UINib nibWithNibName:identify2 bundle:nil] forCellReuseIdentifier:identify2];
[tableView registerNib:[UINib nibWithNibName:identify3 bundle:nil] forCellReuseIdentifier:identify3];
[tableView registerNib:[UINib nibWithNibName:identify4 bundle:nil] forCellReuseIdentifier:identify4];
}
- (void)searchDisplayController:(UISearchDisplayController *)controller willUnloadSearchResultsTableView:(UITableView *)tableView {
NSLog(@"will unload table");
}
- (void)searchDisplayController:(UISearchDisplayController *)controller willShowSearchResultsTableView:(UITableView *)tableView {
NSLog(@"will show table");
}
- (void)searchDisplayController:(UISearchDisplayController *)controller didShowSearchResultsTableView:(UITableView *)tableView {
NSLog(@"did show table");
}
- (void)searchDisplayController:(UISearchDisplayController *)controller willHideSearchResultsTableView:(UITableView *)tableView {
NSLog(@"will hide table");
}
- (void)searchDisplayController:(UISearchDisplayController *)controller didHideSearchResultsTableView:(UITableView *)tableView {
NSLog(@"did hide table");
}
- (BOOL)searchDisplayController:(UISearchDisplayController *)controller shouldReloadTableForSearchString:(NSString *)searchString {
NSLog(@"should reload table for search string?");
// 如果是本地搜索就用下面的方法过滤,我这里用假数据模拟下网络加载
// NSPredicate *predicate = [NSPredicate predicateWithFormat:@"SELF CONTAINS[cd] %@", searchString];
// self.searchResultDataSource = [[NSMutableArray alloc] initWithArray:[self.dataSource filteredArrayUsingPredicate:predicate]];;
// self.resultVC.resultDataSource = self.searchResultDataSource;
// [self.resultVC.tableView reloadData];
self.resultTagDataSource = [[NSMutableArray alloc] initWithArray:@[@"忠犬八公的故事",@"肖申克的救赎",@"致命魔术",@"致命ID",@"搏击俱乐部",@"绝命毒师",@"恐怖游轮",@"一只鸡",@"三只鸡",@"X男人",@"异次元骇客的呵呵呵呵呵呵呵呵"]];
self.resultArticleDataSource = [[NSMutableArray alloc] initWithArray:@[@"http://g1.ykimg.com/0130391F4555E1ADCCAB0C2BC11A026B822DCD-20CB-492B-E573-2C134BEAACD6",
@"http://photo.880sy.com/4/2615/97666_small.jpg",
@"http://android.tgbus.com/xiaomi/UploadFiles_8974/201204/20120419104740904.jpg",
@"http://file.ynet.com/2/1507/26/10257216.jpg",
@"https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcRJrreS_lZ4ha_qf4wuCBjvqqcNe_9mfGDvSpL6yW-s7Hw2acuwdA",
@"http://images.china.cn/attachement/jpg/site1000/20160114/c03fd55670b218013ce02e.jpg",
@"http://g2.ykimg.com/0130391F455393CD019187003FF99B6B5AD97A-861D-4127-04FC-F206FA63EF4D",
@"https://encrypted-tbn1.gstatic.com/images?q=tbn:ANd9GcTWyinrltRMmvUI_o0cu3oaQO0mbGoRLR9qXqttq4kOO-Aox44MAg",
@"http://i0.sinaimg.cn/edu/2015/0417/U1151P42DT20150417152321.jpg",
@"http://news.xinhuanet.com/world/2010-02/26/124271_11n.jpg",
@"https://encrypted-tbn3.gstatic.com/images?q=tbn:ANd9GcSCXeL1x-x7y5Pk7qj4DlB_MGMHbY8DUuIgWR8mCLQIwRvCOyaO"]];
// 这里的returnYES代表数据回来立马刷新,但是如果是网络请求,咱们可以先给NO,然后再请求网络数据回来的异步方法里面进行一些刷新UI的操作
return YES;
}
基本的介绍就OK了,由于我这里用到的都是IB实现的
需要细看Demo的朋友可以点击以下传送门:点击打开链接下载Demo
如果跑起来出现library not found for -lPods,说明链接不到cocoapods,各位自己重新pod install一下,由于
cocoapods版本不同(1.0上下还是有区别的,需要包个Target 和 end作为外层)会部分不能运行
,不过问题不大,上面已经介绍很详细了,可以根本写个Demo试试
外国友人是如何修改内置TableView属性的介绍:点击打开链接
这里搜集了几个UISearchDisplayController的几点不足之处
3.不足之处
UISearchDisplayController从我使用过程中,感觉到有三点不足。
(1)使用UISearchDisplayController当键盘弹出来的时候,会默认把navagationBar给隐藏起来。如果不需要隐藏navagationBar,最好的处理方式就是重写UISearchDisplayController的-(void)setActive:(BOOL)visible animated:(BOOL)animated方法:
自定义一个类CustomSearchDisplayController,继承自UISearchDisplayController,然后在.m文件中重写该方法,并在该方法中主动显示navagationBar。
@implementation CustomDisplaySearchViewController
- (void)setActive:(BOOL)visible animated:(BOOL)animated {
[super setActive:visible animated:animated];
[self.searchContentsController.navigationController setNavigationBarHidden:NOanimated:NO];
}
@end
(2)UISearchDisplayController的tableView有一个标签,当没有匹配的结果时,默认会在tableView上显示一个“No Result”的标签。如果说想自定义这个标签,可以通过循环遍历出tableView上标签。
- (BOOL)searchDisplayController:(UISearchDisplayController *)controller
shouldReloadTableForSearchString:(NSString *)searchString {
for (UIView* v in self.customDisplaySearch.searchResultsTableView.subviews) {
if ([v isKindOfClass: [UILabel class]] &&
[[(UILabel*)v text] isEqualToString:@"No Results"]) {
UILabel *label = (UILabel *)v;
label.text = @"没有结果";
break;
}
}
return YES;
}
(3)UISearchDisplayController的UISearchBar输入框当无输入时,SearchResultsTableView无法根据个人需求让表展示出来。我尝试过通过点击搜索栏delegate方法中去处理表展示问题,可是尝试失败了。