ChatGPT解决这个技术问题 Extra ChatGPT

什么是 NSLayoutConstraint“UIView-Encapsulated-Layout-Height”,我应该如何强制它干净地重新计算?

我有一个在 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(我猜它是自动调整单元格大小的计算高度),我应该如何强制它干净地重新计算?

交叉发布到 Apple 开发论坛:devforums.apple.com/thread/238803
我通过以下方式解决了类似的问题,它适用于 iOS 7/8。 1) 将其中一个约束优先级降低到 750。我会尝试第一个或第二个 2) 在 awakeFromNib 集 self.contentView.autoresizingMask = UIViewAutoresizingFlexibleHeight; 的单元子类中。我认为最初设置自动调整掩码会阻止添加最后一个约束。我在这里找到了这个解决方案:github.com/wordpress-mobile/WordPress-iOS/commit/…
@RogerNolan,有什么消息吗?我在 Interface Builder 中使用 Auto-layout 时发现了同样的问题。有些细胞会导致这个问题,有些则不会。
我认为添加自动调整大小标记不是一个好的解决方案。
@RogerNolan 您是否在执行这些布局更改之前在 IB 中生成此单元格?我一直在调试同样的问题,但我没有添加任何额外的约束。我设法通过从头开始重建视图来抑制警告,当我区分 2 个故事板文件时,唯一的区别是带有警告的版本在其定义中缺少行 <rect key="frame" x="0.0" y="0.0" width="600" height="110"/>,这让我相信这是一个 IB 错误。至少我的还是这样。

O
Ortwin Gentz

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

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

另外:虽然系统提供的约束是否正确可能存在争议,但与框架抗争是没有意义的。只需接受系统约束优先。如果您认为系统约束是错误的,请确保从委托中返回正确的 rowHeight。


那将与我想要的完全相反。 UIView-Encapsulated-Layout-Height 错误 - 它属于以前的布局。
一旦确定了高度,UITableView 就会添加 UIView-Encapsulated-Layout-Height 约束。我根据 contentView 的 systemLayoutSizeFittingSize 计算高度。在这里,UIView-Encapsulated-Layout-Height 无关紧要。然后,tableView 将 contentSize 显式设置为 heightForRowAtIndexPath: 返回的值。在这种情况下,降低自定义约束的优先级是正确的,因为在计算 rowHeights 之后 tableView 约束必须优先。
@OrtwinGentz:仍然没有明白这一点降低我们的自定义约束的优先级是正确的,因为在计算 rowHeights 之后 tableView 约束必须优先。问题是如果我不降低优先级,UIView-Encapsulated-Layout-Height 就是错误的......
我坚持认为这是避免冲突而不是解决实际问题——尽管这仍然感觉像是苹果的错误,但我认为这可能是正确的做法。尤其是考虑到 Apple 重新计算了这个约束并且在错误打印后一切都正确布局。
-1 对于这个答案,因为它假定添加的约束是正确的。在我的情况下添加的 UIView-Encapsulated-Layout-Width 是错误的,但它似乎比我在运行时的显式约束更受欢迎。
R
Rajan Balana

我有一个类似的场景:一个带有一个行单元格的表格视图,其中有几行 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

最后!这是一个正确的答案。在 WWDC 会议上,有人提到,如果你要使用自动行高调整,那么你应该设置一个估计的行高度,否则会发生不好的事情。 (是的,苹果真的发生了令人讨厌的事情)
FWIW,加上估计对我来说根本没有任何区别。
呵呵。我又回到了同样的答案,并带着喜悦和希望去实施它。再一次,这对我来说没有任何区别:-)
tableView:estimatedHeightForRowAtIndexPath 返回的估计值:必须至少与单元格一样大。否则,表格的计算高度会小于实际高度,表格可能会向上滚动(例如 unwind segue 返回表格后)。不应使用 UITableViewAutomaticDimension。
请注意,estimatedRowHeight 越低,最初调用 cellForRowAtIndexPath 的频率越高。准确地说,tableview 高度除以estimatedRowHeight 次。在 12 英寸 iPad Pro 上,这可能是数以千计的数字,并且会影响数据源并可能导致显着延迟。
U
UMAD

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
            ])

美丽的解释。
很棒的解释。
没错,谢谢。为我工作。但我无法理解其背后的逻辑,也看不出在此错误发生时单元格的高度为 0。控制台中的日志显示类似 'UIView-Encapsulated-Layout-Height' UITableViewCellContentView:0x7fa661635050.height == 150 的内容。这意味着单元格的内容视图得到了它的高度。对我来说,没有高度为 0 的视图。
J
Jeff Bowen

我能够通过在约束中的一个值上指定 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 将此属性设置为视图可用的最佳大小,考虑到它及其子视图所持有的所有约束,并满足使视图尽可能小的偏好。此属性中的大小值永远不会是负数。

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


那是杰夫。对我来说仍然像一个错误。苹果没有回应 rdar :-(
s
scopchanov

通过删除我在 tableViewcellForRowAt 方法中的虚假 cell.layoutIfNeeded(),我能够解决此错误。


是的,这也为我解决了这个问题。我正在做代码布局约束,所以我最初认为我可能会错过一些东西。谢谢
同样的事情!谢谢!
A
Austin

尝试重新加载单元格,而不是通知表视图更新其约束:

[tableView beginUpdates];

[_briefCell collapse:!_showFullBriefText];

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

[tableView endUpdates];

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


我应该在我的问题中说明,我已经尝试过了,但它不起作用。我同意你对 UIView-Encapsulated-Layout-Height 的猜测
无论如何,至少要提交一个答案。似乎SO只会让它蒸发,否则。
C
Cullen SUN

另一种可能:

如果您使用自动布局计算单元格高度(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

}

在我仅在某些模拟器(iPad 6 Plus)中非常复杂的单元格布局中出现布局高度模糊的情况下,这确实对我有所帮助。在我看来,由于一些内部舍入错误,内容有点压缩,如果不准备压缩约束,我就会变得模棱两可。因此,我没有在 heightForRowAtIndexPath 中返回 UITableViewAutomaticDimension,而是返回 [sizingCell.contentView systemLayoutSizeFittingSize:UILayoutFittingCompressedSize] + 0.1
我的意思当然是[sizingCell.contentView systemLayoutSizeFittingSize:UILayoutFittingCompressedSize].height + 0.1
惊人地;删除分隔符是我的表的行为,所以谢谢。
P
Phu Nguyen

正如Jesse在问题评论中提到的,这对我有用:

self.contentView.autoresizingMask = UIViewAutoresizingFlexibleHeight;

仅供参考,iOS 10 中不会出现此问题。


在 Swift 4.2 中:self.contentView.autoresizingMask = [.flexibleHeight]
你把它放在哪里?
C
Che

使用 UITableViewAutomaticDimension 并更改单元格内视图的高度约束时出现此错误。

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

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

这是使我进入我的问题的评论。我有一个动态加载(增长和缩小)的图像单元格和一个估计RowHeight = 50 和rowHeight = UITableViewAutomaticDimension 的表格视图。即使 tableview 是正确的高度,我仍然在打破约束。结果分隔符的高度为 0.3333,这就是我在单元格中的图像大小限制被打破的原因。关闭分离器后一切都很好。感谢 Che 给了我要找的东西。
在这种情况下,创建一个额外的约束,例如,bottomMargin >= view.bottomMargin+1@900。 AutoLayout 尝试容纳额外的 1 点,调整单元格的大小,由于分隔符高度而感到困惑,尝试打破/放松一些约束,找到 @900 的那个,然后丢弃它。您可以在没有警告的情况下获得所需的布局。
拯救我的一天。反正几个小时
R
Rich

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

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

牢房的高度是正确的,警告消失了。


谢谢,我今天遇到了类似的问题,唯一有效的解决方案是将最低优先级降低到 999。
感谢您提供确切的解决方案和自我解释的答案......拯救了我的一天......
谢谢!...这也适用于表格视图单元格。
m
mokagio

我有同样的问题。对我来说,错误是 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

我尝试了一切,这有效,但我需要分隔符:(
也许您可以在底部的单元格本身中添加一个 1px 视图。
K
Kim

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

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

你在哪里做的?在布局子视图中?
A
Alcides Eduardo Zelaya

在花了几个小时来解决这个错误之后,我终于找到了一个适合我的解决方案。我的主要问题是我为不同的单元格类型注册了多个笔尖,但特别允许一种单元格类型具有不同的大小(并非该单元格的所有实例都将具有相同的大小)。因此,当 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});

希望这可以帮助!


k
kathy zhou

设置此 view.translatesAutoresizingMaskIntoConstraints = NO; 应该可以解决此问题。


这对我来说非常有效,即使它不是完美的解决方案。
A
Ankur Lahiry

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

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

https://i.stack.imgur.com/7yWSw.png


s
shelll

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

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


天哪,我什么都试过了。这终于奏效了!
u
user2962814

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 的优先级下设置最高优先级。


u
user1687195

我收到这样的消息:

无法同时满足约束... ... ... ... 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-| 之间的区别,这有点神秘。但是指定大小对我来说不是问题,它消除了日志,我不需要降低所需约束的优先级。


F
Frank

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

你应该做这些事情:

Go Size Inspector 页面 选中 Row Height 复选框 Automatic。记录行高值将 tableView.estimatedRowHeight 设置为该值

🎉没有更多警告⚠️


如果苹果不解决这个问题,它可能被称为修复。 (但我更喜欢称它为 workaround🤪)
S
Stefan Olarescu

就我而言,我确实设法通过将 Xcode 抱怨的约束的优先级设置为 750 来消除警告,但在删除并将其重新插入表格后,单元格仍然使用错误的高度。

问题来自这样一个事实,即对 beginUpdates()endUpdates()(在我进行删除和插入之间)方法的调用被封装在 UIView.animate(withDuration:) 动画块中,我想在其中控制动画的持续时间更新表时。删除对动画块的调用为我解决了这个问题。


D
Dhruv Saraswat

Che's answer 让我走上正轨。我在 UITableViewCell 中创建了一个高度约束的出口,并且我在运行时根据某些条件更改了这个高度约束。

而不是这个 -

myHeightConstraint.constant = 0.0

我写 -

myHeightConstraint.constant = CGFloat.zero

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


关注公众号,不定期副业成功案例分享
关注公众号

不定期副业成功案例分享

领先一步获取最新的外包任务吗?

立即订阅