Frage

Ich bin nicht sicher, was ich tue falsch, aber ich versuche, Berührungen auf einem MKMapView Objekt zu fangen. Ich subclassed es durch die folgende Klasse zu erstellen:

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

@interface MapViewWithTouches : MKMapView {

}

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

@end

Und die Umsetzung:

#import "MapViewWithTouches.h"
@implementation MapViewWithTouches

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

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

}
@end

Aber es sieht so aus, als ich diese Klasse zu verwenden, ich sehe nichts auf der Konsole:

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

Jede Idee, was ich falsch mache?

War es hilfreich?

Lösung

Der beste Weg, die ich gefunden habe, dies zu erreichen, ist mit einem Gestenerkenner. Andere Möglichkeiten, drehen viele hackish Programmierung einzubeziehen heraus, dass Apples Code unvollkommen dupliziert, vor allem im Fall von Multi-Touch.

Hier ist, was ich tue: eine Gestenerkenner implementieren, die nicht verhindert werden können, und das kann nicht andere Geste Erkenner verhindern. Fügen Sie es zu der Kartenansicht, und verwenden Sie dann die GestureRecognizer des touchesBegan, touchesMoved etc. zu Ihrer Phantasie.

Wie jede Hahn erkennen innerhalb eines MKMapView (ohne Tricks)

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

Andere Tipps

Nach einem Tag an Pizzen, Rufe, fand ich endlich die Lösung! Sehr ordentlich!

Peter, habe ich Ihren Trick oben und es ein wenig gezwickt endlich eine Lösung zu haben, die mit MKMapView perfekt funktionieren und soll mit UIWebView

arbeiten auch

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

Ich hoffe, dass einige von Ihnen helfen!

Prost

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

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

Für eine MKMapView die reale Arbeitslösung ist mit Geste recognization!

Me wollte ich auf meinem Standort Aktualisieren der Mitte der Karte stoppen, wenn ich die Karte oder Prise vergrößern ziehen.

So erstellen und Ihre Gestenerkenner zum mapView hinzufügen:

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

Sehen Sie die UIGestureRecognizer Klassenreferenz um zu sehen, alle verfügbaren Gestenerkenner.

Weil wir den Delegierten selbst definiert haben, müssen wir das Protocole UIGestureRecognizerDelegate implementieren:

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

Und überschreiben die Methode GestureRecognizer: GestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer: um mehrere Gesten gleichzeitig zu ermöglichen, zu erkennen, wenn ich verstehe rechts:

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

Sie nun die methodes schreiben, die durch unsere Geste Erkenner aufgerufen wird:

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

Für den Fall, jemand versucht, das gleiche wie ich zu tun: ich eine Anmerkung zu dem Punkt, wo die Benutzer Taps schaffen wollte. Dafür habe ich die UITapGestureRecognizer Lösung:

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

Allerdings wurde didTapOnMap: auch aufgerufen, wenn ich auf der Anmerkung und ein neuer erstellt werden würde angezapft. Die Lösung ist die UIGestureRecognizerDelegate zu implementieren:

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

Sie werden wahrscheinlich brauchen eine transparente Sicht zu überlagern die Berührungen nur zu fangen wie getan wird so oft mit UIWebView-basierten Steuerungen. Die Kartenansicht bereits tut ein paar spezielle Dinge mit einem Hauch, um die Karte zu ermöglichen, bewegt zu werden, zentriert, gezoomt, etc ..., dass die Nachrichten nicht an Ihre App sprudelte zu werden.

Zwei weitere (ungetestet) Optionen, die ich mir vorstellen kann:

1) Resign die Ersthelfer über IB und setzt sie auf „Datei Besitzer“ Datei Besitzer zu erlauben, die Berührungen zu reagieren. Ich eine fragwürdige, dass dies funktionieren wird, weil MKMapView NSObject erstreckt, nicht UIView ans Ergebnis wird die Berührungsereignisse können noch nicht zu dir nach oben weitergegeben erhalten.

2) Wenn Sie zu stoppen wollen, wenn die Karte Zustandsänderungen (wie auf einem Zoom) nur das MKMapViewDelegate Protokoll implementieren für bestimmte Ereignisse zu hören. Meine Vermutung ist, ist dies die beste Chance auf eine Interaktion leicht Trapping (kurz von dem transparenten Blick über die Karte der Umsetzung). Vergessen Sie nicht, den View-Controller-Gehäuse die MKMapView als die Karte des Delegierten zu setzen (map.delegate = self).

Good Luck.

ich nicht experimentiert haben, aber es gibt eine gute Chance MapKit um eine Klasse Cluster basiert und daher Subklassen es schwierig und ineffektiv ist.

Ich würde vorschlagen, die MapKit Herstellung einer Subview einer benutzerdefinierten Ansicht zu sehen, die es erlauben, sollten Sie Touch-Ereignisse abzufangen, bevor sie es erreichen.

So nach einem halben Tag Herumspielen mit diesem fand ich folgendes:

  1. Wie alle anderen gefunden, Kneifen nicht funktioniert. I versucht, beide Subklassen MKMapView und das oben beschriebene Verfahren (Abfangen it). Und das Ergebnis ist das gleiche.
  2. In der Stanford iPhone Videos, sagt ein Mann von Apple, dass viele der Dinge UIKit verursachen eine Menge von Fehlern, wenn Sie „Transfer“ die Touch-Anfragen (auch bekannt als die beiden oben beschriebenen Verfahren), und Sie werden wahrscheinlich nicht bekommen sie arbeiten.

  3. DIE LÖSUNG : beschrieben hier: Intercepting / Hijacking iPhone touch-Events für MKMapView . Grundsätzlich Sie „fangen“ das Ereignis vor jedem Responder wird es, und interpretieren es dort.

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

    }

}

Machen Sie die MKMapView eine Subview einer benutzerdefinierten Ansicht und implementieren

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

in der benutzerdefinierten Ansicht Selbst zurückzukehren anstelle des subview.

Danke für die Pizza und screamings - Sie hat mich gerettet viel Zeit

.

multipletouchenabled sporadisch funktioniert.

viewTouch.multipleTouchEnabled = TRUE;

Am Ende wechselte ich die Ansichten, wenn ich brauchte die Berührung (anderen Zeitpunkt als um pinchzooms) zu erfassen:

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

Ich stelle fest, dass Sie die Anzahl und Lage von Berührungen verfolgen können, und die Lage der jeweils in einer Ansicht erhalten:

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

Hat jemand versucht, diese Werte mit der Karte des Zoomstufe aktualisieren? Es wäre darum, die Startpositionen der Aufnahme und dann die Zielstellen, die relative Differenz zu berechnen und die Karte zu aktualisieren.

ich mit dem Basiscode ich spiele von Martin zur Verfügung gestellt, und das sieht aus wie es funktioniert ...

Hier ist, was ich zusammen, dass Prise im Simulator erlaubt zoomt (habe auf einem echten iPhone nicht versucht), aber ich denke, wäre in Ordnung:

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

Die Grundidee ist, dass, wenn der Benutzer zwei Finger verwendet, um die Werte verfolgen. Ich den Start aufnehmen und Punkte in Startpunkte A und B endet dann die aktuellen Tracking-Punkte, die ich aufnehmen, und wenn ich fertig bin, auf touchesEnded, kann ich eine Routine aufrufen, um die relativen Längen der Linie zwischen den Punkten zu berechnen Ich beginne mit und die Linie zwischen dem Punkt I mit mit einfacher Hypotenuse calc beenden. Das Verhältnis zwischen ihnen ist die Zoom-Betrag: i. Die Region Spanne um diesen Betrag multiplizieren

Hoffe, dass es für jemanden nützlich ist.

habe ich die Idee einer „Overlay“ transparent Ansicht von MystikSpiral Antwort, und es funktionierte perfekt für das, was ich erreichen wollte; schnelle und saubere Lösung.

Kurz gesagt, hatte ich eine benutzerdefinierte UITableViewCell (entworfen in IB) mit einem MKMapView auf der linken Seite und einigen UILabels auf der rechten Seite. Ich wollte die benutzerdefinierte Zelle machen, so können Sie es überall berühren konnten, und dies würde das einen neuen View-Controller. Allerdings hat die Karte berührt nicht berührt ‚up‘ Pass auf die UITableViewCell, bis ich einfach eine UIView von der gleichen Größe wie der Kartenansicht rechts oben drauf (in IB) hinzugefügt und machte es ist Hintergrund der ‚klare Farbe‘ im Code ( glaube nicht, dass Sie in Clear IB einstellen ??):

dummyView.backgroundColor = [UIColor clearColor];

Ich dachte, es jemand anderes helfen könnte; sicher, wenn Sie das gleiche Verhalten für eine Tabellenansicht Zelle erreichen wollen.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top