Jak przechwycić dotknięcia zdarzeń w obiektach MKMapView lub UIWebView?


96

Nie jestem pewien, co robię źle, ale staram się złapać dotknięcie MKMapViewprzedmiotu. Podklasowałem go, tworząc następującą klasę:

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

@interface MapViewWithTouches : MKMapView {

}

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

@end

I realizacja:

#import "MapViewWithTouches.h"
@implementation MapViewWithTouches

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

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

}
@end

Ale wygląda na to, że kiedy używam tej klasy, nic nie widzę na konsoli:

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

Masz pojęcie, co robię źle?

Odpowiedzi:


147

Najlepszym sposobem, w jaki udało mi się to osiągnąć, jest Rozpoznawanie gestów. Inne sposoby okazują się obejmować dużo hakerskiego programowania, które niedoskonale powiela kod Apple, szczególnie w przypadku multitouch.

Oto, co robię: Zaimplementuj aparat rozpoznawania gestów, któremu nie można zapobiec i który nie może uniemożliwić innym rozpoznawania gestów. Dodaj go do widoku mapy, a następnie użyj gestu Dotyk Rozpoznawania Rozpoczynający, dotknięcia Przeniesiony itp. Według własnego uznania.

Jak wykryć dowolne dotknięcie w MKMapView (bez sztuczek)

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
do czego służy „LockOnUserLocation”?
jowie

to jest obca zmienna specyficzna dla mojej aplikacji. śledzi, czy system powinien automatycznie wyśrodkować mapę na bieżącej lokalizacji
gonzojive

To idealne rozwiązanie. Potrzebuję jednego wyjaśnienia: w metodzie „- (void) touchesBegan: (NSSet *) dotyka zdarzenia ze zdarzeniem: (UIEvent *)”, jaki jest cel użycia kodu: if (touchesBeganCallback) touchesBeganCallback (touches, event);
Satyam

1
W większości działa to świetnie, ale znalazłem z tym jeden problem. Jeśli kod HTML w widoku sieci Web zawiera videotag HTML5 z kontrolkami, moduł rozpoznawania gestów uniemożliwi użytkownikowi korzystanie z tych elementów sterujących. Szukałem obejścia tego problemu, ale jeszcze go nie znalazłem.
Bryan Irace

Dziękuję za udostępnienie. Nie rozumiem, dlaczego nie ma odpowiedniej metody delegata do śledzenia interakcji użytkowników z widokiem mapy, ale to działa dobrze.
Justin Driscoll

29

Po całym dniu pizzy i krzyków w końcu znalazłem rozwiązanie! Bardzo schludny!

Peter, użyłem twojej sztuczki powyżej i trochę go poprawiłem, aby w końcu mieć rozwiązanie, które doskonale współpracuje z MKMapView i powinno działać również z 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

Mam nadzieję, że niektórym z Was to pomoże!

Twoje zdrowie


14
Miły. Mała sugestia: należy unikać nazywania własnych klas przedrostkiem interfejsu użytkownika. Apple zastrzega / odradza używanie NS lub UI jako przedrostka klasy, ponieważ mogą one skończyć się konfliktem z klasą Apple (nawet jeśli jest to klasa prywatna).
Daniel Dickison

Hej Daniel, masz całkowitą rację, ja też tak myślałem! Aby uzupełnić powyższą odpowiedź, dodam małe ostrzeżenie: Mój przykład zakłada, że ​​jest tylko jeden dotknięty widok obiektu, który konsumuje wszystkie zdarzenia. Ale to nieprawda. Możesz mieć adnotacje na górze mapy, a wtedy mój kod już nie działa. Aby pracować w 100%, musisz pamiętać dla każdego trafienia Przetestuj widok powiązany z tym konkretnym zdarzeniem (i ostatecznie zwolnij go, gdy aktywowane jest dotknięcie zakończone lub dotknięcie anulowane, więc nie musisz śledzić zakończonych wydarzeń ...).
Martin

1
Bardzo przydatny kod, dzięki Martin! Zastanawiałem się, czy po wdrożeniu tego próbowałeś powiększać mapę przez szczypanie? Dla mnie, kiedy uruchomiłem go, używając w zasadzie tego samego kodu, który masz nad wszystkim, wydawało się, że działa, z wyjątkiem powiększania mapy przez szczypanie. Czy ktoś ma jakieś pomysły?
Adam Alexander

Hej Adam, ja też mam to ograniczenie i nie bardzo rozumiem dlaczego! To naprawdę denerwujące. Jeśli znajdziesz rozwiązanie, daj mi znać! Thx
Martin

Ok, zagłosowałem za tym, ponieważ początkowo wydawało się, że rozwiązuje mój problem. JEDNAK...! Nie mogę zmusić multi-touch do pracy. Oznacza to, że nawet jeśli bezpośrednio mijam dotknięcia Rozpoczęty i dotknie Przeniesiony, aby wyświetlić Dotykany (przechwytuję po dotknięciu Zakończony), nie mogę powiększać mapy za pomocą gestów szczypania. (Ciąg dalszy ...)
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
Nie jestem pewien, dlaczego to nie jest najlepsza odpowiedź. Wydaje się działać idealnie i jest znacznie prostszy.
elsurudo

12

W przypadku MKMapView prawdziwym działającym rozwiązaniem jest rozpoznawanie gestów!

Ja chciałem przestać aktualizować środek mapy w mojej lokalizacji, gdy przeciągam mapę lub szczypię, aby powiększyć.

Zatem utwórz i dodaj swój aparat rozpoznawania gestów do 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];
}

Przejrzyj odwołanie do klasy UIGestureRecognizer, aby wyświetlić wszystkie dostępne narzędzia do rozpoznawania gestów.

Ponieważ zdefiniowaliśmy delegata do siebie, musimy zaimplementować protokół 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;
...

I zastąp metodę gestRecognizer: GestRecognizer shouldRecognizeSimultacentWithGestureRecognizer: aby umożliwić rozpoznawanie wielu gestów jednocześnie, jeśli dobrze zrozumiałem:

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

Teraz napisz metody, które będą wywoływane przez nasze rozpoznawanie gestów:

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

To idealne rozwiązanie! Oto kilka ciekawostek: jeśli chcesz przechwycić, gdy użytkownik zakończy wykonywanie jakiejkolwiek akcji, powinno wystarczyć - (void) handleLongPressAndPinchGesture: (UIGestureRecognizer *) sender {if (sender.state == UIGestureRecognizerStateEnded) {NSLog (@ "handleLongestPressAndPure) ; }}
Alejandro Luengo

Nie zapomnij również dodać delegata <UIGestureRecognizerDelegate>
Alejandro Luengo

6

Na wypadek, gdyby ktoś próbował zrobić to samo co ja: chciałem utworzyć adnotację w miejscu, w którym użytkownik stuka. Do tego użyłem UITapGestureRecognizerrozwiązania:

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

Jednak didTapOnMap:został również wywołany, gdy dotknąłem adnotacji i utworzono nową. Rozwiązaniem jest wdrożenie UIGestureRecognizerDelegate:

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

To świetne rozwiązanie! Ale to nie działa, jeśli używasz widoku niestandardowego jako MKAnnotation. W takim przypadku możesz mieć widok podrzędny innej adnotacji wyzwalający rozpoznawanie gestów. Musiałem rekurencyjnie sprawdzić podgląd touch.view, aby znaleźć potencjalny MKAnnotationView
KIDdAe

3

Prawdopodobnie będziesz musiał nałożyć przezroczysty widok, aby uchwycić dotknięcia, tak jak to się często dzieje w przypadku kontrolek opartych na UIWebView. Widok mapy robi już wiele specjalnych rzeczy za pomocą dotyku, aby umożliwić przesuwanie, wyśrodkowanie, powiększanie mapy itp., Że wiadomości nie są wyświetlane w aplikacji.

Dwie inne (NIETESTOWANE) opcje, które przychodzą mi do głowy:

1) Zrezygnuj z pierwszej osoby odpowiadającej za pośrednictwem IB i ustaw ją na „Właściciel pliku”, aby umożliwić właścicielowi pliku reagowanie na dotknięcia. Wątpię, czy to zadziała, ponieważ MKMapView rozszerza NSObject, a nie UIView, w wyniku czego zdarzenia dotykowe nadal mogą nie być propagowane do Ciebie.

2) Jeśli chcesz przechwytywać zmiany stanu mapy (na przykład przy powiększaniu), po prostu zaimplementuj protokół MKMapViewDelegate, aby nasłuchiwać określonych zdarzeń. Moje przeczucie jest takie, że jest to najlepsza szansa na łatwe przechwycenie niektórych interakcji (poza implementacją przezroczystego widoku na mapie). Nie zapomnij ustawić kontrolera widoku mieszczącego MKMapView jako delegata mapy ( map.delegate = self).

Powodzenia.


MKMapView zdecydowanie podklasa UIView.
Daniel Dickison

2

Nie eksperymentowałem, ale istnieje duża szansa, że ​​MapKit jest oparty na klastrze klas, dlatego tworzenie podklas jest trudne i nieskuteczne.

Sugerowałbym, aby widok MapKit był widokiem podrzędnym widoku niestandardowego, który powinien umożliwiać przechwytywanie zdarzeń dotykowych, zanim do niego dotrą.


Cześć Graham! Dziękuję za pomoc! Jeśli utworzę super niestandardowy widok, taki jak sugerujesz, w jaki sposób mogę przesłać zdarzenia do MKMapView? Dowolny pomysł?
Martin

2

Tak więc po pół dnia zabawy z tym znalazłem:

  1. Jak wszyscy stwierdzili, szczypanie nie działa. Próbowałem zarówno podklasować MKMapView, jak i metodę opisaną powyżej (przechwytując ją). Rezultat jest taki sam.
  2. W filmach na temat iPhone'a ze Stanforda pewien facet z Apple mówi, że wiele rzeczy związanych z UIKit spowoduje wiele błędów, jeśli „prześlesz” żądania dotyku (czyli dwie metody opisane powyżej) i prawdopodobnie nie zadziała.

  3. ROZWIĄZANIE : jest opisane tutaj: Przechwytywanie / przechwytywanie zdarzeń dotykowych iPhone'a dla MKMapView . Zasadniczo „łapiesz” zdarzenie, zanim otrzyma je jakikolwiek respondent i tam je interpretujesz.


2

W 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

Ustaw widok MKMapView jako widok podrzędny widoku niestandardowego i zaimplementuj

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

w widoku niestandardowym, aby zwrócić siebie zamiast widoku podrzędnego.


Cześć Peter, dziękuję za odpowiedź! Ale myślę, że robiąc to, MKMapView może nie być w stanie uzyskać żadnych wydarzeń dotykowych, prawda? Szukam sposobu, aby po prostu złapać wydarzenie, a następnie przesłać je do MKMapView.
Martin

0

Dzięki za pizzę i krzyki - zaoszczędziłeś mi dużo czasu.

multipletouchenabled będzie działać sporadycznie.

viewTouch.multipleTouchEnabled = TRUE;

W końcu zmieniłem widoki, kiedy musiałem uchwycić dotyk (inny punkt w czasie niż potrzeba pinchzoomów):

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

ale nie działa z powiększaniem na żywo. Wydaje się też, że zawsze się oddala.
Rog

0

Zauważyłem, że możesz śledzić liczbę i lokalizację dotknięć, a także uzyskać lokalizację każdego z nich w widoku:

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

Czy ktoś inny próbował użyć tych wartości, aby zaktualizować poziom powiększenia mapy? To byłaby kwestia zapisania pozycji startowych, a następnie końcowych, obliczenia względnej różnicy i aktualizacji mapy.

Bawię się podstawowym kodem dostarczonym przez Martina i wygląda na to, że zadziała ...


0

Oto, co stworzyłem, co pozwala na przybliżanie przez szczypanie w symulatorze (nie próbowałem na prawdziwym iPhonie), ale myślę, że byłoby dobrze:

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

Główną ideą jest to, że jeśli użytkownik używa dwóch palców, można śledzić wartości. Zapisuję punkty początkowe i końcowe w punktach początkowych A i B. Następnie zapisuję bieżące punkty śledzenia, a kiedy skończę, po dotknięciu Koniec, mogę wywołać procedurę, aby obliczyć względne długości linii między punktami, od których zaczynam i linię między punktem, który kończę za pomocą prostego obliczenia przeciwprostokątnego. Stosunek między nimi to wielkość powiększenia: mnożę zakres regionu przez tę wartość.

Mam nadzieję, że to komuś się przyda.


0

Pomysł „nakładkowego” przezroczystego widoku zaczerpnąłem z odpowiedzi MystikSpiral i działał on doskonale w tym, co próbowałem osiągnąć; szybkie i czyste rozwiązanie.

Krótko mówiąc, miałem niestandardowy UITableViewCell (zaprojektowany w IB) z MKMapView po lewej stronie i kilkoma etykietami UIL po prawej. Chciałem zrobić niestandardową komórkę, aby można było jej dotknąć w dowolnym miejscu, a to spowodowałoby uruchomienie nowego kontrolera widoku. Jednak dotknięcie mapy nie spowodowało dotknięcia `` w górę '' do UITableViewCell, dopóki po prostu nie dodałem UIView o tym samym rozmiarze co widok mapy bezpośrednio nad nią (w IB) i nie nadałem jego tła `` wyraźnego koloru '' w kodzie ( nie sądzisz, że możesz ustawić clearColor w IB ??):

dummyView.backgroundColor = [UIColor clearColor];

Pomyślałem, że może to pomóc komuś innemu; z pewnością, jeśli chcesz osiągnąć to samo zachowanie dla komórki widoku tabeli.


„Jednak dotknięcie mapy nie spowodowało dotknięcia 'w górę' do UITableViewCell, dopóki po prostu nie dodałem UIView o tym samym rozmiarze, co widok mapy na samej górze.” To nie jest prawda. Mapa przetwarza dotknięcia, ponieważ ma własne interakcje użytkownika, takie jak przewijanie itp. Jeśli chcesz wykryć chociaż w komórce zamiast wchodzić w interakcję z mapą, po prostu ustaw map.isUserInteractionEnabled = false Możesz następnie po prostu użyć didSelectRowAtIndexPath w tabeli zobacz delegata.
BROK3N S0UL
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.