以编程方式更改 UILabel 上的约束打破布局

Posted

技术标签:

【中文标题】以编程方式更改 UILabel 上的约束打破布局【英文标题】:Programmatically change constraint on a UILabel breaks layout 【发布时间】:2019-06-24 22:40:28 【问题描述】:

我有一个带有两个约束的 UILabel。我激活的约束之一和我禁用的另一个约束。我有一个按钮可以禁用第一个约束并激活第二个约束。然后当第二次点击时,禁用第二个约束并重新激活第一个约束。第一次点击此按钮按预期工作。 UILabel 包括所有其他 UILabel 约束到它,移动到不同的位置。但是,当点击按钮将其移回时,布局会中断。 UILabel 移动到几乎与之前相同的位置,但所有其他 UILabel 都被限制在原地不动,不会回到原来的位置。

这是处理激活约束的函数。

- (void)tapOnce:(UIGestureRecognizer *)gesture

    if (_constraintLabelOne.isActive) 
        [_constraintLabelOne setActive:NO];
        [_constraintLabelTwo setActive:YES];
     else 
        [_constraintLabelTwo setActive:NO];
        [_constraintLabelOne setActive:YES];
    
    [self layoutIfNeeded];

约束看起来像这样。

_constraintLabelOne = [NSLayoutConstraint constraintWithItem:myLabel
                                                                   attribute:NSLayoutAttributeTop
                                                                   relatedBy:NSLayoutRelationEqual
                                                                      toItem:topView
                                                                   attribute:NSLayoutAttributeBottom
                                                                  multiplier:1.0
                                                                    constant:6];
_constraintLabelTwo = [NSLayoutConstraint constraintWithItem:myLabel
                                                                   attribute:NSLayoutAttributeTop
                                                                   relatedBy:NSLayoutRelationEqual
                                                                      toItem:topView
                                                                   attribute:NSLayoutAttributeBottom
                                                                  multiplier:1.0
                                                                    constant:20];

在第一次调用tapOnce 后,限制为myLabel 的其他标签卡在第二个位置。 myLabel 约束更新后,它们为什么不回到初始位置?

我附上了一张带有当前行为图的屏幕截图。蓝色框代表topView。洋红色框代表myLabel。第一个屏幕是在调用tapOnce 之前。中间的屏幕是在拨打tapOnce 一次之后。右侧画面是调用tapOnce 两次之后。

【问题讨论】:

期望的行为是什么? 第二次调用tapOnce 后,所需的行为是屏幕看起来像最左边的样子。布局不应看起来像最右边的屏幕。 抱歉,我以为最左边的屏幕是起点。您说“第一个屏幕是在调用 tapOnce 之前”。如果不是,您第一次拨打tapOnce 之前的屏幕是什么样的?您能否展示tapOnce 的效果应该是什么,即之前和之后?或者你是说一次点击会使我们从第一个屏幕转到第二个屏幕,而第二次点击应该使我们从第二个屏幕回到第一个屏幕? 我是说想要的效果是,一次点击使我们从第一个屏幕转到第二个屏幕,第二次点击应该使我们从第二个屏幕回到第一个屏幕。跨度> 【参考方案1】:

我这样做的方法是将其中一个约束的priority 设置得较低,因此它是“可选的”,例如UILayoutPriorityDefaultHigh,甚至更高,例如999.

然后您可以同时激活两个约束,所需的约束将优先。

要设置它,为默认状态设置必需的约束,为按钮按下状态设置可选的约束。

您可以从激活两个约束开始,然后当按下按钮时,您只需禁用(设置constraint.active = NO)所需的约束,另一个将优先。当再次按下按钮时,您只需重新启用所需的约束即可。

【讨论】:

我刚刚尝试过这种方式,但我仍然遇到同样的问题。当我重新启用所需的约束时,视图中的所有其他项目都保持不变,并且 UILabel 移动到它下方的项目和 topView 项目之间的点。【参考方案2】:

我想通了。发生的事情是 UILabel 的高度没有受到约束,因此当第二个约束被停用并且第一个约束重新激活时,高度会自动增加(见下图)。为了解决这个问题,我必须手动限制所有视图项的高度。如果有人有更好的解决方案,请发布它,因为我不希望手动设置每个项目的高度,因为这可能会导致布局在不同设备上中断。

更新

感谢 siburb 指出 UI 元素的 Content Hugging Priority 属性。我的问题的根源是没有设置这个属性,这允许我的 UILabel 的高度扩大。解决方案是将此属性设置为 750 或更高的值。

[myLabel setContentHuggingPriority:750 forAxis:UILayoutConstraintAxisVertical];

【讨论】:

您不必手动限制高度。您应该只增加标签的Content Hugging Priority,以便它们在不超出其固有内容大小时更加自信。最初尝试超过 750,但您可能需要增加到“必需”或 1000。 感谢您的建议。我不知道 UI 元素上的 Content Hugging Priority 属性。这绝对是我问题的根源,谢谢。 @Michael 如果您现在有解决方案,请修改您的答案以反映它。

以上是关于以编程方式更改 UILabel 上的约束打破布局的主要内容,如果未能解决你的问题,请参考以下文章

ios 6:使用自动布局约束垂直居中 UILabel

如何在显示旋转时以编程方式更改布局约束

以编程方式更改约束布局中的高度?

UIScrollview 内的 UIView 内的 UILabel 上的 UIGestureRecognizer

按单词包装 UILabel 并以编程方式扩展 UITableViewCell 高度

iOS 以编程方式生成的视图具有隐藏的约束,导致与自动布局发生冲突