允许在另一个UIView下与UIView进行交互

del*_*any 115 cocoa-touch objective-c ios

是否有一种简单的方法允许与位于另一个UIView下的UIView中的按钮进行交互 - 在按钮顶部没有来自顶部UIView的实际对象?

例如,目前我有一个UIView(A),顶部有一个对象,屏幕底部有一个对象,中间没有任何东西.它位于另一个UIView的顶部,中间有按钮(B).但是,我似乎无法与B中间的按钮进行交互.

我可以看到B中的按钮 - 我已经将A的背景设置为clearColor - 但是B中的按钮似乎没有接收到触摸,尽管实际上在这些按钮的顶部没有来自A的对象.

编辑 - 我仍然希望能够与顶级UIView中的对象进行交互

当然有一种简单的方法可以做到这一点?

gyi*_*yim 97

您应该为顶视图创建一个UIView子类并覆盖以下方法:

- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event {
    // UIView will be "transparent" for touch events if we return NO
    return (point.y < MIDDLE_Y1 || point.y > MIDDLE_Y2);
}
Run Code Online (Sandbox Code Playgroud)

您还可以查看hitTest:event:方法.

  • 什么是middle_y1/y2代表这里? (19认同)

Stu*_*art 40

虽然这里的许多答案都有效,但我有点惊讶地发现这里没有给出最方便,最通用和最简单的答案.@Ash最接近,除了返回超级视图有一些奇怪的事情......不要这样做.

这个答案来自我在这里给出类似问题的答案.

- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
{
    UIView *hitView = [super hitTest:point withEvent:event];
    if (hitView == self) return nil;
    return hitView;
}
Run Code Online (Sandbox Code Playgroud)

[super hitTest:point withEvent:event]将返回该视图所触及的层次结构中最深的视图.如果hitView == self(即,如果有触摸点在无子视图),返回nil,指定,这种观点应该接收触摸.响应者链的工作方式意味着将继续遍历此点上方的视图层次结构,直到找到将响应触摸的视图.不要返回超级视图,因为它不符合此视图是否应该接受触摸的超级视图!

这个解决方案是:

  • 方便,因为它不需要引用任何其他视图/子视图/对象;
  • generic,因为它适用于任何纯粹作为可触摸子视图的容器的视图,并且子视图的配置不会影响它的工作方式(如果您覆盖pointInside:withEvent:以返回特定的可触摸区域).
  • 万无一失,代码不多......而且这个概念并不难理解.

我经常使用它,我已经将它抽象为一个子类,以便为一个覆盖保存无意义的视图子类.作为奖励,添加一个属性以使其可配置:

@interface ISView : UIView
@property(nonatomic, assign) BOOL onlyRespondToTouchesInSubviews;
@end

@implementation ISView
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
{
    UIView *hitView = [super hitTest:point withEvent:event];
    if (hitView == self && onlyRespondToTouchesInSubviews) return nil;
    return hitView;
}
@end
Run Code Online (Sandbox Code Playgroud)

然后狂野地使用此视图,无论您在哪里使用平原UIView.配置它是作为设置简单onlyRespondToTouchesInSubviewsYES.

  • 唯一正确的答案.要清楚,如果你想忽略对视图的触摸,而不是视图中包含的任何按钮(比如说)**,就像Stuart解释的那样.(我通常将其称为"持有人视图",因为它可以无害地"保持"某些按钮,但是,它不会影响持有者"下方"的任何内容.) (2认同)

Tyl*_*ler 31

有几种方法可以解决这个问题.我最喜欢的是覆盖hitTest:withEvent:在一个视图中,它是冲突视图的常见超视图(可能是间接的)(听起来就像你称之为A和B).例如,像这样的东西(这里A和B是UIView指针,其中B是"隐藏"指针,通常被忽略):

- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
    CGPoint pointInB = [B convertPoint:point fromView:self];

    if ([B pointInside:pointInB withEvent:event])
        return B;

    return [super hitTest:point withEvent:event];
}
Run Code Online (Sandbox Code Playgroud)

您也可以pointInside:withEvent:像gyim建议的那样修改方法.这可以通过在A中有效地"戳一个洞"来实现基本相同的结果,至少对于触摸来说.

另一种方法是事件转发,这意味着覆盖touchesBegan:withEvent:和类似的方法(如touchesMoved:withEvent:等)将一些触摸发送到不同于它们第一次去的对象.例如,在A中,您可以编写如下内容:

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
    if ([self shouldForwardTouches:touches]) {
        [B touchesBegan:touches withEvent:event];
    }
    else {
        // Do whatever A does with touches.
    }
}
Run Code Online (Sandbox Code Playgroud)

但是,这并不总是像你期望的那样工作!最重要的是像UIButton这样的内置控件总是会忽略转发的触摸.因此,第一种方法更可靠.

有一篇很好的博客文章更详细地解释了所有这些,以及一个小型的工作xcode项目来演示这些想法,可在此处获得:

http://bynomial.com/blog/?p=74


Ben*_*Cox 29

你必须设置upperView.userInteractionEnabled = NO;,否则上面的视图将拦截触摸.

Interface Builder版本是View Attributes面板底部的一个复选框,名为"User Interaction Enabled".取消选中它,你应该好好去.

  • 这个解决方案适合我.我根本不需要上部视图来对触摸作出反应. (2认同)

sam*_*tte 11

pointInside的自定义实现:withEvent:确实似乎是要走的路,但处理硬编码坐标对我来说似乎很奇怪.所以我最后使用CGRectContainsPoint()函数检查CGPoint是否在按钮CGRect中:

- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event {
    return (CGRectContainsPoint(disclosureButton.frame, point));
}
Run Code Online (Sandbox Code Playgroud)


Seg*_*gev 8

最近我写了一个课程,这将帮助我.将其用作UIButtonUIView将传递在透明像素上执行的触摸事件的自定义类.

此解决方案比接受的答案稍好一些,因为您仍然可以单击UIButton半透明下的UIView,而非透明部分UIView仍然会响应触摸事件.

GIF

正如你在GIF中看到的那样,长颈鹿按钮是一个简单的矩形,但透明区域上的触摸事件会传递到UIButton下面的黄色.

链接到课堂

  • 由于您的代码时间不长,因此您应该在答案中包含相关部分,以防将来某个时候移动或删除项目. (2认同)