你能够将UIGestureRecognizer附加到多个视图上吗?

256
UITapGestureRecognizer *tapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tapTapTap:)];
[self.view1 addGestureRecognizer:tapGesture];
[self.view2 addGestureRecognizer:tapGesture];
[tapGesture release];

在上面的代码中,只有对view2的点击被识别。如果我注释掉第三行,则可以识别对view1的点击。如果我正确,只能使用一次手势识别器,那么我不确定这是一个错误还是需要更多的文档说明。

13个回答

366
一个 UIGestureRecognizer 应该与一个单独的视图一起使用。我认为文档有点零散。这个 UIGestureRecognizer 只有一个 view 属性,这就显而易见了。

view

手势识别器附加到的视图。(只读)

@property(nonatomic, readonly) UIView *view

讨论:你可以使用 addGestureRecognizer: 方法将手势识别器添加到 UIView 对象中。


12
因为将手势识别器添加到视图中是在运行时完成的(而非编译时)。 - TomSwift
1
我明白了,但就像检测到我们没有使用变量一样,XCode可以根据代码判断我们是否将相同的识别器传递给多个视图,并警告编码人员。 - Zoltán Matók
1
编译器关于多个视图分配相同的UITapGestureRecognizer的警告是无意义的,因为你可能有意这样做,例如如果你想将点击手势识别器从一个视图移动到另一个视图。话虽如此,手势识别器不能在多个视图上使用是一个愚蠢的限制。 - Erik van der Neut
1
iOS 9现在强制每个手势识别器只能有一个视图,我一直在使用下面的接口构建方法,但是当我尝试使用它时,我会收到以下消息(为了简洁起见,某些细节被省略):警告:手势识别器(<UITapGestureRecognizer:.....>)在故事板/xib中设置为添加到多个视图(-> <UIView:; frame =(0 44; 600 536); autoresize = RM + BM; gestureRecognizers = <NSArray ...:>; layer = <CALayer:...>>)这从未被允许,并且现在已经得到执行。从iOS 9.0开始,它将放置在加载它的第一个视图中。 - George Brown
如果您第二次添加视图时,此识别器附加的视图将自动取消附加。UITapGestureRecognizer *tapRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(didPressed:)];[self.view1 addGestureRecognizer:tapRecognizer]; [self.view2 addGestureRecognizer:tapRecognizer];输出: _view1没有手势识别器数组; view2已经获得了手势识别器数组。 - kokos8998

49

我通过使用以下方法解决了这个问题。

for (UIButton *aButton in myButtons) {

            UILongPressGestureRecognizer *longPress = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(handleLongPress:)];
            longPress.minimumPressDuration=1.0;
            [aButton addGestureRecognizer:longPress];
            [longPress release];

}

在我的 handleLongPress 方法中,我只需将一个 UIButton 设置为手势识别器的视图,并根据该按钮分支处理。

- (void)handleLongPress:(UILongPressGestureRecognizer*)gesture {
    if ( gesture.state == UIGestureRecognizerStateEnded ) {
        UIButton *whichButton=(UIButton *)[gesture view];
        selectedButton=(UIButton *)[gesture view];
    ....
}

1
非常好的回答。非常感谢。如果问题是“如何将UIGestureRecognizer附加到多个视图?”,那么这可能会成为被接受的答案。 - DD_
8
这个对我没用(或类似的)。我在Interface Builder中为一个tap手势识别器添加了几个视图,并将识别器连接到一个动作。无论何时点击一个连接的视图,该动作都会被调用,但是gesture.view始终是最后一个连接的视图。 - Aneil Mallavarapu
这是一个非常好的答案,也非常有帮助,我同意@MicRO +1。 - Dilip Manek
3
Aneil,这是因为你没有创建手势识别器的新实例。这个答案中循环中发生的事情是创建了手势识别器的新实例,每个实例只附加一个视图。它们都可以指向同一个处理程序,在那里你可以检查视图以查看哪个被触摸了。 - Erik van der Neut
1
有其他人能够确认在当前版本的 Obj-C / Swift 中这个已经不再起作用了吗? - Maxi Mus
这不算是一个解决方法,只是一个循环,可以节省几行代码。所有的操作都是添加多个手势,这些手势都调用同一个选择器。最终,每个视图都有自己的新手势识别器实例。调用选择器的发送者在调用时是不同的。 - Vlad

18

对于需要Swift 3版本的人:

基于上面Bhavik Rathod的回答。

 func setGestureRecognizer() -> UIPanGestureRecognizer {

        var panRecognizer = UIPanGestureRecognizer()

        panRecognizer = UIPanGestureRecognizer (target: self, action: #selector(pan(panGesture:)))
        panRecognizer.minimumNumberOfTouches = 1
        panRecognizer.maximumNumberOfTouches = 1
        return panRecognizer
    }

        ///set the recognize in multiple views
        view1.addGestureRecognizer(setGestureRecognizer())
        view2.addGestureRecognizer(setGestureRecognizer())

3
基本上,这就是为两个视图创建多个手势,规则仍然相同:每个手势只能附加到一个视图。 - Abdoelrhman
3
不,每次调用该函数都会创建一个手势。 - Abdoelrhman
2
函数名称不正确。这里的逻辑函数是一个获取函数。因此,它应该被命名为:getGestureRecognize,因为这就是这个函数所做的事情。 - David Seek
对我来说很好用!而且代码比创建多个变量或将整个创建代码放在addGestureRecognizer中更加清晰。 - Codenator81

14

不,您不应该将手势识别器附加到多个视图上。

在Apple的文档中明确提到:

手势识别器被附加到视图

每个手势识别器都与一个视图相关联。相比之下,一个视图可以有多个手势识别器,因为一个视图可能会响应多种不同的手势。为了让手势识别器识别发生在特定视图中的触摸事件,您必须将手势识别器附加到该视图上。

iOS事件处理指南-手势识别器 Apple开发者文库

虽然像其他人提到的那样,在某些情况下它们可能起作用,但这显然违反了文档说明,并且在任何未来的iOS版本中都可能发生变化。

您可以为要监视的视图添加单独的手势识别器,它们可以共享一个公共操作。


12

我们可以像这样做,非常简单易行。

1) 在您的控制器中创建以下函数(此函数将返回GestureRecognizer)

-(UITapGestureRecognizer*)setRecognizer{
     UITapGestureRecognizer *gestureRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(openProfile)];
     [gestureRecognizer setNumberOfTapsRequired:1];
     return gestureRecognizer;
}

2) 现在将此识别器设置在多个视图中

[self.view1 addGestureRecognizer:[self setRecognizer]]; 
[self.view2 addGestureRecognizer:[self setRecognizer]];

当我使用两个标签而不是视图时,它对我不起作用。 - Mihir Oza
3
@Mihir Oza,直接针对UILabel无法工作,因为标签对用户交互没有意义。如果您想要为UILabel添加手势,请在Swift中添加此单行labelName.isUserInteractionEnabled = true,然后添加手势即可。 - Naresh
太晚了,我已经修复了。但是感谢您的建议。您的评论将对Stack用户有所帮助。非常感谢! - Mihir Oza
1
我猜测这行代码 setNumberOfTapsRequired:1 不是必要的。 - Naveed Abbas

4
如果有人不想为多个按钮添加手势视图编写代码,如kwalker上面所回答的,而是希望通过Interface Builder完成,那么这可能会对您有所帮助。
1)您可以从对象库中添加长按手势识别器,就像添加其他对象(如UIButtons和UILabels)一样。 enter image description here 最初我使用的是一个
2)将引用输出设置为UIButton,并使用File's Owner发送操作。

enter image description here

注意:如果您有多个UIButton或任何其他对象,则需要为每个对象单独设置手势识别器。有关更多详细信息,请参阅我的这个问题。长按手势识别器上获取错误的UIButton标签


使用IB将多个UIView绑定到手势识别器非常容易。问题是关于代码生成的。 - AlexeyVMP

3
如果你有固定的观点,我建议你像这样做。
[self.view1 addGestureRecognizer:[[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tapTapTap:)]];
[self.view2 addGestureRecognizer:[[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tapTapTap:)]];

那样做将减少多个不同且无用的变量。

3
您可以在视图上创建一个通用的扩展程序,以便轻松添加手势识别器。这只是一个示例,但它可能看起来像这样。
extension UIView {

    func setGestureRecognizer<Gesture: UIGestureRecognizer>(of type: Gesture.Type, target: Any, actionSelector: Selector, swipeDirection: UISwipeGestureRecognizer.Direction? = nil, numOfTaps: Int = 1) {
    let getRecognizer = type.init(target: target, action: actionSelector)

    switch getRecognizer {
    case let swipeGesture as UISwipeGestureRecognizer:
        guard let direction = swipeDirection else { return }
        swipeGesture.direction = direction
        self.addGestureRecognizer(swipeGesture)
    case let tapGesture as UITapGestureRecognizer:
        tapGesture.numberOfTapsRequired = numOfTaps
        self.addGestureRecognizer(tapGesture)
    default:
        self.addGestureRecognizer(getRecognizer)
    }
  }

}

要在一个视图上添加双击识别器,只需调用以下代码:

在一个视图上添加双击识别器,只需调用:

let actionSelector = #selector(actionToExecute)
view.setGestureRecognizer(of: UITapGestureRecognizer.self, target: self, actionSelector: actionSelector, numOfTaps: 2)

您还可以轻松地添加滑动识别器

view.setGestureRecognizer(of: UISwipeGestureRecognizer.self, target: self, actionSelector: actionSelector, swipeDirection: .down)

等等其他内容。 请记住,目标必须与选择器链接。


2
每次添加指向相同函数的手势识别器时,重新编写(重新创建)您的GestureRecognize如何? 在下面的情况下它可以工作。我正在使用IBOutletCollection。
Swift 2:
@IBOutlet var topicView: [UIView]!

override func viewDidLoad() {
        for view in self.topicView as [UIView] {
        view.addGestureRecognizer(UITapGestureRecognizer(target: self, action: "viewClicked:"))
    }
}

func viewClicked(recognizer: UITapGestureRecognizer) {
    print("tap")
}

2

使用'<UIScrollViewDelegate>'覆盖类

并在.m类中使用此方法:

- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer{
    return YES;
}

这种方法可以帮助您在单个视图上启用多个滑动功能。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接