ios 将 UIGesture 转发到后面的视图

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/9209998/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-30 16:43:53  来源:igfitidea点击:

Forwarding UIGesture to views behind

iphoneiosipadtouchuigesturerecognizer

提问by Krishnabhadra

I am working on an iphone (iOS 4.0 or later) app and having some troubles with touch handling between multiple views. I am having a view structure like this

我正在开发一个 iphone(iOS 4.0 或更高版本)应用程序,并且在多个视图之间的触摸处理方面遇到了一些麻烦。我有这样的视图结构

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

enter image description here

在此处输入图片说明

Basically I have a superView, and sibling subviews A and B. B has the same frame as A, hence hiding A completely.

基本上我有一个超级视图,以及兄弟子视图 A 和 B。B 与 A 具有相同的框架,因此完全隐藏了 A。

Now my requirement is this.

现在我的要求是这个。

  1. SubView B should receive all swipe and tap (single and double) gestures.
  2. SubView A should receive all pinch gestures.
  1. 子视图 B 应该接收所有滑动和点击(单次和双次)手势。
  2. 子视图 A 应接收所有捏合手势。

This is how I added gesture recognizers to the views

这就是我向视图添加手势识别器的方式

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

I did some research and to me UIGestureRecognizerDelegatelooked promising and I implemented the delegate method

我做了一些研究,对我来说UIGestureRecognizerDelegate看起来很有希望,我实现了委托方法

- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer

I returned NO for SubView B, hoping that underlying view will get these event. No such luck though

我为 SubView B 返回 NO,希望底层视图会得到这些事件。虽然没有这样的运气

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

Then I disabled user interaction of SubView B inside the delegate callback (commented code in above block), when pinch gesture is recognized, hoping remaining part of gesture will be received by SubView A. No such luck there too..

然后我在委托回调中禁用了子视图 B 的用户交互(上面块中的注释代码),当识别出捏合手势时,希望子视图 A 接收手势的剩余部分。那里也没有这样的运气..

So that is where I stand now. I have seen thisquestion, where the accepted answer involves property cancelsTouchesInView. But I believe cancelsTouchesInViewonly cancel a particular touch event, do not forward it.

所以这就是我现在的立场。我见过这个问题,其中接受的答案涉及 property cancelsTouchesInView。但我相信cancelsTouchesInView只取消特定的触摸事件,不要转发它。

Any other way to achieve my requirement? I am ready to work on whatever hint you provide.

还有其他方法可以达到我的要求吗?我准备好处理您提供的任何提示。

EDIT : BOUNTY TIME

编辑:赏金时间

My so called subView A is actually an instance of a 3rd party library's View class, which takes all touches away and I don't have any control over any gestures on it. I want different implementation for left and right swipe and I want pinch, tap etc work just like it is working with this third party view. So I put a view on top of A (SubView B) to get left and right swipes. But now I want to forward other gesture events to underlying library.

我所谓的 subView A 实际上是 3rd 方库的 View 类的一个实例,它带走了所有的触摸,我无法控制它的任何手势。我想要左右滑动的不同实现,我想要捏合、点击等工作,就像它与这个第三方视图一起工作一样。所以我在 A (SubView B) 的顶部放置了一个视图来左右滑动。但现在我想将其他手势事件转发到底层库。

采纳答案by SentineL

If i undestand your problem correct, you may just add another, clear view with rect, same as you A and B view, and implement all gesture on it: when you do pinch gesture, control subView A, when swipe and tap (single and double) gestures - control subView B. You can do it different ways: via pointers or just sending recived gesture to method in class, wich controls your sub view.

如果我理解您的问题是正确的,您可以添加另一个带有 rect 的清晰视图,与 A 和 B 视图相同,并在其上实现所有手势:当您执行捏合手势时,控制子视图 A,当滑动和点击时(单次和双)手势 - 控制子视图 B。您可以通过不同的方式进行操作:通过指针或仅将接收到的手势发送到类中的方法,从而控制您的子视图。

for example:

例如:

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

or:

或者:

- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer{
    if ([gestureRecognizer isKindOfClass:[UIPinchGestureRecognizer class]]) {
        NSLog(@"pinchGesture");
        [subViewBcontroller solvePinchGesture: gestureRecognizer];
    }
//etc
    return YES;
}

回答by flypig

The pinch gesture will Never received by the aView, because you view hierarchy is incorrect.

aView 永远不会收到捏合手势,因为您的视图层次结构不正确。

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

bView will captures all multi-touches including the pinch gesture, but bView will not handle the pinch gesture, so it will pass it to Next responder in the Responder chain, i.e. the superView of bView or the viewController of bView. The pinch event will never passed to its sibling views, because they are in parallel (no parent-child or view-viewController relationship).

bView 会捕获包括捏合手势在内的所有多点触控,但 bView 不会处理捏合手势,因此会将其传递给 Responder 链中的 Next Responder,即 bView 的 superView 或 bView 的 viewController。pinch 事件永远不会传递给它的兄弟视图,因为它们是并行的(没有父子视图或视图控制器关系)。

You can change your view hierarchy as below:

您可以按如下方式更改视图层次结构:

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

Please get more information from below link: https://developer.apple.com/library/ios/#documentation/EventHandling/Conceptual/EventHandlingiPhoneOS/EventsiPhoneOS/EventsiPhoneOS.html#//apple_ref/doc/uid/TP40009541-CH2-SW1

请从以下链接获取更多信息:https: //developer.apple.com/library/ios/#documentation/EventHandling/Conceptual/EventHandlingiPhoneOS/EventsiPhoneOS/EventsiPhoneOS.html#//apple_ref/doc/uid/TP40009541-CH2-SW1

--> Important note of the event passing in the responder chain "When the system delivers a touch event, it first sends it to a specific view. For touch events, that view is the one returned by hitTest:withEvent:; for “shaking”-motion events, remote-control events, action messages, and editing-menu messages, that view is the first responder. If the initial view doesn't handle the event, it travels up the responder chain along a particular path:

--> 响应者链中事件传递的重要说明“当系统传递触摸事件时,它首先将其发送到特定视图。对于触摸事件,该视图是hitTest:withEvent:;对于“shaking”返回的视图”-运动事件、远程控制事件、动作消息和编辑菜单消息,该视图是第一响应者。如果初始视图不处理该事件,它会沿着特定路径沿着响应者链向上传播:

  1. The hit-test view or first responder passes the event or message to its view controller if it has one; if the view doesn't have a view controller, it passes the event or message to its superview.
  2. If a view or its view controller cannot handle the event or message, it passes it to the superview of the view.
  3. Each subsequent superview in the hierarchy follows the pattern described in the first two steps if it cannot handle the event or message.
  4. The topmost view in the view hierarchy, if it doesn't handle the event or message, passes it to the window object for handling.
  5. The UIWindow object, if it doesn't handle the event or message, passes it to the singleton application object."
  1. 命中测试视图或第一响应者将事件或消息传递给它的视图控制器(如果有的话);如果视图没有视图控制器,它会将事件或消息传递给它的超级视图。
  2. 如果视图或其视图控制器无法处理事件或消息,则将其传递给视图的超级视图。
  3. 如果它不能处理事件或消息,层次结构中的每个后续超视图都遵循前两个步骤中描述的模式。
  4. 视图层次结构中最顶层的视图,如果它不处理事件或消息,则将其传递给 window 对象进行处理。
  5. UIWindow 对象,如果它不处理事件或消息,则将其传递给单例应用程序对象。”

my answer above is not the only solution, what you need to do is make swipe and pinch gesture handling logics in the same responder chain, so the un-handle pinch gesture event will passed to the correct responder. For example, you can omit subview b, and add the swipe left/right gestures to superView of aView.

我上面的回答不是唯一的解决方案,您需要做的是在同一个响应者链中制作滑动和捏合手势处理逻辑,因此取消处理捏合手势事件将传递给正确的响应者。比如可以省略subview b,在aView的superView中添加左右滑动手势。

回答by Jonathan Cichon

I did something similar some time ago. I wanted to do two different things with one finger and two or more fingers. My solution might need some tweak for you and i can't ensure everything will work exactly the same in your code-environment. I did not find proper documentation of hitTest and why it is called multiple times in a row. So i did some testing and came to a solution, how to separate one and multi-finger gestures, which worked very well for my needs.

前段时间我做过类似的事情。我想用一根手指和两根或更多根手指做两种不同的事情。我的解决方案可能需要对您进行一些调整,我不能确保一切都在您的代码环境中完全相同。我没有找到正确的 hitTest 文档以及为什么它被连续多次调用。所以我做了一些测试并得出了一个解决方案,如何分离单指和多指手势,这对我的需求非常有效。

I had this view hierarchy:

我有这个视图层次结构:

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

I implemented the switch in hitTest of the superView:

我在 superView 的 hitTest 中实现了开关:

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

回答by Kuldeep

I would suggest you to use below line of code where needed may help you out.

我建议您在需要的地方使用以下代码行可能会帮助您。

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

//--->for aview gesture to detect pinch
[rightSwipe requireGestureRecognizerToFail:rightSwipe]; ||
[leftSwipe requireGestureRecognizerToFail:pinch];

回答by Warif Akhand Rishi

If bView has pinch gesture. From bView's pinch gesture action if called aView's pinch gesture action then...

如果 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 is a thirdParty lib. Can you call handlePinchAmethod from outside? NSLog gestureRecognizersprints me action=handlePinchA

aView 是第三方库。您可以从外部调用handlePinchA方法吗?NSLog的gestureRecognizers打印我的行动= handlePinchA

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

Prints me:

打印我:

gestureRecognizers (
"<UIPinchGestureRecognizer: 0x8b3e500; state = Possible; view = <UIView 0x8b2f470>; target= <(action=handlePinchA:, target=....