如何在mapbox的mapView上检测是否点击了singleTap?

时间:2016-06-28 07:15:11

标签: ios objective-c mapbox mapbox-gl

iOS上需要mapBox地图。(我不是在谈论MKMapView) 我们如何检测是否在mapView上点击了singleTap或注释?我需要只在地图的空白区域(没有引脚)处理singleTap,并且当我点击引脚时调用didSelectAnnotation。

但我在android上发现我们有这样的方法

mapboxMap.setOnMapClickListener(new MapboxMap.OnMapClickListener() {
            public void onMapClick(@NonNull LatLng point) {
                Toast.makeText(getActivity(),"on Tap "+point.getLatitude(),Toast.LENGTH_LONG).show();
            }
        });

以及

mapboxMap.setInfoWindowAdapter(new MapboxMap.InfoWindowAdapter() { ... })将显示注释。

难道我们在iOS中没有相同的概念吗?

iOS中的实际问题是,当我在 Mapbox的mapView

上添加singleTapGesture
UITapGestureRecognizer *singleTapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleSingleTap:)];
[self.mapView addGestureRecognizer:singleTapGesture];

mapbox的mapView 的委托方法不会调用。

- (nullable UIView <MGLCalloutView> *)mapView:(MGLMapView *)mapView calloutViewForAnnotation:(id <MGLAnnotation>)annotation;

要确保委托方法必须调用,那么我不必使用singleTapGesture

这里的情况要么是这个,要么就是这样,但根据我的需要,我需要两者。

期待任何解决方案。 谢谢,

8 个答案:

答案 0 :(得分:2)

我在这里做了另一个解决方法

我克隆了 release-ios-v3.3.0 并使用Building the SDK创建了包,并添加了一个委托方法 MGLMapvViewDelegate.h 根据我的需要,这样的东西。 -(void)mapView:(MGLMapView *)mapView tapOnNonAnnotationAreaWithPoints:(CGPoint)points

在MGLMapView.mm中我更新了代码,

else{
if(self.selectedAnnotation)
      [self deselectAnnotation:self.selectedAnnotation animated:YES];
else if([self.delegate respondsToSelector:@selector(mapView:tapOnNonAnnotationAreaWithPoints:)])
      [self.delegate mapView:self tapOnNonAnnotationAreaWithPoints:tapPoint];
}

-(void)handleSingleTapGesture:(UITapGestureRecognizer *)singleTap 方法中。

它为我工作,因为我能够检测到非注释区域的单击。之后我将传递的points转换为地理坐标,以便我使用新的坐标坐标。

注意

  1. 新创建的图书馆大小约为48.7 MB,其中下载的图书馆为38.3 MB
  2. 样式无法正常工作,您可以在附加的图片中看到。enter image description here enter image description here(尝试使用不同的样式网址,但没有一个与之前相同)
  3. 我觉得应用程序性能不足,缩放和平移不像Mapbox人员给动态库那样顺畅。
  4. 我还在探索。 lemme知道你是否想让我去检查/探索。

    如果有帮助,请接受此答案。

    谢谢,

    快乐编码。

答案 1 :(得分:1)

尝试swift 3

//determined is Point inner to realm polygons
func determinedInPoligon(point:CLLocationCoordinate2D, poligon:[CLLocationCoordinate2D]) -> Bool {

    var result:Bool = false
    var j = poligon.count - 1

    for i in 0 ..< poligon.count {

        if  (poligon[i].longitude < point.longitude && poligon[j].longitude >= point.longitude || poligon[j].longitude < point.longitude && poligon[i].longitude >= point.longitude) &&
            (poligon[i].latitude + (point.longitude - poligon[i].longitude) / (poligon[j].longitude - poligon[i].longitude) * (poligon[j].latitude - poligon[i].latitude) < point.latitude) {
            result = !result;
        }
        j = i;

    }
    return result
}

func squareFrom(location: CGPoint, radius: Double) -> CGRect {//return rect with CGPoint center and radius
    let length = radius
    return CGRect(x: Double(location.x - CGFloat(length / 2)), y: 
   Double(location.y - CGFloat(length / 2)), width: length, height: length)
} 

功能句柄

func handleTap(_ gesture: UITapGestureRecognizer) {

    // Get the CGPoint where the user tapped.
    let spot = gesture.location(in: mapView)

    let my_features = mapView.visibleFeatures(at: spot)
    let strZoomValue = mapView.zoomLevel > 15 ? "6" : "4"
    //the feature structur object value not equal annotation object
    for value in my_features.enumerated() {// feature items
        if value.element is MGLPointAnnotation  {

            for annot in (mapView.annotations?.enumerated())! { // annotation items
                if annot.element is MGLPointAnnotation {

                    //rounded lat and lng value
                    var arr_cllocation = [CLLocationCoordinate2D]()
                    for cllocation in [(annot.element as! MGLPointAnnotation).coordinate, (value.element as! MGLPointAnnotation).coordinate] {

                        let strLat = String(format: "%."+strZoomValue+"f", cllocation.latitude)
                        let strLon = String(format: "%."+strZoomValue+"f", cllocation.longitude)
                        arr_cllocation.append(
                            CLLocationCoordinate2D(latitude: CLLocationDegrees(strLat)!, longitude: CLLocationDegrees(strLon)!) )
                    }

                    if arr_cllocation.count == 2 &&
                        memcmp(&arr_cllocation[0], &arr_cllocation[1], MemoryLayout<CLLocationCoordinate2D>.size) == 0 {// 0 if equal object
                        // to do custom popup view
                        let instViewPopupLineClass = UINib(nibName: "ViewPopupBase", bundle: nil).instantiate(withOwner: self, options: nil).first as! UIView

                        for objectInst in instViewPopupLineClass.subviews.enumerated() {

                            if objectInst.element is UILabel {
                                let asdasdas = (annot.element as! MGLPointAnnotation).subtitle
                                (objectInst.element as! UILabel).text = asdasdas
                                MyCustomPopup(customView: instViewPopupLineClass, positionXY: spot)
                                break
                            }
                        }
                    }
                }
            } //for end

        }
    }
}

或不准确的方法,但是工人;)

func handleTap(_ gesture: UITapGestureRecognizer) {// Get the CGPoint where the user tapped.

    let spot = gesture.location(in: mapView)
    let cllcordinatTap = mapView.convert(spot, toCoordinateFrom: mapView)
                 //for determined zoom scala
            let  deltScalaMap = abs(self.mapView.maximumZoomLevel - self.mapView.zoomLevel) 
           //The large is zoom maps, then smal size is tap location, and vice versa.
            let checkScalaMap = deltScalaMap == 0 ? 1 : deltScalaMap
            let _rect = squareFrom(location: CGPoint(x: cllcordinatTap.latitude, y: cllcordinatTap.longitude), radius: 0.00005 * checkScalaMap)
  for annot in (mapView.annotations?.enumerated())! {

       if annot.element is MGLPointAnnotation {
          let _cordinatCurrentAnnotation = (annot.element as! MGLPointAnnotation).coordinate

          if  determinedInPoligon(point: _cordinatCurrentAnnotation, poligon:
              [CLLocationCoordinate2D(latitude: CLLocationDegrees(_rect.minX), longitude: CLLocationDegrees(_rect.minY)),
               CLLocationCoordinate2D(latitude: CLLocationDegrees(_rect.maxX), longitude: CLLocationDegrees(_rect.minY)),
               CLLocationCoordinate2D(latitude: CLLocationDegrees(_rect.maxX), longitude: CLLocationDegrees(_rect.maxY)),
               CLLocationCoordinate2D(latitude: CLLocationDegrees(_rect.minX), longitude: CLLocationDegrees(_rect.maxY))
          ]) {

              // to do, if tap MGLPointAnnotation, annot.element

          }
      }
  }
}

答案 2 :(得分:1)

子类MGLMapView并委托其“INDEX

touchesEnded

当调用protocol MapViewTapDelegate: class { func mapViewTapped(withTouchLocation touchLocation: CLLocationCoordinate2D) } class MapView: MGLMapView { weak var tapDelegate: MapViewTapDelegate? override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) { super.touchesEnded(touches, with: event) guard let touch = touches.first else { return } let touchLocation = convert(touch.location(in: self), toCoordinateFrom: nil) tapDelegate?.mapViewTapped(withTouchLocation: touchLocation) } } 时未触发touchesEnded,反之亦然,这就是您所需要的。

didSelect annotation

答案 3 :(得分:0)

我认为这个方法 -(void)mapView:(MGLMapView *)mapView didSelectAnnotation:(id<MGLAnnotation>)annotation将解决您选择注释的问题。

答案 4 :(得分:0)

我为您的问题尝试了示例项目,效果很好。

·H

#import <UIKit/UIKit.h>
#import <MapKit/MapKit.h>
@interface ViewController : UIViewController<MKMapViewDelegate>
@property (strong, nonatomic) IBOutlet MKMapView *mapTapAnnotation;
@end

的.m

#import "ViewController.h"

@interface ViewController ()

@end

@implementation ViewController


@synthesize mapTapAnnotation;

- (void)viewDidLoad {
 [super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
  [self setMapViewWithAnnnoationPin];
}

- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}

-(void)setMapViewWithAnnnoationPin
{

  mapTapAnnotation.showsUserLocation = YES;
  mapTapAnnotation.mapType = MKMapTypeHybrid;

  CLLocationCoordinate2D coord = CLLocationCoordinate2DMake(12.900988, 80.227930);

  MKCoordinateSpan span = MKCoordinateSpanMake(0.005,  0.005);
  MKCoordinateRegion region = {coord, span};

  MKPointAnnotation *annotation = [[MKPointAnnotation alloc] init];
  [annotation setCoordinate:coord];
  [annotation setTitle:@"Single Tap"]; //You can set the subtitle too


  [mapTapAnnotation setRegion:region];
  [mapTapAnnotation addAnnotation:annotation];


  UITapGestureRecognizer *tapGesture = [[UITapGestureRecognizer alloc]initWithTarget:self action:@selector(detectSingleTap)];
  tapGesture.numberOfTapsRequired = 1;
  [mapTapAnnotation addGestureRecognizer:tapGesture];
}

-(void)detectSingleTap
{
   NSLog(@"Finded the single tap on map view");
}


 - (void)mapView:(MKMapView *)mapView didSelectAnnotationView:(MKAnnotationView *)view
{
    NSLog(@"The single tap annotation pin point is - %ld",(long)view.tag);
}

打印输出结果为

Finded the single tap on map view
The single tap annotation pin point is - 0

答案 5 :(得分:0)

在这里,我得到了一些有助于满足我的要求的解决方法。 根据我的需要,我可以在 mapbox注释标记以及地图的空白区域

上进行单击检测

我为MGLMapView创建了类别,( MGLMapView + EDCMapboxView )并覆盖了触摸方法

-touchesBegan:withEvent:
-touchesMoved:withEvent:
-touchesEnded:withEvent:
-touchesCancelled:withEvent:

<强> MGLMapView + EDCMapboxView.h

@protocol EDCMapboxViewDelegate <NSObject>
@optional
- (void)mapboxViewDidCreateNewTicket:(MGLMapView*)mapView;
@end

@interface MGLMapView (EDCMapboxView)

@property (assign, nonatomic) BOOL shouldCreateNewTicket;

@property (weak, nonatomic) id <EDCMapboxViewDelegate> mapboxDelegate;
@end

<强> MGLMapView + EDCMapboxView.m

@implementation MGLMapView (EDCMapboxView)
@dynamic mapboxDelegate;

#pragma mark -- Accessor
- (BOOL)shouldCreateNewTicket {
    return [objc_getAssociatedObject(self, @selector(shouldCreateNewTicket)) boolValue];
}
- (void)setShouldCreateNewTicket:(BOOL)flag {
    objc_setAssociatedObject(self, @selector(shouldCreateNewTicket), @(flag), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}

-(id<EDCMapboxViewDelegate>)mapboxDelegate{
    return objc_getAssociatedObject(self, @selector(mapboxDelegate));
}
- (void)setMapboxDelegate:(id<EDCMapboxViewDelegate>)mapboxDelegate{
    objc_setAssociatedObject(self, @selector(mapboxDelegate), mapboxDelegate, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}

#pragma mark -- Overrided method for UIResponder
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(nullable UIEvent *)event{
    NSLog(@"touchesBegan");
}

- (void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(nullable UIEvent *)event{
    NSLog(@"touchesMoved");
    self.shouldCreateNewTicket = NO;
}

- (void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(nullable UIEvent *)event{
    NSLog(@"touchesEnded");
}

- (void)touchesCancelled:(nullable NSSet<UITouch *> *)touches withEvent:(nullable UIEvent *)event{
    NSLog(@"touchesCancelled");

    [self createNewTicket];
}

- (void)createNewTicket{
    if(self.shouldCreateNewTicket){
        NSLog(@"Allowed to Create New ticket");
        // Tells that tap is on empty area.
        if([self.mapboxDelegate respondsToSelector:@selector(mapboxViewDidCreateNewTicket:)]){
            [self.mapboxDelegate mapboxViewDidCreateNewTicket:self];
        }
    }
    else{
        NSLog(@"Not allowed to create New ticket");
        // Tells tap is on annotation marker.
        self.shouldCreateNewTicket = YES;
    }
}

<强> EDCMapboxViewController.m

- (void)viewDidLoad {
    [super viewDidLoad];
    self.mapView.shouldCreateNewTicket = YES;   
    .....
    .......
    ........
}

- (BOOL)mapView:(MGLMapView *)mapView annotationCanShowCallout:(id <MGLAnnotation>)annotation {
    NSLog(@"annotationCanShowCallout");

    // Tells that annotation is tapped, then do not allow to create ticket.
    self.mapView.shouldCreateNewTicket = NO;

    return YES;
}

- (void)mapboxViewDidCreateNewTicket:(MGLMapView*)mapView{
   // Tells that tap is on empty area and not on marker, then allow to create ticket. 
}

它对我有用,希望它也会帮助你们。 感谢。

答案 6 :(得分:0)

以下是我如何解决它,你应该得到这个概念。

func onMapSingleTapped(recognizer: UITapGestureRecognizer)
{
    let viewLocation: CGPoint = recognizer.locationInView(map)

    // check if any annotations are hit
    if(map.annotations != nil)
    {
        for annotation in map.annotations!
        {
            if(annotation.isKindOfClass(MapCheckInAnnotation))
            {
                let pin: MapCheckInAnnotation = annotation as! MapCheckInAnnotation

                if let pinView = pin.view
                {
                    print("pinview \(pinView.frame.origin)")

                    // check if hit pin instead of just map
                    if(viewLocation.x >= pinView.frame.origin.x && viewLocation.x < pinView.frame.origin.x + pinView.frame.width)
                    {
                        if(viewLocation.y >= pinView.frame.origin.y && viewLocation.y < pinView.frame.origin.y + pinView.frame.height)
                        {
                            mapView(map, didSelectAnnotationView: pinView)
                            return
                        }
                    }
                }
            }
        }
    }

    // nope, no annotations were clicked
    let mapLocation: CLLocationCoordinate2D = map.convertPoint(viewLocation, toCoordinateFromView: map)

    print("onMapSingleTapped \(mapLocation)")

}

答案 7 :(得分:0)

由于我在上述解决方案上没有取得成功,因此在始终认可的地图点击事件之后实施了一个小的延迟。如果在此较小的时间范围内选择了注释,则不会再调用任何东西。否则,仅点击地图视图,不添加注释。 就我而言,效果很好。

MapView零件

 protocol MapDelegate: class { 
     func didTapMapView() 
 }

 class AppleMapView: MKMapView {

     weak var mapDelegate: MapDelegate?

     override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
             super.touchesEnded(touches, with: event)

             if touches.first != nil {
                 mapDelegate?.didTapMapView()
             }
        }
        ........
}

MapViewController部分

// property to store last tap at annotation
var lastAnnotationSelectionTime = Date()

// this method is called on every tap 
func didTapMapView() {
    let overlaySelectionDelayTime: TimeInterval = 0.2
    DispatchQueue.main.asyncAfter(deadline: .now() + overlaySelectionDelayTime, execute: { [weak self] in

         guard let self = self else { return }

         if self.lastAnnotationSelectionTime.distance(to: Date()) > overlaySelectionDelayTime {
                 // only called if no annotation was tapped meanwhile
                 [ HANDLE TAP ON MAP HERE ]
            }
         })
   }

// MARK: Marker Selection
func mapView(_ mapView: MKMapView, didSelect view: MKAnnotationView) {
     lastAnnotationSelectionTime = Date() // setten if annotation is tapped
     [ HANDLE TAP ON ANNOTATION HERE ]
}