前段時(shí)間,在項(xiàng)目上做了一些地圖相關(guān)的功能,解決了三個(gè)特殊需求,在此總結(jié)一下,以備日后使用:
1. MKOverlayView的拖動實(shí)現(xiàn);
2. 大頭針pin 的拖動實(shí)現(xiàn);
3. 隨地圖的縮放而縮放的焦點(diǎn)框功能實(shí)現(xiàn);

1. MKOverlayView的拖動實(shí)現(xiàn)
首先,直接給MKOverlayView視圖本身添加一個(gè)拖動手勢是不可行的,因?yàn)镸KOverlayView不是通過簡單的addSubview:添加到地圖圖層上的。
MKOverlayView視圖的現(xiàn)實(shí)過程:
1.1 使用MKPolygon的addOverlay:方法來添加OverlayView數(shù)據(jù)
源overlay,其中包括了圖層的經(jīng)緯度坐標(biāo)數(shù)據(jù)。
CLLocationCoordinate2D leftUp = CLLocationCoordinate2DMake(center.latitude - gap, center.longitude - gap);
CLLocationCoordinate2D rightUp = CLLocationCoordinate2DMake(center.latitude + gap, center.longitude - gap);
CLLocationCoordinate2D leftDown = CLLocationCoordinate2DMake(center.latitude - gap, center.longitude + gap);
CLLocationCoordinate2D rightDown = CLLocationCoordinate2DMake(center.latitude + gap, center.longitude + gap);
CLLocationCoordinate2D coordinates[4] = {leftUp, leftDown, rightUp, rightDown};
MKPolygon *polygon = [MKPolygon polygonWithCoordinates:coordinates count:4];
[m_MapView addOverlay:polygon];
1.2 然后再實(shí)現(xiàn)MKMapView的代理方法:
-(MKOverlayView *)mapView:(MKMapView *)mapView viewForOverlay:(id<MKOverlay>)overlay
{
MKOverlayView *pview = [[MKOverlayView alloc] initWithOverlay:overlay];
pview.backgroundColor = [[UIColor redColor] colorWithAlphaComponent:0.4f];
return pview;
}```
這樣就會給地圖的圖層中嵌入一個(gè)指定位置和大小的視圖,效果上是與地圖合為一體的,可以一起移動和縮放,
也就是說通過`addOverlay: `來添加視圖的數(shù)據(jù)源,然后在
-(MKOverlayView *)mapView:(MKMapView *)mapView viewForOverlay:(id<MKOverlay>)overlay```
方法中去繪制視圖本身(圖層顏色,也可以繪制文字在上面)。
MKOverlayView的類型參數(shù)有以下幾種:
| 數(shù)據(jù)類 | 對應(yīng)view | 說明 |
|---|---|---|
| MKPolygon | MKOverlayView | 矩形 |
| MKCircle | MKCircleView | 圓形 |
| MKPolyline | MKPolylineView | 線條 |
那如何拖動MKOverlayView呢?
- (CGPoint)convertCoordinate:(CLLocationCoordinate2D)coordinate toPointToView:(nullable UIView *)view
使用上述方法將MKOverlayView的經(jīng)緯度坐標(biāo)轉(zhuǎn)化為相對于MKMapView的點(diǎn)坐標(biāo),然后在同樣的位置加上一個(gè)同樣大小的透明子視圖,給自視圖添加拖動手勢UIPanGestureRecognizer,每次拖動的時(shí)候先使用removeOverlay:刪除之前的MKOverlayView,然后再重新以拖動點(diǎn)為中心,加載新的MKOverlayView即可,效果很良好,不會出現(xiàn)閃爍的現(xiàn)象。
由于地圖本身可以放大,縮小,移動,所以在這些操作之后也需要調(diào)整這個(gè)子視圖的大小:
//當(dāng)拖拽,放大,縮小,雙擊手勢開始時(shí)調(diào)用
- (void)mapView:(MKMapView *)mapView regionWillChangeAnimated:(BOOL)animated
//當(dāng)拖拽,放大,縮小,雙擊手勢結(jié)束時(shí)調(diào)用
- (void)mapView:(MKMapView *)mapView regionDidChangeAnimated:(BOOL)animated
2. 大頭針pin 的拖動實(shí)現(xiàn);
拖動大頭針的方法系統(tǒng)API有提供:
- 新建一個(gè)繼承
<MKAnnotation>協(xié)議的大頭針數(shù)據(jù)類,并添加坐標(biāo)coordinate,title,subtitle屬性; - 使用
addAnnotation:添加大頭針數(shù)據(jù)源,在
-(MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id<MKAnnotation>)annotation
方法中添加大頭針視圖MKPinAnnotationView,并且設(shè)置拖動屬性為YES:pin.draggable = YES;
- 下面方法是拖拽回調(diào)方法,在
MKAnnotationViewDragStateEnding狀態(tài)時(shí),調(diào)整大頭針位置即可
- (void)mapView:(MKMapView *)mapView annotationView:(MKAnnotationView *)view didChangeDragState:(MKAnnotationViewDragState)newState
fromOldState:(MKAnnotationViewDragState)oldState
最后的效果就是長按大頭針,會有一個(gè)大頭針升起的動畫,然后拖動釋放又會有大頭針落下的動畫,效果很炫。
這是使用系統(tǒng)方法實(shí)現(xiàn)大頭針的拖拽,其實(shí)也可以使用1中拖拽MKOverlayView的方法,在大頭針的位置放一個(gè)透明的子視圖,然后拖動這個(gè)子視圖的同時(shí),移除舊的大頭針,再在相應(yīng)位置添加新的大頭針即可。
只不過這就沒有大頭針升起/落下的動畫了,當(dāng)然,也不用長按大頭針才能拖動,你可以自定義。
3. 隨地圖的縮放而縮放的焦點(diǎn)框功能實(shí)現(xiàn)
由于系統(tǒng)沒有提供MKMapView的縮放比例,所以得自己計(jì)算這個(gè)比例:
- 地圖縮放的時(shí)候其實(shí)是
MKCoordinateSpan在變化
typedef struct {
CLLocationDegrees latitudeDelta;//緯度范圍
CLLocationDegrees longitudeDelta;//緯度范圍
} MKCoordinateSpan;
比例計(jì)算公式
float ratio = oldSpanArea/newSpan
說明:老的span面積/新的span面積
span面積=latitudeDelta * longitudeDelta最后計(jì)算焦點(diǎn)框的邊長
float width = sqrtf(ratio * (fenceView.frame.size.width * fenceView.frame.size.height));
然后改變焦點(diǎn)框的frame就可以了。
當(dāng)然,最后的計(jì)算賦值都在縮放結(jié)束后的函數(shù)中進(jìn)行:
//當(dāng)拖拽,放大,縮小,雙擊手勢結(jié)束時(shí)調(diào)用
- (void)mapView:(MKMapView *)mapView regionDidChangeAnimated:(BOOL)animated
補(bǔ)充:
前幾天對地圖這邊做優(yōu)化,發(fā)現(xiàn)方案三還可以有另一種做法:
可以通過給mapview對象添加:
UIPanGestureRecognizer,UITapGestureRecognizer,UIPinchGestureRecognizer
三種手勢來監(jiān)聽對地圖的拖動,點(diǎn)擊,縮放的事件,既然監(jiān)聽到了這些事件,那么后面的事情就好辦了。
當(dāng)然,不要忘了在代碼中實(shí)現(xiàn)UIGestureRecognizerDelegate的這個(gè)方法:
// called when the recognition of one of gestureRecognizer or otherGestureRecognizer would be blocked by the other
// return YES to allow both to recognize simultaneously. the default implementation returns NO (by default no two gestures can be recognized simultaneously)
//
// note: returning YES is guaranteed to allow simultaneous recognition. returning NO is not guaranteed to prevent simultaneous recognition, as the other gesture's delegate may return YES
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
{
return YES;
}
這個(gè)方法的意思是:
當(dāng)一個(gè)手勢被另外一個(gè)手勢堵塞后,就會調(diào)用這個(gè)回調(diào),默認(rèn)是返回NO的,
如果返回YES的話,才會允許多個(gè)手勢共存。
以上補(bǔ)充已在demo中添加。
Demo下載鏈接:CamouflageLocationDemo