iOS开发——高级技术&调用地图功能的实现
调用地图功能的实现
一:苹果自带地图
学习如逆水行舟,不进则退。古人告诉我们要不断的反思和总结,日思则日精,月思则月精,年思则年精。只有不断的尝试和总结,才能让我们的工作和生活更加 轻松愉快和美好。连着做了两个大的商城外包项目,智慧城市,搜牧通,花费了近四个月的时间,终于在反复修改后完美收工。期间的困难自不必说,以后多多总结 和沟通吧。百度地图的使用之前已经发表了一篇文章,说的很详细了,这里不再涉及,言归正传,我们说一下如何调用苹果自带的地图
第一步:导入地图文件 #import <MapKit/MapKit.h>
第二步:获取当前位置和目的地的经纬度,然后打开地图即可
//获取当前位置
1 MKMapItem *mylocation = [MKMapItem mapItemForCurrentLocation];
//当前经维度
1 float currentLatitude=mylocation.placemark.location.coordinate.latitude;
2
3 float currentLongitude=mylocation.placemark.location.coordinate.longitude;
4
5
6
7 CLLocationCoordinate2D coords1 = CLLocationCoordinate2DMake(currentLatitude,currentLongitude);
//目的地位置
1 coordinate.latitude=[[dataSource objectForKey:@"lat"] floatValue];
2
3 coordinate.longitude=[[dataSource objectForKey:@"lng"] floatValue];
4
5
6
7
8
9 CLLocationCoordinate2D coords2 = coordinate;
10
11
12
13 // ios6以下,调用google map
14
15 if (SYSTEM_VERSION_LESS_THAN(@"6.0"))
16
17 {
18
19 NSString *urlString = [[NSString alloc] initWithFormat:@"http://maps.google.com/maps?saddr=%f,%f&daddr=%f,%f&dirfl=d", coords1.latitude,coords1.longitude,coords2.latitude,coords2.longitude];
20
21 NSURL *aURL = [NSURL URLWithString:urlString];
22
23 //打开网页google地图
24
25 [[UIApplication sharedApplication] openURL:aURL];
26
27 }
28
29 else
30
31 // 直接调用ios自己带的apple map
32
33 {
34
35 //当前的位置
36
37 MKMapItem *currentLocation = [MKMapItem mapItemForCurrentLocation];
38
39 //起点
40
41 //MKMapItem *currentLocation = [[MKMapItem alloc] initWithPlacemark:[[MKPlacemark alloc] initWithCoordinate:coords1 addressDictionary:nil]];
42
43 //目的地的位置
44
45 MKMapItem *toLocation = [[MKMapItem alloc] initWithPlacemark:[[MKPlacemark alloc] initWithCoordinate:coords2 addressDictionary:nil]];
46
47
48
49 toLocation.name = @"目的地";
50
51 NSString *myname=[dataSource objectForKey:@"name"];
52
53 if (![XtomFunction xfunc_check_strEmpty:myname])
54
55 {
56
57 toLocation.name =myname;
58
59 }
60
61
62
63 NSArray *items = [NSArray arrayWithObjects:currentLocation, toLocation, nil];
64
65 NSDictionary *options = @{ MKLaunchOptionsDirectionsModeKey:MKLaunchOptionsDirectionsModeDriving, MKLaunchOptionsMapTypeKey: [NSNumber numberWithInteger:MKMapTypeStandard], MKLaunchOptionsShowsTrafficKey:@YES };
66
67 //打开苹果自身地图应用,并呈现特定的item
68
69 [MKMapItem openMapsWithItems:items launchOptions:options];
70
71 }
72
73
通过这两步就可以轻松的开启苹果自带地图导航,感觉真是挺不错的,唯一的缺点是开启地图获取路线信息耗费的手机流量比较大,最好在wifi条件下调用。如果不是必须,尽量还是用高德或者百度自带的地图就好。
二:百度地图
一:首先我们有一点与用googlemap开发的不同,需要创建BMKMapManager管理应用程序的map,如果没有这个类,地图则不能够显示。
下面红色的字体是自己在百度官方申请的地图api——key;
1 BMKMapManager *_mapManager = [[BMKMapManager alloc] init];
2
3 BOOL ret = [_mapManager start:@"C3252C69EDB6D21A10B3FC9657FD1DDC7E0000**"generalDelegate:self];
4
5 if (!ret) {
6
7 NSLog(@"manager start failed!");
8
9 }
二:在view中添加BMKMapView,同时设置BMKMapViewDelegate,添加annotation(记录兴趣点,BMKAnnotation),同时每个兴趣点可以设置其title(设置annotation的标题),以及subtitle(子标题)。
1 @interface MapBaiDu : UIViewController <BMKMapViewDelegate> { }
2
3 @property (nonatomic, strong) BMKMapView *_mapView;
4
5 @end
6
7
8 - (void)viewDidLoad {
9
10
11
12 _mapView = [[BMKMapView alloc] initWithFrame:CGRectMake(0, 39, 320, 377)]; //创建MKMapView
13
14 [self.view addSubview:_mapView];
15
16 [_mapView release];
17
18
19
20 _mapView.delegate = self; //设置代理
21
22 _mapView.showsUserLocation = YES; //设置为可以显示用户位置
23
24 CLLocationCoordinate2D coordinate; //设定经纬度
25
26 coordinate.latitude = 40.027283; //纬度
27
28 coordinate.longitude = 116.313217; //经度
29
30
31
32 BMKCoordinateRegion viewRegion = BMKCoordinateRegionMake(coordinate, BMKCoordinateSpanMake(1.0,1.0));
33
34 BMKCoordinateRegion adjustedRegion = [_mapView regionThatFits:viewRegion];
35
36 [_mapView setRegion:adjustedRegion animated:YES];
37
38 }
上 面最后一行 :设置当前地图的经纬度范围,设定的该范围可能会被调整为适合地图窗口显示的范围。region是BMKMapView的一个属性,类型BMKCoordinateRegion ,这行的意思是创建一个以coordinate为中心,上下左右个0.5个经(纬)度。但是这时我们需要注意一个问题就是,创建的区域是一个正方形,并不 符合我们所需要的BMKMapView比例;之后用方法regionThatFits调整显示范围。
1 ///表示一个经纬度区域
2
3 typedef struct {
4
5 CLLocationCoordinate2D center; ///< 中心点经纬度坐标
6
7 BMKCoordinateSpan span; ///< 经纬度范围
8
9 } BMKCoordinateRegion;
10
11
12
13
14
15 ///表示一个经纬度范围
16
17 typedef struct {
18
19 CLLocationDegrees latitudeDelta; ///< 纬度范围
20
21 CLLocationDegrees longitudeDelta; ///< 经度范围
22
23 } BMKCoordinateSpan;
三:下面我们简单说一下delegate
1:地图区域改变时候调用函数:
1 - (void)mapView:(BMKMapView *)mapView regionWillChangeAnimated:(BOOL)animated;
- (void)mapView:(BMKMapView *)mapView regionDidChangeAnimated:(BOOL)animated;
2:annotation
*根据anntation生成对应的View
- (BMKAnnotationView *)mapView:(BMKMapView *)mapView viewForAnnotation:(id<BMKAnnotation>)annotation;
*当mapView新添加annotation views时,调用此接口
- (void)mapView:(BMKMapView *)mapView didAddAnnotationViews:(NSArray *)views;
*当选中一个annotation views时,调用此接口
- (void)mapView:(BMKMapView *)mapView didSelectAnnotationView:(BMKAnnotationView *)view;
*当取消选中一个annotation views时,调用此接口
- (void)mapView:(BMKMapView *)mapView didDeselectAnnotationView:(BMKAnnotationView *)view;
而annotation分为两部分:BMKAnotation该类为标注点的protocol,提供了标注类的基本信息函数,title和subtitle分别是标题和子标题;同时可以设置标注的左边,在拖曳时候会被调用setCoordinate;
BMKAnnotationView为标注点显示视图类,该类继承UIView,可以设置此view显示的图像,可以设置centerOffset(中心的位置,正的偏移使view超右下方 移动,负的朝右上方移动,单位为像素),还可以设置calloutOffset改变淡出的气泡位置(正的偏移使view超右下方移动,负的朝左上方移动, 单位是像素)。还可以设置其触摸事件,默认情况下为YES,可以选中,也可以是enabled = NO。其他的属性还 有:selected,canShowCallout,leftCalloutAccessoryView,rightCalloutAccessoryView。 等等
四:当地图view定位时调用函数:
*当取消选中一个annotation views时,调用此接口
- (void)mapView:(BMKMapView *)mapView didDeselectAnnotationView:(BMKAnnotationView *)view;
*在地图View将要启动定位时,会调用此函数
- (void)mapViewWillStartLocatingUser:(BMKMapView *)mapView;
*在地图View停止定位后,会调用此函数
- (void)mapViewDidStopLocatingUser:(BMKMapView *)mapView;
*定位失败后,会调用此函数
- (void)mapView:(BMKMapView *)mapView didFailToLocateUserWithError:(NSError *)error;
*用户位置更新后,会调用此函数
- (void)mapView:(BMKMapView *)mapView didUpdateUserLocation:(BMKUserLocation *)userLocation;
五:当有overlay(阴影标示某一个区域)生成或者新添加的时候调用此接口
*根据overlay生成对应的View
- (BMKOverlayView *)mapView:(BMKMapView *)mapView viewForOverlay:(id <BMKOverlay>)overlay;
*当mapView新添加overlay views时,调用此接口
- (void)mapView:(BMKMapView *)mapView didAddOverlayViews:(NSArray *)overlayViews;
六:当点击annotation view弹出的泡泡时,调用此接口
*当点击annotation view弹出的泡泡时,调用此接口
- (void)mapView:(BMKMapView *)mapView annotationViewForBubble:(BMKAnnotationView *)view;
九:annotation view有许多不同的状态,在不同状态的时候我们都可以设置不同的操作,拖动annotation view时view的状态变化
1 - (void)mapView:(BMKMapView *)mapView annotationView:(BMKAnnotationView *)view didChangeDragState:(BMKAnnotationViewDragState)newState
2
3 fromOldState:(BMKAnnotationViewDragState)oldState;
4
5
6
7
8
9 enum {
10
11 BMKAnnotationViewDragStateNone = 0, ///< 静止状态.
12
13 BMKAnnotationViewDragStateStarting, ///< 开始拖动
14
15 BMKAnnotationViewDragStateDragging, ///< 拖动中
16
17 BMKAnnotationViewDragStateCanceling, ///< 取消拖动
18
19 BMKAnnotationViewDragStateEnding ///< 拖动结束
20
21 };
22
23
24
25 typedef NSUInteger BMKAnnotationViewDragState;
三:高德地图
之前工作在一家智能设备的公司,做过一个亲友定位监控系统,类似现在比较流行的360儿童手环。所以这里简单介绍定位与地图。
1 定位服务
iOS设备提供三种不同定位途径,蜂窝式移动电话基站定位;WiFi定 位,通过查询一个WiFi路由器的地理位置信息,比较省电;GPS卫星定位,通过3~4颗卫星定位,最为准确,但是耗电量大。iOS系统如果能够接收 GPS信息,那么设备优先采用GPS,其次是WiFi,最后是基站,开发人员不能选择哪种定位方式。
定位服务使用CoreLocation框架,主要使用CLLocationMananger、 CLLocationManangerDelegate和CLLocation三个类,CLLocationMananger是定位服务管理类,获取设备 的位置信息,CLLocationManangerDelegate是代理协议,CLLocation封装了位置信息。
这里要注意,CLLocationManangerDelegate 的locationManager:didUpdateToLocation:fromLocation:方法得到的坐标是火星坐标,这个原因你懂得,所 以需要转换成真实的地理坐标。我使用的是一个第三方的CSqlite类,有一个转换坐标的数据库,你调用就可以转换为正确坐标了。
得到经纬度后,要进行地理位置信息反编码,使用CLGeocoder类实现,将地理坐标转换为地理文字描述信息,这些文字描述信息被封装在CLPlacemark类中。
当然给定地理信息的文字描述,也可以进行地理信息编码查询,转换为地理坐标,也是采用CLGeocoder类。
判断一个坐标点是否在一个无规则的多边形内
1 // 在范围内返回1,不在返回0
2 -(int)mutableBoundConrtolAction:(NSMutableArray *)arrSome:(CLLocationCoordinate2D )myCoordinate4{
3 int n=arrSome.count;
4 float vertx[n];
5 float verty[n];
6 for (int i=0; i<arrSome.count; i++) {
7 //MyPoint类存储的是经度和纬度
8 vertx[i]=((MyPoint *)(arrSome[i])).x;
9 verty[i]=((MyPoint *)(arrSome[i])).y;
10 }
11 if (arrSome.count==0) {
12
13 return 1;
14 }
15 BOOL i=pnpoly(arrSome.count, vertx, verty, myCoordinate4.latitude, myCoordinate4.longitude);
16
17
18 if (i) {
19 return 1;
20 }else{
21 return 0;
22 }
23
24
25 return 1;
26 }
27 //多边形由边界的坐标点所构成的数组组成,参数格式 该数组的count, 多边形边界点x坐标 的组成的数组,多边形边界点y坐标 的组成的数组,需要判断的点的x坐标,需要判断的点的y坐标
28 BOOL pnpoly (int nvert, float *vertx, float *verty, float testx, float testy) {
29 int i, j;
30 BOOL c=NO;
31 for (i = 0, j = nvert-1; i < nvert; j = i++) {
32
33 if ( ( (verty[i]>testy) != (verty[j]>testy) ) &&
34 (testx < (vertx[j]-vertx[i]) * (testy-verty[i]) / (verty[j]-verty[i]) + vertx[i]) )
35 c = !c;
36 }
37 return c;
38 }
2 系统地图
地图我目前用过系统、百度以及高德,开发人员使用都是差不多的,下面的代码涉及的类都是高德地图api提供的类。
我之前做项目,使用高德地图,做到后期,项目会出现闪退,后来查出是地图区域内存的问题,然后重新布局了地图区域,使得每一个地图区域能够及时销毁,虽然闪退周期明显延长,但是还是存在,这里不知道是何原因,说来惭愧。
设置地图区域
1 -(void)SetMapRegion:(CLLocationCoordinate2D)myCoordinate
2 {
3 MACoordinateRegion theRegion = { {0.0, 0.0 }, { 0.0, 0.0 } };
4 theRegion.center=myCoordinate;
5 [self.m_map setScrollEnabled:YES];
6 theRegion.span.longitudeDelta = 0.01f;
7 theRegion.span.latitudeDelta = 0.01f;
8 [self.m_map setRegion:theRegion animated:YES];
9 }
平移地图,上下左右
1 -(void)panMap:(NSString *)direction{
2 CLLocationCoordinate2D changeCoordinate=self.m_map.centerCoordinate;
3 CGPoint changePoint=[self.m_map convertCoordinate:changeCoordinate toPointToView:self.m_map];
4
5 if ([direction isEqualToString:@"up"]) {
6 changePoint.y=changePoint.y+50;
7
8 }else if ([direction isEqualToString:@"down"]) {
9 changePoint.y=changePoint.y-50;
10 }else if ([direction isEqualToString:@"left"]) {
11 changePoint.x=changePoint.x-50;
12 }else if ([direction isEqualToString:@"right"]) {
13 changePoint.x=changePoint.x+50;
14 }
15 changeCoordinate=[self.m_map convertPoint:changePoint toCoordinateFromView:self.m_map];
16 [self.m_map setCenterCoordinate:changeCoordinate animated:YES];
17 }
判断某一个坐标点是否在当前地图区域内
1 -(void)isAtCurrentRegion:(CLLocationCoordinate2D)coordiante{
2
3 CGPoint point=[self.m_map convertCoordinate:coordiante toPointToView:self.view];
4 if ((point.x<0)||(point.y<0)||(point.x>WScreen)||(point.y>HScreen)) {
5 // 如果不在 设置该点为地图中心点
6 [self SetMapRegion:coordiante];
7 }
8
9
10 }
在地图上添加标注
系统地图使用MapKit框架,核心是MKMapView类,显示地图只要添加MKMapView实例就可以了。如果要实现在地图上添加标注点, 第以是触发添加动作,第二实现MKMapViewDelegate的mapView:viewForAnnotation:完成添加标注。
高德地图实现的原理也是一样的,高德地图使用的是MAMapKit框架。 对于annotation,一般会自定义一个继承NSobject并且实现了maannotation协议的类,然后使用mapview的addAnnotation:方法就可以。MKReverseGeocoder类可以实现coordinate的反编码,这里需要实现它的代理,把得到的 地理文字描述信息赋给annotation。这里需要实现代理的mapView:viewForAnnotation:方法,一个标注其实就是一个MAAnnotationView,标注有点类似tableviewcell,这里也有重用机制。实现代理的mapView:annotationView:calloutAccessoryControlTapped:方法可以响应leftCalloutAccessoryView或者rightCalloutAccessoryView的点击事件,不过这个accessory view必须继承自UIControl。
在地图上绘制线条和多边形
MAPolyline类定义一个由多个点相连的多段线,点与点之间尾部想 连但第一点与最后一个点不相连, 通常MAPolyline是MAPolylineView的model,它提供了两个方法polylineWithPoints:count:、polylineWithCoordinates:count:用来添加线条,然后再通过map view的addOverlay:方法把Polyline实例添加进去,最后实现mapviewdelegate的mapView:viewForOverlay:方法就可以了。注意如果一开始添加的不是coordinate,而是point,可以通过map view的convertPoint:toCoordinateFromView:方法进行转换。
MAPolygon类定义的就是一个不规则的由多个点组成的闭合多边形, 点与点之间按顺序尾部相连, 第一个点与最后一个点相连, 通常MAPolygon是MAPolygonView的model,首先需要添加坐标点的数组,可以使用polygonWithCoordinates:count:方法或者polygonWithPoints:count:方法,然后把这个polygon通过addOverlay:方法添加到map view上就可以了。然后可以在mapviewdelegate里面的mapView:viewForOverlay:方法里面给MAPolygonView的属性赋值,这样一个完整的多边形就出来了。
不管是高德地图还是百度地图等第三方,都会有一个mapsearchkit,这是一个用于查询的框架,有兴趣的朋友可以多加研究。
注:作为一个iOS开发者,其实如果可以的话你可以学会使用者三种方式,但是一般我们没有比较,除非你确实闲的蛋疼,
以后在公司中,我们一半都只会使用一种,所以作为一个合格的程序员,我们应该非常熟悉的使用其中的一种,这里比较推荐百度和苹果自带的,当你狠熟悉的使用这一种之后,另外两种学起来就很简单了,
当然,对于两外两种我们也应该有个大志的了解就可以,具体使用哪个看公司。
如果想要更深入的学习与研究请查看相应的官方实例代码和API