为啥 UIView 的这些自动布局约束会阻止 UIButton 子视图接收触摸事件?

Posted

技术标签:

【中文标题】为啥 UIView 的这些自动布局约束会阻止 UIButton 子视图接收触摸事件?【英文标题】:Why do these Auto Layout constraints for a UIView prevent UIButton subviews from receiving touch events?为什么 UIView 的这些自动布局约束会阻止 UIButton 子视图接收触摸事件? 【发布时间】:2015-08-07 15:21:50 【问题描述】:

我正在以编程方式使用自动布局。我有一个简单的UIViewController 和一些控件,包括并排排列的两个UIButtons。我经常将相关控件分组到 UIView 中,作为容器,使 groups-of-controls 的排列更易于管理。您将在下面看到 _iapButtonsView,它包含两个按钮和一些垫片。

我的问题。在以下示例中,我被我认为是对约束的有效更改所吸引,这实际上导致UIButtons 没有接收触摸事件。

代码提取 - 按钮确实接收触摸事件的约束:

- (void)viewDidLoad

    [super viewDidLoad];

    ...

    _buyButton = [ViewCreationHelper createRoundedBorderButtonBold];
    [_buyButton addTarget:self action:@selector(buyTap:) forControlEvents:UIControlEventTouchUpInside];

    _restoreButton = [ViewCreationHelper createRoundedBorderButton];
    [_restoreButton addTarget:self action:@selector(restorePurchaseTap:) forControlEvents:UIControlEventTouchUpInside];

    _iapButtonsView = [UIView new];
    _iapButtonsView.translatesAutoresizingMaskIntoConstraints = NO;
    [contentView addSubview:_iapButtonsView];

    ...

    [_iapButtonsView addSubview:_buyButton];
    [_iapButtonsView addSubview:_restoreButton];

    // Constraints
    NSDictionary* views = NSDictionaryOfVariableBindings(scrollView, contentView, iapDesciption, _iapButtonsView, _buyButton, _restoreButton, spacer1, spacer2, spacer3);

    ...

    constraints = [NSLayoutConstraint
                   constraintsWithVisualFormat:@"V:|-25-[iapDesciption]-40-[_iapButtonsView]|"
                   options:0
                   metrics:nil
                   views:views];

    [contentView addConstraints:constraints];

    constraints = [NSLayoutConstraint constraintsWithVisualFormat:@"H:|-20-[_iapButtonsView]-20-|" options:0 metrics:nil views:views];
    [contentView addConstraints:constraints];

    constraints = [NSLayoutConstraint constraintsWithVisualFormat:@"H:|[spacer1][_buyButton(==120.0)][spacer2(==spacer1)][_restoreButton(==_buyButton)][spacer3(==spacer1)]|" options:NSLayoutFormatAlignAllCenterY metrics:nil views:views];
    [_iapButtonsView addConstraints:constraints];

    constraints = [NSLayoutConstraint constraintsWithVisualFormat:@"V:|[_buyButton(==80.0)]|" options:0 metrics:nil views:views];
    [_iapButtonsView addConstraints:constraints];

    constraints = [NSLayoutConstraint constraintsWithVisualFormat:@"V:|[_restoreButton(==80.0)]|" options:0 metrics:nil views:views];
    [_iapButtonsView addConstraints:constraints];

    ...

有问题的约束是_iapButtonsView 的垂直约束。在开发过程中(这是一个应用内购买屏幕)我在底部有一些 debug 控件,这就是为什么我将尾随 | 连接到超级视图的底部边缘,如下所示:

constraintsWithVisualFormat:@"V:|-25-[iapDesciption]-40-[_iapButtonsView][someSpacer][someControls]|"

当我取出那些 debug 控件时,我将这些约束更改为:

constraintsWithVisualFormat:@"V:|-25-[iapDesciption]-40-[_iapButtonsView]"

认为这更正确:它们是从顶部锚定的,只有 _iapButtonsView 从它的子视图(主要是两个按钮)获取它的大小,所以我不应该连接到底部边缘的superview...

进行该更改后,按钮不再接收触摸事件。为了进行实验,我尝试显式设置_iapButtonsView 的垂直大小,但仍然没有连接到超级视图的底部边缘,例如

constraintsWithVisualFormat:@"V:|-25-[iapDesciption]-40-[_iapButtonsView(==80.0)]"

有了这些限制,按钮仍然不会接收到触摸事件。

我不明白什么?

(编辑:根据 daddy warbucks 的建议,我删除了上面代码中重复的 [contentView addSubview:_iapButtonsView];

【问题讨论】:

我经常遇到意外地将视图的宽度或高度更改为 0 的约束。你能设置_iapButtonsView.clipsToBounds = YES 看看这是否揭示了问题吗?如果是这样,您的按钮应该会消失。 感谢您的建议。我会试一试,虽然我已经尝试给 _iapButtonsView 一个背景颜色......它显示为预期的大小。 这也说明了 iabButtonsView 的问题。也许是按钮本身在改变大小。 @DanLoughney - 我试过_iapButtonsView.clipsToBounds = YES,但没有发现问题。与其他实验一起,我认为 _iapButtonsView 的大小是正确的...只是不确定为什么限制会导致没有触摸事件通过...谢谢。 【参考方案1】:

一个问题,你已经调用了两次:

[contentView addSubview:_iapButtonsView];

不确定这是否有帮助,但这可能是个问题。

另外,你不必使用“(==80.0)”,只需使用“(80.0)”,甚至“(80)”不确定这是否有帮助,但是嘿,它可以,对吧?

【讨论】:

很好看。我认为双 [contentView addSubview:_iapButtonsView]; 只是我编辑问题中的代码提取时出现的错误,但实际上我在完整的 viewDidLoad 方法中调用了两次。不幸的是,删除重复调用并不能纠正/回答我看到的行为,更苗条的(==80) 建议也不能。感谢现场和建议;) 我还假设您的垫片的属性 translatesAutoresizingMaskIntoConstraints 也设置为“NO”,对吗?并且每一次机会,是包含嵌套在 UIScrollView 中的按钮的 UIView 吗?此外,您是否尝试过将选项“options:NSLayoutFormatAlignAllCenterY”设置为“options:0”?我只使用编程视图进行编程,我的代码通常最终每个视图都有一千行代码,具体取决于复杂性,而且我从未使用过“NSLayoutFormat ...”选项,这只是一个猜测,但也许值得一试.我总是将选项设置为“选项:0” 我也会改变这个“[_iapButtonsView addConstraints:constraints];”对此:“[_iapButtonsView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@”H:|-20-[_iapButtonsView]-20-|”options:0 metrics:nil views:views]];”并删除这些行:“ [_iapButtonsView addConstraints:constraints];”您可以添加这些约束,而无需先将它们设置为变量“约束” 啊,还有,我想这会成功,我现在记得为什么会这样,你必须添加这个“ [_buyButton addTarget:self action:@selector(buyTap:) forControlEvents: UIControlEventTouchUpInside];"仅在添加约束后,看看是否有帮助 @Loxx 你救了我!感谢您为我节省了很多时间来尝试解决 AutoLayout“歧义”或错误边界问题。我沿着这条路走了一点,但我决定尝试您的建议,即在创建约束之后添加事件,瞧!现在一切正常。

以上是关于为啥 UIView 的这些自动布局约束会阻止 UIButton 子视图接收触摸事件?的主要内容,如果未能解决你的问题,请参考以下文章

Interface Builder 运行时自动布局约束阻止视图占用固有大小,我该如何解决这个问题?

为啥这些受约束的布局有不同的尺寸?

空 UIView 上的自动布局约束无法按预期工作

向添加到 UIWindow 的视图添加约束时,为啥无法设置自动布局约束?

使用自动布局约束在 UIScrollView 内为 UIView 设置动画

Swift UIView 自动布局和约束