什么是NSLayoutConstraint &; uiview - encapsulation - layout - height;我应该如何去强迫它重新计算干净?

我有一个UITableView运行在iOS 8和我使用自动单元格高度约束在一个故事板。

我的一个单元格包含一个UITextView,我需要它根据用户输入收缩和展开-点击收缩/展开文本。

我通过在文本视图中添加一个运行时约束,并在响应用户事件时改变约束上的常数来做到这一点:

-(void)collapse:(BOOL)collapse; {


_collapsed = collapse;


if(collapse)
[_collapsedtextHeightConstraint setConstant: kCollapsedHeight]; // 70.0
else
[_collapsedtextHeightConstraint setConstant: [self idealCellHeightToShowFullText]];


[self setNeedsUpdateConstraints];


}

每当我这样做时,我都将它包装在tableView updates中,并调用[tableView setNeedsUpdateConstraints]:

[tableView beginUpdates];


[_briefCell collapse:!_showFullBriefText];


[tableView setNeedsUpdateConstraints];
// I have also tried
// [self.tableView reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationTop];
// with exactly the same results.


[tableView endUpdates];

当我这样做的时候,我的单元格确实会扩展(并在做的时候动画),但我得到一个约束警告:

2014-07-31 13:29:51.792 OneFlatEarth[5505:730175] Unable to simultaneously satisfy constraints.


Probably at least one of the constraints in the following list is one you don't want. Try this: (1) look at each constraint and try to figure out which you don't expect; (2) find the code that added the unwanted constraint or constraints and fix it. (Note: If you're seeing NSAutoresizingMaskLayoutConstraints that you don't understand, refer to the documentation for the UIView property translatesAutoresizingMaskIntoConstraints)


(


"<NSLayoutConstraint:0x7f94dced2b60 V:[UITextView:0x7f94d9b2b200'Brief text: Lorem Ipsum i...'(388)]>",


"<NSLayoutConstraint:0x7f94dced2260 V:[UITextView:0x7f94d9b2b200'Brief text: Lorem Ipsum i...']-(15)-|   (Names: '|':UITableViewCellContentView:0x7f94de5773a0 )>",


"<NSLayoutConstraint:0x7f94dced2350 V:|-(6)-[UITextView:0x7f94d9b2b200'Brief text: Lorem Ipsum i...']   (Names: '|':UITableViewCellContentView:0x7f94de5773a0 )>",


"<NSLayoutConstraint:0x7f94dced6480 'UIView-Encapsulated-Layout-Height' V:[UITableViewCellContentView:0x7f94de5773a0(91)]>"
)


Will attempt to recover by breaking constraint


<NSLayoutConstraint:0x7f94dced2b60 V:[UITextView:0x7f94d9b2b200'Brief text: Lorem Ipsum i...'(388)]>

388是我计算的高度,UITextView上的其他约束是来自Xcode/IB的。

最后一个是困扰我的-我猜UIView-Encapsulated-Layout-Height是单元格第一次呈现时的计算高度-(我将UITextView高度设置为>= 70.0),然而,这个派生约束然后推翻更新的用户cnstraint似乎是不对的。

更糟糕的是,尽管布局代码说它试图打破我的高度限制,但它并没有-它继续重新计算单元格的高度,一切都按照我想要的方式绘制。

那么,什么是NSLayoutConstraint UIView-Encapsulated-Layout-Height(我猜它是自动单元格大小的计算高度),我应该如何强制它重新计算干净?

107861 次浏览

与其通知表视图更新它的约束,不如尝试重新加载单元格:

[tableView beginUpdates];


[_briefCell collapse:!_showFullBriefText];


[tableView reloadRowsAtIndexPaths:@[[tableView indexPathForCell:_briefCell]] withRowAnimation:UITableViewRowAnimationNone];


[tableView endUpdates];

UIView-Encapsulated-Layout-Height可能是在初始加载期间根据单元格当时的约束为单元格计算的表视图的高度。

尝试将_collapsedtextHeightConstraint的优先级降低到999。这样,系统提供的UIView-Encapsulated-Layout-Height约束总是优先。

它基于你在-tableView:heightForRowAtIndexPath:中返回的内容。确保返回正确的值和你自己的约束,生成的值应该是相同的。您自己的约束的较低优先级只是暂时需要,以防止折叠/展开动画运行时发生冲突。

补充:虽然系统提供的约束是否正确是有争议的,但与框架争论是没有意义的。简单地接受系统约束优先。如果您认为系统约束是错误的,请确保从委托返回正确的rowHeight。

调整文本视图的大小以适应其内容,并将高度约束常数更新为结果高度,为我修复了UIView-Encapsulated-Layout-Height约束冲突,例如:

[self.textView sizeToFit];
self.textViewHeightConstraint.constant = self.textView.frame.size.height;

另一种可能性:

如果你使用auto layout来计算单元格高度(contentView的高度,大多数时候如下所示),如果你有uitableview分隔符,你需要添加分隔符高度,以便返回单元格高度。一旦你得到正确的高度,你就不会有自动布局警告。

- (CGFloat)calculateHeightForConfiguredSizingCell:(UITableViewCell *)sizingCell {
[sizingCell setNeedsLayout];
[sizingCell layoutIfNeeded];
CGSize size = [sizingCell.contentView systemLayoutSizeFittingSize:UILayoutFittingCompressedSize];
return size.height; // should + 1 here if my uitableviewseparatorstyle is not none


}

我能够通过在约束中的一个值上指定优先级来消除警告,警告消息说它必须打破(在"Will attempt to recover by breaking constraint"下面)。似乎只要我将优先级设置为大于49的值,警告就会消失。

对我来说,这意味着改变我的约束,警告说它试图打破:

@"V:|[contentLabel]-[quoteeLabel]|"

:

@"V:|-0@500-[contentLabel]-[quoteeLabel]|"

事实上,我可以为约束的任何元素添加优先级,它都可以工作。哪一个似乎都不重要。我的单元格最终达到了适当的高度,并且没有显示警告。Roger,以你的例子为例,尝试在388高度值约束之后添加@500(例如388@500)。

我不完全确定为什么会这样,但我做了一些调查。在NSLayoutPriority枚举中,NSLayoutPriorityFittingSizeCompression优先级似乎是50。该优先级级别的文档说:

当你发送一个fittingSize消息到一个视图,最小的大小 足够大,可以计算视图的内容。这是 视图希望尽可能小的优先级级别 计算。相当低。这样做一般是不合适的 在这个优先级上做一个约束。你想要更高还是 低。< / p >

引用的fittingSize消息的文档如下:

满足它所持有的约束的视图的最小大小。 (只读)< / p > AppKit将此属性设置为视图可用的最佳大小, 考虑到它及其子视图所拥有的所有约束和 满足使视图尽可能小的偏好。的 此属性中的大小值从不为负

我还没有深入研究,但这似乎与问题所在有关。

我有一个类似的场景:一个只有一行单元格的表格视图,其中有几行UILabel对象。我使用iOS 8和自动布局。

当我旋转时,我得到了错误的系统计算的行高(43.5远小于实际高度)。它看起来是这样的:

"<NSLayoutConstraint:0x7bc2b2c0 'UIView-Encapsulated-Layout-Height' V:[UITableViewCellContentView:0x7bc37f30(43.5)]>"

这不仅仅是一个警告。我的表格视图单元格的布局是可怕的-所有的文本重叠在一个文本行。

让我惊讶的是,下面这一行神奇地“解决”了我的问题(自动布局没有抱怨,我在屏幕上得到了我所期望的):

myTableView.estimatedRowHeight = 2.0; // any number but 2.0 is the smallest one that works

不管有没有这句话:

myTableView.rowHeight = UITableViewAutomaticDimension; // by itself this line only doesn't help fix my specific problem
从委托中获取单元格的indexPath高度。 然后从cellForRowAtIndexPath中获取cell:

top (10@1000)
cell
bottom (0@1000)
如果cell.contentView < p >。-> (uiview - encapsulation - layout - height: 0@1000) top(10@1000) conflict with (uiview - encapsulation - layout - height:0@1000),

因为它们的优先级等于1000。 我们需要在UIView-Encapsulated-Layout-Height的优先级下设置最高优先级

我收到的信息是这样的:

不能同时满足约束 < br >… < br >… < br >… NSLayoutConstraint: 0 x7fe74bdf7e50 UIView-Encapsulated-Layout-Height” V: [UITableViewCellContentView: 0 x7fe75330c5c0 (21.5)] < br > < br >… < br >… Will attempt to recover by breaking constraint x7fe0f9b1e090 NSLayoutConstraint: 0 x7fe0f9b200c0 UITableViewCellContentView: 0。bottomMargin == UILabel:0x7fe0f9b1e970.bottom

我使用自定义的UITableViewCellUITableViewAutomaticDimension作为高度。我还实现了estimatedHeightForRowAtIndex:方法。

给我带来问题的约束条件是这样的

[NSLayoutConstraint constraintsWithVisualFormat:@"V:|-[title]-|" options:0 metrics:nil views:views];

将约束改为this将解决问题,但就像另一个答案一样,我觉得这是不正确的,因为它降低了我希望被要求的约束的优先级:

[NSLayoutConstraint constraintsWithVisualFormat:@"V:|-6@999-[title]-6@999-|" options:0 metrics:nil views:views];

然而,我注意到的是,如果我实际上只是删除优先级,这也是有效的,我不会得到破坏约束日志:

[NSLayoutConstraint constraintsWithVisualFormat:@"V:|-6-[title]-6-|" options:0 metrics:nil views:views];

关于|-6-[title]-6-||-[title-|之间的区别是什么,这有点神秘。但是,指定大小对我来说不是问题,它消除了日志,而且我不需要降低所需约束的优先级。

在花了几个小时对这个bug挠头之后,我终于找到了一个适合我的解决方案。我的主要问题是,我有多个nibs注册为不同的单元格类型,但一个单元格类型被允许有不同的大小(不是所有实例的单元格将是相同的大小)。当tableview试图让该类型的单元格出队列时,问题就出现了,而它恰好有不同的高度。我通过设置来解决

< p > <代码> self.contentView。autoresizingMask = UIViewAutoresizingFlexibleHeight; self.frame = CGRectMake(0,0, self.frame.size.width,{正确的高度})

当单元格有数据来计算它的大小时。 我想它可以在

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath

类似的

< p > <代码> cell.contentView。autoresizingMask = UIViewAutoresizingFlexibleHeight; cell.frame = CGRectMake(0,0, self.frame.size.width,{正确的高度});

.width

希望这能有所帮助!

正如杰西在问题的评论中提到的,这对我来说是有效的:

self.contentView.autoresizingMask = UIViewAutoresizingFlexibleHeight;

供你参考,这个问题在iOS 10中没有出现。

设置这个view.translatesAutoresizingMaskIntoConstraints = NO;可以解决这个问题。

我能够通过删除我在tableViewcellForRowAt方法中拥有的伪cell.layoutIfNeeded()来解决这个错误。

当使用UITableViewAutomaticDimension并在单元格内的视图上更改高度约束时,我有这个错误。

我最终发现这是由于约束常数值没有四舍五入到最近的整数。

let neededHeight = width / ratio // This is a CGFloat like 133.2353
constraintPictureHeight.constant = neededHeight // Causes constraint error
constraintPictureHeight.constant = ceil(neededHeight) // All good!

我在集合视图单元格中遇到了类似的问题。

我通过降低链接到单元格底部的最终约束的优先级来解决这个问题(从视图的顶部到底部的链中的最后一个约束-这最终决定了它的高度)到999。

细胞的高度是正确的,警告就消失了。

99.9%的情况下,在使用自定义单元格或标头时,所有UITableViews的冲突都发生在表第一次加载时。一旦加载,你通常不会再看到冲突。

这是因为大多数开发人员通常使用固定高度或某种类型的锚定约束来布局单元格/标题中的元素。发生冲突是因为当UITableView第一次加载/被布局时,它将其单元格的高度设置为0。这显然与您自己的约束相冲突。要解决这个问题,只需将任何固定高度约束设置为较低的优先级(.defaultHigh)。仔细阅读控制台消息,看看布局系统决定打破哪个约束。通常这是需要改变优先级的一个。你可以像这样改变优先级:

let companyNameTopConstraint = companyNameLabel.topAnchor.constraint(equalTo: companyImageView.bottomAnchor, constant: 15)
companyNameTopConstraint.priority = .defaultHigh


NSLayoutConstraint.activate([
companyNameTopConstraint,
the rest of your constraints here
])

没有人回答如何解决故事板中的案例。你有我的情况,top bottom constraintfixed height优先级为1000。这就是为什么,当第一次加载时,由于表格视图单元格高度为0,试图设置具有固定高度的元素,导致约束冲突。(我试图在一个0像素的高度区域插入40像素,所以编译器尝试丢弃40像素的高度)。一旦tableview加载,它将不会生成(如弹出回tableview或切换标签栏选项卡)

因此将优先级从required @1000更改为High @750Low @250。第一次加载时,不会考虑较低的优先级,然后调整layoutSubviews()中的所有约束的大小

enter image description here

我也有同样的问题。对我来说,误差是0.5像素。

2020-08-06 21:33:20.947369+0530 DemoNestedTableView[4181:384993] [LayoutConstraints] Unable to simultaneously satisfy constraints.
Probably at least one of the constraints in the following list is one you don't want.
Try this:
(1) look at each constraint and try to figure out which you don't expect;
(2) find the code that added the unwanted constraint or constraints and fix it.
(


"<NSLayoutConstraint:0x600000a3abc0 UICollectionView:0x7fde0780c200.height == 326   (active)>",
"<NSLayoutConstraint:0x600000a3ae40 V:|-(0)-[UICollectionView:0x7fde0780c200]   (active, names: '|':UITableViewCellContentView:0x7fde05e0cb10 )>",
"<NSLayoutConstraint:0x600000a3af30 V:[UICollectionView:0x7fde0780c200]-(0)-|   (active, names: '|':UITableViewCellContentView:0x7fde05e0cb10 )>",
"<NSLayoutConstraint:0x600000a2a4e0 'UIView-Encapsulated-Layout-Height' UITableViewCellContentView:0x7fde05e0cb10.height == 326.5   (active)>"
)


Will attempt to recover by breaking constraint
<NSLayoutConstraint:0x600000a3abc0 UICollectionView:0x7fde0780c200.height == 326   (active)>


Make a symbolic breakpoint at UIViewAlertForUnsatisfiableConstraints to catch this in the debugger.
The methods in the UIConstraintBasedLayoutDebugging category on UIView listed in <UIKitCore/UIView.h> may also be helpful.

只要加上这个就行了。

self.tableView.separatorStyle = .none

在我的例子中,问题是在UITableViewCell中有一个垂直的UIStackView,它根据数据显示/隐藏行。我使用自动调整单元格大小,单元格本身总是显示正确,具有正确的高度。只是日志中充满了关于UIView-Encapsulated-Layout-Height的约束异常。

我通过为UIStackView的顶部和底部约束设置较低的优先级来解决这个问题(999而不是默认的1000)。现在没有约束异常,并且表的外观和行为完全相同。

如果在Xib/Storyboard的调试控制台中有UIView-Encapsulated-Layout-Height警告。

你应该做这些事情:

  1. 进入Size Inspector页面
  2. 选中Row Height复选框Automatic。记录Row Height Value
  3. tableView.estimatedRowHeight设置为那个Value

🎉不再警告⚠️

在我的情况下,我设法摆脱警告通过设置约束的优先级,Xcode抱怨750,但单元格仍然使用一个错误的高度后删除和重新插入到表。

问题来自于对beginUpdates()endUpdates()(在我做删除和插入的地方之间)方法的调用被封装在UIView.animate(withDuration:)动画块中,在那里我想在更新表时控制动画的持续时间。 移除对动画块的调用为我解决了这个问题

切的答案使我走上了正确的轨道。我已经在UITableViewCell中创建了高度约束的出口,并且我在运行时基于某些条件更改了这个高度约束。

而不是这个——

myHeightConstraint.constant = 0.0

我写道——

myHeightConstraint.constant = CGFloat.zero

这为我解决了这个警告。基本上,heightForRowAt方法中返回的任何高度都应该与运行时使用约束计算的高度相同。