iOS開發tips-UITableView、UICollectionView行高/尺寸自適應

UITableView

咱們都知道UITableView從iOS 8開始實現行高的自適應相對比較簡單,首先必須設置estimatedRowHeight給出預估高度,設置rowHeightUITableViewAutomaticDimension(注意:若是不修改rowHeight默認就是UITableViewAutomaticDimension),對於這兩個參數除了直接修改tableview對應的屬性以外仍然支持使用對應的代理方法設置。最後只要在UITableViewCell中設置contentView的約束便可。因爲UITableViewCell的寬度等同於UITableView所以約束的設置事實上只是爲了自動計算高度。一般的作法就是設置contentView的top和bottom約束,然後其內部子視圖能夠提供intrinsicContentSize(例如UIButtonUILabel默認就已經提供)或者已經有明確的height約束。這樣一來就能夠作到子控件肯定了自身高度,而contentView子控件又設置了和contentView相關的bottom約束來反向計算出UITableViewCell的實際高度。
下面仍然之前面UITableView文章的自定義Cell舉例,相比以前大量的運算而言Self-Sizing Cells能夠說簡化了不少。除了設置estimatedRowHeight外最重要的就是添加相關Autolayout約束。因爲頭像高度已經固定,內容高度能夠經過固有高度自動計算,而兩者的間隔和top、bottom約束已經固定,從而Self-Sizing Cells能夠自動計算出Cell的高度。
高度計算約束關係:

Cell佈局代碼:html

import UIKit
    import SnapKit
    
    class StatusTableViewCell: UITableViewCell {
    
        // MARK: - 公共屬性
        var status:Status! {
            didSet {
                self.avatarImageView.image = UIImage(named: status.profileImageUrl)
                self.userNameLabel.text = status.userName
                self.mtypeImageView.image = UIImage(named: status.mbtype)
                self.createdAtLabel.text = status.createdAt
                self.sourceLabel.text = status.source
                self.contentLabel.text = status.text
            }
        }
        
        // MARK: - 生命週期及方法覆蓋
        override func awakeFromNib() {
            super.awakeFromNib()
        }
        
        override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
            super.init(style: style, reuseIdentifier: reuseIdentifier)
            self.setup()
        }
        
        required init?(coder aDecoder: NSCoder) {
            super.init(coder: aDecoder)
        }
    
        override func setSelected(_ selected: Bool, animated: Bool) {
    
        }
        
        // MARK: - 私有方法
        private func setup() {
            self.contentView.addSubview(self.avatarImageView)
            self.contentView.addSubview(self.userNameLabel)
            self.contentView.addSubview(self.mtypeImageView)
            self.contentView.addSubview(self.createdAtLabel)
            self.contentView.addSubview(self.sourceLabel)
            self.contentView.addSubview(self.contentLabel)
            
            self.avatarImageView.snp.makeConstraints { (make) in
                make.top.left.equalTo(10.0)
                make.size.equalTo(CGSize(width: 40.0, height: 40.0))
            }
            
            self.userNameLabel.snp.makeConstraints { (make) in
                make.top.equalTo(self.avatarImageView.snp.top)
                make.left.equalTo(self.avatarImageView.snp.right).offset(8.0)
            }
            
            self.mtypeImageView.snp.makeConstraints { (make) in
                make.top.equalTo(self.userNameLabel.snp.top)
                make.left.equalTo(self.userNameLabel.snp.right).offset(8.0)
                make.size.equalTo(CGSize(width: 14.0, height: 14.0))
            }
            
            self.createdAtLabel.snp.makeConstraints { (make) in
                make.left.equalTo(self.userNameLabel.snp.left)
                make.bottom.equalTo(self.avatarImageView.snp.bottom)
            }
            
            self.sourceLabel.snp.makeConstraints { (make) in
                make.left.equalTo(self.createdAtLabel.snp.right).offset(10.0)
                make.bottom.equalTo(self.createdAtLabel.snp.bottom)
                make.right.lessThanOrEqualTo(-8.0)
            }
            
            self.contentLabel.snp.makeConstraints { (make) in
                make.top.equalTo(self.avatarImageView.snp.bottom).offset(8.0)
                make.left.equalTo(self.avatarImageView.snp.left)
                make.right.equalTo(-8.0)
                make.bottom.equalTo(-10.0) // 注意此處必須設置,不然contentView沒法自適應高度
            }
            
        }
        
        // MARK: - 私有屬性
        private lazy var avatarImageView:UIImageView = {
            let temp = UIImageView()
            return temp
        }()
        
        private lazy var mtypeImageView:UIImageView = {
            let temp = UIImageView()
            return temp
        }()
        
        private lazy var userNameLabel:UILabel = {
            let temp = UILabel()
            temp.textColor = UIColor(red: 50.0/255.0, green: 50.0/255.0, blue: 50.0/255.0, alpha: 1.0)
            temp.font = UIFont.systemFont(ofSize: 14.0)
            return temp
        }()
        
        private lazy var createdAtLabel:UILabel = {
            let temp = UILabel()
            temp.textColor = UIColor(red: 120.0/255.0, green: 120.0/255.0, blue: 120.0/255.0, alpha: 1.0)
            temp.font = UIFont.systemFont(ofSize: 12.0)
            return temp
        }()
        
        private lazy var sourceLabel:UILabel = {
            let temp = UILabel()
            temp.textColor = UIColor(red: 120.0/255.0, green: 120.0/255.0, blue: 120.0/255.0, alpha: 1.0)
            temp.font = UIFont.systemFont(ofSize: 12.0)
            return temp
        }()
        
        private lazy var contentLabel:UILabel = {
            let temp = UILabel()
            temp.textColor = UIColor(red: 50.0/255.0, green: 50.0/255.0, blue: 50.0/255.0, alpha: 1.0)
            temp.font = UIFont.systemFont(ofSize: 14.0)
            temp.numberOfLines = 0
            return temp
        }()
        
    }

最終效果:

不管是UITableView仍是後面的UICollectionview,Self-Sizing Cells的概念均是從iOS 8開始提出的。若是是iOS 8以前的版本則須要經過systemLayoutSizeFitting()進行計算或者經過frame直接設置。swift

UICollectionView

瞭解了UITableView的Cell行高自適應以後,要理解UICollectionviewCell的Size自適應並不難,由於UICollectionViewCell相比較於UITableViewCell除了要經過AutoLayout肯定contentView的高度以外還要肯定其寬度,其寬度肯定原則和UITableViewCell的高度肯定是相似的,只是要經過UICollectionviewCell的contentView子控件自身肯定其寬度,而後設置子控件和contentView相關的right約束便可。固然對於UICollectionViewCell自適應尺寸一樣必須設置UICollectionViewFlowLayout的estimatedItemSize屬性(若是使用UICollectionViewFlowLayout)。
下面的的demo演示了類淘寶商品展現的UICollectionview佈局,除了經過AutoLayout肯定高度外,經過商品圖片的left、right約束和width的設置能夠反向推斷出contentView的寬度,經過Self-Sizing Cells就能夠最終肯定UICollectionviewCell的size。
Cell佈局代碼:less

import UIKit
    
    class ProductCollectionViewCell: UICollectionViewCell {
        
        // MARK: - 公共屬性
        var product:Product! {
            didSet {
                self.productImageView.image = UIImage(named: product.image)
                self.contentLabel.text = product.text
                self.priceLabel.text = "¥\(product.price)"
                self.salesLabel.text = "\(product.sale)人購買"
            }
        }
        
        // MARK: - 生命週期及方法覆蓋
        override init(frame: CGRect) {
            super.init(frame: frame)
            self.setup()
        }
        
        required init?(coder aDecoder: NSCoder) {
            super.init(coder: aDecoder)
        }
        
        override func preferredLayoutAttributesFitting(_ layoutAttributes: UICollectionViewLayoutAttributes) -> UICollectionViewLayoutAttributes {
            return super.preferredLayoutAttributesFitting(layoutAttributes)
        }
        // MARK: - 私有方法
        private func setup() {
            self.backgroundColor = UIColor.white
            self.contentView.addSubview(self.productImageView)
            self.contentView.addSubview(self.contentLabel)
            self.contentView.addSubview(self.priceLabel)
            self.contentView.addSubview(self.salesLabel)
            
            let screenWidth = UIScreen.main.bounds.width
            self.productImageView.snp.makeConstraints { (make) in
                make.top.left.right.equalTo(0.0)
                make.height.equalTo(screenWidth*0.5).priority(999)
                make.width.equalTo((screenWidth-18)*0.5).priority(999) // 此設置能夠肯定cell寬度,注意儘管下降了默認的優先級僅僅是爲了計算中間步驟不至於約束衝突,最終顯示時此約束仍然會生效
            }
            
            self.contentLabel.snp.makeConstraints { (make) in
                make.top.equalTo(self.productImageView.snp.bottom).offset(4.0)
                make.left.equalTo(8.0)
                make.right.equalTo(-8.0)
                make.height.equalTo(28.0)
            }
            
            self.priceLabel.snp.makeConstraints { (make) in
                make.top.equalTo(self.contentLabel.snp.bottom).offset(8.0)
                make.left.equalTo(self.contentLabel.snp.left)
                make.bottom.equalTo(-8.0) //此設置能夠肯定cell高度
            }
            
            self.salesLabel.snp.makeConstraints { (make) in
                make.centerY.equalTo(self.priceLabel.snp.centerY)
                make.right.equalTo(-8.0)
            }
        }
        
        // MARK: - 私有屬性
        private lazy var productImageView:UIImageView = {
            let temp = UIImageView()
            temp.contentMode = .scaleAspectFit
            temp.clipsToBounds = true
            return temp
        }()
        
        private lazy var contentLabel:UILabel = {
            let temp = UILabel()
            temp.textColor = UIColor(red: 50.0/255.0, green: 50.0/255.0, blue: 50.0/255.0, alpha: 1.0)
            temp.font = UIFont.systemFont(ofSize: 12.0)
            temp.numberOfLines = 2
            return temp
        }()
        
        private lazy var priceLabel:UILabel = {
            let temp = UILabel()
            temp.textColor = UIColor.orange
            temp.font = UIFont.systemFont(ofSize: 14.0)
            return temp
        }()
        
        private lazy var salesLabel:UILabel = {
            let temp = UILabel()
            temp.textColor = UIColor(red: 150.0/255.0, green: 150.0/255.0, blue: 150.0/255.0, alpha: 1.0)
            temp.font = UIFont.systemFont(ofSize: 12.0)
            return temp
        }()
        
    }

最終效果:

除此以外從iOS 8開始,UICollectionViewCell提供了preferredLayoutAttributesFitting()方法用於提供一些cell屬性修改,固然經過此方法能夠從新修改cell的size(包括Self-Sizing Cells自動計算後的size),對於手動計算高度的狀況這也方法也提供了一種不用外部設置cell size的而能提供讓UICollectionView肯定cell尺寸的方式,可是這並不在Self-Sizing Cells討論之列,所以本文再也不深刻討論。ide

相關文章
相關標籤/搜索