Comment intercepter des événements de contact sur un objet MKMapView ou UIWebView?

96

Je ne suis pas sûr de ce que je fais de mal, mais j'essaie d'attraper des touches sur un MKMapViewobjet. Je l'ai sous-classé en créant la classe suivante:

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

@interface MapViewWithTouches : MKMapView {

}

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

@end

Et la mise en œuvre:

#import "MapViewWithTouches.h"
@implementation MapViewWithTouches

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

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

}
@end

Mais il semble que lorsque j'utilise cette classe, je ne vois rien sur la console:

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

Une idée de ce que je fais mal?

Martin
la source

Réponses:

147

Le meilleur moyen que j'ai trouvé pour y parvenir est d'utiliser un outil de reconnaissance de gestes. D'autres moyens s'avèrent impliquer beaucoup de programmation hackish qui duplique imparfaitement le code d'Apple, en particulier dans le cas du multitouch.

Voici ce que je fais: implémenter un outil de reconnaissance de mouvement qui ne peut pas être empêché et qui ne peut pas empêcher d'autres outils de reconnaissance de mouvement. Ajoutez-le à la vue de la carte, puis utilisez les touches gestureRecognizer, touchBegan, touchMoved, etc. à votre guise.

Comment détecter n'importe quel robinet dans un MKMapView (sans astuces)

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
    }
}
gonzojive
la source
3
à quoi sert "lockOnUserLocation"?
jowie
c'est une variable étrangère spécifique à mon application. il garde une trace de savoir si le système doit automatiquement centrer la carte sur l'emplacement actuel
gonzojive
C'est la solution parfaite. J'ai besoin d'une clarification: Dans la méthode "- (void) touchesBegan: (NSSet *) touche withEvent: (UIEvent *) event", quel est le but de l'utilisation du code: if (touchesBeganCallback) touchesBeganCallback (touches, événement);
Satyam
1
Cela fonctionne très bien dans la plupart des cas, mais j'ai trouvé un problème avec cela. Si le code HTML de votre vue Web contient une videobalise HTML5 avec des contrôles, le module de reconnaissance de mouvement empêchera l'utilisateur de pouvoir utiliser les contrôles. Je cherchais une solution de contournement pour cela, mais je n'en ai pas encore trouvé.
Bryan Irace
Merci pour le partage. Pourquoi il n'y a pas de méthode de délégué appropriée pour suivre les interactions des utilisateurs avec une vue cartographique me dépasse, mais cela fonctionne bien.
Justin Driscoll
29

Après une journée de pizzas, de cris, j'ai enfin trouvé la solution! Très propre!

Peter, j'ai utilisé votre astuce ci-dessus et l'ai un peu peaufiné pour enfin avoir une solution qui fonctionne parfaitement avec MKMapView et devrait également fonctionner avec 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

J'espère que cela aidera certains d'entre vous!

À votre santé

Martin
la source
14
Agréable. Petite suggestion: évitez de nommer vos propres classes avec un préfixe d'interface utilisateur. Apple réserve / déconseille d'utiliser NS ou UI comme préfixe de classe, car ceux-ci pourraient finir par entrer en conflit avec une classe Apple (même s'il s'agit d'une classe privée).
Daniel Dickison
Hé Daniel, tu as parfaitement raison, je pensais ça aussi! Pour compléter ma réponse ci-dessus, permettez-moi d'ajouter un petit avertissement: mon exemple suppose qu'il n'y a qu'un seul objet viewTouched, qui consomme tous les événements. Mais ce n'est pas vrai. Vous pourriez avoir des annotations au-dessus de votre carte et mon code ne fonctionnera plus. Pour travailler à 100%, vous devez vous rappeler pour chaque hitTest la vue associée à cet événement spécifique (et finalement la relâcher lorsque touchEnded ou touchCancelled est déclenché afin que vous n'ayez pas besoin de garder une trace des événements terminés ...).
Martin le
1
Code très utile, merci Martin! Je me demandais si vous aviez essayé de zoomer par pincement sur la carte après avoir implémenté cela? Pour moi, quand je l'ai fait fonctionner en utilisant fondamentalement le même code que vous avez au-dessus, tout a semblé fonctionner à l'exception du zoom par pincement sur la carte. Quelqu'un a des idées?
Adam Alexander le
Hey Adam, j'ai aussi cette limitation et je ne comprends pas vraiment pourquoi! C'est vraiment ennuyeux. Si vous trouvez une solution, faites-le moi savoir! Thx
Martin
Ok, j'ai voté celui-ci parce qu'il semblait initialement résoudre mon problème. TOUTEFOIS...! Je n'arrive pas à faire fonctionner le multi-touch. Autrement dit, même si je passe directement touchesBegan et toucheMoved to viewTouched (je fais mon interception sur touchesFin), je ne peux pas zoomer la carte avec des gestes de pincement. (Suite ...)
Olie
24
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];

    //.............
}
Govind
la source
3
Je ne sais pas pourquoi ce n'est pas la meilleure réponse. Semble fonctionner parfaitement et est beaucoup plus simple.
elsurudo
12

Pour un MKMapView, la vraie solution de travail est la reconnaissance des gestes!

Moi je voulais arrêter de mettre à jour le centre de la carte sur ma position lorsque je fais glisser la carte ou la pince pour zoomer.

Alors, créez et ajoutez votre outil de reconnaissance de gestes à la 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];
}

Consultez la référence de classe UIGestureRecognizer pour voir tous les outils de reconnaissance de mouvement disponibles.

Parce que nous avons défini le délégué à self, nous devons implémenter le 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;
...

Et écraser la méthode gestureRecognizer: gestureRecognizer shouldRecognizeSimultaneousWithGestureRecognizer: afin de permettre de reconnaître plusieurs gestes simultanément, si j'ai bien compris:

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

Maintenant, écrivez les méthodes qui seront appelées par nos outils de reconnaissance de gestes:

// 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];
}
Joan
la source
Cette solution est parfaite! Quelques quickies ici: Si vous voulez intercepter lorsque l'utilisateur termine d'effectuer une action en utilisant ceci devrait être suffisant - (void) handleLongPressAndPinchGesture: (UIGestureRecognizer *) sender {if (sender.state == UIGestureRecognizerStateEnded) {NSLog (@ "handleLongPressAndPinchGesture Ended") ; }}
Alejandro Luengo
N'oubliez pas non plus d'ajouter le délégué <UIGestureRecognizerDelegate>
Alejandro Luengo
6

Juste au cas où quelqu'un essaie de faire la même chose comme moi: je voulais créer une annotation au point où l'utilisateur tape. Pour cela, j'ai utilisé la UITapGestureRecognizersolution:

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

Cependant, didTapOnMap:a également été appelé lorsque j'ai tapé sur l'annotation et une nouvelle serait créée. La solution consiste à implémenter UIGestureRecognizerDelegate:

- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch
{
    if ([touch.view isKindOfClass:[MKAnnotationView class]])
    {
        return NO;
    }
    return YES;
}
jimpic
la source
C'est une excellente solution ! Mais cela ne fonctionne pas si vous utilisez une vue personnalisée comme MKAnnotation. Dans ce cas, vous pouvez avoir une sous-vue d'une autre annotation déclenchant la reconnaissance de mouvement. J'ai dû vérifier récursivement la supervision du touch.view pour trouver un potentiel MKAnnotationView
KIDdAe
3

Vous devrez probablement superposer une vue transparente pour capturer les touches, comme cela se fait souvent avec les contrôles basés sur UIWebView. La vue de la carte fait déjà un tas de choses spéciales avec une touche afin de permettre à la carte d'être déplacée, centrée, zoomée, etc ... afin que les messages ne soient pas diffusés dans votre application.

Deux autres options (UNTESTED) auxquelles je peux penser:

1) Démissionnez le premier répondeur via IB et réglez-le sur "File's Owner" pour permettre au propriétaire du fichier de répondre aux touches. Je doute que cela fonctionne parce que MKMapView étend NSObject, pas UIView et par conséquent, les événements tactiles peuvent ne pas être propagés jusqu'à vous.

2) Si vous souhaitez intercepter lorsque l'état de la carte change (comme sur un zoom), implémentez simplement le protocole MKMapViewDelegate pour écouter des événements particuliers. Mon intuition est que c'est votre meilleur moyen de piéger facilement certaines interactions (à moins d'implémenter la vue transparente sur la carte). N'oubliez pas de définir le contrôleur de vue hébergeant MKMapView comme délégué de la carte ( map.delegate = self).

Bonne chance.

MystikSpiral
la source
MKMapView sous-classe définitivement UIView.
Daniel Dickison
2

Je n'ai pas expérimenté, mais il y a de fortes chances que MapKit soit basé sur un cluster de classes, et par conséquent, le sous-classer est difficile et inefficace.

Je suggère de faire de la vue MapKit une sous-vue d'une vue personnalisée, ce qui devrait vous permettre d'intercepter les événements tactiles avant qu'ils ne l'atteignent.

Grahamparks
la source
Bonjour Graham! Merci de votre aide! Si je crée une vue super personnalisée comme vous le suggérez, comment puis-je transférer des événements vers MKMapView? Une idée?
Martin le
2

Donc, après une demi-journée de déconner avec cela, j'ai trouvé ce qui suit:

  1. Comme tout le monde l'a trouvé, le pincement ne fonctionne pas. J'ai essayé à la fois le sous-classement de MKMapView et la méthode décrite ci-dessus (en l'interceptant). Et le résultat est le même.
  2. Dans les vidéos iPhone de Stanford, un gars d'Apple dit que beaucoup de choses UIKit causeront beaucoup d'erreurs si vous "transférez" les requêtes tactiles (alias les deux méthodes décrites ci-dessus), et que vous ne les obtiendrez probablement pas.

  3. LA SOLUTION : est décrite ici: Interception / Détournement d'événements iPhone Touch pour MKMapView . Fondamentalement, vous «attrapez» l'événement avant qu'un répondeur ne l'obtienne, et l'interprétez là.

thuang513
la source
2

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

    }

}
Francisco Castro
la source
0

Faire de MKMapView une sous-vue d'une vue personnalisée et implémenter

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

dans la vue personnalisée pour renvoyer soi au lieu de la sous-vue.

Peter N Lewis
la source
Bonjour Peter, Merci pour votre réponse! Mais je pense qu'en faisant cela, MKMapView pourrait ne pas être en mesure d'obtenir des événements de touche, n'est-ce pas? Je cherche un moyen de simplement attraper un événement et de le transmettre ensuite au MKMapView.
Martin
0

Merci pour la pizza et les cris - vous m'avez fait gagner beaucoup de temps.

multipletouchenabled fonctionnera sporadiquement.

viewTouch.multipleTouchEnabled = TRUE;

En fin de compte, j'ai changé les vues lorsque je devais capturer le toucher (moment différent de celui des zooms pincés):

    [mapView removeFromSuperview];
    [viewTouch addSubview:mapView];
    [self.view insertSubview:viewTouch atIndex:0];
BanqueStrong
la source
mais ne fonctionne pas avec le zoom en direct. Il semble également toujours faire un zoom arrière.
Rog
0

Je remarque que vous pouvez suivre le nombre et l'emplacement des touches, et obtenir l'emplacement de chacune dans une vue:

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

Quelqu'un d'autre a-t-il essayé d'utiliser ces valeurs pour mettre à jour le niveau de zoom de la carte? Il s'agirait d'enregistrer les positions de départ, puis les emplacements d'arrivée, de calculer la différence relative et de mettre à jour la carte.

Je joue avec le code de base fourni par Martin, et il semble que cela fonctionnera ...

Dan Donaldson
la source
0

Voici ce que j'ai mis en place, qui permet des zooms pincés dans le simulateur (je n'ai pas essayé sur un vrai iPhone), mais je pense que ça irait:

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

L'idée principale est que si l'utilisateur utilise deux doigts, vous suivez les valeurs. J'enregistre les points de départ et de fin dans les points de départ A et B.Ensuite, j'enregistre les points de suivi actuels, et lorsque j'ai terminé, sur touchEnded, je peux appeler une routine pour calculer les longueurs relatives de la ligne entre les points avec lesquels je commence , et la ligne entre le point où je termine en utilisant une simple hypoténuse calc. Le rapport entre eux est la quantité de zoom: je multiplie la portée de la région par cette quantité.

J'espère que c'est utile à quelqu'un.

Dan Donaldson
la source
0

J'ai pris l'idée d'une vue transparente "superposée", de la réponse de MystikSpiral, et cela a parfaitement fonctionné pour ce que j'essayais de réaliser; solution rapide et propre.

En bref, j'avais un UITableViewCell personnalisé (conçu en IB) avec un MKMapView sur le côté gauche et quelques UILabels sur la droite. Je voulais créer la cellule personnalisée afin que vous puissiez la toucher n'importe où et cela pousserait un nouveau contrôleur de vue. Cependant, toucher la carte ne passait pas de touches `` vers le haut '' à UITableViewCell jusqu'à ce que j'aie simplement ajouté un UIView de la même taille que la vue de la carte juste au-dessus (dans IB) et que son arrière-plan soit la `` couleur claire '' dans le code ( ne pensez pas que vous pouvez définir clearColor dans IB ??):

dummyView.backgroundColor = [UIColor clearColor];

Je pensais que cela pourrait aider quelqu'un d'autre; certainement si vous souhaitez obtenir le même comportement pour une cellule de vue tableau.

petert
la source
"Cependant, toucher la carte ne passait pas de touches" vers le haut "à l'UITableViewCell jusqu'à ce que j'aie simplement ajouté un UIView de la même taille que la vue de la carte juste au-dessus." Ce n'est pas vrai. La carte traite les touches car elle a ses propres interactions utilisateur telles que le défilement, etc. voir délégué.
BROK3N S0UL