96

何が間違っているのかわかりませんが、MKMapViewオブジェクトのタッチをキャッチしようとしています。次のクラスを作成してサブクラス化しました。

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

@interface MapViewWithTouches : MKMapView {

}

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

@end

そして実装:

#import "MapViewWithTouches.h"
@implementation MapViewWithTouches

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

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

}
@end

しかし、このクラスを使用すると、コンソールに何も表示されないように見えます:

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

私が間違っていることは何か分かりますか?

4

15 に答える 15

149

これを達成するために私が見つけた最良の方法は、Gesture Recognizer を使用することです。他の方法では、特にマルチタッチの場合、Apple のコードを不完全に複製する多くのハックなプログラミングが必要になることが判明しました。

私がやっていることは次のとおりです: 防ぐことができず、他のジェスチャ レコグナイザーを防ぐことができないジェスチャ レコグナイザーを実装します。それをマップビューに追加してから、gestureRecognizer の touchesBegan、touchesMoved などを自由に使用してください。

MKMapView 内のタップを検出する方法 (sans トリック)

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

スイフト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
    }
}
于 2010-10-31T18:44:12.337 に答える
29

ピザと絶叫の 1 日の後、私はついに解決策を見つけました! とてもきれい!

ピーター、私は上記のあなたのトリックを使用し、それを少し調整して、MKMapViewで完全に機能し、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

それがあなたの何人かを助けることを願っています!

乾杯

于 2009-06-27T15:30:05.757 に答える
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];

    //.............
}
于 2011-06-24T10:20:49.547 に答える
12

MKMapView の場合、実際に機能するソリューションはジェスチャー認識です!

地図をドラッグしたり、ピンチしてズームしたりするときに、現在地の地図の中心を更新するのをやめたいと思っていました。

したがって、ジェスチャ認識エンジンを作成して 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];
}

UIGestureRecognizer Class Referenceを参照して、利用可能なすべてのジェスチャ レコグナイザーを確認してください。

デリゲートを自己に定義したため、プロトコル 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;
...

そして、私が正しく理解していれば、複数のジェスチャーを同時に認識できるようにするために、メソッドgestureRecognizer:gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:をオーバーライドします。

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

次に、ジェスチャー認識エンジンによって呼び出されるメソッドを記述します。

// 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];
}
于 2011-04-20T00:55:34.217 に答える
7

誰かが私のように同じことをしようとしている場合に備えて: ユーザーがタップした時点で注釈を作成したかったのです。そのために私はUITapGestureRecognizer解決策を使用しました:

UITapGestureRecognizer *tapGestureRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(didTapOnMap:)];
[self.mapView addGestureRecognizer:tapGestureRecognizer];
[tapGestureRecognizer setDelegate:self];

- (void)didTapOnMap:(UITapGestureRecognizer *)gestureRecognizer
{
    CGPoint point = [gestureRecognizer locationInView:self.mapView];
    CLLocationCoordinate2D coordinate = [self.mapView convertPoint:point toCoordinateFromView:self.mapView];
    .......
}

ただし、didTapOnMap:注釈をタップしたときにも呼び出され、新しい注釈が作成されました。解決策は、次を実装することUIGestureRecognizerDelegateです。

- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch
{
    if ([touch.view isKindOfClass:[MKAnnotationView class]])
    {
        return NO;
    }
    return YES;
}
于 2015-03-29T15:20:17.513 に答える
3

UIWebView ベースのコントロールで頻繁に行われるように、タッチをキャッチするために透明なビューをオーバーレイする必要があるでしょう。マップ ビューは、マップの移動、中央揃え、ズームなどを可能にするために、タッチですでに多くの特別なことを行っています...メッセージがアプリにバブルアップしないようにします。

私が考えることができる他の2つの(テストされていない)オプション:

1) IB を介してファーストレスポンダーを辞任し、「ファイルの所有者」に設定して、ファイルの所有者がタッチに応答できるようにします。UIView ではなく MKMapView が NSObject を拡張しているため、これが機能するかどうかは疑わしいです。結果として、タッチイベントがまだあなたに伝播されない可能性があります。

2) Map の状態が変化したとき (ズーム時など) にトラップしたい場合は、特定のイベントをリッスンする MKMapViewDelegate プロトコルを実装するだけです。私の推測では、これがインタラクションを簡単にトラップするための最良の方法です (透明なビュー オーバー ザ マップの実装を除けば)。MKMapView を格納する View Controller をマップのデリゲートとして設定することを忘れないでください ( map.delegate = self)。

幸運を。

于 2009-06-26T16:21:57.013 に答える
2

それで、これをいじって半日後、私は次のことを見つけました:

  1. 他の誰もが見つけたように、ピンチは機能しません。MKMapViewのサブクラス化と上記のメソッド(インターセプト)の両方を試しました。そして結果は同じです。
  2. スタンフォードのiPhoneビデオで、Appleの人は、タッチリクエスト(上記の2つの方法)を「転送」すると、UIKitの多くのエラーが発生し、おそらく機能しないと言っています。

  3. 解決策:ここで説明します:MKMapViewのiPhoneタッチイベントの傍受/ハイジャック。基本的に、レスポンダーがイベントを取得する前にイベントを「キャッチ」し、そこで解釈します。

于 2010-03-08T08:30:17.380 に答える
2

私は実験していませんが、MapKit がクラス クラスターに基づいている可能性が高いため、サブクラス化は難しく、効果がありません。

MapKit ビューをカスタム ビューのサブビューにすることをお勧めします。これにより、タッチ イベントが到達する前にインターセプトできるようになります。

于 2009-06-26T16:01:56.650 に答える
0

ピザと悲鳴をありがとう - あなたは私に多くの時間を節約しました.

multipletouchenabled は散発的に機能します。

viewTouch.multipleTouchEnabled = TRUE;

最後に、タッチをキャプチャする必要があるときにビューを切り替えました (ピンチズームが必要な時点とは異なります)。

    [mapView removeFromSuperview];
    [viewTouch addSubview:mapView];
    [self.view insertSubview:viewTouch atIndex:0];
于 2009-07-22T12:14:10.157 に答える
0

タッチの数と位置を追跡し、ビュー内のそれぞれの位置を取得できることに気付きました。

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

これらの値を使用してマップのズーム レベルを更新しようとした人はいますか? 開始位置を記録してから終了位置を記録し、相対的な差を計算してマップを更新するだけです。

私はMartinから提供された基本的なコードで遊んでいます.これはうまくいくようです...

于 2009-12-14T23:43:11.460 に答える
0

MKMapView をカスタム ビューのサブビューにして実装する

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

カスタムビューで、サブビューの代わりに自分自身を返します。

于 2009-06-27T03:08:25.653 に答える
0

MystikSpiral の回答から、「オーバーレイ」透明ビューのアイデアを採用しましたが、それは私が達成しようとしていたものに対して完全に機能しました。迅速でクリーンなソリューション。

要するに、左側に MKMapView があり、右側にいくつかの UILabels があるカスタム UITableViewCell (IB で設計された) がありました。カスタムセルを作成して、どこでもタッチできるようにしたいと思いました。これにより、新しいビューコントローラーがプッシュされます。ただし、マップ ビューと同じサイズの UIView を (IB で) マップ ビューのすぐ上に追加し、その背景をコードで「クリア カラー」にするまで、マップに触れてもタッチが UITableViewCell に「上」に渡されませんでした ( IB で clearColor を設定できるとは思わない??):

dummyView.backgroundColor = [UIColor clearColor];

他の誰かを助けるかもしれないと思った; テーブルビューセルに対して同じ動作を実現したい場合は確かに。

于 2010-04-06T14:03:23.130 に答える