加载中…
正文 字体大小:

estimatedRowHeight 配合SnapKit导致的约束冲突

(2017-09-06 17:39:57)
转载请注明出处,码字很辛苦的。谢谢。

estimatedRowHeight

ios8 之后的新特性,可以让开发者从纷繁的cell的高度计算中彻底释放出来。让系统帮我们搞定cell的高度自适应。

常用场景,朋友圈,IM聊天界面,以及所有的高度随着内容变化而变化的场景。

网上的资源是挺多的,但是,基本上是相互copy,有时候你真正遇到的问题,反而无法解决。

注意事项
1.首先必须给个初始值,至于多少无所谓。tableView.estimatedRowHeight = 100
2.其次,不能实现tableView的heightForRow代理方法,是不能实现!不能实现!不能实现!
3.最后,cell内所有的控件必须都是由约束来布局的,这点也很关键

满足以上3点条件,就可以实现cell的自适应高度了。

Coding issue

在返回cell的代理方法里,我给cell一个20~500的高度随机值,cell内部是用SnapKit布局的,布局也是常规布局,没啥复杂性可言。虽然最后的界面效果是没啥问题了,但是控制台却随着视图滑动,疯狂的打印警报。使用过Masonry的同学,肯定知道,当cell非常多的时候,一直打印警告是非常影响tableview的性能的。因此,不能放过这个问题。

tableView Delegate:


func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let reuseID = "testCell"
        var cell = tableView.dequeueReusableCell(withIdentifier: reuseID) as? testCell
        
        if cell == nil {
            cell = testCell(style: .default, reuseIdentifier: reuseID)
        }
        
        cell?.cellHeight = Float(arc4random_uniform(500))+20.0
        
        return cell!
    }

cell:


var cellHeight : Float!{
        didSet{
            self.contentLabel.snp.updateConstraints{ (make) in
                make.height.equalTo(cellHeight)
            }
        }
    }
    
    fileprivate var contentLabel:UILabel!
    
    override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
        super.init(style: style, reuseIdentifier: reuseIdentifier)
        self.selectionStyle = .none
        self.contentLabel = UILabel()
        self.contentLabel.backgroundColor = UIColor.cyan
        self.contentView.addSubview(self.contentLabel)
        
        self.contentLabel.snp.makeConstraints { (make) in
            make.edges.equalToSuperview().inset(20)
            make.height.equalTo(100)
        }
    }

上面的代码,反正我是没看出任何问题。但是问题却客观存在,因此必须想办法解决。

控制台打印:


(
    "<<span class="hljs-name" style="box-sizing: border-box;">SnapKit.LayoutConstraint:0x6000000aca20@testCell.swift#31 UILabel:0x7fb3a8f09dc0.top == UITableViewCellContentView:0x7fb3a8f08b90.top + 20.0>",
    "<<span class="hljs-name" style="box-sizing: border-box;">SnapKit.LayoutConstraint:0x6000000acae0@testCell.swift#31 UILabel:0x7fb3a8f09dc0.bottom == UITableViewCellContentView:0x7fb3a8f08b90.bottom - 20.0>",
    "<<span class="hljs-name" style="box-sizing: border-box;">SnapKit.LayoutConstraint:0x6000000acb40@testCell.swift#32 UILabel:0x7fb3a8f09dc0.height == 478.0>",
    "<<span class="hljs-name" style="box-sizing: border-box;">NSLayoutConstraint:0x600000097a70 'UIView-Encapsulated-Layout-Height' UITableViewCellContentView:0x7fb3a8f08b90.height == 518   (active)>"//此处与下面的高度发生冲突
)

Will attempt to recover by breaking constraint 
<<span class="hljs-name" style="box-sizing: border-box;">SnapKit.LayoutConstraint:0x6000000acb40@testCell.swift#32 UILabel:0x7fb3a8f09dc0.height == 478.0>
)

提示 高度冲突

UIView-Encapsulated-Layout-Height

UIView-Encapsulated-Layout-Height是何方神圣?我没设置它呀,于是在 stackOverFlow里搜到了这个

原来cell默认系统会添加UIView-Encapsulated-Layout-HeightUIView-Encapsulated-Layout-Width(collectionCell也是这样的)来保证cell有一个合适的大小。这两个约束由系统控制,默认优先级比自己添加的约束高,而这俩约束又不受你控制,因此,后面你添加进去的约束是很容易跟这俩货发生冲突,导致添加约束出问题。

因此,我们需要要将与之冲突的约束优先级调高,让系统来适应我们。这样就做到系统约束由我们控制。

解决方案

说了一堆,废话连篇,要不然怎么写博客呢?

Masonry 和 SnapKit一样,这里我就不全写出来了。


self.contentLabel.snp.makeConstraints { (make) in
            make.edges.equalToSuperview().inset(20)
            make.height.equalTo(100).priority(.high)
        }

var cellHeight : Float!{
        didSet{
            self.contentLabel.snp.updateConstraints{ (make) in
                make.height.equalTo(cellHeight).priority(.high)
            }
        }
    }

把我们的约束的优先级调高,让系统来适应我们的约束。 因为你不知道系统的约束高度是多少,比如系统是300,而你自己设个200,这就冲突了。

反过来就可行,因为你设的高度,系统知道,所以系统会自己处理的。

0

阅读 评论 收藏 转载 喜欢 打印举报
已投稿到:
  • 评论加载中,请稍候...
发评论

    发评论

    以上网友发言只代表其个人观点,不代表新浪网的观点或立场。

      

    新浪BLOG意见反馈留言板 不良信息反馈 电话:4006900000 提示音后按1键(按当地市话标准计费) 欢迎批评指正

    新浪简介 | About Sina | 广告服务 | 联系我们 | 招聘信息 | 网站律师 | SINA English | 会员注册 | 产品答疑

    新浪公司 版权所有