超越UIView界限的互动

Tho*_*asM 85 iphone cocoa objective-c uibutton uiview

当UIButton的帧位于其父级帧之外时,UIButton(或任何其他控件)是否可以接收触摸事件?因为当我尝试这个时,我的UIButton似乎无法接收任何事件.我该如何解决这个问题?

jni*_*nic 92

是.您可以覆盖该hitTest:withEvent:方法以返回视图,以获得比该视图包含的更大的点集.请参阅UIView类参考.

编辑:示例:

- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
{
    CGFloat radius = 100.0;
    CGRect frame = CGRectMake(-radius, -radius,
                              self.frame.size.width + radius,
                              self.frame.size.height + radius);

    if (CGRectContainsPoint(frame, point)) {
        return self;
    }
    return nil;
}
Run Code Online (Sandbox Code Playgroud)

编辑2 :(澄清之后:)为了确保按钮被视为在父级边界内,您需要pointInside:withEvent:在父级中覆盖以包含按钮的框架.

- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event
{
    if (CGRectContainsPoint(self.view.bounds, point) ||
        CGRectContainsPoint(button.view.frame, point))
    {
        return YES;
    }
    return NO;
}
Run Code Online (Sandbox Code Playgroud)

请注意,重写pointInside的代码并不完全正确.正如Summon在下面解释的那样,这样做:

-(BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event
    {
    if ( CGRectContainsPoint(self.oversizeButton.frame, point) )
        return YES;

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

请注意,您很可能self.oversizeButton在此UIView子类中使用IBOutlet; 然后你可以拖动有问题的特殊视图中的"超大尺寸按钮".(或者,如果由于某种原因你在一个项目中做了很多这样的事情,你就会有一个特殊的UIButton子类,你可以通过你的子视图列表查看这些类.)希望它有所帮助.


Sum*_*mon 23

@jnic,我正在开发iOS SDK 5.0,为了让你的代码正常工作,我必须这样做:

- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event {
if (CGRectContainsPoint(button.frame, point)) {
    return YES;
}
return [super pointInside:point withEvent:event]; }
Run Code Online (Sandbox Code Playgroud)

在我的例子中,容器视图是一个UIButton,所有子元素也是UIButtons,可以移动到父UIButton的边界之外.

最好


Ja͢*_*͢ck 19

在父视图中,您可以覆盖命中测试方法:

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

    if (CGRectContainsPoint(_myButton.bounds, translatedPoint)) {
        return [_myButton hitTest:translatedPoint withEvent:event];
    }
    return [super hitTest:point withEvent:event];

}
Run Code Online (Sandbox Code Playgroud)

在这种情况下,如果该点落在按钮的范围内,则将呼叫转发到那里; 如果没有,请恢复原始实现.


use*_*977 14

就我而言,我有一个UICollectionViewCell包含a 的子类UIButton.我clipsToBounds在单元格上禁用,按钮在单元格边界外可见.但是,该按钮未接收触摸事件.我能够使用Jack的答案检测按钮上的触摸事件:https://stackoverflow.com/a/30431157/3344977

这是一个Swift版本:

override func hitTest(point: CGPoint, withEvent event: UIEvent?) -> UIView? {

    let translatedPoint = button.convertPoint(point, fromView: self)

    if (CGRectContainsPoint(button.bounds, translatedPoint)) {
        print("Your button was pressed")
        return button.hitTest(translatedPoint, withEvent: event)
    }
    return super.hitTest(point, withEvent: event)
}
Run Code Online (Sandbox Code Playgroud)


Luk*_*meo 9

迅速:

其中targetView是您希望接收事件的视图(可能完全或部分位于其父视图的框架之外).

override func hitTest(point: CGPoint, withEvent event: UIEvent?) -> UIView? {
        let pointForTargetView: CGPoint = targetView.convertPoint(point, fromView: self)
        if CGRectContainsPoint(targetView.bounds, pointForTargetView) {
            return closeButton
        }
        return super.hitTest(point, withEvent: event)
}
Run Code Online (Sandbox Code Playgroud)


Sco*_*Zhu 9

为什么会这样呢?

这是因为当子视图超出超级视图的范围时,实际发生在该子视图上的触摸事件将不会传递到该子视图。然而,WILL交付给它的父。

无论子视图是否在视觉上被裁剪,触摸事件始终会遵循目标视图的父视图的边界矩形。换句话说,在其超级视图的边界矩形之外的视图的一部分中发生的触摸事件不会传递到该视图。链接

你需要做什么?

当您的超级视图接收到上述触摸事件时,您需要明确告知UIKit我的子视图应该是接收此触摸事件的子视图。

那代码呢?

在您的监督下,实施 func hitTest(_ point: CGPoint, with event: UIEvent?)

override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
        if isHidden || alpha == 0 || clipsToBounds { return super.hitTest(point, with: event) }
        // convert the point into subview's coordinate system
        let subviewPoint = self.convert(point, to: subview)
        // if the converted point lies in subview's bound, tell UIKit that subview should be the one that receives this event
        if !subview.isHidden && subview.bounds.contains(subviewPoint) { return subview }
        return super.hitTest(point, with: event)
    }
Run Code Online (Sandbox Code Playgroud)

令人着迷的哥特奇:您必须去“最高的太小的超级观看”

您必须“向上”进入问题视图位于外部的“最高”视图。

典型示例:

假设您有一个带有容器视图C的屏幕S。该容器视图控制器的视图为V。(调用V将位于C内并且大小相同。)V具有一个子视图B(可能是一个按钮)B。B是问题所在实际在V之外的视图

但请注意,B也在C之外。

在这个例子中,你必须应用的解决方案override hitTest其实到C,而不是到V。如果将其应用于V,则不会执行任何操作。

  • 这次真是万分感谢!我在这个问题上花了几个小时。非常感谢您抽出宝贵时间发布此信息。:) (4认同)

Dan*_*ark 6

对于我(对于其他人),答案不是覆盖hitTest方法,而是覆盖pointInside方法。我只需要在两个地方这样做。

Superview 这是一个视图,如果它clipsToBounds设置为 true,它将使整个问题消失。所以这是我UIView在 Swift 3 中的简单操作:

class PromiscuousView : UIView {
    override func point(inside point: CGPoint,
               with event: UIEvent?) -> Bool {
        return true
    }
} 
Run Code Online (Sandbox Code Playgroud)

子视图 您的里程可能会有所不同,但在我的情况下,我还必须覆盖pointInside确定触摸超出范围的子视图的方法。我的是Objective-C,所以:

- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event {
    WEPopoverController *controller = (id) self.delegate;
    if (self.takeHitsOutside) {
        return YES;
    } else {
        return [super pointInside:point withEvent:event];
    }
}
Run Code Online (Sandbox Code Playgroud)

这个答案(以及同一问题的其他一些答案)可以帮助您理清思路。