سؤال

لست متأكدًا من الخطأ الذي أفعله ولكني أحاول أن أتطرق إلى أ 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];

هل لديك أي فكرة عما أفعله خطأ؟

هل كانت مفيدة؟

المحلول

أفضل طريقة وجدتها لتحقيق ذلك هي استخدام أداة التعرف على الإيماءات.وتبين أن الطرق الأخرى تنطوي على الكثير من البرمجة المتسللة التي تكرر بشكل غير كامل كود Apple، خاصة في حالة اللمس المتعدد.

وإليك ما أفعله:قم بتنفيذ أداة التعرف على الإيماءات التي لا يمكن منعها ولا يمكنها منع أدوات التعرف على الإيماءات الأخرى.قم بإضافته إلى عرض الخريطة، ثم استخدم اللمسات التي بدأها برنامج التعرف على الإيماءات، واللمسات المنقولة، وما إلى ذلك.إلى خيالك.

كيفية اكتشاف أي نقرة داخل MKMapView (بدون الحيل)

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

نصائح أخرى

بعد يوم من البيتزا والصراخ، أخيراً وجدت الحل!أنيق جدا!

بيتر، لقد استخدمت خدعتك أعلاه وقمت بتعديلها قليلاً حتى أحصل أخيرًا على حل يعمل بشكل مثالي مع 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

آمل أن يساعد ذلك البعض منكم!

هتافات

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

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

للاطلاع على 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 الدرجة المرجعي لمعرفة كل ما هو متاح التعرف على لفتة.

ولأن لدينا تعريف مندوب في تقرير المصير، لدينا لتنفيذ UIGestureRecognizerDelegate protocole:

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

والآن كتابة METHODES التي سوف يتم استدعاؤها من قبل التعرف فتة لدينا:

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

وفقط في حالة يحاول شخص ما أن تفعل الشيء نفسه مثلي: أردت أن تخلق تعليق توضيحي عند النقطة التي الصنابير المستخدم. لذلك اعتدت الحل 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;
}

وربما كنت سوف تحتاج إلى تراكب وجهة نظر شفافة للقبض على اللمسات مثل فقط ويتم في كثير من الأحيان مع وجود ضوابط تستند UIWebView. شاهد خريطة يفعل بالفعل مجموعة من الأشياء الخاصة مع لمسة من أجل السماح الخريطة لنقلها، تركز، التكبير، الخ ... أن الرسائل لا الحصول على فقاعات تصل إلى التطبيق الخاص بك.

وآخران (مجربة) خيارات أستطيع أن أفكر في:

1) الاستقالة من المستجيب الأول عبر IB وتعيينه إلى "مالك ملف" للسماح مالك الملف للرد على اللمسات. لي مشكوك فيه أن هذا العمل لأن MKMapView يمتد NSObject، وليس UIView الجواب نتيجة لأحداث اللمس لا يزال قد لا تحصل على نشر متروك لكم.

2) إذا كنت ترغب في فخ عندما يتغير خريطة الدولة (مثل على التكبير) فقط تنفيذ بروتوكول MKMapViewDelegate للاستماع لأحداث معينة. في اعتقادي هذا هو افضل بالرصاص في محاصرة بعض التفاعل بسهولة (قصيرة من تنفيذ عرض شفافة على الخريطة). لا تنسى أن تعيين مشاهدة تحكم إسكان MKMapView مندوبا للخريطة (map.delegate = self).

وحظا سعيدا.

وأنا لم جربت، ولكن هناك فرصة جيدة يستند MapKit حول مجموعة الطبقة، وبالتالي إن شاء subclasses ترث من الصعب وغير فعالة.

وأنا أقترح جعل MapKit عرض subview من طريقة عرض مخصصة، الذي يجب أن تسمح لك لاعتراض الأحداث اتصال قبل أن تصل إليها.

وبعد نصف يوم من العبث بهذا الأمر وجدت ما يلي:

  1. كما وجد الجميع، فإن القرص لا يعمل.لقد جربت كلاً من الفئة الفرعية MKMapView والطريقة الموضحة أعلاه (اعتراضها).والنتيجة هي نفسها.
  2. في مقاطع فيديو ستانفورد iPhone ، يقول رجل من Apple أن العديد من أشياء Uikit ستسبب الكثير من الأخطاء إذا "نقلت" طلبات اللمس (المعروفة أيضًا باسم الطريقتين الموصوفين أعلاه) ، وربما لن تعمل على العمل.

  3. الحل:موصوف هنا: اعتراض/اختطاف أحداث iPhone Touch لـ MKMapView.في الأساس، يمكنك "التقاط" الحدث قبل أن يحصل عليه أي مستجيب، وتفسيره هناك.

في سويفت 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)")

    }

}

تأكد من MKMapView على subview من طريقة عرض مخصصة وتنفيذ

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

وفي طريقة العرض المخصصة للعودة الذات بدلا من subview.

وشكرا على البيتزا وscreamings - هل أنقذ لي الكثير من الوقت

.

وmultipletouchenabled ستعمل بشكل متقطع.

viewTouch.multipleTouchEnabled = TRUE;

في النهاية، أنا حولت من وجهات النظر عندما كنت في حاجة للقبض على اتصال (نقطة مختلفة في الوقت المناسب من pinchzooms الحاجة):

    [mapView removeFromSuperview];
    [viewTouch addSubview:mapView];
    [self.view insertSubview:viewTouch atIndex: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];
}

وأي شخص آخر قد حاول استخدام هذه القيم لتحديث مستوى التكبير خريطة ل؟ وسيكون من مسألة تسجيل المواقف البداية، ثم المواقع النهاية، وحساب الفرق النسبي وتحديث الخريطة.

وأنا ألعب مع رمز الأساسية التي تقدمها مارتن، وهذا يبدو وكأنه أنها ستعمل ...

وإليك ما وضعت جنبا إلى جنب، التي لا تسمح بتكبير قرصة في محاكاة (لم يحاكم على اي فون الحقيقي)، ولكن أعتقد أن يكون على ما يرام:

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

والفكرة الرئيسية هي أنه إذا كان المستخدم يستخدم إصبعين، يمكنك تتبع القيم. I تسجيل نقطة البداية والنهاية في startPoints ألف وباء ثم أسجل النقاط تتبع الحالية، وعندما انتهيت، على touchesEnded، يمكن أن أدعو روتين لحساب أطوال النسبية من الخط الفاصل بين النقاط أبدأ مع ، والخط الفاصل بين النقطة وتنتهي باستخدام بسيط وتر أحسب. نسبة بينهما هو مقدار التكبير: I مضاعفة تمتد المنطقة من خلال هذا المبلغ

ونأمل أن تكون مفيدة لشخص ما.

وأخذت فكرة "تراكب" وجهة نظر شفافة، من الإجابة MystikSpiral، ووعملت تماما عن ما كنت تحاول تحقيق. سريع، ونظيفة حل.

وباختصار، كان مخصص UITableViewCell (المصممة في IB) مع MKMapView على جنبا إلى الجانب الأيسر وبعض UILabels على اليمين. أردت أن تجعل الخلية المخصصة لذلك يمكن أن أتطرق إليها في أي مكان وهذا من شأنه أن يدفع وحدة تحكم نظرة جديدة. ومع ذلك لمس الخريطة لم يمر اللمسات 'حتى' إلى UITableViewCell حتى أنا ببساطة بإضافة UIView من نفس الحجم كما عرض الخريطة الحق على أعلى من ذلك (في IB) وجعله من خلفية "لون واضح" في التعليمات البرمجية ( لا أعتقد أنك يمكن أن يحدد clearColor في IB ؟؟):

dummyView.backgroundColor = [UIColor clearColor];

ويعتقد أنه قد يساعد شخص آخر. بالتأكيد إذا كنت ترغب في تحقيق نفس السلوك لخلية عرض جدول.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top