Pregunta

No estoy seguro de lo que estoy haciendo mal, pero yo trato de coger toques en un MKMapView objeto.Yo subclases por la creación de la siguiente clase :

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

@interface MapViewWithTouches : MKMapView {

}

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

@end

Y de la aplicación :

#import "MapViewWithTouches.h"
@implementation MapViewWithTouches

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

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

}
@end

Pero parece que cuando yo uso esta clase, yo no veo nada en la Consola :

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

Alguna idea de lo que estoy haciendo mal?

¿Fue útil?

Solución

La mejor manera que he encontrado para lograr esto es con un Reconocimiento de gestos. Otras formas implican mucha programación pirata que duplica imperfectamente el código de Apple, especialmente en el caso de multitouch.

Esto es lo que hago: implementar un reconocedor de gestos que no se puede evitar y que no puede evitar otros reconocedores de gestos. Agréguelo a la vista del mapa y luego use los toques de GestorReconocidor Comenzar, toques Movidos, etc. a su gusto.

Cómo detectar cualquier toque dentro de un MKMapView (sin trucos)

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
    }
}

Otros consejos

Después de un día de pizzas, gritos, ¡finalmente encontré la solución! Muy ordenado!

Peter, utilicé tu truco anterior y lo modifiqué un poco para finalmente tener una solución que funcione perfectamente con MKMapView y debería funcionar también con 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

¡Espero que eso ayude a algunos de ustedes!

Saludos

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];

    //.............
}

¡Para un MKMapView la solución de trabajo real es con reconocimiento de gestos!

Yo quería dejar de actualizar el centro del mapa en mi ubicación cuando arrastro el mapa o pellizco para hacer zoom.

Entonces, crea y agrega tu reconocedor de gestos al 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];
}

Mire la UIGestureRecognizer Class Reference para ver todos los reconocedores de gestos disponibles.

Debido a que hemos definido el delegado como self, tenemos que implementar el protocolo 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;
...

Y anular el método gestor de reconocimiento de gestos: gestor de reconocimiento debe reconocer de forma simultánea con reconocimiento de gestos: para permitir reconocer múltiples gestos simultáneamente, si entendí bien:

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

Ahora escriba los métodos que serán llamados por nuestros reconocedores de gestos:

// 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];
}

Por si alguien intenta hacer lo mismo que yo: quería crear una anotación en el punto donde el usuario toca. Para eso utilicé la UITapGestureRecognizer solución:

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];
    .......
}

Sin embargo, también se llamó a didTapOnMap: cuando toqué la anotación y se crearía una nueva. La solución es implementar el UIGestureRecognizerDelegate:

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

Usted probablemente necesitará superposición de una visión transparente para coger la toca igual que se hace tan a menudo con UIWebView basado en los controles.La Vista de Mapa ya hace un montón de cosas especiales con un toque en el fin de permitir el mapa para ser movido, centrado, zoom, etc...que los mensajes no están recibiendo direccionado hacia su aplicación.

Otros dos (no probada) de las opciones que puede pensar:

1) Renunciar a la primera respuesta, a través de IB y seleccione "Archivo del Propietario" para permitir que el Propietario del archivo para responder a los toques.Yo un dudoso que esto va a funcionar porque MKMapView se extiende NSObject, no UIView ans resultado de los eventos de toque todavía no puede conseguir propagado hacia arriba para usted.

2) Si desea capturar cuando el Mapa de los cambios de estado (como en un zoom) acaba de implementar el MKMapViewDelegate protocolo para la escucha de los eventos en particular.Mi corazonada es que esta es su mejor tiro en la captura de algunos de interacción con facilidad (corto de implementación de la Visión transparente sobre el Mapa).No olvide configurar el Controlador de Vista de la vivienda de la MKMapView como el mapa del delegado (map.delegate = self).

La Buena Suerte.

No he experimentado, pero hay muchas posibilidades de que MapKit se base en un grupo de clases y, por lo tanto, la subclasificación sea difícil e ineficaz.

Sugeriría hacer que la vista de MapKit sea una subvista de una vista personalizada, lo que debería permitirle interceptar eventos táctiles antes de que lleguen.

Entonces, después de medio día de jugar con esto, encontré lo siguiente:

  1. Como todos los demás encontraron, pellizcar no funciona. Intenté subclasificar MKMapView y el método descrito anteriormente (interceptarlo). Y el resultado es el mismo.
  2. En los videos de Stanford para iPhone, un tipo de Apple dice que muchas de las cosas de UIKit causar muchos errores si " transfiere " las solicitudes táctiles (también conocidas como los dos métodos descritos anteriormente), y probablemente no lo haga funcionar.

  3. LA SOLUCIÓN : se describe aquí: Interceptar / secuestrar eventos táctiles de iPhone para MKMapView . Básicamente usted & Quot; atrapar & Quot; el evento antes de que cualquier respondedor lo obtenga e inténtelo allí.

En 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)")

    }

}

Convierta MKMapView en una subvista de una vista personalizada e implemente

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

en la vista personalizada para devolver self en lugar de la subvista.

Gracias por la pizza y los gritos, me ahorraste mucho tiempo.

multipletouchenabled funcionará esporádicamente.

viewTouch.multipleTouchEnabled = TRUE;

Al final, cambié las vistas cuando necesitaba capturar el toque (un punto diferente en el tiempo que necesitar pinchzooms):

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

Noto que puede rastrear el número y la ubicación de los toques y obtener la ubicación de cada uno en una vista:

- (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];
}

¿Alguien más ha intentado usar estos valores para actualizar el nivel de zoom del mapa? Sería cuestión de registrar las posiciones de inicio y luego las ubicaciones de finalización, calcular la diferencia relativa y actualizar el mapa.

Estoy jugando con el código básico proporcionado por Martin, y parece que funcionará ...

Esto es lo que armé, que permite hacer zoom en el simulador (no lo he probado en un iPhone real), pero creo que estaría bien:

- (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];
}

La idea principal es que si el usuario está usando dos dedos, usted rastrea los valores. Grabo los puntos de inicio y finalización en los puntos de inicio A y B. Luego grabo los puntos de seguimiento actuales, y cuando termino, en touchchesded, puedo llamar a una rutina para calcular las longitudes relativas de la línea entre los puntos con los que empiezo , y la línea entre el punto en el que termino usando hipotenusa simple calc. La relación entre ellos es la cantidad de zoom: multiplico el espacio de la región por esa cantidad.

Espero que sea útil para alguien.

Tomé la idea de un " overlay " vista transparente, de la respuesta de MystikSpiral, y funcionó perfectamente para lo que estaba tratando de lograr; solución rápida y limpia.

En resumen, tenía un UITableViewCell personalizado (diseñado en IB) con un MKMapView en el lado izquierdo y algunos UILabels en el derecho. Quería hacer la celda personalizada para que pudiera tocarla en cualquier lugar y esto empujaría un nuevo controlador de vista. Sin embargo, tocar el mapa no pasó los toques 'arriba' al UITableViewCell hasta que simplemente agregué una UIView del mismo tamaño que la vista del mapa justo encima (en IB) e hice de su fondo el 'color claro' en el código ( ¿No crees que puedes establecer clearColor en IB?):

dummyView.backgroundColor = [UIColor clearColor];

Pensé que podría ayudar a alguien más; ciertamente si desea lograr el mismo comportamiento para una celda de vista de tabla.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top