Как прервать сенсорные события на объектах MKMapView или UIWebView?

Простота против оптимальности

Я считаю, что очень сложно написать простой и оптимальный код.

96
задан Bhavin Ramani 22 December 2016 в 11:53
поделиться

9 ответов

Я взял идею «перекрывающегося» прозрачного представления из ответа MystikSpiral, и он отлично сработал для того, чего я пытался достичь; быстрое и чистое решение.

Короче говоря, у меня был собственный UITableViewCell (разработанный на IB) с MKMapView слева и некоторыми UILabels справа. Я хотел создать настраиваемую ячейку, чтобы вы могли касаться ее где угодно, и это подтолкнуло бы к новому контроллеру представления. Однако прикосновение к карте не передавало прикосновений «вверх» к UITableViewCell, пока я просто не добавил UIView того же размера, что и вид карты, прямо поверх него (в IB) и не сделал его фоном «прозрачным цветом» в коде ( не думаю, что вы можете установить clearColor в IB ??):

dummyView.backgroundColor = [UIColor clearColor];

Думал, это может помочь кому-то другому; конечно, если вы хотите добиться того же поведения для ячейки табличного представления.

0
ответ дан 24 November 2019 в 05:35
поделиться

Сделайте MKMapView подпредставлением настраиваемого представления и реализуйте

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

в настраиваемом представлении для возврата self вместо подпредставления.

0
ответ дан 24 November 2019 в 05:35
поделиться

Вам, вероятно, потребуется наложение прозрачного вида, чтобы улавливать касания, как это часто делается с элементами управления на основе UIWebView. Просмотр карты уже выполняет ряд специальных функций касанием, чтобы можно было перемещать, центрировать, масштабировать карту и т. Д., Чтобы сообщения не попадали в ваше приложение.

Два других (НЕПРОВЕРЕНО) ) варианты, которые я могу придумать:

1) Отказаться от первого респондента через IB и установить для него значение «Владелец файла», чтобы позволить Владельцу файла отвечать на прикосновения. Я сомневаюсь, что это сработает, потому что MKMapView расширяет NSObject, а не UIView, и в результате события касания все еще могут не передаваться вам.

2) Если вы хотите поймать ловушку при изменении состояния карты (например, при увеличении ) просто реализуйте протокол MKMapViewDelegate для прослушивания определенных событий. Я предполагаю, что это ваш лучший способ легко захватить какое-либо взаимодействие (если не считать реализации прозрачного вида поверх карты). Не забудьте установить контроллер представления, в котором находится MKMapView, в качестве делегата карты ( map.delegate = self ).

Удачи.

3
ответ дан 24 November 2019 в 05:35
поделиться

Я не экспериментировал, но есть большая вероятность, что MapKit основан на кластере классов, и, следовательно, создание подклассов сложно и неэффективно.

Я бы предложил сделать MapKit представлением subview настраиваемого представления, которое должно позволить вам перехватывать события касания до того, как они достигнут его.

2
ответ дан 24 November 2019 в 05:35
поделиться

После дня пиццы и криков я наконец нашел решение! Очень аккуратно!

Питер, я использовал ваш трюк, описанный выше, и немного изменил его, чтобы наконец получить решение, которое отлично работает с 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

Надеюсь, это поможет некоторым из вас!

Ура

29
ответ дан 24 November 2019 в 05:35
поделиться

Спасибо за пиццу и крики - вы сэкономили мне много времени.

multitouchenabled будет работать время от времени.

viewTouch.multipleTouchEnabled = TRUE;

В конце концов, я отключил виды, когда мне нужно было снимать прикосновение (другой момент времени, чем требуются щипки):

    [mapView removeFromSuperview];
    [viewTouch addSubview:mapView];
    [self.view insertSubview:viewTouch atIndex:0];
0
ответ дан 24 November 2019 в 05:35
поделиться

Я заметил, что вы можете отслеживать количество и местоположение касаний и получать местоположение каждого касания на виде:

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

Кто-нибудь еще пробовал использовать эти значения для обновления уровня масштабирования карты? Это будет вопрос записи начальных позиций, а затем конечных точек, вычисления относительной разницы и обновления карты.

Я играю с базовым кодом, предоставленным Мартином, и похоже, что он будет работать ...

0
ответ дан 24 November 2019 в 05:35
поделиться

Вот то, что я собрал, что позволяет увеличивать масштаб в симуляторе (не пробовал на реальном iPhone), но я думаю, что все будет хорошо:

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

Основная идея заключается в том, что если пользователь использует два пальца, вы отслеживаете значения. Я записываю начальную и конечную точки в startPoints A и B. Затем я записываю текущие точки отслеживания, и когда я закончу, на touchesEnded я могу вызвать процедуру для вычисления относительной длины линии между точками, с которых я начинаю. , а линия между точкой I заканчивается простым вычислением гипотенузы. Отношение между ними и есть степень масштабирования: я умножаю диапазон области на эту величину.

Надеюсь, это кому-то пригодится.

0
ответ дан 24 November 2019 в 05:35
поделиться

Итак, после полдня возиться с этим, я обнаружил следующее:

  1. Как все выяснили, ущипнуть не работает. Я пробовал создать подкласс MKMapView и метод, описанный выше (перехватывая его). И результат тот же.
  2. В видеороликах Stanford, посвященных iPhone, парень из Apple говорит, что многие вещи, связанные с UIKit, вызовут множество ошибок, если вы «передадите» запросы касания (или два метода, описанных выше), и вы, вероятно, не заставит его работать.

  3. РЕШЕНИЕ : описано здесь: Перехват / захват событий касания iPhone для MKMapView . По сути, вы «ловите» событие до того, как его получит ответчик, и интерпретируете его там.

2
ответ дан 24 November 2019 в 05:35
поделиться