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

Rog*_*Rog 243 cocoa-touch uitableview ios autolayout ios-autolayout

UITableView在iOS 8下运行,并且我在故事板中使用来自约束的自动单元格高度.

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

我这样做是通过向文本视图添加运行时约束并更改约束上的常量以响应用户事件:

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

    _collapsed = collapse;

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

    [self setNeedsUpdateConstraints];

}
Run Code Online (Sandbox Code Playgroud)

当我这样做时,我将其包装在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];
Run Code Online (Sandbox Code Playgroud)

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

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)]>
Run Code Online (Sandbox Code Playgroud)

388是我计算的高度,其他约束UITextView是我的Xcode/IB.

最后一个困扰我 - 我猜UIView-Encapsulated-Layout-Height这是第一次渲染时单元格的计算高度 - (我将我的UITextView高度设置为> = 70.0)但是这个派生约束然后否定了更新了用户cnstraint.

更糟糕的是,虽然布局代码表示它试图打破我的高度限制,但它没有 - 它继续重新计算单元格高度,所有内容都按照我的意愿绘制.

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

Ort*_*ntz 287

尝试将您的优先级降低_collapsedtextHeightConstraint到999.这样系统提供的UIView-Encapsulated-Layout-Height约束始终优先.

它取决于你的回归 -tableView:heightForRowAtIndexPath:.确保返回正确的值和您自己的约束,生成的约束应该相同.只有暂时需要您自己的约束的较低优先级,以防止崩溃/扩展动画在飞行中时发生冲突.

  • 那将完全与我想要的完全相反.UIView-Encapsulated-Layout-Height是错误的 - 它属于以前的布局. (69认同)
  • 我认为这可以避免冲突而不解决实际问题 - 虽然这仍然感觉像是一个Apple漏洞,但我认为这可能是正确的做法.特别是考虑到Apple重新计算此约束并且在错误打印后所有内容都正确布局的事实. (34认同)
  • @OrtwinGentz:仍然没有得到点*降低我们的自定义约束的优先级是正确的,因为tableView约束必须在计算rowHeights之后优先*.问题是如果我不降低优先级,那么`UIView-Encapsulated-Layout-Height`就错了...... (8认同)
  • 一旦确定了高度,UITableView就会添加`UIView-Encapsulated-Layout-Height`约束.我根据contentView的`systemLayoutSizeFittingSize`计算高度.在这里,`UIView-Encapsulated-Layout-Height`并不重要.然后,tableView将contentSize显式设置为`heightForRowAtIndexPath:`返回的值.在这种情况下,降低自定义约束的优先级是正确的,因为tableView约束必须在计算rowHeights之后优先. (7认同)
  • 这个答案为-1,因为它假定添加的约束是正确的.在我的情况下添加的"UIView-Encapsulated-Layout-Width"是错误的,但它似乎比我在运行时的显式约束更受欢迎. (7认同)
  • 在尝试了不同的方法来解决这个问题之后,为冲突的自定义约束分配"999"他们会欺骗.谢谢@OrtwinGentz! (5认同)
  • 我并没有真正改变单元格高度,我正在尝试重用`dequeueReusableCellWithIdentifier`中的现有单元格.因此,我的tableView的`cellForRowAtIndexPath`函数中的单元格高度发生了变化.我只想摆脱这个'UIView-Encapsulated-Layout-Height`约束因为它似乎不需要,因为我的其他约束将导致相同的高度.它只是阻止我更新可重复使用的单元格的高度. (2认同)
  • 不要打架子.`UITiew-Encapsulated-Layout-Height`由`UITableView`自动添加.由于此约束的优先级始终为1000,因此垂直约束应具有较低的优先级以避免冲突. (2认同)
  • @ray 查看我更新的答案。不要与框架抗争,只需接受系统约束优先。如果您认为系统约束是错误的,请确保从委托返回正确的 rowHeight。 (2认同)
  • @RogerNolan这个答案不是解决问题的方法,而是一种肮脏的解决方法。您不应仅因为创建了其他内容而调整约束的优先级。苹果文档说只需要prio 1000,当您输入一个较低的数字时,您最终会要求与系统不同的布局。而“不要与框架打交道”的建议至少很奇怪。解决方案很简单-您只需要提供“ estimatedHeightForRowAtIndexPath”即可。就我而言,使其大于实际高度是可行的。更精确地,返回单元格当前状态的最大高度。 (2认同)

Gol*_*umb 66

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

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

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

这不仅仅是一个警告.我的表格视图单元格的布局很糟糕 - 所有文本都重叠在一个文本行上.

令我惊讶的是,以下行神奇地"修复"了我的问题(自动布局没有抱怨,我得到了我在屏幕上的预期):

myTableView.estimatedRowHeight = 2.0; // any number but 2.0 is the smallest one that works
Run Code Online (Sandbox Code Playgroud)

有或没有这条线:

myTableView.rowHeight = UITableViewAutomaticDimension; // by itself this line only doesn't help fix my specific problem
Run Code Online (Sandbox Code Playgroud)

  • FWIW,加上估算对我来说没有任何区别. (42认同)
  • 最后!这是一个正确的答案.在WWDC会话中,有人提到,如果你要使用自动行高度大小,那么你应该设置一个估计的行高或坏事.(是的,Apple真的发生了令人讨厌的事情) (8认同)
  • 嘿.我又回到了同样的答案,然后带着喜悦和希望来实现它.再一次,它对我来说没有任何区别:-) (3认同)
  • tableView返回的估计值:estimatedHeightForRowAtIndexPath:*必须*至少与单元格一样大.否则,表的计算高度将小于实际高度,并且表可以向上滚动(例如,在展开segue返回到表之后).不应使用UITableViewAutomaticDimension. (3认同)

小智 35

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
            ])
Run Code Online (Sandbox Code Playgroud)

  • 美丽的解释。 (3认同)

Jef*_*wen 31

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

对我而言,这意味着改变我的约束警告称它试图破坏:

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

至:

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

事实上,我可以为该约束的任何元素添加优先级,它将起作用.似乎哪个不重要.我的细胞最终达到了正确的高度,并且没有显示警告.Roger,例如,@500388高度值约束之后尝试添加(例如388@500).

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

将fittingSize消息发送到视图时,将计算足够大的视图内容的最小大小.这是视图希望在该计算中尽可能小的优先级.它很低.通常不恰当地以此优先级进行约束.你想要更高或更低.

引用消息的文档fittingSize如下:

满足其所拥有约束的视图的最小大小.(只读)

AppKit将此属性设置为视图可用的最佳大小,考虑它及其子视图所具有的所有约束并满足偏好以使视图尽可能小.此属性中的大小值永远不会为负数.

我没有挖过这个,但似乎有意义的是这与问题所在.


Cli*_*rum 11

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

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

Aus*_*tin 7

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

[tableView beginUpdates];

[_briefCell collapse:!_showFullBriefText];

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

[tableView endUpdates];
Run Code Online (Sandbox Code Playgroud)

UIView-Encapsulated-Layout-Height 可能是表格视图在初始加载期间为单元格计算的高度,具体取决于当时单元格的约束.

  • 无论如何都要有赏金,至少提交一个答案.似乎SO只会让它蒸发掉. (6认同)

Cul*_*SUN 6

另一种可能性

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

}
Run Code Online (Sandbox Code Playgroud)


Phu*_*yen 6

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

self.contentView.autoresizingMask = UIViewAutoresizingFlexibleHeight;
Run Code Online (Sandbox Code Playgroud)

仅供参考,此问题在 iOS 10 中不会发生。

  • 在 Swift 4.2 中: self.contentView.autoresizingMask = [.flexibleHeight] (4认同)

Che*_*Che 5

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

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

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

  • 这是让我了解我的问题的评论。我有一个动态加载(增长和缩小)的图像单元格和一个估计行高度 = 50 和行高度 = UITableViewAutomaticDimension 的表格视图。即使 tableview 是正确的高度,我仍然在打破约束。结果发现分隔符的高度为 0.3333,这就是我在单元格中打破图像大小限制的原因。关闭分离器后一切都很好。谢谢车给我找什么。 (2认同)