###坑爹的高德
项目中地图原来用的高德sdk,其实我不知道当时为什么就用了高德,但既然用了,那么就在此基础上开发吧。可是写着写着,问题就来了: 问题链接。虽然这个问题不是什么太大的问题,但保不准以后还会遇到什么别的坑,而且以高德的效率,短期内肯定不会解决的。
那为什么还要用那个sdk呢?好像我们用到的功能苹果自带的MapKit都能实现啊,而且高德官网上的合作伙伴(携程、美团)也是用的原生的MapKit(从地图默认加载背景就可以看出来)。后来只花了一点点时间就切换到苹果的MapKit(API是一样的),目前还没发现什么bug。
###坐标转换之中国特色
因为不用高德了,所以当前位置经纬度就用CoreLocation来获取。可是当你把获取的经纬度放到MKMapView中显示,位置竟然有偏移,而且还不小。
这是苹果的bug吗?不是,是有关部门的功劳。我们只有一个地球,所以地球上每个点的经纬度是固定的,国际上有个通用标准,简称国际标准。CoreLocation取到的经纬度符合国际标准。
可是在国内,由于国家安全,有关部门制定了一套新的标准,简称国家标准。只要是在国内发行的地图(包括电子地图),都要按国家标准来,不然就是非法的。所以苹果手机中的国内地图肯定也是改良过的。
我拿着国际标准的经纬度放到改良过的符合国家标准的地图中,显然会发生偏移。
解决办法就是将国际标准的经纬度转换成国家标准的经纬度。算法是现成的(以前用高德sdk获取当前位置的经纬度本来就是国家标准的,所以没事。)
###坑爹的高德
项目中地图原来用的高德sdk,其实我不知道当时为什么就用了高德,但既然用了,那么就在此基础上开发吧。可是写着写着,问题就来了: 问题链接。虽然这个问题不是什么太大的问题,但保不准以后还会遇到什么别的坑,而且以高德的效率,短期内肯定不会解决的。
那为什么还要用那个sdk呢?好像我们用到的功能苹果自带的MapKit都能实现啊,而且高德官网上的合作伙伴(携程、美团)也是用的原生的MapKit(从地图默认加载背景就可以看出来)。后来只花了一点点时间就切换到苹果的MapKit(API是一样的),目前还没发现什么bug。
###坐标转换之中国特色
因为不用高德了,所以当前位置经纬度就用CoreLocation来获取。可是当你把获取的经纬度放到MKMapView中显示,位置竟然有偏移,而且还不小。
这是苹果的bug吗?不是,是有关部门的功劳。我们只有一个地球,所以地球上每个点的经纬度是固定的,国际上有个通用标准,简称国际标准。CoreLocation取到的经纬度符合国际标准。
可是在国内,由于国家安全,有关部门制定了一套新的标准,简称国家标准。只要是在国内发行的地图(包括电子地图),都要按国家标准来,不然就是非法的。所以苹果手机中的国内地图肯定也是改良过的。
我拿着国际标准的经纬度放到改良过的符合国家标准的地图中,显然会发生偏移。
解决办法就是将国际标准的经纬度转换成国家标准的经纬度。算法是现成的(以前用高德sdk获取当前位置的经纬度本来就是国家标准的,所以没事。)
###怎么使用MapKit
用苹果官方的framework最幸福了,因为有标准的文档可查,只要看就是了。目前我们对地图的使用非常简单,就是在地图上加地理位置标注,所以我要看的只有其中一小段。
要实现标注,必须要有以下3个对象:
- AnnotationObject(标注对象)
- AnnotationView(标注视图)
- AnnotationCalloutView(标注弹出视图,可看做是标注视图的一部分)
结合上面的图,AnnotationObject是标注的模型对象,AnnotationView就是大头针,AnnotationCallView就是弹出来的浮层。
######AnnotationObject
AnnotationObject必须实现MKAnnotation协议:
1 | @protocol MKAnnotation <NSObject> |
框架默认提供了一个MKPointAnnotation,只有coordinate、title、subtitle,如果要求不高,这个类也够用了。但更多的情况就需要我们自定义MKAnnotation。
1 | @interface HJGasStationAnnotation : NSObject<MKAnnotation> |
里面的title、address、tel都是我随便加的,只要有需求,加任何东西都可以,但官方文档中也说了,AnnotationObject不能太大,不然地图上标注一多,效率会有问题。
想一下,为什么要有AnnotationObject这个对象?我看到之前的代码没么有自定义什么AnnotationObject,所有的标注都用MKPointAnnotation,然后所有其他的属性都封装成另外一个对象(ObjectB,这个ObjectB也是从项目的其他地方拿来的)都放到AnnotationView去。
这样造成的后果就是AnnotationView中既有AnnotationObject,又有ObjectB(而且ObjectB不够纯粹,有很多冗余的属性),导致AnnotationView代码耦合性较大。而子类话AnnotationObject,就能消除这种耦合,把ObjectB中有用的属性封装到SubAnnotationObject中即可,这样对于AnnotationView而言,只需要知道AnnotationObject,管它什么ObjectB呢!
######AnnotationView
框架提供了默认的MKPinAnnotationView,就是一个大头针。因为不够美观,我们可能要换成其它图片或者自定义视图。
怎么自定义呢?只要继承一下
1 | @interface HJGasStationAnnotationView : MKAnnotationView |
上面的自定义非常简单,只是换了张图片,如果有更复杂的需求,直接在里面加子控件或者重写drewRect都可。
######AnnotationCalloutView
框架里有标准的AnnotationCalloutView,只需要设置AnnotationView的属性canShowCallout为YES,就能展示默认的CalloutView;如果需要自定义CalloutView的话就必须将canShowCallout设为NO,这样就不会弹出默认的CalloutView。
自定义的CalloutView就是一个普通的View,爱怎么写就怎么写。它展示和消失的逻辑是由AnnotationView来控制的,所以说CalloutView其实是AnnotationView的一部分。
交互的逻辑就是重写两个方法,而且必须要重写,变量名根据实际情况会变,但方法逻辑结构就不需要动。
1 | - (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event |
###展现逻辑
像MapView添加标注
1 | [self.mapView addAnnotation:self.annotation]; |
在MapView的代理方法中生成AnnotationView
1 | - (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id<MKAnnotation>)annotation |