Masonry约束自定义TableViewCell自适应行高的约束冲突的问题

问题@iOS-zhouyu类似 iOS Masonry约束自定义TableViewCell自适应行高的约束冲突的问题_上进求知,认真思辨的博客-CSDN博客_masonry tableviewcell

下面是我的情况:设置一个空白cell,通过cellModel的height属性动态设置高度。

self.tableView.estimatedRowHeight = UITableViewAutomaticDimension;
- (void)setupSubviews {
    [self.contentView addSubview:self.line];
}

- (void)zz_bindModel:(ZZBlankAreaCellModel*)viewModel {
    self.viewModel = viewModel;
    self.line.backgroundColor = viewModel.color;

    [self.line mas_remakeConstraints:^(MASConstraintMaker *make) {
        make.top.bottom.mas_equalTo(0);
        make.height.mas_equalTo(viewModel.height).priority(MASLayoutPriorityRequired);
        make.left.mas_equalTo(self.contentView).mas_offset(viewModel.lineEdgeInsets.left);
        make.right.mas_equalTo(self.contentView).mas_offset(-viewModel.lineEdgeInsets.right);
    }];
}

- (UIView *)line
{
    if (!_line) {
        _line = ({
            UIView *view = [UIView new];
            view;
        });
    }
    return _line;
}

当cell复用,高度发生变化时,会出现下面的警告。

2018-09-19 09:35:37.200637+0800 ZZTableView[17299:3212632] [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. 
(
    "<MASLayoutConstraint:0x6000017536c0 UIView:0x7f9cfe5189f0.top == UITableViewCellContentView:0x7f9cfe518800.top>",
    "<MASLayoutConstraint:0x600001750f60 UIView:0x7f9cfe5189f0.bottom == UITableViewCellContentView:0x7f9cfe518800.bottom>",
    "<MASLayoutConstraint:0x600001750f00 UIView:0x7f9cfe5189f0.height == 21>",
    "<NSLayoutConstraint:0x60000106b700 UITableViewCellContentView:0x7f9cfe518800.height == 28>"
)

Will attempt to recover by breaking constraint 
<MASLayoutConstraint:0x600001750f00 UIView:0x7f9cfe5189f0.height == 21>

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.

仔细看

<MASLayoutConstraint:0x600001750f00 UIView:0x7f9cfe5189f0.height == 21>

<NSLayoutConstraint:0x60000106b700 UITableViewCellContentView:0x7f9cfe518800.height == 28>

有冲突导致系统约束警告。 这里我们把断点放过,可以看到视图还是按我们预想的展示的,但是警告总让人不爽,那么尝试来解决这个问题。

问题分析:

  1. 新的约束UIView:0x7f9cfe5189f0.height == 21 试图破坏 UITableViewCellContentView:0x7f9cfe518800.height == 28
  2. 回想一下,上一次我们设置的是约束应该UIView:0x7f9cfe5189f0.height == 28,但是UIView:0x7f9cfe5189f0还有另外2个约束就是上下贴着UITableViewCellContentView,所有间接导致UITableViewCellContentView:0x7f9cfe518800.height == 28。这个时候试图破坏原来的约束就会导致约束冲突。
  3. 另外可以猜想UITableViewCellContentView:0x7f9cfe518800.height == 28这条约束应该是UITableView组件给附加上的,因为我们本身没有写这句话。既然这条约束是系统维护的,他就会在一定时候维护这条约束。不会提前,那就只有延后,在cellForRow之后发生,而我们改变约束是在cellForRow的时候。

解决方法

既然我们是在bindModel改变约束,bindModel是在cellForRow被调用,这个事实不能改变。而且我们不能维护UITableViewCellContentView:0x7f9cfe518800.height的约束(系统维护) 好,分析完了,给出解决方法:

降低与UITableViewCellContentView约束冲突的View的约束优先级

上面的问题就是View.height的约束优先级降低 make.height.mas_equalTo(viewModel.height).priority(MASLayoutPriorityDefaultHigh);

注:不设置priority默认是MASLayoutPriorityRequired

- (void)zz_bindModel:(ZZBlankAreaCellModel*)viewModel {
    self.viewModel = viewModel;
    self.line.backgroundColor = viewModel.color;

    [self.line mas_remakeConstraints:^(MASConstraintMaker *make) {
        make.top.bottom.mas_equalTo(0);
        make.height.mas_equalTo(viewModel.height).priority(MASLayoutPriorityDefaultHigh);
        make.left.mas_equalTo(self.contentView).mas_offset(viewModel.lineEdgeInsets.left);
        make.right.mas_equalTo(self.contentView).mas_offset(-viewModel.lineEdgeInsets.right);
    }];
}

原理分析:

当我们使用

self.tableView.estimatedRowHeight = UITableViewAutomaticDimension;

系统在就会在我们设置好视图后,自动给UITableViewCellContentView附加一个合适的height约束,当布局改变化,这个属性也会改变。 为了不和系统冲突,我们只有适当降低自身约束的优先级

Code

附上我的代码  https://github.com/ZiFy/ZZTableView

修改以后:

修改以后就不会报错误了

Masonry不常用到的方法

关于mas_key

Masonry中用来标记view的key值(a key to associate with this view),通过runtime在view中添加的属性。

mas_key最大的用处是用来在发生约束冲突时,清晰的标记出冲突的出处。例如:

在没有定义mas_key时,发生约束冲突,后台报的错误信息:

(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. 

(

    "<MASLayoutConstraint:0x6000002a0480 UIView:0x7ffadf511da0.left == UIView:0x7ffadf5105e0.left>",

    "<MASLayoutConstraint:0x6000002a0a80 UIView:0x7ffadf511da0.leading == UIView:0x7ffadf5105e0.leading - 10>"

)

Will attempt to recover by breaking constraint 

<MASLayoutConstraint:0x6000002a0a80 UIView:0x7ffadf511da0.leading == UIView:0x7ffadf5105e0.leading - 10>

由于没有定义mas_key,错误信息打印的是对象的内存地址。代码阅读不友好。

当定义mas_key后,发生约束冲突,后台报的错误信息:

(1) look at each constraint and try to figure out which you don't expect; 

(2) find the code that added unwanted constraint or constraints and fix it. 

(

    "<MASLayoutConstraint:0x6000000b7700 UIView:view.left == UIView:self.view.left>",

    "<MASLayoutConstraint:0x6040000b7760 UIView:view.leading == UIView:self.view.leading - 10>"

)

Will attempt to recover by breaking constraint 

<MASLayoutConstraint:0x6040000b7760 UIView:view.leading == UIView:self.view.leading - 10>

定义mas_key后,代码阅读是不是更加友好,而且冲突产生的原因和对象也一目了然了。

当多个view添加到父view时,每个都定义mas_key会很麻烦。Masonry为我们提供了能够快捷添加mas_key的宏定义:

MASAttachKeys(view, self.view);  //批量设置mas_key

关于Relationship 

typedef NS_ENUM(NSInteger, NSLayoutRelation) {

    NSLayoutRelationLessThanOrEqual = -1,

    NSLayoutRelationEqual = 0,

    NSLayoutRelationGreaterThanOrEqual = 1,

};

对应约束中的三种关系分别为:<=, =, >=.

在masonry对应的三个方法分别为:

- (MASConstraint * (^)(id attr))lessThanOrEqualTo;

- (MASConstraint * (^)(id attr))equalTo;

- (MASConstraint * (^)(id attr))greaterThanOrEqualTo;

重点来解释下lessThanOrEqualTo和greaterThanOrEqualTo这2个方法。

有的时候label中显示的字数是不确定的,所以宽度不确定。而我们希望view随着label变化,就可以按下面这么写。意思是view左边大于等于label的左边位置

make.left.greaterThanOrEqualTo(label.mas_left);

同理,当label在view中时,由于字数不确定导致label宽度不确定。我们希望label的左边不超出父view的左边,可以按下面这么写

make.left.lessThanOrEqualTo(label.mas_left);

Masonry需要注意的几个点 - Alex.xue - 博客园

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值