禁止禁用的UIButton传播触摸事件

22

我的应用程序有两个重叠的UIButtons。顶部按钮有时可能会被禁用。但是,在这种情况下,它接收到的任何触摸事件似乎都会传递给底层视图,在我的情况下是另一个按钮。

我需要的是顶部按钮拦截所有触摸事件,并防止它们到达底部按钮,即使处于禁用状态(即使在禁用状态下调用指定操作,我也会很高兴)。

到目前为止,我尝试过:

[topButton setUserInteractionEnabled:YES];

[topButton setExclusiveTouch:YES];

虽然后一种情况可能是不可取的,因为如果它是第一个被点击的视图,我仍然需要底部按钮响应事件。无论哪种方式都没有起作用。

6个回答

17

我在禁用的按钮的父视图中添加了以下方法:

- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
{
    if (!self.watchButton.enabled &&
        [self.watchButton pointInside:[self convertPoint:point toView:self.watchButton]
                            withEvent:nil]) {
        return nil;
    }
    return [super hitTest:point withEvent:event];
}

这在UITableView单元格中很有效。


9

我根据上面@kolyuchiy的答案稍微修改后使其按照我的需求工作。我在UIButton子类中重写了hitTest:withEvent:方法,当按钮被禁用且触摸点在视图框架内时返回self,以此来消耗该触摸,但不会调用按钮的事件处理。

- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
{
    if ( !self.enabled && [self pointInside:[self convertPoint:point toView:self] withEvent:event] )
    {
        return self;
    }
    return [super hitTest:point withEvent:event];
}

Swift版本:

override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
    if !isEnabled, self.point(inside: convert(point, to: self), with: event) {
        return self
    }
    return super.hitTest(point, with: event)
}

4
我知道现在已经很晚了,但我看到了许多类似的问题,而我读到的答案都对我没有帮助(其中一些是因为不起作用,另一些是因为我不喜欢那个解决方案),所以我找到了更好的解决方案,并在这里分享,以帮助可能会阅读这个问题的未来用户。
解决方案:
我总是有一个容器,里面有一些(或仅有一个)按钮。因此,我将按钮容器的IBOutlet连接到我正在使用的UIViewController或UIView上:
@property (weak, nonatomic) IBOutlet UIView *buttonsContainer;

我设置了一个空的单击手势识别器,代码如下:

[self.buttonsContainer addGestureRecognizer:[[UITapGestureRecognizer alloc] initWithTarget:self action:nil]];

通过这种解决方法,如果一个按钮被禁用,buttonsContainer 将捕获此事件并不做任何处理。

希望这可以帮到你。


4

在尝试了@kolyuchiy(需要修改UIButton的行为,超出了其范围)和@patrick-lynch(在我的情况下没有起作用)之后,我找到了一种更优雅的解决方案,适用于UIButton子类:

private static let dummyView: UIView = {
    let view = UIView(frame: .zero)
    view.userInteractionEnabled = true
    return view
}()

public override func hitTest(point: CGPoint, withEvent event: UIEvent?) -> UIView? {
    if !enabled && pointInside(point, withEvent: event) {
        return self.dynamicType.dummyView
    }
    return super.hitTest(point, withEvent: event)
}

3
我所做的是在UIButton下方放置与其大小相同的UIImageView。然后即使你禁用了UIButton,触摸事件也不会传播到UIImageView上。

这实际上是最简单和最快的解决方案。 - codingFriend1

1
创建一个按钮的子类,然后重写这些方法,使得按钮可以捕获事件,但如果某个按钮属性设置为“否”,则忽略这些事件:
touchesBegan:withEvent:
touchesCancelled:withEvent:
touchesEnded:withEvent:
touchesMoved:withEvent:

这是UIResponder的方法。
如果您想处理事件,只需让它们调用其[super...]方法,否则如果您想“吃掉”这些事件,只需不要调用该方法并返回。
如有必要,请参见此链接:在UITableView中观察捏多点触摸手势

1
当按钮被禁用时,似乎这些方法根本没有被调用。 - Mihai Damian
是的,这就是我想到的(所以我说“某个属性”),这也是为什么你可能需要使用第二种方法,或者做一些其他的事情来模拟按钮被禁用的状态。第二种方法可能会很麻烦,因为UIApplication(调用窗口中的sendEvent)可能会忽略被禁用的UI控件。 - Nimrod

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