How to intercept touches events on a MKMapView or UIWebView objects?
Solution 1:
The best way I have found to achieve this is with a Gesture Recognizer. Other ways turn out to involve a lot of hackish programming that imperfectly duplicates Apple's code, especially in the case of multitouch.
Here's what I do: Implement a gesture recognizer that cannot be prevented and that cannot prevent other gesture recognizers. Add it to the map view, and then use the gestureRecognizer's touchesBegan, touchesMoved, etc. to your fancy.
How to detect any tap inside an MKMapView (sans tricks)
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
}
}
Solution 2:
After a day of pizzas, screamings, I finally found the solution! Very neat!
Peter, I used your trick above and tweaked it a little bit to finally have a solution which work perfectly with MKMapView and should work also with 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
I hope that will help some of you!
Cheers