Come intercettare gli eventi di tocco su un oggetto MKMapView o UIWebView?


96

Non sono sicuro di cosa sto facendo di sbagliato, ma cerco di catturare tocchi su un MKMapViewoggetto. L'ho sottoclasse creando la seguente classe:

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

@interface MapViewWithTouches : MKMapView {

}

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

@end

E l'implementazione:

#import "MapViewWithTouches.h"
@implementation MapViewWithTouches

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

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

}
@end

Ma sembra che quando uso questa classe, non vedo nulla sulla console:

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

Hai idea di cosa sto sbagliando?

Risposte:


147

Il modo migliore che ho trovato per raggiungere questo obiettivo è con un riconoscimento di gesti. Altri modi si rivelano coinvolgere un sacco di programmazione hacker che duplica in modo imperfetto il codice Apple, specialmente nel caso del multitouch.

Ecco cosa faccio: implementare un riconoscimento di gesti che non può essere prevenuto e che non può impedire altri riconoscitori di gesti. Aggiungilo alla visualizzazione mappa, quindi utilizza i tocchi di gestureRecognizerBegan, touchesMoved, ecc.

Come rilevare qualsiasi tocco all'interno di un MKMapView (senza trucchi)

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

3
a cosa serve "lockedOnUserLocation"?
jowie

è una variabile estranea specifica per la mia applicazione. tiene traccia se il sistema dovrebbe centrare automaticamente la mappa sulla posizione corrente
gonzojive

Questa è la soluzione perfetta. Ho bisogno di una precisazione: Nel metodo "- (void) touchesBegan: (NSSet *) touches withEvent: (UIEvent *) event", qual è lo scopo dell'utilizzo del codice: if (touchesBeganCallback) touchesBeganCallback (touches, event);
Satyam

1
Questo funziona alla grande per la maggior parte, ma ho trovato un problema con esso. Se l'HTML nella visualizzazione Web contiene un videotag HTML5 con controlli, il riconoscimento dei gesti impedirà all'utente di utilizzare i controlli. Ho cercato una soluzione alternativa per questo, ma devo ancora trovarne una.
Bryan Irace

Grazie per aver condiviso. Il motivo per cui non esiste un metodo delegato adatto per il monitoraggio delle interazioni dell'utente con una visualizzazione mappa va oltre le mie capacità, ma funziona bene.
Justin Driscoll

29

Dopo una giornata di pizze, urla, finalmente ho trovato la soluzione! Molto pulito!

Peter, ho usato il tuo trucco sopra e l'ho modificato un po 'per avere finalmente una soluzione che funzioni perfettamente con MKMapView e dovrebbe funzionare anche 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

Spero che questo possa aiutare alcuni di voi!

Saluti


14
Bello. Piccolo suggerimento: dovresti evitare di nominare le tue classi con un prefisso dell'interfaccia utente. Apple si riserva / sconsiglia di utilizzare NS o UI come prefisso di classe, perché potrebbero finire per scontrarsi con una classe Apple (anche se si tratta di una classe privata).
Daniel Dickison,

Ehi Daniel, hai perfettamente ragione, l'ho pensato anche io! Per completare la mia risposta sopra, lasciami aggiungere un piccolo avvertimento: Il mio esempio presume che ci sia un solo oggetto viewTouched, che sta consumando tutti gli eventi. Ma non è vero. Potresti avere alcune annotazioni in cima alla tua mappa e quindi il mio codice non funziona più. Per funzionare al 100%, è necessario ricordare per ogni hitTest la visualizzazione associata a quell'evento specifico (ed eventualmente rilasciarla quando viene attivato touchesEnded o touchesCancelled in modo da non dover tenere traccia degli eventi finiti ...).
Martin,

1
Codice molto utile, grazie Martin! Mi chiedevo se hai provato a ingrandire la mappa dopo aver implementato questo? Per me quando l'ho fatto funzionare usando fondamentalmente lo stesso codice che hai sopra tutto sembrava funzionare tranne pizzicare lo zoom sulla mappa. Qualcuno ha qualche idea?
Adam Alexander,

Hey Adam, anch'io ho questa limitazione e non capisco davvero perché! Questo è davvero fastidioso. Se trovi una soluzione, fammelo sapere! Thx
Martin

Ok, ho votato perché inizialmente sembrava risolvere il mio problema. PERÒ...! Non riesco a far funzionare il multi-touch. Cioè, anche se passo direttamente touchesBegan e touchesMoved a viewTouched (eseguo l'intercettazione su touchesEnded), non posso ingrandire la mappa con i gesti di pizzico. (Continua ...)
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];

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

3
Non sono sicuro del motivo per cui questa non è la risposta migliore. Sembra funzionare perfettamente ed è molto più semplice.
elsurudo

12

Per un MKMapView la vera soluzione di lavoro è con il riconoscimento dei gesti!

Io volevo interrompere l'aggiornamento del centro della mappa sulla mia posizione quando trascino la mappa o pizzico per ingrandire.

Quindi, crea e aggiungi il tuo riconoscimento di gesti a 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];
}

Guarda il riferimento alla classe UIGestureRecognizer per vedere tutti i riconoscitori di gesti disponibili.

Poiché abbiamo definito il delegato su self, dobbiamo implementare il protocollo 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;
...

E sovrascrivi il metodo gestureRecognizer: gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer: per consentire il riconoscimento simultaneo di più gesti, se ho capito bene:

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

Ora scrivi i metodi che verranno chiamati dai nostri riconoscitori di gesti:

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

Questa soluzione è perfetta! Alcune sveltine qui: se vuoi intercettare quando l'utente finisce di fare qualsiasi azione usando questo dovrebbe essere sufficiente - (void) handleLongPressAndPinchGesture: (UIGestureRecognizer *) sender {if (sender.state == UIGestureRecognizerStateEnded) {NSLog (@ "handleLongPressAndPinchGesture Ended" ; }}
Alejandro Luengo

Inoltre non dimenticare di aggiungere il delegato <UIGestureRecognizerDelegate>
Alejandro Luengo

6

Nel caso qualcuno stia cercando di fare lo stesso come me: volevo creare un'annotazione nel punto in cui l'utente tocca. Per questo ho usato la UITapGestureRecognizersoluzione:

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

Tuttavia, è didTapOnMap:stato chiamato anche quando ho toccato l'annotazione e ne sarebbe stata creata una nuova. La soluzione è implementare UIGestureRecognizerDelegate:

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

Questa è un'ottima soluzione! Ma non funziona se usi una visualizzazione personalizzata come MKAnnotation. In questo caso, potresti avere un'altra sottoview dell'annotazione che attiva il riconoscimento dei gesti. Ho dovuto controllare ricorsivamente la superview di touch.view per trovare un potenziale MKAnnotationView
KIDdAe

3

Probabilmente dovrai sovrapporre una vista trasparente per catturare i tocchi proprio come si fa spesso con i controlli basati su UIWebView. La visualizzazione mappa fa già un sacco di cose speciali con un tocco per consentire alla mappa di essere spostata, centrata, ingrandita, ecc ... in modo che i messaggi non vengano visualizzati nella tua app.

Altre due opzioni (NON TESTATE) a cui posso pensare:

1) Rinuncia al primo risponditore tramite IB e impostalo su "File's Owner" per consentire al proprietario del file di rispondere ai tocchi. Dubito che funzionerà perché MKMapView estende NSObject, non UIView e di conseguenza gli eventi di tocco potrebbero non essere propagati fino a te.

2) Se vuoi intercettare quando lo stato della mappa cambia (come su uno zoom) implementa semplicemente il protocollo MKMapViewDelegate per ascoltare eventi particolari. La mia impressione è che questa sia la tua occasione migliore per catturare facilmente alcune interazioni (a parte l'implementazione della vista trasparente sulla mappa). Non dimenticare di impostare il controller di visualizzazione che ospita MKMapView come delegato della mappa ( map.delegate = self).

In bocca al lupo.


MKMapView sicuramente sottoclasse UIView.
Daniel Dickison,

2

Non ho sperimentato, ma c'è una buona probabilità che MapKit sia basato su un cluster di classi, e quindi creare sottoclassi è difficile e inefficace.

Suggerirei di rendere la vista MapKit una vista secondaria di una vista personalizzata, che dovrebbe consentire di intercettare gli eventi di tocco prima che la raggiungano.


Ciao Graham! Grazie per l'aiuto! Se creo una visualizzazione super personalizzata come suggerisci, come posso inoltrare gli eventi a MKMapView? Qualche idea?
Martin,

2

Quindi, dopo mezza giornata di scherzi con questo, ho trovato quanto segue:

  1. Come tutti gli altri hanno scoperto, pizzicare non funziona. Ho provato sia la sottoclasse MKMapView che il metodo sopra descritto (intercettandolo). E il risultato è lo stesso.
  2. Nei video dell'iPhone di Stanford, un ragazzo di Apple dice che molte delle cose di UIKit causeranno molti errori se "trasferisci" le richieste di tocco (ovvero i due metodi descritti sopra), e probabilmente non riuscirai a farlo funzionare.

  3. LA SOLUZIONE : è descritta qui: Intercettazione / dirottamento degli eventi touch di iPhone per MKMapView . Fondamentalmente "catturi" l'evento prima che qualsiasi rispondente lo riceva e lo interpreti lì.


2

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

    }

}

0

Rendi MKMapView una vista secondaria di una vista personalizzata e implementala

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

nella visualizzazione personalizzata per restituire self invece della sottoview.


Ciao Peter, grazie per la tua risposta! Ma penso che così facendo, MKMapView potrebbe non essere in grado di ottenere alcun evento di tocco, non è vero? Sto cercando un modo per catturare solo l'evento e quindi inoltrarlo a MKMapView.
Martin,

0

Grazie per la pizza e le urla, mi hai fatto risparmiare un sacco di tempo.

multipletouchenabled funzionerà sporadicamente.

viewTouch.multipleTouchEnabled = TRUE;

Alla fine, ho cambiato le visualizzazioni quando avevo bisogno di catturare il tocco (in un momento diverso rispetto alla necessità di zoomare):

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

ma non funziona con lo zoom in tempo reale. Inoltre sembra sempre ridurre lo zoom.
Rog

0

Ho notato che puoi tenere traccia del numero e della posizione dei tocchi e ottenere la posizione di ciascuno in 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];
}

Qualcun altro ha provato a utilizzare questi valori per aggiornare il livello di zoom della mappa? Si tratterebbe di registrare le posizioni di partenza, quindi le posizioni di arrivo, calcolare la differenza relativa e aggiornare la mappa.

Sto giocando con il codice di base fornito da Martin e sembra che funzionerà ...


0

Ecco cosa ho messo insieme, che consente di pizzicare gli zoom nel simulatore (non ho provato su un vero iPhone), ma penso che andrebbe bene:

- (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'idea principale è che se l'utente utilizza due dita, tu monitori i valori. Registro i punti di inizio e di fine nei punti iniziali A e B. Quindi registro i punti di tracciamento correnti e quando ho finito, su touchesEnded, posso chiamare una routine per calcolare le lunghezze relative della linea tra i punti con cui inizio e la linea tra il punto con cui finisco usando semplice ipotenusa calc. Il rapporto tra loro è la quantità di zoom: moltiplico l'estensione della regione per quella quantità.

Spero sia utile a qualcuno.


0

Ho preso l'idea di una vista trasparente "sovrapposta", dalla risposta di MystikSpiral, e ha funzionato perfettamente per quello che stavo cercando di ottenere; soluzione rapida e pulita.

In breve, avevo un UITableViewCell personalizzato (progettato in IB) con un MKMapView sul lato sinistro e alcune UILabels sulla destra. Volevo creare la cella personalizzata in modo da poterla toccare ovunque e questo spingerebbe un nuovo controller di visualizzazione. Tuttavia, toccare la mappa non ha passato i ritocchi a UITableViewCell fino a quando non ho semplicemente aggiunto una UIView della stessa dimensione della visualizzazione della mappa sopra di essa (in IB) e ho reso lo sfondo il 'colore chiaro' nel codice ( non pensi di poter impostare clearColor in IB ??):

dummyView.backgroundColor = [UIColor clearColor];

Ho pensato che potesse aiutare qualcun altro; certamente se vuoi ottenere lo stesso comportamento per una cella della vista tabella.


"Tuttavia, toccare la mappa non ha passato i ritocchi a UITableViewCell fino a quando non ho semplicemente aggiunto una UIView delle stesse dimensioni della visualizzazione della mappa sopra di essa" Questo non è vero. La mappa sta elaborando i tocchi perché ha le sue interazioni utente come lo scorrimento, ecc. Se vuoi rilevare il pensiero sulla cella invece di interagire con la mappa, imposta semplicemente map.isUserInteractionEnabled = false Puoi quindi usare didSelectRowAtIndexPath nella tabella visualizzare delegato.
BROK3N S0UL
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.