/**
最近做的项目中需要到一个仿照网易云音乐的搜索功能,我把它抽离出来供大家分享,有一些写的不好的地方,欢迎大家指正
我在这里用朋友的测试接口展示数据,仅供测试使用,请勿进行商业用途
/
/
这里会讲一下基本的搜索功能的搭建,首先是当点击搜索框的时候,调起键盘并显示搜索历史记录,然后在点击记录时返回搜索数据
当点击键盘时候,显示匹配到的关键字,点击字段返回数据并将该字段存入到历史记录中
虽然搜索功能很小很简单,但是不细心的话也会出现很多bug,我会把我想到的一些注意事项写在项目中,欢迎大家指正补充
**/
效果图如下:
我们先来分析一下:首先点击搜索框的时候,弹出键盘,显示历史记录;点击键盘开始输入时,需要匹配关键字;点击键盘上的搜索,点击历史记录,点击匹配到的关键字的时候都要返回值,然后界面刷新UI。
我们需要搭建一个本地数据库来存储搜索的历史记录
DataBase.h
<pre>
import <Foundation/Foundation.h>
import "SearchModel.h"
@interface DataBase : NSObject
/**
- 创建单例接口
*/
- (DataBase *)shareDataBase;
pragma mark -
/**
- 收藏接口
*/
- (void)saveModel:(SearchModel )model;
/*
- 判断是否已经收藏
*/
- (BOOL)isHadSaveModel:(SearchModel )model;
/*
- 获取收藏的所有数据
*/
- (NSArray )selectAllModel;
/*
- 删除一个收藏
*/
- (void)deleteOneModelByStr:(NSString *)str;
@end
</pre>
.m
<pre>
import "DataBase.h"
import "FMDB.h"
@interface DataBase ()
@property (nonatomic, strong) FMDatabase *db;
@end
@implementation DataBase
// 创建单例
- (DataBase *)shareDataBase
{
static DataBase *single = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
single = [[DataBase alloc] init];
[single creatDataBase];
});
return single;
}
// 创建数据库
-
(void)creatDataBase
{
// 在Documents文件夹下创建db.sqlite
NSString *dbPath = [[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject] stringByAppendingPathComponent:@"db.splite"];// 初始化
self.db = [FMDatabase databaseWithPath:dbPath];
// 打开数据库
[self.db open];
// 创建表格
[self creatTable];
}
(void)creatTable
{
BOOL isSuccess = [self.db executeUpdate:@"create table if not exists MV(id integer primary key autoincrement, historyStr text)"];
NSLog(@"%@", isSuccess ? @"表格创建成功":@"表格创建失败");
}(BOOL)isHadSaveModel:(SearchModel *)model
{
FMResultSet *set = [self.db executeQuery:@"select * from MV where historyStr = ?", model.historyStr];
while ([set next]) {
NSString *historyStr = [set stringForColumn:@"historyStr"];
if ([model.historyStr isEqualToString:historyStr]) {
return YES;
}
}
return NO;
}-
(void)saveModel:(SearchModel *)model
{
BOOL isSuccess = [self.db executeUpdate:@"insert into MV(historyStr) values (?)", model.historyStr];NSLog(@"%@", isSuccess ? @"收藏成功":@"收藏失败");
} (NSArray *)selectAllModel
{
FMResultSet *set = [self.db executeQuery:@"select *from MV"];
NSMutableArray *arr = [NSMutableArray array];
while ([set next]) {
NSString *historyStr = [set stringForColumn:@"historyStr"];
SearchModel *model = [[SearchModel alloc] init];
model.historyStr = historyStr;
[arr addObject:model];
}
return arr;
}-
(void)deleteOneModelByStr:(NSString *)str
{
BOOL isSuccess = [self.db executeUpdate:@"delete from MV where historyStr = ?", str];NSLog(@"%@", isSuccess ? @"删除成功":@"删除失败");
}
</pre>
搭建一下主界面,这里我是用一个collectionView写的. 实现代理方法
<pre>
/**
- 创建一个collectionView
*/
-
(void)creatCollectionView
{
UICollectionViewFlowLayout *flowLayout = [[UICollectionViewFlowLayout alloc] init];
self.myCollection = [[UICollectionView alloc] initWithFrame:CGRectMake(0, 60, ScreenWidth, ScreenHeight - 60) collectionViewLayout:flowLayout];
self.myCollection.backgroundColor = [UIColor orangeColor];
_myCollection.dataSource = self;
_myCollection.delegate = self;
[self.view addSubview:_myCollection];
[_myCollection registerClass:[UserCollectionViewCell class] forCellWithReuseIdentifier:cellID];
[self.view addSubview:_myCollection];// 在collection上添加一个搜索框
self.searchTextField = [[UITextField alloc] initWithFrame:CGRectMake(50, 20, ScreenWidth-100, 40)];
_searchTextField.placeholder = @"🔍输入关键字查询";
_searchTextField.textColor = [UIColor redColor];
_searchTextField.borderStyle = UITextBorderStyleRoundedRect;
[self.view addSubview:_searchTextField];
// 设置textfield的return键为搜索键
_searchTextField.returnKeyType = UIReturnKeySearch;
// 设置textfield的代理
_searchTextField.delegate = self;
}
</pre>
然后我们需要再写一个tableView,用来展示历史记录和匹配到的关键字
<pre>
**
- table的cell有两种,一种是历史的cell,一种是匹配关键字的cell,这里我建了两个cell
- 分别展示,根据传入的一个标识分别创建(因为两个cell只有一个button的区别,也可以创建一
- 个cell再来控制button的显隐性。不过我特么就是想创建两个)
*/
-
(void)creatTableViewWithStr:(NSString *)str
{
self.myTable = [[UITableView alloc] initWithFrame:CGRectMake(0, 60, ScreenWidth, ScreenHeight-60) style:(UITableViewStylePlain)];
if ([str isEqualToString:@"history"]) {
[_myTable registerNib:[UINib nibWithNibName:@"HistoryTableViewCell" bundle:nil] forCellReuseIdentifier:tableCellId];
}else {
[_myTable registerNib:[UINib nibWithNibName:@"KeyWordTableViewCell" bundle:nil] forCellReuseIdentifier:tableCellId];
} _myTable.delegate = self;
_myTable.dataSource = self;// 当tableView滑动时收起键盘
_myTable.keyboardDismissMode = UIScrollViewKeyboardDismissModeOnDrag;
// 取消tableView的分割线
_myTable.separatorStyle = UITableViewCellSeparatorStyleNone;[self.view addSubview:_myTable];
}
</pre>
在这里看一下我在主控制器中所定义的属性,都是要用到的。
<pre>
@interface ViewController ()<UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout, UITextFieldDelegate, UITableViewDelegate, UITableViewDataSource>
@property (nonatomic, strong) UICollectionView *myCollection; //
@property (nonatomic, strong) UITableView *myTable; //
@property (nonatomic, strong) UITextField *searchTextField; // 搜索框
@property (nonatomic, strong) NSMutableArray dataSource; // cell数据源
@property (nonatomic, strong) NSMutableArray textFieldDataSource; // 匹配字段数据源
@property (nonatomic, strong) NSMutableArray historyDataSource; // 搜索历史数据源
/我在这里用一个替换字段接收请求下来的匹配字段的数据。。不这样做的话,在匹配字段返回的cell时,如果是在汉语模式下编辑,在没有确定输入字段时,此时已经匹配到了字段,但是如果点击的时候,在点击事件中,原来的数据源是空的,可能是我在哪里清空了原来的数据源,我没有找到,只能用一个新的数据源来替代。英文模式下输入,暂时没有发现这种问题(读这个源码的朋友如果能改进这个错误请私信我,万分感谢!!!)*/
@property (nonatomic, strong) NSMutableArray *placeTextDataSource; // 替换匹配字段
@property (nonatomic, strong) NSString *judgeStr; // 用来判断展示的table
@property (nonatomic, strong) SearchModel *model;
// 输入显示字段
@property (nonatomic, strong) NSString *textFieldStr;
@end
</pre>
我们要通过textField的代理事件来实现搜索的交互事件
<pre>
pragma mark --- TextFieldDelegate
/**
- 点击键盘搜索按钮
*/
-
(BOOL)textFieldShouldReturn:(UITextField *)textField
{
// 收起键盘
[_searchTextField resignFirstResponder];
// 移除tableView
[_myTable removeFromSuperview];
// 先清空数据源,然后请求数据
[self.dataSource removeAllObjects];
[self getDataByText:textField.text];// 存入搜索历史
_model.historyStr = textField.text;
if (textField.text.length>0) {
if (![[DataBase shareDataBase] isHadSaveModel:_model]) {
[[DataBase shareDataBase] saveModel:_model];
}
}
return YES;
}
/**
- 点击输入框开始编辑时走这个方法。 (我们需要点击输入框时,在输入框下面出现一
- 个tableView来展示搜索的历史记录)
*/
-
(void)textFieldDidBeginEditing:(UITextField *)textField
{// 先移除之前添加上的tableView
[self.myTable removeFromSuperview];
// 再次添加
self.judgeStr = History;
[self creatTableViewWithStr:self.judgeStr];// 打开输入框后,展示搜索历史记录
self.historyDataSource = [NSMutableArray arrayWithArray:[[DataBase shareDataBase] selectAllModel]];
[self.myTable reloadData];
}
/**
- 当我们开始编辑时,根据我们当前输入的字段进行匹配关键字,需要用到这个方法。 这个方法
- 是当输入框内容开始发生变化时调用
*/
-
(BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
{// 输入时开始匹配关键字,显示另一个tableView
// 先移除之前添加上的tableView
[self.myTable removeFromSuperview];
// 再次添加
self.judgeStr = keyWord;
[self creatTableViewWithStr:self.judgeStr];self.textFieldStr = string;
// 请求匹配关键字
// 先清空保存的数据
[self.textFieldDataSource removeAllObjects];
[self getDataByTextfieldText:self.textFieldStr];return YES;
}
</pre>
代码有点多,不明白的同学可以到我的git主页上下载源码,里面注释写的也比较详细
git:https://github.com/you12138/SearchLikeWangYiMusic.git