最近项目要用到微信发送地址查看地址的功能,完成的也差不多了,基本上大部分功能都实现了,现在整理出来,也有一些想和大家一起交流的地方,希望彼此都有所收获。
先讲讲UISearchController
说实话我之前很少有用到它,基本上都是自定义的,但是我看微信的效果虽然不肯定就是UISeachController但是基本上是差不多的,所以我也就踩踩这个坑,其实了解了以后使用还是相当方便的。
问题1:点击searchbar的时候,searchbar飞到视图外面
解决办法
self.definesPresentationContext = YES;
问题2:点击searchbar的时候,searchbar上方也就是状态栏的部分是透明的,和UISearchController的颜色是一样的。
这个情况在默认情况下是不会出现的,但是我们开发的使用经常使用这样一句代码self.edgesForExtendedLayout = UIRectEdgeNone;
来使tableView顶到navigationbar而不是延展到navigationbar里面,并且往往在基类中做这一设置。
解决方法:
self.edgesForExtendedLayout = UIRectEdgeAll;
再就是去掉searchBar底部有一条黑线
_searchController.searchBar.subviews.firstObject.subviews.firstObject.layer.borderColor = [UIColor colorWithHexString:@"c6c6c6"].CGColor;
_searchController.searchBar.subviews.firstObject.subviews.firstObject.layer.borderWidth = .5;
地图功能
进入界面默认显示定位地址,下方的POI列表也是默认显示定位附近的,大头针是一直都指向地图的中心点,之后定位的变化不会再引起POI列表的变化,只有拖动地图,改变大头针指向的位置才会更新POI列表。
poi列表 也就是地图下方的列表
这里的POI列表,我是采用的反地理编码的方式,但是有局限性,也就是百度地图只返回最多十条记录,这就有点尴尬了,百度地图有提供其他查询附近POI的方式(像以坐标为原点xxx米为半径等),但是这些方式都有一个共同点 ,需要加入一个关键字,也就是说,我不能把附近所有的POI都列出来,只能根据类别搜索,像搜美食,酒店这个意思。
暂时没找到好办法,没法像微信那样获取很多记录,有知道的望能告知。
定位
当点击右下角的定位按钮会将用户的位置显示在中心点处,百度地图有四种地位模式
1) BMKUserTrackingModeNone :普通定位模式,显示我的位置,我的位置图标和地图都不会旋转
2) BMKUserTrackingModeFollow : 定位跟随模式,我的位置始终在地图中心,我的位置图标会旋转,地图不会旋转
3) BMKUserTrackingModeFollowWithHeading : 定位罗盘模式,我的位置始终在地图中心,我的位置图标和地图都会跟着旋转
4) BMKUserTrackingModeHeading:普通定位+定位罗盘模式,显示我的位置,我的位置始终在地图中心,我的位置图标会旋转,地图不会旋转。即在普通定位模式的基础上显示方向。
我这里用的是普通定位模式,我认为发送地址重要的是找地址,并不需要了解我面朝南还是朝北。。但是在查看地址的时候,是十分有必要的。
定位这里有个小细节,就是微信在移动地图的时候,他会用当前地图的中心点和用户定位的地点做比较,如果误差在一定范围,那么地图右下角表示已经在当前位置的按钮就会成选中状态。这里我直接用的百度地图的计算API,这里我设定的是100m。
BMKMapPoint centerPoint = BMKMapPointForCoordinate(centerlocation);
BMKMapPoint userPoint = BMKMapPointForCoordinate(self.locService.userLocation.location.coordinate);
CLLocationDistance distance = BMKMetersBetweenMapPoints(centerPoint,userPoint);
if (distance <=100) {
self.locationBtn.selected = YES;
}else{
self.locationBtn.selected = NO;
}
记得将.m文件修改为.mm
搜索地址
这个地方我一开始想参考微信,输入地址,在全国检索,基于这个需求我在百度地图开发文档中找相关接口,结果发现并没有全国搜索这一个接口,只有按城市搜索,结果使用后我认为这就是我要找的。它会默认在你设置的城市中搜索,当前城市没有再在全国范围内搜索,我认为这是比较合理的,否则,单纯的全国搜索还需要考虑权重的问题,如果我想搜索 “体育馆”理所应当我想的是在当前城市搜索体育馆,如果我真想搜其他地方的体育馆,可以加上城市名搜索。
城市POI搜索代码
BMKCitySearchOption *option = [[BMKCitySearchOption alloc]init];
option.pageIndex = 0;
option.pageCapacity = 30;
option.keyword = self.keyword;//搜索关键字
option.city = self.userCity.length == 0?@"青岛市":self.userCity; //userCity是用户定位地址
BOOL flag = [self.poiSearch poiSearchInCity:option];
if(flag)
{
NSLog(@"周边检索发送成功");
}
else
{
NSLog(@"周边检索发送失败");
}
关键字高亮
这里我是将关键字 拆分成每一个单独的字符进行匹配的,效果如图
这里有个坑我有必要提一下,一开始做这个功能的时候,我第一时间想到的是- (NSRange)rangeOfString:(NSString *)searchString;
这个方法,在字符串中查找某个字串的NSRange,但是这有个问题就是这个方法只返回第一个的位置,也就是如果这一行中有两个相同子符,他也永远只返回第一个的,那好吧,看看兄妹方法有没有能适用的,结果找到了这个方法- (NSRange)rangeOfString:(NSString *)searchString options:(NSStringCompareOptions)mask range:(NSRange)rangeOfReceiverToSearch;
这个是可以指定范围查找,看起来很完美。但是。。。。我在实际使用过程中,出现了随机的NSNotFound,但是我用containsString
做了判断了,并且也是确确实实存在的。
图中可见,上下两个结构基本一样,但是第二个就出现了NSNotFound,最后的解决方案是遍历字符串自己构建NSRange,并存放到数组里,再遍历NSRange数组设置高亮
NSMutableAttributedString *attributeStr = [[NSMutableAttributedString alloc] initWithString:origintext];
NSMutableArray *indexArray = @[].mutableCopy;
for(int i =0; i < [origintext length]; I++)
{
NSString *temp = [origintext substringWithRange:NSMakeRange(i,1)];
if ([keyword containsString:temp]) {
[indexArray addObject:[NSValue valueWithRange:NSMakeRange(i, 1)]];
}
}
[indexArray enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
NSRange range = [obj rangeValue];
[attributeStr addAttribute:NSForegroundColorAttributeName value:[UIColor colorWithRed:23/255.0f green:182/255.0f blue:0 alpha:1] range:range];
}];
查看地址模块
这个就比较简单了,传入需要显示的坐标,反地理编码获取POI的name,address显示出来就可以了
这里的定位模式是BMKUserTrackingModeHeading:普通定位+定位罗盘模式 主要方便导航到目的地。
这里有几个功能
- 显示路线
- 街景
-
外部地图导航
显示路线
这里显示的是驾车路线,虽然不一定是最优路线,但是起码无论你步行还是骑行都是能走通的,这个在这里也只是显示个大概,真正的导航还是跳转到各类地图应用吧。
发起请求
BMKPlanNode* start = [[BMKPlanNode alloc]init];
start.pt = self.coordinate;
BMKPlanNode* end = [[BMKPlanNode alloc]init];
end.pt = self.locationService.userLocation.location.coordinate;
BMKDrivingRoutePlanOption *option = [[BMKDrivingRoutePlanOption alloc]init];
option.from = start;
option.to = end;
//发起检索
BOOL flag = [self.routeSearcher drivingSearch:option];
if(flag) {
NSLog(@"驾车检索发送成功");
} else {
NSLog(@"驾车检索发送失败");
}
绘制路线
[self.mapView removeOverlays:self.mapView.overlays];
BMKDrivingRouteLine *plan = (BMKDrivingRouteLine *)[result.routes objectAtIndex:0];
int size = (int)[plan.steps count];
int pointCount = 0;
for (int i = 0; i< size; i++) {
BMKDrivingStep *step = [plan.steps objectAtIndex:i];
pointCount += step.pointsCount;
}
BMKMapPoint *points = new BMKMapPoint[pointCount];
int k = 0;
for (int i = 0; i< size; i++) {
BMKDrivingStep *step = [plan.steps objectAtIndex:i];
for (int j= 0; j<step.pointsCount; j++) {
points[k].x = step.points[j].x;
points[k].y = step.points[j].y;
k++;
}
}
BMKPolyline *polyLine = [BMKPolyline polylineWithPoints:points count:pointCount];
[self.mapView addOverlay:polyLine];
当然你需要先在mapVIew的代理方法``(BMKOverlayView *)mapView:(BMKMapView *)mapView viewForOverlay:(id )overlay` 自定义样式
街景
这个没啥要说的,很简单,需要单独下载SDK
调用地图应用导航
这里我用的是 百度地图 ,高的地图 ,苹果地图, 默认调用的都是驾车路线,当然在地图里面可以更改,使用url的方法调用,[[UIApplication sharedApplication] openURL:url]
,调用之前最好用[[UIApplication sharedApplication] canOpenURL:url]
判断一下,别忘了加Schemes 。
百度地图:baidumap
高德地图:iosamap