通过触摸下面的UIViews


71

我有一个UIView带有4个按钮的按钮,另一个UIView在按钮视图顶部。最顶部的视图包含UIImageView带有的UITapGestureRecognizer

我尝试创建的行为是,当用户点击时,UIImageView它会在屏幕右下角的小和动画变大之间切换。当它很大时,我希望禁用底视图上的按钮;当它很小时,我希望将其触摸到右下角,并使其正常工作。我快到了,但是除非禁用UserInteractions顶视图的按钮,否则无法触摸按钮。

initWithFrame:在顶视图中有这个:

// Add a gesture recognizer to the image view
UITapGestureRecognizer *tapGestureRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(imageTapped:)];
tapGestureRecognizer.cancelsTouchesInView = NO;
[imageView addGestureRecognizer:tapGestureRecognizer];
[tapGestureRecognizer release];

我这是我的imageTapped:方法:

- (void) imageTapped:(UITapGestureRecognizer *) gestureRecognizer {
    // Toggle between expanding and contracting the image
    if (expanded) {
        [self contractAnimated:YES];
        expanded = NO;
        gestureRecognizer.cancelsTouchesInView = NO;
        self.userInteractionEnabled = NO;
        self.exclusiveTouch = NO;
    }
    else {
        [self expandAnimated:YES];
        expanded = YES;
        gestureRecognizer.cancelsTouchesInView = NO;
        self.userInteractionEnabled = YES;
        self.exclusiveTouch = YES;
    }
}

使用上面的代码,当图像较大时,按钮处于非活动状态,当我触摸图像时,按钮将收缩并且按钮变为活动状态。但是,小图像不会受到触摸,因此不会扩展。

如果我self.userInteractionEnabled = YES在这两种情况下都设置了,则触摸时图像会放大和缩小,但按钮将永远不会受到触摸,并且就像被禁用一样。

是否有其他方法可以使图像在触摸时扩展和收缩,但仅当图像处于收缩状态时,下方的按钮才能接收触摸?我在这里做些愚蠢的事情,却缺少明显的东西吗?

我要让它正常工作真是疯了,不胜感激,

戴夫

更新:

为了进一步测试,我覆盖了touchesBegan:touchesCancelled:方法,并在包含UIImageView的视图上调用了它们的超级实现。使用上面的代码,touchesCancelled:永远不会调用,而touchesBegan:总是调用。

这样看来,该视图正在获得触摸,只是它们没有传递到下面的视图。

更新

这是因为响应者链的工作方式吗?我的视图层次结构如下所示:

VC - View1
     -View2
      -imageView1 (has tapGestureRecogniser)
      -imageView2
     -View3
      -button1
      -button2

我认为操作系统首先要执行一次hitTest,因为View2位于最前面,因此应该获得所有触摸,除非将View2的userInteractions设置为NO,否则这些触摸永远都不会传递给View3,在这种情况下,还会阻止imageView1接收触摸。这是如何工作的,View2是否有办法将其传递给View3?


您如何扩展和收缩imageView?编辑:我假设您在imageView上仍然以某种方式掩盖了按钮,即使它在视觉上是收缩的。
肖恩·贝里

UIImageView是另一个视图的子视图。正是这个视图覆盖了按钮,当您在我的代码中看到自己时,它就是UIView的子类,而UIImageView作为子类添加了。希望有道理。
Magic Bullet Dave 2012年

请参阅khanlou.com/2018/09/hacking-hit-tests对此进行了很好的讨论。
安德鲁·邓肯

Answers:


94

我认为UIGestureRecognizer是一个红色鲱鱼。为了解决这个问题,我否决了我的pointInside:withEvent:方法UIView:

- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event {
    BOOL pointInside = NO;

    if (CGRectContainsPoint(imageView.frame, point) || expanded) pointInside = YES;

    return pointInside;
}

如果您触摸imageView或设置了它的扩展标志,这将导致视图捕获所有触摸。如果未展开,则仅捕获触摸在imageView上的触摸。

通过返回NO,顶级VC的View会查询其其余视图层次结构以查找匹配项。


24
感谢您提供此答案中的信息!但是,我最终重载hitTest而不是pointInside。这是因为我试图确保视图不会窃取任何水龙头,但希望所有子视图都能够。通过重写,hitTest您可以简单地调用基本实现来确定点击了哪个视图,如果该视图恰好self返回了nil
柯克·沃尔

1
下面的解决方案更好。直接从情节提要中取消选中“已启用用户交互”
coolcool1994

66

ViewStoryboard或中选择您的XIB...


在此处输入图片说明


还是在Swift中

view.isUserInteractionEnabled = false

72
这也将禁用视图子视图上的触摸..如果有的话
Shailesh

1
如果您从同一超级视图中有2个视图,这将非常有用,谢谢!
Tiago Mendes

6

查看UIGestureRecognizerDelegate协议。特别,gestureRecognizer:shouldReceiveTouch:

您需要将每个UIGestureRecognizer属性设为UIViewController

// .h
@property (nonatomic, strong) UITapGestureRecognizer *lowerTap;

// .m
@synthesize lowerTap;

// When you are adding the gesture recognizer to the image view
self.lowerTap = tapGestureRecognizer

确保您成为UIViewController代表,

[self.lowerTap setDelegate: self];

然后,你会有这样的事情,

- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch {
    if (expanded && gestureRecognizer == self.lowerTap) {    
        return NO;
    }
    else {
        return YES;
    }
}

当然,这不是确切的代码。但这是您要遵循的一般模式。


2
嗨,约翰,您看了一下委托协议,但我看不出这对我有什么用。我希望手势识别器始终接收触摸,只是有时候我希望能够将它们传递给窗口中的其他视图,而不是取消它们。根据文档,cancelsTouchesInView = NO应该通过它们,只要父视图的userInteractionEnabled也设置为NO,它们就可以通过它们。
魔术子弹戴夫

您想传递的触感如何?例如,您可以调用其他视图将以任何方式调用的方法,而不是简单地YESelse子句中返回。
约翰

啊,我知道你要去哪里了。恐怕我真的做不到。将我的视图子类视为控件。我知道它的状态,可以根据用户输入对其进行更改,但对于它所放置的内容或将被调用的任何方法一无所知。那有意义吗?
魔术子弹戴夫

一般来说,这是100%正确的答案。只需使用shouldReceiveTouch通过点击即可。好人,约翰
胖子

@awfullyjohn,如果您需要最高层UIView通过事件怎么办?您不能创建子类。
Iulian Onofrei '16

3

我有另一个解决方案。我有两种观点,我们称它们CustomSubView是重叠的,它们都应该得到感动。因此,我有一个视图控制器和一个自定义UIView类,让我们ViewControllerView在接口构建器中设置它,然后添加两个应该接收该视图的视图的视图。

因此,我ViewControllerView通过覆盖hitTest来拦截触摸:

- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
{
    return self;
}

然后我改写了ViewControllerView

- (void)touchesBegan:(NSSet *)touches
           withEvent:(UIEvent *)event
{
    [super touchesBegan:touches withEvent:event];
    for (UIView *subview in [self.subviews reverseObjectEnumerator])
    {
        if ([subview isKindOfClass:[CustomSubView class]])
        {
            [subview touchesBegan:touches withEvent:event];
        }
    }
}

touchesMoved touchesEnded和完全相同touchesCancelled


3

@Magic Bullet Dave的解决方案但在Swift中

迅捷3

override func point(inside point: CGPoint, with event: UIEvent?) -> Bool {
    var pointInside = false
    if commentTextField.frame.contains(point) {
        pointInside = true
    } else {
        commentTextField.resignFirstResponder()
    }
    return pointInside
}

我在用于ImagePickerViewController.cameraOverlay的CameraOverlayView中使用它,以使用户能够在拍摄新照片时发表评论

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.