iOS開發tips-UITableView、UICollectionView行高/尺寸自適應
UITableView
我們都知道UITableView從iOS 8開始實現行高的自適應相對照較簡單,首先必須設置estimatedRowHeight給出預估高度,設置rowHeight為UITableViewAutomaticDimension(註意:假設不改動rowHeight默認就是UITableViewAutomaticDimension)。對於這兩個參數除了直接改動tableview相應的屬性之外仍然支持使用相應的代理方法設置。
最後僅僅要在UITableViewCell中設置contentView的約束就可以。
因為UITableViewCell的寬度等同於UITableView因此約束的設置其實僅僅是為了自己主動計算高度。
通常的做法就是設置contentView的top和bottom約束。而後其內部子視圖能夠提供intrinsicContentSize(比如UIButtonUILabel默認就已經提供)或者已經有明白的height約束。這樣一來就能夠做到子控件確定了自身高度,而contentView子控件又設置了和contentView相關的bottom約束來反向計算出UITableViewCell的實際高度。
以下仍然曾經面UITableView文章的自己定義Cell舉例。相比之前大量的運算而言Self-Sizing Cells
因為頭像高度已經固定,內容高度能夠通過固有高度自己主動計算。而二者的間隔和top、bottom約束已經固定,從而Self-Sizing Cells能夠自己主動計算出Cell的高度。
高度計算約束關系:
Cell布局代碼:
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直接設置。
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布局代碼:
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討論之列。因此本文不再深入討論。
iOS開發tips-UITableView、UICollectionView行高/尺寸自適應