Pergunta

Eu não sei o que estou fazendo errado, mas eu tento toques de captura em um objeto MKMapView. I subclasse-lo, criando a seguinte classe:

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

@interface MapViewWithTouches : MKMapView {

}

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

@end

E a implementação:

#import "MapViewWithTouches.h"
@implementation MapViewWithTouches

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

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

}
@end

Mas parece que quando eu usar essa classe, não vejo nada no console:

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

Qualquer idéia do que estou fazendo de errado?

Foi útil?

Solução

A melhor maneira que eu encontrei para conseguir isso é com um gesto Recognizer. Outras maneiras vir a envolver um monte de programação hackish que imperfeitamente duplica código da Apple, especialmente no caso de multitouch.

Aqui está o que eu faço: Implementar um reconhecedor gesto que não pode ser evitado e que não pode impedir que outros reconhecedores gesto. Adicioná-lo à visualização do mapa, e então usar touchesBegan do GestureRecognizer, touchesMoved, etc, para a sua fantasia.

Como detectar qualquer torneira dentro de uma MKMapView (sans truques)

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

Outras dicas

Depois de um dia de pizzas, screamings, eu finalmente encontrei a solução! Muito legal!

Peter, eu usei o truque acima e tweaked-lo um pouco para finalmente ter uma solução que funciona perfeitamente com MKMapView e deve funcionar também com 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 vai ajudar alguns de vocês!

Felicidades

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 uma MKMapView a solução de trabalho real é com gesto recognization!

Me Eu queria parar de atualizar o centro do mapa no meu local quando eu arrastar o mapa ou beliscar para ampliar.

Assim, criar e adicionar o seu gesto de reconhecimento ao 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];
}

Veja a UIGestureRecognizer Classe Referência para ver todas gesto reconhecedor disponível.

Porque nós definimos o delegado para si mesmo, temos que implementar a UIGestureRecognizerDelegate protocole:

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

E substituir o methode GestureRecognizer: GestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer: de modo a permitir reconhecer vários gestos ao mesmo tempo, se eu entendi direito:

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

Agora, escrevem os metodos que serão chamados pelos nossos reconhecedores gesto:

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

Apenas no caso de alguém está tentando fazer o mesmo como eu: eu queria criar uma anotação no ponto onde as torneiras do usuário. Para que eu usei a solução 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];
    .......
}

No entanto, didTapOnMap: também foi chamado quando eu bati na anotação e um novo será criado. A solução é implementar o UIGestureRecognizerDelegate:

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

Você provavelmente terá de sobrepor uma visão transparente para pegar os toques apenas como é feito muitas vezes com controles baseados em UIWebView. O mapa já faz um monte de coisas especiais com um toque de modo a permitir o mapa para ser movido, centrado, ampliada, etc ... que as mensagens não estão sendo borbulhou para a sua aplicação.

Duas outras opções (não testado) eu posso pensar de:

1) Demitir o socorrista via IB e configurá-lo para "Proprietário do Arquivo" para permitir proprietário do arquivo para responder aos toques. I um duvidoso que isso vai funcionar porque MKMapView estende NSObject, não UIView ans resultado dos eventos de toque ainda pode não obter propagadas até você.

2) Se você quiser armadilha quando as mudanças de estado Mapa (como em um zoom) apenas implementar o protocolo MKMapViewDelegate para escutar eventos particulares. Meu palpite é que este é o seu melhor tiro na captura de alguma interação com facilidade (abreviação de implementar a visão transparente sobre o mapa). Não se esqueça de definir a habitação View Controller o MKMapView como delegado do mapa (map.delegate = self).

Good Luck.

Eu não tenho experimentado, mas há uma boa chance MapKit é baseado em torno de um conjunto de classes, e, portanto, subclassificação é difícil e ineficaz.

Eu sugiro fazer o ponto de vista MapKit um subview de uma exibição personalizada, que deve permitir que você eventos de interceptação de toque antes de alcançá-lo.

Assim, depois de metade de um dia de andar com este eu encontrei o seguinte:

  1. Como todos encontraram outra coisa, beliscando não funciona. I tentou tanto subclasse MKMapView e o método descrito acima (interceptando-lo). E o resultado é o mesmo.
  2. Nos vídeos para iPhone Stanford, um cara da Apple diz que muitas das coisas UIKit será causar um monte de erros se você "transferência" os pedidos de toque (aka os dois métodos descritos acima), e você provavelmente não vai fazê-lo funcionar.

  3. A SOLUÇÃO : é descrito aqui: Eventos Interceptar / Hijacking iPhone Toque para MKMapView . Basicamente você "pegar" o evento antes de qualquer resposta recebe-lo e interpretá-lo lá.

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

    }

}

Faça o MKMapView um subview de uma exibição personalizada e implementar

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

na exibição personalizada para retornar auto em vez do subexibição.

Obrigado pela pizza e screamings - você me salvou muito tempo

.

multipletouchenabled vai funcionar esporadicamente.

viewTouch.multipleTouchEnabled = TRUE;

No final, eu troquei os pontos de vista quando eu precisava para capturar o toque (ponto diferente no tempo de pinchzooms necessitam):

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

Eu noto que você pode acompanhar o número ea localização dos toques, e obter a localização de cada um em uma visão:

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

Tem mais alguém tentou usar esses valores para atualizar o nível de zoom do mapa? Seria uma questão de gravar as posições de início e, em seguida os locais de acabamento, calculando a diferença relativa e atualizar o mapa.

Eu estou jogando com o código básico fornecido por Martin, e isso parece que vai funcionar ...

Aqui está o que eu coloquei, que não permite zooms pitada no simulador (não tentei em um iPhone real), mas acho que seria ótimo:

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

A idéia principal é que, se o usuário está usando dois dedos, você rastrear os valores. Eu gravo os pontos inicial e final em startPoints A e B. Então eu gravar os pontos de monitoramento atuais, e quando eu terminar, em touchesEnded, posso chamar uma rotina para calcular os comprimentos relativos a linha entre os pontos que começam com , e a linha entre o ponto final que com o uso de simples calc hipotenusa. A relação entre eles é a quantidade de zoom:. Eu multiplicar o tempo de região por essa quantidade

A esperança é útil a alguém.

Eu levei a idéia de uma "sobreposição" visão transparente, de resposta de MystikSpiral, e funcionou perfeitamente para o que eu estava tentando alcançar; rápido, e solução limpa.

Em suma, eu tinha um UITableViewCell personalizado (projetado em IB) com um MKMapView na mão do lado esquerdo e algumas UILabels à direita. Eu queria fazer a célula personalizado para que você possa tocá-lo em qualquer lugar e isso iria empurrar um novo controlador de vista. No entanto tocando o mapa não passou toques 'up' para o UITableViewCell até que eu simplesmente adicionado um UIView do mesmo tamanho que o mapa da vista direita em cima dela (em IB) e fez do fundo do 'cor clara' em código ( não acho que você pode definir clearColor em IB ??):

dummyView.backgroundColor = [UIColor clearColor];

pensei que poderia ajudar alguém; Certamente, se você quer conseguir o mesmo comportamento para uma célula de exibição de tabela.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top