UITableViewCell为啥会有自动布局约束冲突

Posted

技术标签:

【中文标题】UITableViewCell为啥会有自动布局约束冲突【英文标题】:Why Does UITableViewCell Have Auto Layout Constraint ConflictsUITableViewCell为什么会有自动布局约束冲突 【发布时间】:2014-04-23 18:54:16 【问题描述】:

我有一个 UITableViewSubclass(样式:UITableViewCellStyleValue1),它有一个自定义标签,本质上是普通textLabel 标签的替代品。它应该与普通textLabel 左对齐,但宽度要在普通detailTextLabel 左侧8 点处结束。我得到了想要的效果,但是当使用单元格时,会抛出无法同时满足约束的异常。

我不明白为什么它会抱怨,没有明显的冲突。在设置约束后调用 [self layoutIfNeeded] 会使异常静音。或者,降低配置尾随属性的约束的优先级(例如,UILayoutPriorityDefaultHigh 而不是默认的UILayoutPriorityRequired)也会使异常静音,显然是因为我通知自动布局引擎可以中断/忽略那个约束。

我假设标准 UITableView 实现可能会奇怪地布置 textLabeltextLabel 的方式使得最初无法描述所描述的视图。例如,如果textLabel 实际上放在单元格的右侧,而detailTextLabel 放在左侧,那么我描述的布局将是不可能的。

在这种情况下,自动布局何时生效。它是在抢先一步并试图在它应该做的事情之前进行布局吗?

没有使用故事板或 XIB。纯粹基于代码。

#import "EBTStoreTableViewCell.h"

@interface EBTStoreTableViewCell ()
@property (nonatomic, readonly, weak) UILabel *storeNameLabel;
@end

@implementation EBTStoreTableViewCell

- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier

    self = [super initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:reuseIdentifier];
    if (self) 
        self.textLabel.text = @" ";

        self.detailTextLabel.text = @" ";

        UILabel *storeNameLabel = [[UILabel alloc] init];
        storeNameLabel.translatesAutoresizingMaskIntoConstraints = NO;
        [storeNameLabel setContentCompressionResistancePriority:UILayoutPriorityDefaultLow forAxis:UILayoutConstraintAxisHorizontal];
        [self.contentView addSubview:storeNameLabel];
        _storeNameLabel = storeNameLabel;

        [self.contentView addConstraint:[NSLayoutConstraint constraintWithItem:storeNameLabel attribute:NSLayoutAttributeLeading relatedBy:NSLayoutRelationEqual toItem:self.textLabel attribute:NSLayoutAttributeLeading multiplier:1.0f constant:0.0f]];
        [self.contentView addConstraint:[NSLayoutConstraint constraintWithItem:storeNameLabel attribute:NSLayoutAttributeBaseline relatedBy:NSLayoutRelationEqual toItem:self.textLabel attribute:NSLayoutAttributeBaseline multiplier:1.0f constant:0.0f]];

        [self.contentView addConstraint:[NSLayoutConstraint constraintWithItem:storeNameLabel attribute:NSLayoutAttributeTrailing relatedBy:NSLayoutRelationEqual toItem:self.detailTextLabel attribute:NSLayoutAttributeLeading multiplier:1.0f constant:-8.0f]];

//        [self layoutIfNeeded]; // doing this makes the issue go away
    
    return self;


// Setters ommited

@end

这是我收到的异常消息:

2014-04-23 11:50:42.092 Application[32507:60b] 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:0xde03910 UILabel:0xde036b0.leading == UILabel:0xde00590.leading>",
    "<NSLayoutConstraint:0xde03a30 UILabel:0xde036b0.trailing == UITableViewLabel:0xde00c10.leading - 8>",
    "<NSAutoresizingMaskLayoutConstraint:0xde01920 h=--& v=--& UILabel:0xde00590.midX ==>",
    "<NSAutoresizingMaskLayoutConstraint:0xde1de50 h=--& v=--& H:[UILabel:0xde00590(0)]>",
    "<NSAutoresizingMaskLayoutConstraint:0x17f50a10 h=--& v=--& UITableViewLabel:0xde00c10.midX ==>"
)

Will attempt to recover by breaking constraint 
<NSLayoutConstraint:0xde03a30 UILabel:0xde036b0.trailing == UITableViewLabel:0xde00c10.leading - 8>

Break on objc_exception_throw to catch this in the debugger.
The methods in the UIConstraintBasedLayoutDebugging category on UIView listed in <UIKit/UIView.h> may also be helpful.

【问题讨论】:

我也遇到了同样的问题,这么简单的事情就这么让人头疼,让我厌倦了ios SDK。 UITableView 已经过时了。我宁愿在所有需要表格的地方使用 UICollectionView - 它显然得到了 Apple 支持团队的更多爱,这包括自动布局支持 【参考方案1】:

您不应该混合使用textLabeldetailTextLabel。这些不能受到限制,因为它们的大小是在内部管理的,可能会产生意想不到的结果。

您应该创建自己的标签,然后对其施加约束。

来自UITableViewCell 的文档:

创建单元格时,您可以自己自定义它们或使用多种预定义样式中的一种。预定义的单元格样式是最简单的选项。使用预定义样式,单元格提供位置和样式固定的标签和图像子视图。您所要做的就是提供文本和图像内容以进入这些固定视图。要使用具有预定义样式的单元格,请使用 initWithStyle:reuseIdentifier: 方法对其进行初始化,或者在 Xcode 中使用该样式配置单元格。要设置单元格的文本和图像,请使用textLabeldetailTextLabelimageView 属性。

如果您想超越预定义的样式,可以将子视图添加到单元格的contentView 属性。添加子视图时,您负责定位这些视图并自行设置其内容。

【讨论】:

以上是关于UITableViewCell为啥会有自动布局约束冲突的主要内容,如果未能解决你的问题,请参考以下文章

具有自动布局的 UITableviewCell 约束失败

缩进 UITableViewCell 中的所有自动布局约束

具有自动布局约束的 UITableViewCell 子类大小不正确

UITableViewCell中的自动布局约束不匹配

UITableViewCell 子视图不遵守自动布局约束

是啥导致两个具有相同自动布局约束的 UITableViewCell 表现不同?