我有一个在 iOS 8 下运行的 UITableView
,我正在使用情节提要中约束的自动单元格高度。
我的一个单元格包含一个 UITextView
,我需要它根据用户输入收缩和扩展 - 点击以收缩/扩展文本。
我通过向文本视图添加运行时约束并更改约束上的常量以响应用户事件来做到这一点:
-(void)collapse:(BOOL)collapse; {
_collapsed = collapse;
if(collapse)
[_collapsedtextHeightConstraint setConstant: kCollapsedHeight]; // 70.0
else
[_collapsedtextHeightConstraint setConstant: [self idealCellHeightToShowFullText]];
[self setNeedsUpdateConstraints];
}
每当我这样做时,我都会将其包装在 tableView
更新中并调用 [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
(我猜它是自动调整单元格大小的计算高度),我应该如何强制它干净地重新计算?
self.contentView.autoresizingMask = UIViewAutoresizingFlexibleHeight;
的单元子类中。我认为最初设置自动调整掩码会阻止添加最后一个约束。我在这里找到了这个解决方案:github.com/wordpress-mobile/WordPress-iOS/commit/…
<rect key="frame" x="0.0" y="0.0" width="600" height="110"/>
,这让我相信这是一个 IB 错误。至少我的还是这样。
尝试将 _collapsedtextHeightConstraint
的优先级降低到 999。这样系统提供的 UIView-Encapsulated-Layout-Height
约束总是优先。
它基于您在 -tableView:heightForRowAtIndexPath:
中返回的内容。确保返回正确的值和您自己的约束,并且生成的约束应该相同。您自己的约束的较低优先级仅在临时需要以防止在折叠/展开动画运行时发生冲突。
另外:虽然系统提供的约束是否正确可能存在争议,但与框架抗争是没有意义的。只需接受系统约束优先。如果您认为系统约束是错误的,请确保从委托中返回正确的 rowHeight。
我有一个类似的场景:一个带有一个行单元格的表格视图,其中有几行 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
estimatedRowHeight
越低,最初调用 cellForRowAtIndexPath
的频率越高。准确地说,tableview 高度除以estimatedRowHeight 次。在 12 英寸 iPad Pro 上,这可能是数以千计的数字,并且会影响数据源并可能导致显着延迟。
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
])
'UIView-Encapsulated-Layout-Height' UITableViewCellContentView:0x7fa661635050.height == 150
的内容。这意味着单元格的内容视图得到了它的高度。对我来说,没有高度为 0 的视图。
我能够通过在约束中的一个值上指定 priority 来消除警告,警告消息说它必须打破(低于 "Will attempt to recover by breaking constraint"
)。看来,只要我将优先级设置为大于 49
,警告就会消失。
对我来说,这意味着改变我的约束,警告说它试图打破:
@"V:|[contentLabel]-[quoteeLabel]|"
至:
@"V:|-0@500-[contentLabel]-[quoteeLabel]|"
事实上,我可以为该约束的任何元素添加优先级,它会起作用。哪一个似乎并不重要。我的单元格最终达到了正确的高度,并且不显示警告。 Roger,对于您的示例,尝试在 388
高度值约束(例如 388@500
)之后添加 @500
。
我不完全确定为什么会这样,但我做了一些调查。在 NSLayoutPriority enum 中,NSLayoutPriorityFittingSizeCompression
优先级似乎是 50
。该优先级的文档说:
当您向视图发送 FittingSize 消息时,会计算足够大以容纳视图内容的最小尺寸。这是视图希望在该计算中尽可能小的优先级。这是相当低的。在这个优先级上进行约束通常是不合适的。你想要更高或更低。
引用的 fittingSize
消息的 documentation 内容如下:
满足它所持有的约束的视图的最小尺寸。 (只读)AppKit 将此属性设置为视图可用的最佳大小,考虑到它及其子视图所持有的所有约束,并满足使视图尽可能小的偏好。此属性中的大小值永远不会是负数。
我还没有深入研究,但这似乎与问题所在有关。
通过删除我在 tableView
的 cellForRowAt
方法中的虚假 cell.layoutIfNeeded()
,我能够解决此错误。
尝试重新加载单元格,而不是通知表视图更新其约束:
[tableView beginUpdates];
[_briefCell collapse:!_showFullBriefText];
[tableView reloadRowsAtIndexPaths:@[[tableView indexPathForCell:_briefCell]] withRowAnimation:UITableViewRowAnimationNone];
[tableView endUpdates];
UIView-Encapsulated-Layout-Height
可能是表格视图在初始加载期间根据单元格当时的约束为单元格计算的高度。
另一种可能:
如果您使用自动布局计算单元格高度(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
}
heightForRowAtIndexPath
中返回 UITableViewAutomaticDimension
,而是返回 [sizingCell.contentView systemLayoutSizeFittingSize:UILayoutFittingCompressedSize] + 0.1
[sizingCell.contentView systemLayoutSizeFittingSize:UILayoutFittingCompressedSize].height + 0.1
正如Jesse在问题评论中提到的,这对我有用:
self.contentView.autoresizingMask = UIViewAutoresizingFlexibleHeight;
仅供参考,iOS 10 中不会出现此问题。
使用 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 来解决它。
牢房的高度是正确的,警告消失了。
我有同样的问题。对我来说,错误是 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
调整文本视图的大小以适合其内容,并将高度约束常量更新为结果高度,为我修复了 UIView-Encapsulated-Layout-Height
约束冲突,例如:
[self.textView sizeToFit];
self.textViewHeightConstraint.constant = self.textView.frame.size.height;
在花了几个小时来解决这个错误之后,我终于找到了一个适合我的解决方案。我的主要问题是我为不同的单元格类型注册了多个笔尖,但特别允许一种单元格类型具有不同的大小(并非该单元格的所有实例都将具有相同的大小)。因此,当 tableview 试图使该类型的单元格出列并且它恰好具有不同的高度时,就会出现问题。我通过设置解决了
self.contentView.autoresizingMask = UIViewAutoresizingFlexibleHeight; self.frame = CGRectMake(0, 0, self.frame.size.width, {correct height});
每当单元格有数据来计算其大小时。我想它可以在
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
就像是
cell.contentView.autoresizingMask = UIViewAutoresizingFlexibleHeight; cell.frame = CGRectMake(0, 0, self.frame.size.width, {correct height});
希望这可以帮助!
设置此 view.translatesAutoresizingMaskIntoConstraints = NO;
应该可以解决此问题。
没有人回答如何解决故事板中的案例。你有我的情况,top bottom constraint
和 fixed height
优先级为 1000。这就是为什么在第一次加载时,由于表格视图单元格高度为 0,尝试将元素设置为固定高度,从而导致约束冲突. (我试图在 0 像素高度区域中插入 40 像素,因此编译器尝试丢弃 40 像素高度)。一旦加载了tableview,它就不会生成(比如弹回tableview或切换tab bar tab)
因此,将优先级从 required @1000
更改为 High @750
或 Low @250
。第一次加载,不考虑较低的优先级,然后调整 layoutSubviews()
中的所有约束
https://i.stack.imgur.com/7yWSw.png
就我而言,问题在于 UITableViewCell
中的垂直 UIStackView
,它根据数据显示/隐藏行。我正在使用自动调整单元格大小,并且单元格本身始终正确显示,高度正确。只是日志中充满了关于 UIView-Encapsulated-Layout-Height
的约束异常。
我通过为 UIStackView
的顶部和底部约束设置较低的优先级(999 而不是默认的 1000)解决了这个问题。现在没有约束例外,表的外观和行为相同。
TableView 从委托获取 indexPath 处单元格的高度。然后从 cellForRowAtIndexPath
获取单元格:
top (10@1000)
cell
bottom (0@1000)
如果 cell.contentView.height:0 //<-> (UIView-Encapsulated-Layout-Height:0@1000) top(10@1000) 与 (UIView-Encapsulated-Layout-Height:0@1000) 冲突,
因为它们的优先级等于 1000。我们需要在 UIView-Encapsulated-Layout-Height
的优先级下设置最高优先级。
我收到这样的消息:
无法同时满足约束... ... ... ... NSLayoutConstraint:0x7fe74bdf7e50 'UIView-Encapsulated-Layout-Height' V:[UITableViewCellContentView:0x7fe75330c5c0(21.5)] ... ... 将尝试恢复打破约束 NSLayoutConstraint:0x7fe0f9b200c0 UITableViewCellContentView:0x7fe0f9b1e090.bottomMargin == UILabel:0x7fe0f9b1e970.bottom
我正在使用带有 UITableViewAutomaticDimension
的自定义 UITableViewCell
作为高度。而且我还实现了 estimatedHeightForRowAtIndex:
方法。
给我带来问题的约束看起来像这样
[NSLayoutConstraint constraintsWithVisualFormat:@"V:|-[title]-|" options:0 metrics:nil views:views];
将约束更改为此将解决问题,但就像另一个答案一样,我觉得这是不正确的,因为它降低了我想要的约束的优先级:
[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-|
之间的区别,这有点神秘。但是指定大小对我来说不是问题,它消除了日志,我不需要降低所需约束的优先级。
如果您在带有 Xib/Storyboard 的调试控制台中出现 UIView-Encapsulated-Layout-Height
警告。
你应该做这些事情:
Go Size Inspector 页面 选中 Row Height 复选框 Automatic。记录行高值将 tableView.estimatedRowHeight 设置为该值
🎉没有更多警告⚠️
workaround
🤪)
就我而言,我确实设法通过将 Xcode 抱怨的约束的优先级设置为 750
来消除警告,但在删除并将其重新插入表格后,单元格仍然使用错误的高度。
问题来自这样一个事实,即对 beginUpdates()
和 endUpdates()
(在我进行删除和插入之间)方法的调用被封装在 UIView.animate(withDuration:)
动画块中,我想在其中控制动画的持续时间更新表时。删除对动画块的调用为我解决了这个问题。
Che's answer 让我走上正轨。我在 UITableViewCell
中创建了一个高度约束的出口,并且我在运行时根据某些条件更改了这个高度约束。
而不是这个 -
myHeightConstraint.constant = 0.0
我写 -
myHeightConstraint.constant = CGFloat.zero
这为我解决了这个警告。基本上,在 heightForRowAt
方法中返回的任何高度都应该与在运行时使用约束计算的高度相同。
UIView-Encapsulated-Layout-Height
约束。我根据 contentView 的systemLayoutSizeFittingSize
计算高度。在这里,UIView-Encapsulated-Layout-Height
无关紧要。然后,tableView 将 contentSize 显式设置为heightForRowAtIndexPath:
返回的值。在这种情况下,降低自定义约束的优先级是正确的,因为在计算 rowHeights 之后 tableView 约束必须优先。UIView-Encapsulated-Layout-Height
就是错误的......UIView-Encapsulated-Layout-Width
是错误的,但它似乎比我在运行时的显式约束更受欢迎。