NSLayoutConstraint 并确定哪个子视图更宽

Posted

技术标签:

【中文标题】NSLayoutConstraint 并确定哪个子视图更宽【英文标题】:NSLayoutConstraint and determining which subview is wider 【发布时间】:2015-04-23 17:33:23 【问题描述】:

我在子类容器UIView 中创建了一个UISwitch 和一个UILabel

UISwitch *toggleSwitch = [UISwitch new];
toggleSwitch.translatesAutoresizingMaskIntoConstraints = FALSE;
[toggleSwitch addTarget:self action:@selector(switchToggleDetected:) forControlEvents:UIControlEventValueChanged];
self.toggleSwitch = toggleSwitch;

[self addSubview:toggleSwitch];

UILabel *label = [UILabel new];
label.translatesAutoresizingMaskIntoConstraints = FALSE;
label.textAlignment = NSTextAlignmentCenter;
label.textColor = [UIColor whiteColor];
label.font = [UIFont fontWithName:HELVETICA_FONT_STYLE_BOLD size:10.0];
label.text = [self.text uppercaseString];

self.label = label;

[self addSubview:label];

[self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[toggleSwitch]-5-[label]" options:NSLayoutFormatAlignAllCenterX metrics:nil views:NSDictionaryOfVariableBindings(toggleSwitch, label)]];
[self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[toggleSwitch]|" options:NSLayoutFormatAlignAllCenterY metrics:nil views:NSDictionaryOfVariableBindings(toggleSwitch, label)]];

[self addConstraint:[NSLayoutConstraint constraintWithItem:self attribute:NSLayoutAttributeRight relatedBy:NSLayoutRelationEqual toItem:label attribute:NSLayoutAttributeRight multiplier:1 constant:0]];
[self addConstraint:[NSLayoutConstraint constraintWithItem:self attribute:NSLayoutAttributeBottom relatedBy:NSLayoutRelationEqual toItem:label attribute:NSLayoutAttributeBottom multiplier:1 constant:0]];

这给了我这个:

最后两个约束让我给容器视图一个“固有大小”。容器将是 rect(0,0,0,0)。然后我告诉右侧应该与UILabel 具有相同的宽度,而底部应该与UILabel 具有相同的值以给出高度。

我可能会遇到的问题是标签比开关短时:

这将导致开关容器视图放置不正确的问题:

[contentImageView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|-5-[toggleView]" options:0 metrics:nil views:NSDictionaryOfVariableBindings(toggleView)]];

所以我想将约束基于两者中较长的一个,无论是开关还是标签,但我不确定如何确定哪个更宽。开关是一个恒定的宽度,但我无法获得标签的宽度,直到添加到屏幕之后,这为时已晚。

我尝试添加[self layoutIfNeeded][label layoutIfNeeded]

[self layoutIfNeeded];
[label layoutIfNeeded];

DLog(@"label: %@", label);
DLog(@"switch: %@", toggleSwitch);

//Constraints added here

结果:

DEBUG | -[SwitchContainerView createContainerSwitch] | label: <UILabel: 0x7af7c6d0; frame = (0 0; 0 0); text = 'ON'; userInteractionEnabled = NO; layer = <_UILabelLayer: 0x7af7c7b0>>
DEBUG | -[SwitchContainerView createContainerSwitch] | switch: <UISwitch: 0x7af7b370; frame = (0 0; 51 31); layer = <CALayer: 0x7af7b430>>

关于确定两者中哪一个更宽的任何建议?

【问题讨论】:

【参考方案1】:

没有必要“确定哪个更宽” - 只需设置约束并走开,让自动布局完成它的工作。只需使用两个具有不等式/优先级的约束来确定包含的父视图相对于两个子视图的宽度。

我仅使用约束就实现了这种安排 - 我现在所做的只是更改标签的文本:

只要标签文本很短,superview 就会比 switch 宽 20 点。每当标签文本很长时,superview 比标签宽 20 点。这是完全用约束配置的。以下是在我的屏幕截图中确定黄色超级视图宽度的约束(如调试器控制台中所示):

UIView:0x7fdb03435450.width >= UISwitch:0x7fdb034355c0.width + 20 priority:999
UIView:0x7fdb03435450.width >= UILabel:0x7fdb03433260'Infundibulum'.width + 20 priority:999

约束不变;我设置它们一次,布局就可以正常工作,并随着标签文本的变化继续工作。

【讨论】:

我使用术语固有大小是因为它的大小将由其子视图的大小决定。如果我不使用NSLayoutAttributeRightNSLayoutAttributeBottom,除非我设置了我试图避免的高度和宽度,否则视图将没有大小。感谢您的回复,并指出在界面构建中是可能的。不幸的是,我不喜欢使用它,而是更喜欢编写代码。 很好,你可以在代码中完成。这与我所说的无关。你会简单地做出我所做的所有相同的限制。我在 IB 中制作它们,您将在代码中制作它们。没问题。我只是表明纯约束解决方案有效。没有“如果”,你不需要知道哪个更宽,不需要来来去去的约束。做所有的约束然后走开。我的意思是您“确定哪个更宽”——让约束来完成所有工作。 我确实尝试使用不等式/优先级来解决它,但我不确定如何设置这些值。我可以将最小宽度设置为 55 并设置为高优先级,然后根据标签宽度设置第二个约束的优先级较低,但这不总是默认为 55 的最小宽度吗?如果我做相反的事情,如果标签只有 20 宽度,我会不会遇到容器视图小于 55 的最小宽度的问题? 我在回答中解释了。超级视图的宽度基于两个大于或等于约束 - 一个针对标签宽度,一个针对开关宽度。 我已经发布了实际宽度限制作为我的答案的一部分。

以上是关于NSLayoutConstraint 并确定哪个子视图更宽的主要内容,如果未能解决你的问题,请参考以下文章

如何确定 NSLayoutConstraint 方向?

如何确定 NSLayoutConstraint 是水平的还是垂直的?

Swift:UIButton 不可点击,NSLayoutConstraint heightAnchor 问题

如何知道在相对布局中单击了哪个子视图

识别 Plotly 中的子图

在 NSLayoutConstraint 上更新“常量”失败,并显示“无法分配给 'constraint' 中的 'constant'”