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布局代码:

    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讨论之列,因此本文不再深入讨论。

时间: 2024-10-22 10:56:18

iOS开发tips-UITableView、UICollectionView行高/尺寸自适应的相关文章

ios开发之根据内容行数调整cell 高度,与label高度

设置cell高度 - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath { NoticeMessage* msg = [arrayNoticeMessage objectAtIndex:indexPath.section];//取出对应的section或者cell UIFont *msgFont = [UIFont fontWithName:@"arial&qu

IOS开发系列--UITableView使用全面解析

--UIKit之UITableView 概述 在iOS开发中UITableView可以说是使用最广泛的控件,我们平时使用的软件中到处都可以看到它的影子,类似于微信.QQ.新浪微博等软件基本上随处都是UITableView.当然它的广泛使用自然离不开它强大的功能,今天这篇文章将针对UITableView重点展开讨论.今天的主要内容包括: 基本介绍 数据源 代理 性能优化 UITableViewCell 常用操作 UITableViewController MVC模式 基本介绍 UITableVie

iOS开发系列--UITableView全面解析

概述 在iOS开发中UITableView可以说是使用最广泛的控件,我们平时使用的软件中到处都可以看到它的影子,类似于微信.QQ.新浪微博等软件基本上随处都是UITableView.当然它的广泛使用自然离不开它强大的功能,今天这篇文章将针对UITableView重点展开讨论.今天的主要内容包括: 基本介绍 数据源 代理 性能优化 UITableViewCell 常用操作 UITableViewController MVC模式 基本介绍 UITableView有两种风格:UITableViewSt

IOS开发中UITableView(表视图)的性能优化及自定义Cell

IOS开发中UITableView(表视图)的滚动优化及自定义Cell IOS 开发中UITableView是非常常用的一个控件,我们平时在手机上看到的联系人列表,微信好友列表等都是通过UITableView实现的.UITableView这个控件中的列表的每一行是一个cell,当UITableView中cell数量特别大的时候,由于每次都需要alloc分配内存并初始化,会导致app运行不流畅,所以可以使用苹果提供的几个方法进行优化,我把这个过程记录下来供自己以后查阅. 当然,既然说到优化,那我们

iOS开发 统计xcode代码行数

如果要统计ios开发代码,包括头文件的,终端命令进入项目目录下,命令如下 find . -name "*.m" -or -name "*.h" -or -name "*.xib" -or -name "*.c" |xargs wc -l 列出每个文件的行数 find . -name "*.m" -or -name "*.h" -or -name "*.xib" -or

iOS开发tips总结

tip 1 :  给UIImage添加毛玻璃效果 func blurImage(value:NSNumber) -> UIImage { let context = CIContext(options:[KCIContextUseSoftwareRenderer:true]) let ciImage = CoreImage.CIImage(image:self) let blurFilter = CIFilter(name:"CIGassianBlur") blurFilter?

iOS开发基础-UITableView控件简单介绍

 UITableView 继承自 UIScrollView ,用于实现表格数据展示,支持垂直滚动.  UITableView 需要一个数据源来显示数据,并向数据源查询一共有多少行数据以及每一行显示什么内容等.凡是遵守 UITableViewDataSource 协议的Objc对象,都可以是 UITableView 的数据源.  - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView  返回共有多少组数据.  - (NSI

iOS开发:UITableView加载多张照片导致内存上涨的问题

最近在写一个文件管理的页面,里面功能挺多的,有缩略图下载(socket),有文件下载(http). 说说缩略图下载这一块,我的UITableView的一个cell要加载四张缩略图,iPhone5s的屏幕能加载8行.大概UI上是这样布局的.(另外缩略图需要一张张下载) 刚开始进到文件管理界面,内存占用20几M,随着缩略图不断下载下来内存在上涨,我不断的往上拉,内存一值在涨.达到一定值app出现闪退.大概是300多M的时候闪退.什么原因造成的呢? 先看下图: 我是这样写的:建立一个对象,这个对象有一

IOS开发 统计XCODE 代码行数

如果要统计ios开发代码,包括头文件的,终端命令进入项目目录下,命令如下 find . -name "*.m" -or -name "*.h" -or -name "*.xib" -or -name "*.c" |xargs wc -l 列出每个文件的行数 find . -name "*.m" -or -name "*.h" -or -name "*.xib" -or