【问题标题】:How to intercept touches events on a MKMapView or UIWebView objects?如何拦截 MKMapView 或 UIWebView 对象上的触摸事件?
【发布时间】:2010-11-06 04:47:22
【问题描述】:

我不确定我做错了什么,但我试图捕捉到MKMapView 对象。我通过创建以下类对其进行了子类化:

#import <UIKit/UIKit.h>
#import <MapKit/MapKit.h>

@interface MapViewWithTouches : MKMapView {

}

- (void) touchesBegan:(NSSet *)touches withEvent:(UIEvent *) event;   

@end

以及实现:

#import "MapViewWithTouches.h"
@implementation MapViewWithTouches

- (void) touchesBegan:(NSSet *)touches withEvent:(UIEvent *) event {

    NSLog(@"hello");
    //[super touchesBegan:touches   withEvent:event];

}
@end

但是当我使用这个类时,我在控制台上看不到任何东西:

MapViewWithTouches *mapView = [[MapViewWithTouches alloc] initWithFrame:self.view.frame];
[self.view insertSubview:mapView atIndex:0];

知道我做错了什么吗?

【问题讨论】:

    标签: ios objective-c cocoa-touch mkmapview iphone-sdk-3.0


    【解决方案1】:

    我没有尝试过,但 MapKit 很有可能是基于类集群的,因此对其进行子类化既困难又无效。

    我建议将 MapKit 视图设为自定义视图的子视图,这样您就可以在触摸事件到达之前拦截它们。

    【讨论】:

    • 你好格雷厄姆!谢谢您的帮助!如果我按照您的建议制作一个超级自定义视图,那么我如何将事件转发到 MKMapView?有什么想法吗?
    【解决方案2】:

    您可能需要覆盖一个透明视图来捕捉触摸,就像经常使用基于 UIWebView 的控件一样。地图视图已经通过触摸做了很多特殊的事情,以允许地图被移动、居中、缩放等......消息不会被冒泡到您的应用程序中。

    我能想到的另外两个(未测试)选项:

    1) 通过 IB 退出第一响应者并将其设置为“文件所有者”以允许文件所有者响应触摸。我怀疑这是否可行,因为 MKMapView 扩展了 NSObject,而不是 UIView,因此触摸事件可能仍然无法传播给您。

    2) 如果您想在 Map 状态更改时进行捕获(例如在缩放时),只需实现 MKMapViewDelegate 协议以侦听特定事件。我的直觉是,这是您轻松捕获一些交互的最佳方法(没有在地图上实现透明视图)。不要忘记将包含 MKMapView 的 View Controller 设置为地图的委托 (map.delegate = self)。

    祝你好运。

    【讨论】:

    • MKMapView 绝对是 UIView 的子类。
    【解决方案3】:

    使 MKMapView 成为自定义视图的子视图并实现

    - (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
    

    在自定义视图中返回 self 而不是子视图。

    【讨论】:

    • 您好彼得,感谢您的回答!但我认为通过这样做,MKMapView 可能无法获得任何触摸事件,不是吗?我正在寻找一种方法来捕获事件,然后将其转发到 MKMapView。
    【解决方案4】:

    经过一天的比萨,尖叫,我终于找到了解决方案!非常整洁!

    彼得,我在上面使用了你的技巧并对其进行了一些调整,最终得到了一个与 MKMapView 完美配合并且也应该与 UIWebView 配合使用的解决方案

    MKTouchAppDelegate.h

    #import <UIKit/UIKit.h>
    @class UIViewTouch;
    @class MKMapView;
    
    @interface MKTouchAppDelegate : NSObject <UIApplicationDelegate> {
        UIWindow *window;
        UIViewTouch *viewTouch;
        MKMapView *mapView;
    }
    @property (nonatomic, retain) UIViewTouch *viewTouch;
    @property (nonatomic, retain) MKMapView *mapView;
    @property (nonatomic, retain) IBOutlet UIWindow *window;
    
    @end
    

    MKTouchAppDelegate.m

    #import "MKTouchAppDelegate.h"
    #import "UIViewTouch.h"
    #import <MapKit/MapKit.h>
    
    @implementation MKTouchAppDelegate
    
    @synthesize window;
    @synthesize viewTouch;
    @synthesize mapView;
    
    
    - (void)applicationDidFinishLaunching:(UIApplication *)application {
    
        //We create a view wich will catch Events as they occured and Log them in the Console
        viewTouch = [[UIViewTouch alloc] initWithFrame:CGRectMake(0, 0, 320, 480)];
    
        //Next we create the MKMapView object, which will be added as a subview of viewTouch
        mapView = [[MKMapView alloc] initWithFrame:CGRectMake(0, 0, 320, 480)];
        [viewTouch addSubview:mapView];
    
        //And we display everything!
        [window addSubview:viewTouch];
        [window makeKeyAndVisible];
    
    
    }
    
    
    - (void)dealloc {
        [window release];
        [super dealloc];
    }
    
    
    @end
    

    UIViewTouch.h

    #import <UIKit/UIKit.h>
    @class UIView;
    
    @interface UIViewTouch : UIView {
        UIView *viewTouched;
    }
    @property (nonatomic, retain) UIView * viewTouched;
    
    - (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event;
    
    - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event;
    - (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event;
    - (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event;
    - (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event;
    
    @end
    

    UIViewTouch.m

    #import "UIViewTouch.h"
    #import <MapKit/MapKit.h>
    
    @implementation UIViewTouch
    @synthesize viewTouched;
    
    //The basic idea here is to intercept the view which is sent back as the firstresponder in hitTest.
    //We keep it preciously in the property viewTouched and we return our view as the firstresponder.
    - (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
        NSLog(@"Hit Test");
        viewTouched = [super hitTest:point withEvent:event];
        return self;
    }
    
    //Then, when an event is fired, we log this one and then send it back to the viewTouched we kept, and voilà!!! :)
    - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
        NSLog(@"Touch Began");
        [viewTouched touchesBegan:touches withEvent:event];
    }
    - (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
        NSLog(@"Touch Moved");
        [viewTouched touchesMoved:touches withEvent:event];
    }
    
    - (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
        NSLog(@"Touch Ended");
        [viewTouched touchesEnded:touches withEvent:event];
    }
    
    - (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event {
        NSLog(@"Touch Cancelled");
    }
    
    @end
    

    希望对大家有所帮助!

    干杯

    【讨论】:

    • 不错。小建议:您应该避免使用 UI 前缀命名您自己的类。 Apple 保留/不鼓励使用 NS 或 UI 作为类前缀,因为它们最终可能会与 Apple 类发生冲突(即使它是私有类)。
    • 嘿,丹尼尔,你说的完全正确,我也这么认为!为了完成我上面的回答,让我添加一点警告:我的示例假设只有一个对象 viewTouched,它正在消耗所有事件。但事实并非如此。你可以在你的地图上放一些注释,然后我的代码就不再工作了。要 100% 工作,您需要为每个 hitTest 记住与该特定事件关联的视图(并最终在触发 touchesEnded 或 touchesCancelled 时释放它,因此您无需跟踪已完成的事件......)。
    • 非常有用的代码,谢谢马丁!我想知道您是否在实施此操作后尝试捏缩放地图?对我来说,当我使用基本相同的代码开始工作时,除了捏缩放地图之外,一切似乎都可以工作。有人有什么想法吗?
    • 嘿亚当,我也有这个限制,我真的不明白为什么!这真的很烦人。如果您找到解决方案,请告诉我!谢谢
    • 好的,我投了这个票,因为它最初似乎解决了我的问题。然而...!我似乎无法让多点触控工作。也就是说,即使我直接将 touchesBegan 和 touchesMoved 传递给 viewTouched(我在 touchesEnded 上进行拦截),我也无法使用捏合手势缩放地图。 (续...)
    【解决方案5】:

    感谢你的披萨和尖叫——你为我节省了很多时间。

    multipletouchenabled 偶尔会起作用。

    viewTouch.multipleTouchEnabled = TRUE;
    

    最后,当我需要捕捉触摸时(与需要捏缩放不同的时间点),我切换了视图:

        [mapView removeFromSuperview];
        [viewTouch addSubview:mapView];
        [self.view insertSubview:viewTouch atIndex:0];
    

    【讨论】:

    • 但不适用于实时缩放。它似乎也总是缩小。
    【解决方案6】:

    我注意到您可以跟踪触摸的次数和位置,并在视图中获取每个触摸的位置:

    - (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
        NSLog(@"Touch Moved %d", [[event allTouches] count]);
    
     NSEnumerator *enumerator = [touches objectEnumerator];
     id value;
    
     while ((value = [enumerator nextObject])) {
      NSLog(@"touch description %f", [value locationInView:mapView].x);
     }
        [viewTouched touchesMoved:touches withEvent:event];
    }
    

    有没有其他人尝试使用这些值来更新地图的缩放级别?这将是记录开始位置,然后是结束位置,计算相对差异并更新地图的问题。

    我正在使用 Martin 提供的基本代码,看起来它可以工作......

    【讨论】:

      【解决方案7】:

      这是我整理的,确实允许在模拟器中进行捏合缩放(尚未在真正的 iPhone 上尝试过),但我认为会很好:

      - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
          NSLog(@"Touch Began %d", [touches count]);
       reportTrackingPoints = NO;
       startTrackingPoints = YES;
          [viewTouched touchesBegan:touches withEvent:event];
      }
      
      - (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
       if ([[event allTouches] count] == 2) {
        reportTrackingPoints = YES;
        if (startTrackingPoints == YES) {
         BOOL setA = NO;
         NSEnumerator *enumerator = [[event allTouches] objectEnumerator];
         id value;
         while ((value = [enumerator nextObject])) {
          if (! setA) {
           startPointA = [value locationInView:mapView];
           setA = YES;
          } else {
           startPointB = [value locationInView:mapView];
          }
         }
         startTrackingPoints = NO;
        } else {
         BOOL setA = NO;
         NSEnumerator *enumerator = [[event allTouches] objectEnumerator];
         id value;
         while ((value = [enumerator nextObject])) {
          if (! setA) {
           endPointA = [value locationInView:mapView];
           setA = YES;
          } else {
           endPointB = [value locationInView:mapView];
          }
         }
        }
       }
       //NSLog(@"Touch Moved %d", [[event allTouches] count]);
          [viewTouched touchesMoved:touches withEvent:event];
      }
      
      - (void) updateMapFromTrackingPoints {
       float startLenA = (startPointA.x - startPointB.x);
       float startLenB = (startPointA.y - startPointB.y);
       float len1 = sqrt((startLenA * startLenA) + (startLenB * startLenB));
       float endLenA = (endPointA.x - endPointB.x);
       float endLenB = (endPointA.y - endPointB.y);
       float len2 = sqrt((endLenA * endLenA) + (endLenB * endLenB));
       MKCoordinateRegion region = mapView.region;
       region.span.latitudeDelta = region.span.latitudeDelta * len1/len2;
       region.span.longitudeDelta = region.span.longitudeDelta * len1/len2;
       [mapView setRegion:region animated:YES];
      }
      
      - (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
       if (reportTrackingPoints) {
        [self updateMapFromTrackingPoints];
        reportTrackingPoints = NO;
       }
      
      
          [viewTouched touchesEnded:touches withEvent:event];
      }
      

      主要思想是,如果用户使用两根手指,您就可以跟踪这些值。我在 startPoints A 和 B 中记录了起点和终点。然后我记录了当前的跟踪点,当我完成后,在 touchesEnded 上,我可以调用一个例程来计算我开始的点之间的线的相对长度,以及我使用简单斜边计算结束的点之间的线。它们之间的比率是缩放量:我将区域跨度乘以该量。

      希望它对某人有用。

      【讨论】:

        【解决方案8】:

        所以在搞砸了半天后,我发现了以下内容:

        1. 正如其他人所发现的,捏合不起作用。我尝试了子类化 MKMapView 和上述方法(拦截它)。结果是一样的。
        2. 在斯坦福的 iPhone 视频中,一位来自 Apple 的人说 UIKit 的很多东西都会 如果你“传输”触摸请求(也就是上面描述的两种方法)会导致很多错误,而且你可能无法让它工作。

        3. 解决方案:在此处描述:Intercepting/Hijacking iPhone Touch Events for MKMapView。基本上,您在任何响应者得到它之前“捕捉”事件,并在那里解释它。

        【讨论】:

          【解决方案9】:

          我从 MystikSpiral 的回答中采用了“叠加”透明视图的想法,它非常适合我想要实现的目标;快速、清洁的解决方案。

          简而言之,我有一个自定义 UITableViewCell(在 IB 中设计),左侧有一个 MKMapView,右侧有一些 UILabel。我想制作自定义单元格,以便您可以在任何地方触摸它,这将推动一个新的视图控制器。然而,触摸地图并没有将触摸“向上”传递给 UITableViewCell,直到我简单地在其顶部(在 IB 中)添加了与地图视图相同大小的 UIView 并使其成为代码中的“清晰颜色”的背景(不认为你可以在 IB 中设置 clearColor 吗??):

          dummyView.backgroundColor = [UIColor clearColor];
          

          认为它可能对其他人有所帮助;当然,如果您想为表格视图单元格实现相同的行为。

          【讨论】:

          • “然而,触摸地图并没有将触摸‘向上’传递给 UITableViewCell,直到我简单地在它上面添加了一个与地图视图相同大小的 UIView”这不是真的。地图正在处理触摸,因为它有自己的用户交互,例如滚动等。如果您想检测单元格上的内容而不是与地图交互,只需设置 map.isUserInteractionEnabled = false 然后您可以在表中使用 didSelectRowAtIndexPath查看委托。
          【解决方案10】:

          我发现实现这一目标的最佳方法是使用手势识别器。其他方式最终涉及大量不完美地复制 Apple 代码的骇人听闻的编程,尤其是在多点触控的情况下。

          这就是我要做的:实现一个无法阻止且无法阻止其他手势识别器的手势识别器。将其添加到地图视图中,然后根据您的喜好使用gestureRecognizer的touchesBegan、touchesMoved等。

          如何检测 MKMapView 内的任何点击(无技巧)

          WildcardGestureRecognizer * tapInterceptor = [[WildcardGestureRecognizer alloc] init];
          tapInterceptor.touchesBeganCallback = ^(NSSet * touches, UIEvent * event) {
                  self.lockedOnUserLocation = NO;
          };
          [mapView addGestureRecognizer:tapInterceptor];
          

          WildcardGestureRecognizer.h

          //
          //  WildcardGestureRecognizer.h
          //  Copyright 2010 Floatopian LLC. All rights reserved.
          //
          
          #import <Foundation/Foundation.h>
          
          typedef void (^TouchesEventBlock)(NSSet * touches, UIEvent * event);
          
          @interface WildcardGestureRecognizer : UIGestureRecognizer {
              TouchesEventBlock touchesBeganCallback;
          }
          @property(copy) TouchesEventBlock touchesBeganCallback;
          
          
          @end
          

          WildcardGestureRecognizer.m

          //
          //  WildcardGestureRecognizer.m
          //  Created by Raymond Daly on 10/31/10.
          //  Copyright 2010 Floatopian LLC. All rights reserved.
          //
          
          #import "WildcardGestureRecognizer.h"
          
          
          @implementation WildcardGestureRecognizer
          @synthesize touchesBeganCallback;
          
          -(id) init{
              if (self = [super init])
              {
                  self.cancelsTouchesInView = NO;
              }
              return self;
          }
          
          - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
          {
              if (touchesBeganCallback)
                  touchesBeganCallback(touches, event);
          }
          
          - (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
          {
          }
          
          - (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
          {
          }
          
          - (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
          {
          }
          
          - (void)reset
          {
          }
          
          - (void)ignoreTouch:(UITouch *)touch forEvent:(UIEvent *)event
          {
          }
          
          - (BOOL)canBePreventedByGestureRecognizer:(UIGestureRecognizer *)preventingGestureRecognizer
          {
              return NO;
          }
          
          - (BOOL)canPreventGestureRecognizer:(UIGestureRecognizer *)preventedGestureRecognizer
          {
              return NO;
          }
          
          @end
          

          SWIFT 3

          let tapInterceptor = WildCardGestureRecognizer(target: nil, action: nil)
          tapInterceptor.touchesBeganCallback = {
              _, _ in
              self.lockedOnUserLocation = false
          }
          mapView.addGestureRecognizer(tapInterceptor)
          

          WildCardGestureRecognizer.swift

          import UIKit.UIGestureRecognizerSubclass
          
          class WildCardGestureRecognizer: UIGestureRecognizer {
          
              var touchesBeganCallback: ((Set<UITouch>, UIEvent) -> Void)?
          
              override init(target: Any?, action: Selector?) {
                  super.init(target: target, action: action)
                  self.cancelsTouchesInView = false
              }
          
              override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent) {
                  super.touchesBegan(touches, with: event)
                  touchesBeganCallback?(touches, event)
              }
          
              override func canPrevent(_ preventedGestureRecognizer: UIGestureRecognizer) -> Bool {
                  return false
              }
          
              override func canBePrevented(by preventingGestureRecognizer: UIGestureRecognizer) -> Bool {
                  return false
              }
          }
          

          【讨论】:

          • 什么是“lockedOnUserLocation”?
          • 这是一个特定于我的应用程序的无关变量。它跟踪系统是否应该自动将地图居中在当前位置
          • 这是完美的解决方案。我需要澄清一下:在方法“- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event”中,使用代码的目的是什么:if (touchesBeganCallback) touchesBeganCallback(touches, event);
          • 这在大多数情况下都很好用,但我发现它有一个问题。如果您的 Web 视图中的 HTML 包含带有控件的 HTML5 video 标记,则手势识别器将阻止用户使用这些控件。我一直在寻找解决方法,但还没有找到。
          • 谢谢分享。为什么没有合适的委托方法来跟踪用户与地图视图的交互,我无法理解,但这很好用。
          【解决方案11】:

          对于 MKMapView,真正可行的解决方案是手势识别!

          我想在拖动地图或捏合缩放时停止更新我所在位置的地图中心。

          因此,创建手势识别器并将其添加到 mapView 中:

          - (void)viewDidLoad {
          
              ...
          
              // Add gesture recognizer for map hoding
              UILongPressGestureRecognizer *longPressGesture = [[[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(handleLongPressAndPinchGesture:)] autorelease];
              longPressGesture.delegate = self;
              longPressGesture.minimumPressDuration = 0;  // In order to detect the map touching directly (Default was 0.5)
              [self.mapView addGestureRecognizer:longPressGesture];
          
              // Add gesture recognizer for map pinching
              UIPinchGestureRecognizer *pinchGesture = [[[UIPinchGestureRecognizer alloc] initWithTarget:self action:@selector(handleLongPressAndPinchGesture:)] autorelease];
              pinchGesture.delegate = self;
              [self.mapView addGestureRecognizer:pinchGesture];
          
              // Add gesture recognizer for map dragging
              UIPanGestureRecognizer *panGesture = [[[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(handlePanGesture:)] autorelease];
              panGesture.delegate = self;
              panGesture.maximumNumberOfTouches = 1;  // In order to discard dragging when pinching
              [self.mapView addGestureRecognizer:panGesture];
          }
          

          查看UIGestureRecognizer Class Reference 以查看所有可用的手势识别器。

          因为我们已经定义了自己的委托,所以我们必须实现协议 UIGestureRecognizerDelegate :

          typedef enum {
              MapModeStateFree,                    // Map is free
              MapModeStateGeolocalised,            // Map centred on our location
              MapModeStateGeolocalisedWithHeading  // Map centred on our location and oriented with the compass
          } MapModeState;
          
          @interface MapViewController : UIViewController <CLLocationManagerDelegate, UIGestureRecognizerDelegate> {
              MapModeState mapMode;
          }
          
          @property (nonatomic, retain) IBOutlet MKMapView *mapView;
          ...
          

          并覆盖方法gestureRecognizer:gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer: 为了允许同时识别多个手势,如果我理解正确的话:

          // Allow to recognize multiple gestures simultaneously (Implementation of the protocole UIGestureRecognizerDelegate)
          - (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer {
              return YES;
          }
          

          现在编写我们的手势识别器将调用的方法:

          // On map holding or pinching pause localise and heading
          - (void)handleLongPressAndPinchGesture:(UIGestureRecognizer *)sender {
              // Stop to localise and/or heading
              if (sender.state == UIGestureRecognizerStateBegan && mapMode != MapModeStateFree) {
                  [locationManager stopUpdatingLocation];
                  if (mapMode == MapModeStateGeolocalisedWithHeading) [locationManager stopUpdatingHeading];
              }
              // Restart to localise and/or heading
              if (sender.state == UIGestureRecognizerStateEnded && mapMode != MapModeStateFree) {
                  [locationManager startUpdatingLocation];
                  if (mapMode == MapModeStateGeolocalisedWithHeading) [locationManager startUpdatingHeading];
              }
          }
          
          // On dragging gesture put map in free mode
          - (void)handlePanGesture:(UIGestureRecognizer *)sender {
              if (sender.state == UIGestureRecognizerStateBegan && mapMode != MapModeStateFree) [self setMapInFreeModePushedBy:sender];
          }
          

          【讨论】:

          • 这个解决方案很完美!这里有一些快速操作:如果你想在用户结束使用它进行任何操作时进行拦截应该足够了 - (void)handleLongPressAndPinchGesture:(UIGestureRecognizer *)sender { if (sender.state == UIGestureRecognizerStateEnded) { NSLog(@"handleLongPressAndPinchGesture Ended") ; } }
          • 另外不要忘记添加委托
          【解决方案12】:
          UITapGestureRecognizer *tgr = [[UITapGestureRecognizer alloc]initWithTarget:self action:@selector(handleGesture:)];   
          tgr.numberOfTapsRequired = 2;
          tgr.numberOfTouchesRequired = 1;
          [mapView addGestureRecognizer:tgr];
          [tgr release];
          
          
          - (void)handleGesture:(UIGestureRecognizer *)gestureRecognizer
          {
              if (gestureRecognizer.state != UIGestureRecognizerStateEnded)
                  return;
          
              CGPoint touchPoint = [gestureRecognizer locationInView:mapView];
              CLLocationCoordinate2D touchMapCoordinate = [mapView convertPoint:touchPoint toCoordinateFromView:mapView];
          
              //.............
          }
          

          【讨论】:

          • 我不确定为什么这不是最佳答案。似乎工作得很好,而且更简单。
          【解决方案13】:

          以防万一有人像我一样尝试做同样的事情:我想在用户点击的地方创建一个注释。为此,我使用了UITapGestureRecognizer 解决方案:

          UITapGestureRecognizer *tapGestureRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(didTapOnMap:)];
          [self.mapView addGestureRecognizer:tapGestureRecognizer];
          [tapGestureRecognizer setDelegate:self];
          
          - (void)didTapOnMap:(UITapGestureRecognizer *)gestureRecognizer
          {
              CGPoint point = [gestureRecognizer locationInView:self.mapView];
              CLLocationCoordinate2D coordinate = [self.mapView convertPoint:point toCoordinateFromView:self.mapView];
              .......
          }
          

          但是,当我点击注释时,didTapOnMap: 也会被调用,并且会创建一个新注释。解决方案是实现UIGestureRecognizerDelegate:

          - (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch
          {
              if ([touch.view isKindOfClass:[MKAnnotationView class]])
              {
                  return NO;
              }
              return YES;
          }
          

          【讨论】:

          • 这是一个很好的解决方案!但是,如果您将自定义视图用作MKAnnotation,则它不起作用。在这种情况下,您可能有另一个注释的子视图触发手势识别器。我不得不递归检查 touch.view 的 superview 以找到潜在的 MKAnnotationView
          【解决方案14】:

          在 Swift 3.0 中

          import UIKit
          import MapKit
          
          class CoordinatesPickerViewController: UIViewController {
          
              @IBOutlet var mapView: MKMapView!
              override func viewDidLoad() {
                  super.viewDidLoad()
          
                  let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(clickOnMap))
                  mapView.addGestureRecognizer(tapGestureRecognizer)
              }
          
              @objc func clickOnMap(_ sender: UITapGestureRecognizer) {
          
                  if sender.state != UIGestureRecognizerState.ended { return }
                  let touchLocation = sender.location(in: mapView)
                  let locationCoordinate = mapView.convert(touchLocation, toCoordinateFrom: mapView)
                  print("Tapped at lat: \(locationCoordinate.latitude) long: \(locationCoordinate.longitude)")
          
              }
          
          }
          

          【讨论】:

            【解决方案15】:

            我不明白为什么其他答案如此复杂。解决方案实际上只有一行:

            mapView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(mapTapped)))
            
            @objc func mapTapped() {}
            

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 2014-09-11
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多