Наблюдение за жестами мультисенсорного ввода в UITableView

Я хочу реализовать защемление / выход поверх UITableView, я просмотрел несколько методов, включая этот:

Аналогичный вопрос

Но хотя я могу создать объект UIViewTouch и наложить его на свой UITableView, события прокрутки не передаются в мой UITableView, я все еще могу выбирать ячейки, и они правильно реагируют, инициируя переход к новому объекту ViewController. Но я не могу прокручивать UITableView, несмотря на передачу событий touchesBegan, touchesMoved и touchesEnded.


person Community    schedule 04.01.2010    source источник


Ответы (2)


Кажется, это классическая проблема. В моем случае я хотел перехватить некоторые события через UIWebView, которые нельзя разделить на подклассы и т. Д.

Я обнаружил, что лучший способ сделать это - перехватить события с помощью UIWindow:

EventInterceptWindow.h

@protocol EventInterceptWindowDelegate
- (BOOL)interceptEvent:(UIEvent *)event; // return YES if event handled
@end


@interface EventInterceptWindow : UIWindow {
    // It would appear that using the variable name 'delegate' in any UI Kit
    // subclass is a really bad idea because it can occlude the same name in a
    // superclass and silently break things like autorotation.
    id <EventInterceptWindowDelegate> eventInterceptDelegate;
}

@property(nonatomic, assign)
    id <EventInterceptWindowDelegate> eventInterceptDelegate;

@end

EventInterceptWindow.m:

#import "EventInterceptWindow.h"

@implementation EventInterceptWindow

@synthesize eventInterceptDelegate;

- (void)sendEvent:(UIEvent *)event {
    if ([eventInterceptDelegate interceptEvent:event] == NO)
        [super sendEvent:event];
}

@end

Создайте этот класс, измените класс вашего UIWindow в вашем MainWindow.xib на EventInterceptWindow, затем где-нибудь установите eventInterceptDelegate на контроллер представления, который вы хотите перехватывать события. Пример перехвата двойного касания:

- (BOOL)interceptEvent:(UIEvent *)event {
    NSSet *touches = [event allTouches];
    UITouch *oneTouch = [touches anyObject];
    UIView *touchView = [oneTouch view];
    //  NSLog(@"tap count = %d", [oneTouch tapCount]);
    // check for taps on the web view which really end up being dispatched to
    // a scroll view
    if (touchView && [touchView isDescendantOfView:webView]
            && touches && oneTouch.phase == UITouchPhaseBegan) {
        if ([oneTouch tapCount] == 2) {
            [self toggleScreenDecorations];
            return YES;
        }
    }   
    return NO;
}

Связанная информация здесь: http://iphoneincubator.com/blog/windows-views/360idev-iphone-developers-conference-presentation

person Nimrod    schedule 05.01.2010
comment
Этот метод отлично работает и чище, чем метод наложения объектов. Спасибо! - person ; 07.01.2010
comment
Действительно отличное решение, искал что-то вроде этого, чтобы добавить жесты для scrollview / tableview, спасибо! :) - person Jaanus; 12.01.2010
comment
Отличное решение. У меня уже несколько месяцев он работает нормально, фиксируя жест двойного касания тремя пальцами. Недавно один из моих более невротичных коллег смог вызвать аварию, которая, по всей видимости, была вызвана этим. Трассировка стека не показывает кода objective-c, однако выдает EXEC_BAD_ACCESS на [UIScrollViewPanGestureRecognizer touchesCancelled:withEvent:] -[UIApplication _cancelTouches:withEvent:sendingTouchesCancelled:includingGestures:]. Моя гипотеза состоит в том, что первые 3 нажатия запускают PanGesture, но затем я просто убиваю событие (не изящно). Постоянный возврат NO исправляет это. - person Michael Kernahan; 30.12.2010
comment
Выбежал из комнаты наверху ... любые мысли о изящной отмене были бы очень признательны. - person Michael Kernahan; 30.12.2010
comment
См. stackoverflow.com/q/10403137/779419, чтобы узнать, как использовать настраиваемый UIWindow при использовании раскадровок. - person schieferstapel; 11.03.2013

Нимрод писал:

где-то установите eventInterceptDelegate на контроллер представления, который вы хотите перехватывать события

Я не сразу понял это утверждение. Для пользы всех, у кого была такая же проблема, как у меня, я сделал это, добавив следующий код в свой подкласс UIView, который должен обнаруживать прикосновения.

- (void) viewDidAppear:(BOOL)animated
{
    [super viewDidAppear:animated];

    // Register to receive touch events
    MyApplicationAppDelegate *appDelegate = (MyApplicationAppDelegate *) [[UIApplication sharedApplication] delegate];
    EventInterceptWindow *window = (EventInterceptWindow *) appDelegate.window;
    window.eventInterceptDelegate = self;
}


- (void) viewWillDisappear:(BOOL) animated
{
    // Deregister from receiving touch events
    MyApplicationAppDelegate *appDelegate = (MyApplicationAppDelegate *) [[UIApplication sharedApplication] delegate];
    EventInterceptWindow *window = (EventInterceptWindow *) appDelegate.window;
    window.eventInterceptDelegate = nil;

    [super viewWillDisappear:animated];
}


- (BOOL) interceptEvent:(UIEvent *) event
{
    NSLog(@"interceptEvent is being called...");
    return NO;
}


Эта версия interceptEvent: представляет собой простую реализацию обнаружения масштабирования пальцем. NB. Некоторая часть кода была взята из раздела Начало разработки iPhone 3 компанией Apress.

CGFloat initialDistance;

- (BOOL) interceptEvent:(UIEvent *) event
{
    NSSet *touches = [event allTouches];

    // Give up if user wasn't using two fingers
    if([touches count] != 2) return NO;

    UITouchPhase phase = ((UITouch *) [touches anyObject]).phase;
    CGPoint firstPoint = [[[touches allObjects] objectAtIndex:0] locationInView:self.view];
    CGPoint secondPoint = [[[touches allObjects] objectAtIndex:1] locationInView:self.view];

    CGFloat deltaX = secondPoint.x - firstPoint.x;
    CGFloat deltaY = secondPoint.y - firstPoint.y;
    CGFloat distance = sqrt(deltaX*deltaX + deltaY*deltaY);

    if(phase == UITouchPhaseBegan)
    {
        initialDistance = distance;
    }
    else if(phase == UITouchPhaseMoved)
    {
        CGFloat currentDistance = distance;
        if(initialDistance == 0) initialDistance = currentDistance;
        else if(currentDistance - initialDistance > kMinimumPinchDelta) NSLog(@"Zoom in");
        else if(initialDistance - currentDistance > kMinimumPinchDelta) NSLog(@"Zoom out");
    }
    else if(phase == UITouchPhaseEnded)
    {
        initialDistance = 0;
    }

    return YES;
}


Изменить: хотя этот код на 100% работал в симуляторе iPhone, когда я запускал его на устройстве iPhone, я обнаружил странные ошибки, связанные с прокруткой таблицы. Если это тоже происходит с вами, то заставьте метод interceptEvent: возвращать NO во всех случаях. Это означает, что суперкласс также будет обрабатывать событие касания, но, к счастью, это не нарушило мой код.

person Adam    schedule 17.02.2011
comment
Почему бы не использовать self.view.window в viewDidAppear? - person schieferstapel; 11.03.2013