什么是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 (我猜它是自动细胞大小的计算高度)我应该如何强制它重新计算干净呢?

回答(13)

3 years ago

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

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

3 years ago

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

3 years ago

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

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

3 years ago

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

[tableView beginUpdates];

[_briefCell collapse:!_showFullBriefText];

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

[tableView endUpdates];

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

3 years ago

另一个可能性:

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

}

3 years ago

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

3 years ago

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

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

3 years ago

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

希望这可以帮助!

3 years ago

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

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

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

3 years ago

TableView从delegate获取indexPath处单元格的高度 . 然后从 cellForRowAtIndexPath 获取单元格:

top (10@1000)
    cell
bottom (0@1000)

if cell.contentView.height:0 // < - >(UIView-Encapsulated-Layout-Height:0 @ 1000)top(10 @ 1000)与(UIView-Encapsulated-Layout-Height:0 @ 1000)冲突,

因为他们的优先级等于1000.我们需要在 UIView-Encapsulated-Layout-Height 的优先级下设定最高优先级 .

3 years ago

我收到这样的消息:

无法同时满足约束...... ...... ...... NSLayoutConstraint:0x7fe74bdf7e50'UIView-Encapsulated-Layout-Height'V:[UITableViewCellContentView:0x7fe75330c5c0(21.5)] ......将尝试恢复通过破坏约束NSLayoutConstraint:0x7fe0f9b200c0 UITableViewCellContentView:0x7fe0f9b1e090.bottomMargin == UILabel:0x7fe0f9b1e970.bottom

我正在使用自定义 UITableViewCellUITableViewAutomaticDimension 作为高度 . 我还实现了 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-| 之间的区别,这有点神秘 . 但是指定大小是否需要降低我所需约束的优先级 .

3 years ago

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

self.contentView.autoresizingMask = UIViewAutoresizingFlexibleHeight;

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

3 years ago

设置此 view.translatesAutoresizingMaskIntoConstraints = NO; 应解决此问题 .