Переадресация UIGesture на взгляды позади - PullRequest
55 голосов
/ 09 февраля 2012

Я работаю над приложением для iphone (iOS 4.0 или более поздней версии) и у меня возникают проблемы с обработкой касаний между несколькими видами.У меня есть структура вида, подобная этой

---> A superView 
     |
     ---> SubView - A 
     |
     ---> SubView - B (exactly on top of A, completely blocking A).

enter image description here

По сути, у меня есть superView, и дочерние подпредставления A и B. B имеет тот же кадр, что и A, следовательно, скрывает Aполностью.

Теперь мое требование таково.

  1. SubView B должен принимать все жесты смахивания и нажатия (одиночные и двойные).
  2. SubView A должен принимать все жесты сужения.

Так я добавил распознаватели жестов к представлениям

UISwipeGestureRecognizer *leftSwipe  =  [[UISwipeGestureRecognizer alloc]initWithTarget:self action:@selector(leftSwipe:)];
[leftSwipe setDirection:(UISwipeGestureRecognizerDirectionLeft)];
leftSwipe.delegate  =   self;
[bView addGestureRecognizer:leftSwipe];
[leftSwipe release];

UISwipeGestureRecognizer *rightSwipe  =  [[UISwipeGestureRecognizer alloc]initWithTarget:self action:@selector(rightSwipe:)];
[rightSwipe setDirection:(UISwipeGestureRecognizerDirectionRight)];
rightSwipe.delegate   =   self;
[bView addGestureRecognizer:rightSwipe];
[rightSwipe release];

UIPinchGestureRecognizer *pinch   =  [[UIPinchGestureRecognizer alloc]initWithTarget:self action:@selector(handlePinch:)];
pinch.delegate    =   self;
[aView addGestureRecognizer:pinch];
[pinch release];

Я провел небольшое исследование, и мне UIGestureRecognizerDelegate показалось многообещающим, и я реализовал метод делегата

- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer

Я возвратил NO для SubView B, надеясь, что базовое представление получит эти события.Не повезло, хотя

- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer{
    if ([gestureRecognizer isKindOfClass:[UIPinchGestureRecognizer class]] && [gestureRecognizer view] == bView) {
        NSLog(@"pinchGesture");
        //bView.userInteractionEnabled = NO;
        return NO;
    }
    return YES;
}

Затем я отключил взаимодействие с пользователем SubView B внутри обратного вызова делегата (закомментированный код в приведенном выше блоке), когда распознается жест повышения, надеясь, что оставшаяся часть жеста будет получена SubView AТам тоже нет такой удачи ..

Так вот где я стою сейчас.Я видел этот вопрос, где принятый ответ включает свойство cancelsTouchesInView.Но я считаю, cancelsTouchesInView только отменяет определенное сенсорное событие, но не пересылает его.

Есть ли другой способ выполнить мое требование?Я готов работать над любым указанием, которое вы нам предоставите.

РЕДАКТИРОВАТЬ: BOUNTY TIME

Мой так называемый subView A на самом деле является экземпляром класса View сторонней библиотеки, который убирает все прикосновения иЯ не имею никакого контроля над жестами.Мне нужна другая реализация для левого и правого пролистывания, и я хочу, чтобы пинч, тап и т. Д. Работали так же, как это работает с этим представлением третьей стороны.Поэтому я поместил вид сверху на A (SubView B), чтобы получить пролистывание влево и вправо.Но теперь я хочу переслать другие события жестов в базовую библиотеку.

Ответы [ 5 ]

18 голосов
/ 17 мая 2012

Если я правильно понимаю вашу проблему, вы можете просто добавить другое, четкое представление с прямоугольником, такое же, как у вас с представлениями A и B, и реализовать на нем все жесты: когда вы делаете жест щипка, управляете подвидом A, когда проводите и нажимаете (одиночные и двойные) жесты - управляйте подвидом B. Вы можете сделать это разными способами: с помощью указателей или просто отправив полученный жест в метод в классе, который управляет вашим подвидом.

например:

UISwipeGestureRecognizer *leftSwipe  =  [[UISwipeGestureRecognizer alloc]initWithTarget:self action:@selector(leftSwipe:)];
[leftSwipe setDirection:(UISwipeGestureRecognizerDirectionLeft)];
leftSwipe.delegate  =   subViewAcontroller;
[clearView addGestureRecognizer:leftSwipe];
[leftSwipe release];

UISwipeGestureRecognizer *rightSwipe  =  [[UISwipeGestureRecognizer alloc]initWithTarget:self action:@selector(rightSwipe:)];
[rightSwipe setDirection:(UISwipeGestureRecognizerDirectionRight)];
rightSwipe.delegate   =   subViewAcontroller;
[clearView addGestureRecognizer:rightSwipe];
[rightSwipe release];

UIPinchGestureRecognizer *pinch   =  [[UIPinchGestureRecognizer alloc]initWithTarget:self action:@selector(handlePinch:)];
pinch.delegate    =   subViewBcontroller;
[clearView addGestureRecognizer:pinch];
[pinch release];

или

- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer{
    if ([gestureRecognizer isKindOfClass:[UIPinchGestureRecognizer class]]) {
        NSLog(@"pinchGesture");
        [subViewBcontroller solvePinchGesture: gestureRecognizer];
    }
//etc
    return YES;
}
15 голосов
/ 15 мая 2012

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

---> A superView 
 |
 ---> SubView - A 
 |
 ---> SubView - B (exactly on top of A, completely blocking A).

bView будет захватывать все мультитач, включая жест сжимания, но bView не будет обрабатывать жест сжимания, поэтому он передает его следующему респонденту в цепочке респондеров, то есть superView из bView или viewController из bView. Событие pinch никогда не будет передано в одноуровневые представления, потому что они параллельны (нет отношений parent-child или view-viewController).

Вы можете изменить иерархию представлений, как показано ниже:

---> A superView 
 |
 ---> SubView - A 
       |
       ---> SubView - B (child view of b, exactly on top of A, completely blocking A)



- (void)viewDidLoad {
    [super viewDidLoad];

    aView = [[UIView alloc] initWithFrame:self.view.bounds];
    aView.backgroundColor = [UIColor blueColor];

    bView = [[UIView alloc] initWithFrame:self.view.bounds];
    bView.backgroundColor = [UIColor redColor];

    [self.view addSubview:aView];
    [aView addSubview:bView];

    UISwipeGestureRecognizer *leftSwipe  =  [[UISwipeGestureRecognizer alloc]initWithTarget:self action:@selector(leftSwipe:)];
    [leftSwipe setDirection:(UISwipeGestureRecognizerDirectionLeft)];
    leftSwipe.delegate  =   self;
    [bView addGestureRecognizer:leftSwipe];

    UISwipeGestureRecognizer *rightSwipe  =  [[UISwipeGestureRecognizer alloc]initWithTarget:self action:@selector(rightSwipe:)];
    [rightSwipe setDirection:(UISwipeGestureRecognizerDirectionRight)];
    rightSwipe.delegate   =   self;
    [bView addGestureRecognizer:rightSwipe];

    UIPinchGestureRecognizer *pinch   =  [[UIPinchGestureRecognizer alloc]initWithTarget:self action:@selector(handlePinch:)];
    pinch.delegate    =   self;
    [aView addGestureRecognizer:pinch];
}

Пожалуйста, получите больше информации по ссылке ниже: https://developer.apple.com/library/ios/#documentation/EventHandling/Conceptual/EventHandlingiPhoneOS/EventsiPhoneOS/EventsiPhoneOS.html#//apple_ref/doc/uid/TP40009541-CH2-SW1

-> Важное замечание о прохождении события в цепочке респондента «Когда система доставляет событие касания, она сначала отправляет его в конкретное представление. Для событий касания это представление, возвращаемое hitTest: withEvent :; для событий« встряхивания », событий дистанционного управления, сообщений действий, и сообщения меню редактирования, это представление является первым респондентом. Если исходное представление не обрабатывает событие, оно перемещается вверх по цепочке респондента по определенному пути:

  1. Представление проверки попадания или первый респондент передает событие или сообщение своему контроллеру представления, если оно имеется; если представление не имеет контроллера представления, оно передает событие или сообщение своему суперпредставлению.
  2. Если представление или его контроллер представления не могут обработать событие или сообщение, оно передает его суперпредставлению представления.
  3. Каждое последующее суперпредставление в иерархии следует шаблону, описанному в первых двух шагах, если оно не может обработать событие или сообщение.
  4. Самое верхнее представление в иерархии представлений, если оно не обрабатывает событие или сообщение, передает его объекту окна для обработки.
  5. Объект UIWindow, если он не обрабатывает событие или сообщение, передает его объекту синглтон-приложения. "

Мой ответ выше - не единственное решение, вам нужно сделать логики обработки жестов смахивания и сжатия в одной цепочке респондента, чтобы событие отмены обработки жеста было передано правильному респонденту. Например, вы можете опустить подпредставление b и добавить жесты влево / вправо к суперпредставлению aView.

1 голос
/ 15 мая 2012

Я делал нечто подобное некоторое время назад. Я хотел сделать две разные вещи с одним пальцем и двумя или более пальцами. Мое решение может нуждаться в некоторой настройке для вас, и я не могу гарантировать, что все будет работать точно так же в вашей среде кода. Я не нашел надлежащей документации для hitTest и почему он вызывается несколько раз подряд. Поэтому я провел некоторое тестирование и пришел к решению, как разделить жесты одним и несколькими пальцами, что очень хорошо подошло для моих нужд.

У меня была эта иерархия представлений:

---> A superView 
 |
 ---> ScrollView - A - all touches
 |
 ---> ScrollView - B - only two+ finger scroll (exactly on top of A, completely blocking A).

Я реализовал переключатель в hitTest из superView:

BOOL endDrag; // indicates scrollviewB did finish
BOOL shouldClearHitTest; // bool to help calling the method clearHitTest only once.
BOOL multiTouch; // needed for the method clearHitTest to know if a multi-touch is detected or not
NSMutableArray *events; // store all events within one eventloop/touch;


- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
    if (!endDrag) {
        if (!shouldClearHitTest) { // as hitTest will be called multible times in one event-loop and i need all events, i will clean the temporaries when everything is done
            shouldClearHitTest = YES;
            [[NSRunLoop mainRunLoop] performSelector:@selector(clearHitTest) target:self argument:nil order:1 modes:[NSArray arrayWithObject:NSRunLoopCommonModes]];
        }
        [events addObject:event]; // store the events so i can access them after all hittests for one touch are evaluated

        if (event.type == UIEventTypeTouches && ([_events count] > 3 || [[event allTouches] count] > 0 || _currentEvent)) { // two or more fingers detected. at least for my view hierarchy
            multiTouch = YES;
            return scrollViewB;
        }
    }else {
        endDrag = NO;
    }
    return scrollViewA;
}

- (void)clearHitTest {
    if (shouldClearHitTest) {
        shouldClearHitTest = NO;
        [events removeAllObjects];
        if (multiTouch) {
           // Do some special stuff for multiTouch
        }
        multiTouch = NO;
    }
}
0 голосов
/ 20 мая 2012

Если bView имеет жест щипка.Из действия жеста сжатия bView, если оно называется действием жеста сжатия aView, то ...

UIPinchGestureRecognizer *pinchA   =  [[UIPinchGestureRecognizer alloc]initWithTarget:self action:@selector(handlePinchA:)];
[aView addGestureRecognizer:pinchA];
[pinchA release];    

UIPinchGestureRecognizer *pinchB   =  [[UIPinchGestureRecognizer alloc]initWithTarget:self action:@selector(handlePinchB:)];
[bView addGestureRecognizer:pinchB];
[pinchB release];


-(void)handlePinchA:(UIGestureRecognizer*)gestureRecognizer
{
NSLog(@"handlePinch on A");  
}

-(void)handlePinchB:(UIGestureRecognizer*)gestureRecognizer
{
NSLog(@"handlePinch on B");
[self handlePinchA:gestureRecognizer];  
}

aView - это библиотека третьих сторон.Можете ли вы вызвать handlePinchA метод извне?NSLog жестРекспознаватели печатает меня action = handlePinchA

NSLog(@"gestureRecognizers %@", [aView gestureRecognizers]);

печатает меня:

gestureRecognizers (
"<UIPinchGestureRecognizer: 0x8b3e500; state = Possible; view = <UIView 0x8b2f470>; target= <(action=handlePinchA:, target=....
0 голосов
/ 17 мая 2012

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

//--->for bview gesture to detect swipe
[pinch requireGestureRecognizerToFail:leftSwipe]; && 
[pinch requireGestureRecognizerToFail:rightSwipe];

//--->for aview gesture to detect pinch
[rightSwipe requireGestureRecognizerToFail:rightSwipe]; ||
[leftSwipe requireGestureRecognizerToFail:pinch];
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...