效果展示:
项目需求:
用户点击街道栏,跳转到一个新页面,要求:
- 进入页面时展示用户位置;
- 用户在输入框进行输入时需要实时展示搜索提示(类似百度搜索框效果)
- 点击定位按钮可以重新定位
思路:
展示用户位置需要知道用户的位置,那么首先需要定位,然后再根据定位的坐标进行周边搜索;由此可知搜索提示就是根据关键字进行周边搜索!(呵呵,真的是这样吗?)
关键点讲解:
一. 定位。定位主要是为了获取用户当前坐标,在位置更新那个回调方法里获取:
// 位置更新回调
- (void)amapLocationManager:(AMapLocationManager *)manager didUpdateLocation:(CLLocation *)location{
// 定位结果
NSLog(@"location:{lat:%f; lon:%f; accuracy:%f}", location.coordinate.latitude, location.coordinate.longitude, location.horizontalAccuracy);
// 赋值给全局变量
self.location = location;
// 发起周边搜索
[self searchAroundWithKeyword:nil];
// 停止定位
[self.locationManager stopUpdatingLocation];
}
二. 周边搜索。根据定位的结果进行周边搜索,得到展示列表的数据源。
// 构造AMapPOIAroundSearchRequest对象,设置周边搜索参数
AMapPOIAroundSearchRequest *request = [[AMapPOIAroundSearchRequest alloc] init];
request.location = [AMapGeoPoint locationWithLatitude:self.location.coordinate.latitude longitude:self.location.coordinate.longitude];
// types属性表示限定搜索POI的类别,默认为:餐饮服务|商务住宅|生活服务
// POI的类型共分为20种大类别
request.types = @"汽车服务|汽车销售|汽车维修|摩托车服务|餐饮服务|购物服务|生活服务|体育休闲服务|医疗保健服务|住宿服务|风景名胜|商务住宅|政府机构及社会团体|科教文化服务|交通设施服务|金融保险服务|公司企业|道路附属设施|地名地址信息|公共设施";
request.sortrule = 0;
request.requireExtension = YES;
//发起周边搜索
[self.search AMapPOIAroundSearch: request];
当然,处理数据还是需要在周边搜索对应的回调方法里进行处理:
#pragma mark - 周边搜索相关代理
// 实现POI搜索对应的回调函数
- (void)onPOISearchDone:(AMapPOISearchBaseRequest *)request response:(AMapPOISearchResponse *)response{
NSLog(@"周边搜索回调");
// 将搜索出的POI保存到数组
self.addressArray = [NSMutableArray arrayWithArray:response.pois];
// 周边搜索完成后,刷新tableview
[self.tableView reloadData];
// 停止旋转菊花
[CQHud dismiss];
}
三. 搜索提示。好吧,其实周边搜索还是比较费时间的,所以如果要想快速而频繁的进行周边搜索显然是不太现实的(不信你可以后头看下那张GIF展示图,搜索框文本变化后,瞬间出结果,周边搜索是不会那么快的)。针对这个高德提供了一个专门的类AMapInputTipsSearchRequest
,如下:
#pragma mark - AMapInputTipsSearchRequest
/// 搜索提示请求
@interface AMapInputTipsSearchRequest : AMapSearchObject
@property (nonatomic, copy) NSString *keywords; //!< 查询关键字
@property (nonatomic, copy) NSString *city; //!< 查询城市,可选值:cityname(中文或中文全拼)、citycode、adcode.
@end
用这个类提供的方法即可实现我们想要的效果。搜索提示也就是几行代码:
AMapInputTipsSearchRequest *tipsRequest = [[AMapInputTipsSearchRequest alloc] init];
// 如果选择了城市就显示城市,否则默认 成都
tipsRequest.city = @"四川省成都市";
tipsRequest.keywords = keyword;
[self.search AMapInputTipsSearch:tipsRequest];
还是。。。要在对应的回调方法里进行数据处理:
#pragma mark - 搜索提示回调函数
// 输入提示查询回调函数
-(void)onInputTipsSearchDone:(AMapInputTipsSearchRequest*)request response:(AMapInputTipsSearchResponse *)response
{
// 现将数组清空
[self.addressArray removeAllObjects];
// 通过AMapInputTipsSearchResponse对象处理搜索结果
// 将搜索出的POI保存到数组
for (AMapTip *tip in response.tips) {
NSLog(@"InputTipsName: %@", tip.district);
if(tip.district && [tip.district rangeOfString:@"四川省成都市"].location != NSNotFound){
[self.addressArray addObject:tip];
NSLog(@"InputTipsName: %@", tip.name);
}
}
// 周边搜索完成后,刷新tableview
[self.tableView reloadData];
// 停止旋转菊花
[CQHud dismiss];
}
小结
思路大概就是这样的,听说有人想问文本框是怎样及时回调搜索提示方法的,如下:
[self.searchTextField addTarget:self
action:@selector(textFieldChanged:)
forControlEvents:UIControlEventEditingChanged];
代码都在这里了
代码结构:
代码:
@interface AddAddressListViewController (){
BOOL _isSearchAround; // 用于区分是周边搜索还是输入提示查询
}
/** 搜索地址textField */
@property (nonatomic,strong) UITextField *searchTextField;
/** 地址展示tableView */
@property (nonatomic,strong) UITableView *tableView;
/** 地址数组 */
@property (nonatomic,strong) NSMutableArray *addressArray;
/** 定位管理 */
@property (nonatomic,strong) AMapLocationManager *locationManager;
/** 当前位置 */
@property (nonatomic,strong) CLLocation *location;
/** 搜索 */
@property (nonatomic,strong) AMapSearchAPI *search;
@end
@implementation AddAddressListViewController
#pragma mark - 懒加载
- (NSMutableArray *)addressArray{
if (_addressArray == nil) {
_addressArray = [NSMutableArray array];
}
return _addressArray;
}
#pragma mark - 生命周期
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
if ([[[UIDevice currentDevice]systemVersion]floatValue] >= 8) {
UIUserNotificationSettings *notificationSettings = [UIUserNotificationSettings settingsForTypes:UIUserNotificationTypeAlert | UIUserNotificationTypeBadge | UIUserNotificationTypeSound categories:nil];
[[UIApplication sharedApplication] registerUserNotificationSettings:notificationSettings];
}else {
[[UIApplication sharedApplication] registerForRemoteNotificationTypes:UIRemoteNotificationTypeAlert | UIRemoteNotificationTypeBadge | UIRemoteNotificationTypeSound];
}
// UI搭建
[self setUpUI];
// 定位
[self getLocation];
}
#pragma mark - 返回上一页
/** 返回上一页 */
- (void)backToPreviousPage{
[self dismissViewControllerAnimated:NO completion:nil];
}
#pragma mark - UI搭建
/** UI搭建 */
- (void)setUpUI{
self.view.backgroundColor=[UIColor colorWithHexString:@"f7f7f9"];
// 导航栏
UIView *naviView = [[UIView alloc]initWithFrame:CGRectMake(0, 20, screenWidth, 44)];
[self.view addSubview:naviView];
// 返回按钮
UIButton *backButton = [[UIButton alloc]initWithFrame:CGRectMake(0, 0, 44, 44)];
[backButton setBackgroundImage:[UIImage imageNamed:@"v3_back"] forState:UIControlStateNormal];
[backButton addTarget:self action:@selector(backToPreviousPage) forControlEvents:UIControlEventTouchDown];
[naviView addSubview:backButton];
// 街道label
UILabel *streetLabel = [[UILabel alloc]initWithFrame:CGRectMake(backButton.maxX, 0, 60, 44)];
streetLabel.text = @"街 道:";
[streetLabel adjustWidthToMin];
[naviView addSubview:streetLabel];
// 搜索栏textField
self.searchTextField = [[UITextField alloc]initWithFrame:CGRectMake(streetLabel.maxX, 0, screenWidth - streetLabel.maxX - 60, 44)];
self.searchTextField.delegate = self;
self.searchTextField.placeholder = @"写字楼、街道、标志性建筑物";
self.searchTextField.clearButtonMode = UITextFieldViewModeWhileEditing;
[self.searchTextField addTarget:self action:@selector(textFieldChanged:) forControlEvents:UIControlEventEditingChanged];
[self.searchTextField becomeFirstResponder];
[naviView addSubview:self.searchTextField];
// 定位button
UIButton *locationButton = [[UIButton alloc]initWithFrame:CGRectMake(self.searchTextField.maxX + 10, 2, 40, 40)];
[locationButton setBackgroundImage:[UIImage imageNamed:@"lbs_able"] forState:UIControlStateNormal];
[locationButton addTarget:self action:@selector(locationButtonClicked) forControlEvents:UIControlEventTouchDown];
[naviView addSubview:locationButton];
// 分割线
UIView *grayLineView = [[UIView alloc]initWithFrame:CGRectMake(0, naviView.height - 1, naviView.width, 1)];
grayLineView.backgroundColor = [UIColor colorWithHexString:@"f3f3f3"];
[naviView addSubview:grayLineView];
// 展示地址列表的tableView
self.tableView = [[UITableView alloc]initWithFrame:CGRectMake(0, naviView.maxY, screenWidth, screenHeight - naviView.maxY)];
self.tableView.dataSource = self;
self.tableView.delegate = self;
[self.view addSubview:self.tableView];
}
#pragma mark - TextField编辑变化时调用
-(void)textFieldChanged:(UITextField *)TextField{
[self searchAroundWithKeyword:[TextField.text stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]]];
}
#pragma mark - 定位按钮点击
/** 定位按钮点击 */
- (void)locationButtonClicked{
self.searchTextField.text = @"";
[self getLocation];
}
#pragma mark - 定位
/** 定位 */
- (void)getLocation{
// 显示菊花
[CQHud show];
self.locationManager = [[AMapLocationManager alloc] init];
[self.locationManager setDelegate:(id)self];
[self.locationManager setPausesLocationUpdatesAutomatically:NO];
[self.locationManager setAllowsBackgroundLocationUpdates:NO];
//开始定位
[self.locationManager startUpdatingLocation];
}
#pragma mark - 根据定位坐标或关键字进行周边搜索
/** 根据定位坐标或关键字进行周边搜索 */
- (void)searchAroundWithKeyword:(NSString *)keyword{
self.search = [[AMapSearchAPI alloc] init];
self.search.delegate = (id)self;
if (keyword == nil) { // 未输入关键字,则为POI周边搜索
_isSearchAround = YES;
// 构造AMapPOIAroundSearchRequest对象,设置周边搜索参数
AMapPOIAroundSearchRequest *request = [[AMapPOIAroundSearchRequest alloc] init];
request.location = [AMapGeoPoint locationWithLatitude:self.location.coordinate.latitude longitude:self.location.coordinate.longitude];
// types属性表示限定搜索POI的类别,默认为:餐饮服务|商务住宅|生活服务
// POI的类型共分为20种大类别
request.types = @"汽车服务|汽车销售|汽车维修|摩托车服务|餐饮服务|购物服务|生活服务|体育休闲服务|医疗保健服务|住宿服务|风景名胜|商务住宅|政府机构及社会团体|科教文化服务|交通设施服务|金融保险服务|公司企业|道路附属设施|地名地址信息|公共设施";
request.sortrule = 0;
request.requireExtension = YES;
//发起周边搜索
[self.search AMapPOIAroundSearch: request];
}else{ // 输入了关键字,则为搜索提示请求
_isSearchAround = NO;
AMapInputTipsSearchRequest *tipsRequest = [[AMapInputTipsSearchRequest alloc] init];
// 如果选择了城市就显示城市,否则默认 成都
tipsRequest.city = @"四川省成都市";
tipsRequest.keywords = keyword;
[self.search AMapInputTipsSearch:tipsRequest];
}
}
#pragma mark - 定位相关代理方法
// 定位错误回调
- (void)amapLocationManager:(AMapLocationManager *)manager didFailWithError:(NSError *)error{
//定位错误
NSLog(@"%s, amapLocationManager = %@, error = %@", __func__, [manager class], error);
[CQHud dismiss];
// 停止定位
[self.locationManager stopUpdatingLocation];
// 弹窗提示定位错误
UIAlertView *alertView = [[UIAlertView alloc]initWithTitle:@"温馨提示" message:@"若要使用定位功能,请到【设置】->【隐私】->【定位服务】里开启【快健康】的定位服务功能" delegate:self cancelButtonTitle:@"去开启" otherButtonTitles:@"取消", nil];
[alertView show];
// 判断用户的iOS版本是否小于10.0
if ([[UIDevice currentDevice].systemVersion floatValue] < 10.0) {
alertView.tag = 100;
}else{
alertView.tag = 101;
}
}
// 位置更新回调
- (void)amapLocationManager:(AMapLocationManager *)manager didUpdateLocation:(CLLocation *)location{
// 定位结果
NSLog(@"location:{lat:%f; lon:%f; accuracy:%f}", location.coordinate.latitude, location.coordinate.longitude, location.horizontalAccuracy);
// 赋值给全局变量
self.location = location;
// 发起周边搜索
[self searchAroundWithKeyword:nil];
// 停止定位
[self.locationManager stopUpdatingLocation];
}
#pragma mark - 周边搜索相关代理
// 实现POI搜索对应的回调函数
- (void)onPOISearchDone:(AMapPOISearchBaseRequest *)request response:(AMapPOISearchResponse *)response{
NSLog(@"周边搜索回调");
// 将搜索出的POI保存到数组
self.addressArray = [NSMutableArray arrayWithArray:response.pois];
// 周边搜索完成后,刷新tableview
[self.tableView reloadData];
// 停止旋转菊花
[CQHud dismiss];
}
#pragma mark - 搜索提示回调函数
// 输入提示查询回调函数
-(void)onInputTipsSearchDone:(AMapInputTipsSearchRequest*)request response:(AMapInputTipsSearchResponse *)response
{
// 现将数组清空
[self.addressArray removeAllObjects];
// 通过AMapInputTipsSearchResponse对象处理搜索结果
// 将搜索出的POI保存到数组
for (AMapTip *tip in response.tips) {
NSLog(@"InputTipsName: %@", tip.district);
if(tip.district && [tip.district rangeOfString:@"四川省成都市"].location != NSNotFound){
[self.addressArray addObject:tip];
NSLog(@"InputTipsName: %@", tip.name);
}
}
// 周边搜索完成后,刷新tableview
[self.tableView reloadData];
// 停止旋转菊花
[CQHud dismiss];
}
#pragma mark - AlertView相关代理方法
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex{
if (alertView.tag == 100) { // iOS10之前的
if (buttonIndex == 1) {
// 取消
[self dismissViewControllerAnimated:NO completion:nil];
}else{
// 去系统设置页面
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:@"prefs:root=Privacy&path=LOCATION"]];
}
}else if (alertView.tag == 101){ // iOS10之后的
// 返回上一页
[self dismissViewControllerAnimated:NO completion:nil];
if (buttonIndex != 0) {
[CQHud showToastWithMessage:@"去设置"];
// 去系统设置页面
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:@"prefs:root=Privacy&path=LOCATION"]];
}
}
}
#pragma mark - TableView相关代理方法
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
return self.addressArray.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
NSString *cellID = [NSString stringWithFormat:@"%ld_%ld",indexPath.section,indexPath.row];
AddressListCell *cell = [tableView dequeueReusableCellWithIdentifier:cellID];
if (cell == nil) {
cell = [[AddressListCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellID];
}
AMapPOI *tip = self.addressArray[indexPath.row];
[cell showAddress:tip];
if (indexPath.row == 0 && _isSearchAround == YES) {
// 周边搜索的第一个cell要显示“推荐”
[cell showRecommend];
}else{
[cell showNormal];
}
return cell;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
if ([self.delegate respondsToSelector:@selector(chooseLocation:)]) {
[self.delegate chooseLocation:self.addressArray[indexPath.row]];
}
[self dismissViewControllerAnimated:YES completion:nil];
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{
return 50;
}
@end
代码仅供参考,思路才是王道。若代码欠妥,欢迎各位看官进行指正。
2018年1月12日更新
最近产品那边反馈有些地址明明就存在但是却没有在提示列表中展示出来,我看了下安卓那边是有的,于是我写了个demo集成最新的SDK试了下,发现OK了。项目里的高德地图SDK之前是手动集成的,有一定的历史了。。。
根据输入给出提示语demo
总结:项目中的核心SDK最好还是自动集成,有更新就及时更新,这样能避免一些潜在的问题。