一個普通的searchBar的使用
寫在前面
這兩天想給app加一個搜索功能,打算采用系統(tǒng)自帶的UISearch等一系列控件。
后來發(fā)現(xiàn)在iOS8前后,使用也是不一樣的。
UISearchBar + UISearchDisplayController 是iOS8之前的常用寫法。
而在iOS8之后,就開始采用UISearchController。
昨天和今天主要是在嘗試iOS8之前那種方式時,始終出不來效果。直到今天才是解決了,還是寫篇文章記錄下,防止以后忘記了。
Before iOS8
在iOS8之前,搜索功能可以只用SearchBar,具體的Search功能實(shí)現(xiàn)就可以卸載SearchBar的代理中,具體實(shí)現(xiàn)就不多說了。
但是我覺得通過UISearchBar + UISearchDisplayController 的一起使用,可以獲得iOS系統(tǒng)上更原生的體驗(yàn),如本文開頭動圖所示。
UISearchBar
UISearchBar通常就是我們看到的那個輸入框,具體的其屬性配置不多說了,可以查看官方文檔
UISearchDisplayController
UISearchDisplayController我覺得有點(diǎn)像一個UITableViewController,它也有Tableview(searchResultTableView),有dataSource(searchResultsDataSource),也有delegate(searchResultsDelegate)。
而且它的dataSource是和我們展示所用的普通Tableview的dataSource是一樣的。
所以說UISearchDisplayController其實(shí)就是一個單純展示搜索結(jié)果的UITableViewController,只是蘋果幫我們很好的封裝了起來。
官方的指南寫法也是如此:
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
//直接在dataSource中判斷當(dāng)前是哪個TableView
if (tableView == self.tableView) {
return ...;
}
// If necessary (if self is the data source for other table views),
// check whether tableView is searchController.searchResultsTableView.
return ...;
}
demoBefore iOS8
直接上完整代碼:
#import "SearchVBefore8.h"
@interface SearchVBefore8 ()
/**數(shù)據(jù)源*/
@property (nonatomic, strong) NSArray *dataArray;
/**經(jīng)過搜索之后的數(shù)據(jù)源*/
@property (nonatomic, strong) NSArray *searchResultArray;
/**我們的UISearchDisplayController*/
@property (nonatomic, strong) UISearchDisplayController *displayer;
@end
@implementation SearchVBefore8
- (NSArray *)getDataArray
{
/**模擬一組數(shù)據(jù)*/
NSMutableArray *resultArray = [[NSMutableArray alloc] init];
for (int i = 0; i< 20; i++) {
NSString *dataString = [NSString stringWithFormat:@"%d",i];
[resultArray addObject:dataString];
}
return resultArray;
}
- (void)viewDidLoad {
[super viewDidLoad];
[self setupSearchBar];
self.dataArray = [self getDataArray];
}
- (void)setupSearchBar{
/**配置Search相關(guān)控件*/
UISearchBar *searchBar = [[UISearchBar alloc] initWithFrame:CGRectMake(0, 0, self.view.frame.size.width, 44)];
self.tableView.tableHeaderView = searchBar;
self.displayer = [[UISearchDisplayController alloc] initWithSearchBar:searchBar contentsController:self];
/**searchBar的delegate看需求進(jìn)行配置*/
searchBar.delegate = self;
/**以下都比較重要,建議都設(shè)置好代理*/
self.displayer.searchResultsDataSource = self;
self.displayer.searchResultsDelegate = self;
self.displayer.delegate = self;
}
#pragma mark - Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
/**對TableView進(jìn)行判斷,如果是搜索結(jié)果展示視圖,返回不同結(jié)果*/
if (tableView == self.displayer.searchResultsTableView) {
return self.searchResultArray.count;
}
else{
return self.dataArray.count;
}
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"mainCell"];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"mainCell"];
}
/**對TableView進(jìn)行判斷,如果是搜索結(jié)果展示視圖,返回不同數(shù)據(jù)源*/
if (tableView == self.displayer.searchResultsTableView) {
cell.textLabel.text = [NSString stringWithFormat:@"%@",self.searchResultArray[indexPath.row]];
}
else{
cell.textLabel.text = [NSString stringWithFormat:@"%@",self.dataArray[indexPath.row]];
}
return cell;
}
- (BOOL)searchBarShouldBeginEditing:(UISearchBar *)searchBar
{
NSLog(@"begin");
return YES;
}
-(BOOL)searchBarShouldEndEditing:(UISearchBar *)searchBar
{
NSLog(@"end");
return? YES;
}
/**UISearchDisplayController的代理實(shí)現(xiàn)*/
- (BOOL)searchDisplayController:(UISearchDisplayController *)controller shouldReloadTableForSearchString:(NSString *)searchString
{
/**通過謂詞修飾的方式來查找包含我們搜索關(guān)鍵字的數(shù)據(jù)*/
NSPredicate *resultPredicate = [NSPredicate predicateWithFormat:@"self contains[cd] %@",searchString];
self.searchResultArray = [self.dataArray filteredArrayUsingPredicate:resultPredicate];
return? YES;
}
之前主要一直看不到結(jié)果是因?yàn)橛衧elf.searchDisplayController這個默認(rèn)屬性的存在,所以配置都是用在了self.searchDisplayController上。至今仍然很奇怪為什么,這是之前的代碼:
UISearchDisplayController *displayerControllr = [[UISearchDisplayController alloc] initWithSearchBar:searchBar contentsController:self];
displayerControllr.searchResultsDataSource = self;
displayerControllr.searchResultsDelegate = self;
displayerControllr.delegate = self;
NSLog(@"%@? 和? %@",displayerControllr,self.searchDisplayController);
我們看后臺打印:
2016-05-06 14:49:07.546 SearchBarDemo[26008:3948409] ? 和?
兩者地址也是一樣的,所以不明白為什么不起作用。當(dāng)換回自己寫的新屬性self.displayer的時候就可以了,而且self.displayer和self.searchDisplayController的地址也是一樣的。
以上只是粗略demo,但是功能實(shí)現(xiàn)了。如果要美化或者添加功能的再自己豐富下吧。
UISearchController
它的init方法:
- (instancetype)initWithSearchResultsController:(nullable UIViewController *)searchResultsController;
它不用像iOS8之前那樣我們還需要單獨(dú)寫一個UISearchBar,UISearchController在我們創(chuàng)建的時候會自己生成一個UISearchBar,而UISearchController本身更像是一個容器與膠水,它把我們當(dāng)前的ViewController與結(jié)果展示的ViewController相連一起,相較之于之前的UISearchDisplayController相比來說,這樣的方式可以讓我們更能去高度定制我們所需要的結(jié)果展示ViewController。
searchResultsUpdater
更新代理,負(fù)責(zé)通知searchResultsController進(jìn)行更新。
相比較來說,個人覺得iOS8之后的配置會顯得更加容易方便一點(diǎn)。
demoAfteriOS8
效果圖
在主界面實(shí)現(xiàn)文件中:
#import "MainTableViewController.h"
#import "Product.h"
#import "MySearchTableViewController.h"
@interface MainTableViewController ()
@property(nonatomic,strong)NSArray *allProducts;
/**搜索結(jié)果ViewController*/
@property(nonatomic,strong)MySearchTableViewController *mySRTVC;
@property(nonatomic,strong)UISearchController *svc;
@end
@implementation MainTableViewController
-(NSArray *)allProducts
{
if (!_allProducts) {
_allProducts=[Product demoData];
}
return _allProducts;
}
- (void)viewDidLoad {
[super viewDidLoad];
[self.tableView registerClass:[UITableViewCell class] forCellReuseIdentifier:@"cell"];
//創(chuàng)建兩個屬性實(shí)例
self.mySRTVC=[[MySearchTableViewController? alloc]init];
self.svc=[[UISearchController alloc]initWithSearchResultsController:self.mySRTVC];
//設(shè)置與界面有關(guān)的樣式
[self.svc.searchBar sizeToFit];? //大小調(diào)整
self.tableView.tableHeaderView=self.svc.searchBar;
//設(shè)置搜索控制器的結(jié)果更新代理對象
self.svc.searchResultsUpdater=self;
//Scope:就是效果圖中那個分類選擇器
self.svc.searchBar.scopeButtonTitles=@[@"設(shè)備",@"軟件",@"其他"];
//為了響應(yīng)scope改變時候,對選中的scope進(jìn)行處理 需要設(shè)置search代理
self.svc.searchBar.delegate=self;
self.definesPresentationContext=YES;? //迷之屬性,打開后搜索結(jié)果頁界面顯示會比較好。
//看文檔貌似是頁面轉(zhuǎn)換模式為UIModalPresentationCurrentContext,如果該選項(xiàng)打開,那么就會使用當(dāng)前ViewController的一個presentContenxt
//否則就向父類中進(jìn)行尋找并使用。
}
/**普通的tableview展示實(shí)現(xiàn)。*/
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return self.allProducts.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"cell" forIndexPath:indexPath];
Product *p=self.allProducts[indexPath.row];
cell.textLabel.text=p.name;
return cell;
}
#pragma mark - UISearchResultsUpdating
/**實(shí)現(xiàn)更新代理*/
-(void)updateSearchResultsForSearchController:(UISearchController *)searchController
{
//獲取scope被選中的下標(biāo)
NSInteger selectedType=searchController.searchBar.selectedScopeButtonIndex;
//獲取到用戶輸入的數(shù)據(jù)
NSString *searchText=searchController.searchBar.text;
NSMutableArray *searchResult=[[NSMutableArray alloc]init];
for (Product *p in self.allProducts) {
NSRange range=[p.name rangeOfString:searchText];
if (range.length>0&&p.type==selectedType) {
[searchResult addObject:p];
}
}
self.mySRTVC.searchProducts=searchResult;
/**通知結(jié)果ViewController進(jìn)行更新*/
[self.mySRTVC.tableView reloadData];
}
#pragma mark - UISearchBarDelegate
/**點(diǎn)擊按鈕后,進(jìn)行搜索頁更新*/
-(void)searchBar:(UISearchBar *)searchBar selectedScopeButtonIndexDidChange:(NSInteger)selectedScope
{
[self updateSearchResultsForSearchController:self.svc];
}
@end
在搜索結(jié)果頁面中(就是一個普通Tableview的展示):
#import "MySearchTableViewController.h"
#import "Product.h"
@interface MySearchTableViewController ()
@end
@implementation MySearchTableViewController
- (void)viewDidLoad {
[super viewDidLoad];
[self.tableView registerClass:[UITableViewCell class] forCellReuseIdentifier:@"mycell"];
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#pragma mark - Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return self.searchProducts.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"mycell" forIndexPath:indexPath];
Product *p=self.searchProducts[indexPath.row];
cell.textLabel.text=p.name;
return cell;
}
@end
———————————分割線—————————————
關(guān)于評論中有人提出搜索結(jié)果頁(MySearchTableViewController)不能Push到下一個界面的問題,補(bǔ)上一點(diǎn)漏掉的內(nèi)容。
個人覺得MySearchTableViewController只是作為一個childViewController被添加到MainTableViewController上面,所以它本身是沒有被壓入navigationController的棧中,因此簡單的在MySearchTableViewController中調(diào)用
[self.navigationController pushViewController:someVC animated:YES];
是不行的,因此self.navigationController是空的。
本例中可以這樣解決:
@interface MySearchTableViewController : UIViewController
@property (nonatomic, strong) NSArray *resultDataArray;
//在MySearchTableViewController添加一個指向展示頁的【弱】引用屬性。
@property (nonatomic, weak) UIViewController *mainSearchController;
@end
//然后在mainSearchController的創(chuàng)建MySearchTableViewController實(shí)例的代碼中添加下面一句:
self.mySRTVC=[[MySearchTableViewController? alloc]init];
self.mySRTVC.mainSearchController = self;
//最后在想要實(shí)現(xiàn)push的地方這樣實(shí)現(xiàn)
[self.mainSearchController.navigationController pushViewController:detailViewController animated:YES];