先看下 App 中实现的搜索效果图:

效果图1

左侧为搜索初始页面,右侧为搜索的结果列表页面,现在说一下实现的思路和步骤:

  1. 点击搜索按钮跳转到搜索页面(包含头部热门搜索视图以及搜索历史视图和清空搜索历史功能)
  2. 搜索框输入内容开始搜索并展示搜索结果列表
  3. 点击搜索列表的某一行跳转到下级页面,并记录搜索历史数据
  4. 如果清空搜索框内的内容,再次展示搜索页面

搜索页面

创建 基于 UIViewController 的 DHSearchViewController 类为搜索页面,当点击搜索按钮时跳转到 DHSearchViewController

创建 TableView 到整个视图中,其中热门搜索标签视图为整个 TableView 的 HeadView,搜索历史数据为 TableView 的表格数据,清空搜索记录按钮为 TableView 的 FootView。

设置搜索页面的 TableView

设置 TableView 以及 HeadView 和 FootView

self.tableView = [[UITableView alloc] initWithFrame:CGRectMake(0, 0, kScreenWidth, kScreenHeight) style:UITableViewStylePlain];
    [self.tableView registerClass:[UITableViewCell class] forCellReuseIdentifier:@"cell"];
    self.tableView.delegate = self;
    self.tableView.dataSource = self;
    [self.view addSubview:self.tableView];
    
    // 创建搜索框
    UISearchBar *searchBar = [[UISearchBar alloc] initWithFrame:CGRectMake(10, 0, kScreenWidth-20, 20)];
    searchBar.placeholder = @"搜索平台";
    searchBar.delegate = self;
    searchBar.backgroundColor = [UIColor clearColor];
    searchBar.showsCancelButton = YES;
    searchBar.tintColor = [UIColor blueColor];
    self.searchBar = searchBar;
    self.navigationItem.titleView = self.searchBar;
    
    // headView
    self.headerView = [[UIView alloc] init];
    self.headerView.sd_x = 0;
    self.headerView.sd_y = 0;
    self.headerView.sd_width = kScreenWidth;
    
    UILabel *titleLabel = [[UILabel alloc] initWithFrame:CGRectMake(10, 20, kScreenWidth-20, 30)];
    titleLabel.text = @"热门推荐";
    titleLabel.font = [UIFont systemFontOfSize:13];
    titleLabel.textColor = [UIColor grayColor];
    [titleLabel sizeToFit];
    [self.headerView addSubview:titleLabel];
    
    self.tagsView = [[UIView alloc] init];
    self.tagsView.sd_x = 10;
    self.tagsView.sd_y = titleLabel.sd_y+30;
    self.tagsView.sd_width = kScreenWidth-20;
    [self.headerView addSubview:self.tagsView];
    self.tableView.tableHeaderView = self.headerView;
    
    
    UIView *footView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, kScreenWidth, 40)];
    UILabel *footLabel = [[UILabel alloc] initWithFrame:footView.frame];
    footLabel.textColor = [UIColor grayColor];
    footLabel.font = [UIFont systemFontOfSize:13];
    footLabel.userInteractionEnabled = YES;
    footLabel.text = @"清空搜索记录";
    footLabel.textAlignment = NSTextAlignmentCenter;
    [footLabel addGestureRecognizer:[[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(emptySearchHistoryDidClick)]];
    [footView addSubview:footLabel];
    self.tableView.tableFooterView = footView;
    ```

### 创建搜索页面的标签视图

根据热门搜索的内容长度设置标签

```Objc
#pragma mark -- 设置标签
- (void)tagsViewWithTag
{
    CGFloat allLabelWidth = 0;
    CGFloat allLabelHeight = 0;
    int rowHeight = 0;
    for (int i = 0; i < self.tagsArray.count; i++) {
        if (i != self.tagsArray.count-1) {
            
            CGFloat width = [self getWidthWithTitle:self.tagsArray[i+1] font:[UIFont systemFontOfSize:14]];
            if (allLabelWidth + width+10 > self.tagsView.frame.size.width) {
                rowHeight++;
                allLabelWidth = 0;
                allLabelHeight = rowHeight*40;
            }
        }else{
            CGFloat width = [self getWidthWithTitle:self.tagsArray[self.tagsArray.count-1] font:[UIFont systemFontOfSize:14]];
            if (allLabelWidth + width+10 > self.tagsView.frame.size.width) {
                rowHeight++;
                allLabelWidth = 0;
                allLabelHeight = rowHeight*40;
            }
        }
        UILabel *rectangleTagLabel = [[UILabel alloc] init];
        // 设置属性
        rectangleTagLabel.userInteractionEnabled = YES;
        rectangleTagLabel.font = [UIFont systemFontOfSize:14];
        rectangleTagLabel.textColor = [UIColor whiteColor];
        rectangleTagLabel.backgroundColor = RandomColor;
        rectangleTagLabel.text = self.tagsArray[i];
        rectangleTagLabel.textAlignment = NSTextAlignmentCenter;
        [rectangleTagLabel addGestureRecognizer:[[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tagDidCLick:)]];
        CGFloat labelWidth = [self getWidthWithTitle:self.tagsArray[i] font:[UIFont systemFontOfSize:14]];
        rectangleTagLabel.layer.cornerRadius = 3;
        [rectangleTagLabel.layer setMasksToBounds:YES];
        if (labelWidth > kScreenWidth-20) {
            labelWidth = kScreenWidth-20;
        }
        rectangleTagLabel.frame = CGRectMake(allLabelWidth, allLabelHeight, labelWidth, 25);
        [self.tagsView addSubview:rectangleTagLabel];
        allLabelWidth = allLabelWidth+10+labelWidth;
    }
    self.tagsView.sd_height = rowHeight*40+40;
    self.headerView.sd_height = self.tagsView.sd_y+self.tagsView.sd_height+10;
}

标签的彩色背景从以下色彩池中随机获取

- (NSMutableArray *)colorPol
{
    if (!_colorPol) {
        NSArray *colorStrPol = @[@"009999", @"0099cc", @"0099ff", @"00cc99", @"00cccc", @"336699", @"3366cc", @"3366ff", @"339966", @"666666", @"666699", @"6666cc", @"6666ff", @"996666", @"996699", @"999900", @"999933", @"99cc00", @"99cc33", @"660066", @"669933", @"990066", @"cc9900", @"cc6600" , @"cc3300", @"cc3366", @"cc6666", @"cc6699", @"cc0066", @"cc0033", @"ffcc00", @"ffcc33", @"ff9900", @"ff9933", @"ff6600", @"ff6633", @"ff6666", @"ff6699", @"ff3366", @"ff3333"];
        NSMutableArray *colorPolM = [NSMutableArray array];
        for (NSString *colorStr in colorStrPol) {
            UIColor *color = [UIColor dh_colorWithHexString:colorStr];
            [colorPolM addObject:color];
        }
        _colorPol = colorPolM;
    }
    return _colorPol;
}

读取本地保存的搜索历史数据并刷新 TableView

懒加载数组并刷新视图

- (NSMutableArray *)searchHistories
{
    
    if (!_searchHistories) {
        self.searchHistoriesCachePath = SEARCH_SEARCH_HISTORY_CACHE_PATH;
        _searchHistories = [NSMutableArray arrayWithArray:[NSKeyedUnarchiver unarchiveObjectWithFile:self.searchHistoriesCachePath]];
    }
    return _searchHistories;
}

- (void)setSearchHistoriesCachePath:(NSString *)searchHistoriesCachePath
{
    _searchHistoriesCachePath = [searchHistoriesCachePath copy];
    // 刷新
    self.searchHistories = nil;
    [self.tableView reloadData];
}

展示搜索页面完毕后现在实现搜索功能

添加搜索结果页面并默认隐藏

添加搜索结果页面,当搜索框内容为空时默认隐藏搜索结果页。当搜索框内容变化时发送通知,并在搜索结果页面接收通知,添加搜索结果到视图中

- (DHSearchSuggestViewController *)searchSuggestionVC
{
    if (!_searchSuggestionVC) {
        DHSearchSuggestViewController *searchSuggestionVC = [[DHSearchSuggestViewController alloc] initWithStyle:UITableViewStylePlain];
        __weak typeof(self) _weakSelf = self;
        searchSuggestionVC.didSelectText = ^(NSString *didSelectText) {
            
            if ([didSelectText isEqualToString:@""]) {
                [self.searchBar resignFirstResponder];
            }else{
                // 设置搜索信息
                _weakSelf.searchBar.text = didSelectText;
                // 缓存数据并且刷新界面
                [_weakSelf saveSearchCacheAndRefreshView];
            }
        };
        searchSuggestionVC.view.frame = CGRectMake(0, 64, self.view.frame.size.width, self.view.frame.size.height-64);
        searchSuggestionVC.view.backgroundColor = [UIColor whiteColor];
        
        [self.view addSubview:searchSuggestionVC.view];
        [self addChildViewController:searchSuggestionVC];
        _searchSuggestionVC = searchSuggestionVC;
    }
    return _searchSuggestionVC;
}

- (void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText
{
    if ([searchText isEqualToString:@""]) {
        self.searchSuggestionVC.view.hidden = YES;
        self.tableView.hidden = NO;
    }else{
        self.searchSuggestionVC.view.hidden = NO;
        self.tableView.hidden = YES;
        [self.view bringSubviewToFront:self.searchSuggestionVC.view];
        
        //创建一个消息对象
        NSNotification * notice = [NSNotification notificationWithName:@"searchBarDidChange" object:nil userInfo:@{@"searchText":searchText}];
        //发送消息
        [[NSNotificationCenter defaultCenter]postNotification:notice];
    }
}

在搜索结果页面接收通知并开始搜索

当搜索框开始搜索时,在搜索结果页面 DHSearchSuggestViewController 接收通知

// 获取通知中心单例对象
    NSNotificationCenter * center = [NSNotificationCenter defaultCenter];
    // 添加当前类对象为一个观察者,name和object设置为nil,表示接收一切通知
    [center addObserver:self selector:@selector(handleColorChange:) name:@"searchBarDidChange" object:nil];

接收到通知后根据搜索内容查询数据并展示:

-(void)handleColorChange:(NSNotification* )sender
{
    NSString *text = sender.userInfo[@"searchText"];
    NSLog(@"%@", text);
    // 搜索方法写在这里并刷新数据
}

搜索数据后,隐藏搜索页面的 TableView,展示搜索结果页的 searchSuggestionVC

在搜索结果页面处理对应交互

当用户点击搜索列表的某一行时再进行其他的操作

// 取消选中
    [tableView deselectRowAtIndexPath:indexPath animated:YES];
    self.didSelectText([tableView cellForRowAtIndexPath:indexPath].textLabel.text);
    
    UIViewController *vc = [[UIViewController alloc]init];
    [self.navigationController pushViewController:vc animated:YES];

注意点

1、用户点击搜索按钮后,保存搜索数据到本地

- (void)saveSearchCacheAndRefreshView
{
    UISearchBar *searchBar = self.searchBar;
    // 回收键盘
    [searchBar resignFirstResponder];
    // 先移除再刷新
    [self.searchHistories removeObject:searchBar.text];
    [self.searchHistories insertObject:searchBar.text atIndex:0];
    
    // 移除多余的缓存
    if (self.searchHistories.count > self.searchHistoriesCount) {
        // 移除最后一条缓存
        [self.searchHistories removeLastObject];
    }
    // 保存搜索信息
    [NSKeyedArchiver archiveRootObject:self.searchHistories toFile:self.searchHistoriesCachePath];
    [self.tableView reloadData];
}

2、用户点击搜索框的关闭按钮时调用 UISearchBar 的代理方法,并隐藏搜索结果页面,展示搜索页面。

3、点击搜索页面清空搜索历史按钮后,清空本地的搜索数据并刷新视图

- (void)emptySearchHistoryDidClick
{
    self.tableView.tableFooterView.hidden = YES;
    // 移除所有历史搜索
    [self.searchHistories removeAllObjects];
    // 移除数据缓存
    [NSKeyedArchiver archiveRootObject:self.searchHistories toFile:self.searchHistoriesCachePath];
    [self.tableView reloadData];
}

4、点击热门搜索标签后,使标签文本填充到搜索框并进行搜索。同时隐藏搜索页面,打开搜索结果页

- (void)tagDidCLick:(UITapGestureRecognizer *)gr
{
    UILabel *label = (UILabel *)gr.view;
    self.searchBar.text = label.text;
    //  缓存数据并且刷新界面
    [self saveSearchCacheAndRefreshView];
    self.tableView.tableFooterView.hidden = NO;
    self.searchSuggestionVC.view.hidden = NO;
    self.tableView.hidden = YES;
    [self.view bringSubviewToFront:self.searchSuggestionVC.view];
    
    // 创建一个消息对象
    NSNotification *notice = [NSNotification notificationWithName:@"searchBarDidChange" object:nil userInfo:@{@"searchText":label.text}];
    // 发送消息
    [[NSNotificationCenter defaultCenter]postNotification:notice];
}

5、设置历史搜索页面的单条记录清除按钮,当点击按钮后清除单条数据,并刷新视图

- (void)closeDidClick:(UIButton *)sender
{
    // 获取当前cell
    UITableViewCell *cell = (UITableViewCell *)sender.superview;
    // 移除搜索信息
    [self.searchHistories removeObject:cell.textLabel.text];
    // 保存搜索信息
    [NSKeyedArchiver archiveRootObject:self.searchHistories toFile:SEARCH_SEARCH_HISTORY_CACHE_PATH];
    if (self.searchHistories.count == 0) {
        self.tableView.tableFooterView.hidden = YES;
    }
    // 刷新
    [self.tableView reloadData];
}

<font-color = 'blue'>附Demo下载链接