如何拦截接触活动在MKMapView或UIWebView的对象?
-
20-08-2019 - |
题
我不确定我在做什么错误的,但我试图抓住倒是一个 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];
任何想法什么我做错了什么?
解决方案
我发现实现这一目标的最佳方法是使用一个姿势识别。其他方式转出涉及到很多的hackish编程是不完全复制苹果的代码,尤其是在多点触控的情况。
下面是我做的:实现不能阻止一个手势识别,并不能阻止其他手势识别。将它添加到地图视图,然后使用gestureRecognizer的的touchesBegan,touchesMoved,等你看中。
如何检测内部的的MKMapView(SANS花样)的任何抽头
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
}
}
其他提示
比萨饼,screamings一整天后,我终于找到了解决方案!很整齐!
彼得,我用你的绝招以上和调整它一点点终于可以与完美的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
我希望这将帮助一些你!
干杯
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];
//.............
}
对于一个的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类参考一>以查看所有可用姿势识别。
由于我们已经定义了代表自,我们必须实现protocole 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;
}
现在写将由我们的手势识别器被称作methodes:
// 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];
}
万一有人试图做同样的我一样:我想在的地步用户点击创建注释。对于我用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;
}
您可能会需要覆盖透明视图赶上触摸俨然就是基于UIWebView中的控件这样做频繁。地图视图已经这样做,以便让地图触摸一串特别的东西移动,居中,缩放,等等...这些消息都没有得到冒泡应用程式。
我能想到的其他两个(UNTESTED)选项:
1)辞职经由1B中的第一响应,并将其设置为“文件的所有者”,以允许文件的所有者向触摸作出响应。我半信半疑的,这将工作,因为的MKMapView延长NSObject的,而不是UIView的ANS结果的触摸事件仍可能不会传播到你。
2)如果你想陷阱时,地图状态的改变(如缩放),只需实现MKMapViewDelegate协议侦听特定事件。我的直觉是,这是在容易捕捉一些互动(短落实在地图透明视窗的)你最好的拍摄。不要忘记设置视图控制器容纳的MKMapView作为地图的委托(map.delegate = self
)。
好运。
我还没有尝试,但有一个很好的机会MapKit是根据各地的一类集群,因此继承它是困难的,无效的。
我建议把MapKit查看自定义视图,它应该让你到达之前拦截触摸事件的子视图。
这么半天后的乱搞这个我发现了以下:
- 作为别人都找到,捏不起作用。我试图这两个子类MKMapView和上述方法(拦截它)。结果是一样的。
在斯坦福的iPhone视频,一个家伙从苹果说,许多UIKit事会 原因很多错误。如果"转让"触摸请求(又名的两个方法上所述),并且你可能不会得到它的工作。
该解决方案:这里描述的是: 拦截/劫持的iPhone触摸事件MKMapView.基本上你的"追赶"事件之前任何响应方得到它,并且解释它在那里。
<强>在夫特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)")
}
}
请的的MKMapView定制视图的子视图和实施
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
在自定义视图返回,而不是子视图自
感谢您的比萨饼和screamings - 你救了我大量的时间
。multipletouchenabled将零星工作。
viewTouch.multipleTouchEnabled = TRUE;
在最后,我切换出的观点,当我需要捕捉触摸(不同点在时间上比需要pinchzooms):
[mapView removeFromSuperview];
[viewTouch addSubview:mapView];
[self.view insertSubview:viewTouch atIndex:0];
我注意到可以跟踪的触摸的数量和位置,并在视图中获得的每一个位置:
- (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];
}
有其他人使用这些值来更新地图的缩放级别试过吗?这将是记录的起始位置,然后完成位置,计算相对差异和更新地图的问题。
我在玩与马丁提供的基本代码,这看起来像它会工作...
下面是我放在一起,那确实允许捏在模拟器放大(没有尝试过一个真正的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,我可以调用一个子程序来计算我开始与点之间的线的相对长度和I与使用简单的斜边计算值结束的点之间的线。它们之间的比例是变焦量:i。由该量乘以区域跨度
希望这是有用的人。
我把这个想法的“叠加”透明的看法,从MystikSpiral的答案,它完美地工作对我试图来实现的;快速,清洁溶液。
在短,我曾与一个的MKMapView在左手侧和右侧一些UILabels一个自定义的UITableViewCell(在IB设计)。我想使自定义单元格,所以你可以在任何地方触摸它,这将推动新的视图控制器。但是触摸地图没有通过触摸“上升”到的UITableViewCell,直到我只是增加了大小相同的权利在它(在IB)顶部的地图视图的一个UIView,并使它的背景代码中的“晴彩”(不要以为你可以设置clearColor在IB ??):
dummyView.backgroundColor = [UIColor clearColor];
想这可能帮助别人;当然,如果你想实现一个表视图细胞相同的行为。