[IOS]UICollectionView CELL 自适应大小

目的

自己研究是一个痛苦的过程,网上的完整一点的文章很少,而且总是遇到各种各样的问题,所以记录下来去帮助那些像我一样的人少走一些弯路

实现目标 可以横向滚动的菜单栏

点击任意一个CELL ,下方的数据发生改变,这里先用横向滚动的UITableView,不好做,然后用第三方的UIScrollView做的,也不好用

image.png

过程

一个小小的自适应代码,搞了两天两夜,IOS相比Android编码,简直太落后了,

1. 通过约束自适应大小会出现最后1-2个数据不显示的问题,count是正确的,此时发现设置sectionInset>0 以及 minimumLineSpacing=1 就显示完整了,但是这样代码就变成了魔术代码,后经过多方请教,发现决定性因素是 返回 CELL size的时候不能写 zero, 宽度自适应的时候,设置宽>0,高度自适应的时候要设置高>0,不然会出现末尾的几个cell丢失的不明不白的问题,如果约束写在自定义CELL 的 layoutSubviews 方法里,则需要在显示CELL的时候即cellForItemAt方法里调用 cell.layoutIfNeeded(),不然会出现CELL错乱的问题

2. 通过自己计算CELL的大小,最开始是稀里糊涂的,结果现在已经豁然开朗,在 sizeForItemAt 里面返回的是 CELL.frame 的size ,但是CELL frame 里面的子view 都还没有设置大小,所以会不显示,需要在 layoutSubviews 里对子view进行设置大小,所以我认为 layoutSubviews 方法是在 sizeForItemAt之后调用的,已经可以拿到 CELL frame size

3. 总结: 约束自适应优先使用,因为CELL越来越复杂的话自己计算就会十分麻烦,当然也可以自己计算大小然后通过约束来搞定subView 之间的位置关系混着用

代码

import SnapKit
import Foundation

class HorizontalCollectionView: UIView, UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout {
    var dataArray: [String] = [] {
        didSet {
            collectionView.reloadData()
            collectionView.collectionViewLayout.invalidateLayout()
            //collectionView.reloadSections(IndexSet(integersIn: 0...0))
        }
    }
    var collectionView: UICollectionView!

    override init(frame: CGRect) {
        super.init(frame: frame)
        self.setUp()
    }

    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    func requestViewData() {
        dataArray = ["0", "1", "22", "333", "4444", "55555", "666666", "7777777", "88888888", "999999999", "10101010101010101010", "A", "1111111111111111111111", "12", "13", "14"]
        print("dataArray.count=", dataArray.count)
    }

    private func setUp() {
        let flowLayout = UICollectionViewFlowLayout()

        flowLayout.minimumInteritemSpacing = 10;
        flowLayout.sectionInset = UIEdgeInsetsMake(0, 0, 0, 0)
        flowLayout.scrollDirection = .horizontal

        //===== CELL 约束自适应 必备条件 1 =====
        if #available(iOS 10.0, *) {
            flowLayout.estimatedItemSize = UICollectionViewFlowLayoutAutomaticSize
        } else {
            flowLayout.estimatedItemSize = CGSize(width: 15, height: 48)
        }

        collectionView = UICollectionView.init(frame: CGRect(0, 0, SCREEN_WIDTH, 48), collectionViewLayout: flowLayout)
        collectionView.delegate = self
        collectionView.dataSource = self
        collectionView.backgroundColor = UIColor.yellow
        collectionView.showsHorizontalScrollIndicator = false
        self.addSubview(collectionView)

        collectionView.register(HorizontalLabelCell.classForCoder(), forCellWithReuseIdentifier: "_cell")
    }

    func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
        print("点击了 cell")
    }

    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        print("dataArray.count=", dataArray.count)
        return dataArray.count
    }

    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "_cell", for: indexPath) as! HorizontalLabelCell
        cell.title = dataArray[indexPath.row]
        return cell
    }

    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
        //===== CELL 计算自适应 必备条件 1 =====
        // return CGSize(dataArray[indexPath.row].widthWithFont(), 48)

        //===== CELL 约束自适应 必备条件 2 ===== 宽度或者高度一定要  大于 0 ,否则会出现丢失错误等不可预料问题
        return CGSize(0.01, 48)
    }
}

class HorizontalLabelCell: UICollectionViewCell {

    private lazy var titleLabel: UILabel = {
        let label = UILabel()
        label.numberOfLines = 1
        label.font = UIFont.systemFont(ofSize: 18)
        label.textAlignment = .center
        label.textColor = UIColor.red
        return label
    }()

    var title: String = "" {
        didSet {
            self.titleLabel.text = title
        }
    }

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
    }

    override init(frame: CGRect) {
        super.init(frame: frame)
        self.contentView.addSubview(self.titleLabel)

        //===== CELL 约束自适应 必备条件 3 =====
        self.titleLabel.snp.makeConstraints { maker in
            maker.left.top.equalTo(self.contentView)
            maker.width.greaterThanOrEqualTo(5)
            maker.height.greaterThanOrEqualTo(48)
            maker.height.equalTo(self.contentView).priorityLow()
            maker.right.equalTo(self.contentView).priorityLow()
        }
    }

    //===== CELL 计算自适应 必备条件 2 ===== 如果没有使用约束,则在这里赋值 subViews frame 大小
    /*override func layoutSubviews() {
        super.layoutSubviews()
        self.titleLabel.frame = self.contentView.frame
    }*/

}

使用

    lazy var topMenuView: HorizontalCollectionView = {
        let horizontalView = HorizontalCollectionView.init(frame: CGRect(0, 0, SCREEN_WIDTH, 48))
        return horizontalView
    }()

    override func viewDidLoad() {
        self.view.addSubview(self.topMenuView)
        self.topMenuView.snp.makeConstraints { maker in
            maker.left.right.top.equalTo(self.view)
            maker.height.equalTo(48)
        }
        self.topMenuView.requestViewData()
    }

后续

经过多次使用总结: 上面通过约束自适应的方案当只有一个 cell 的时候,会居中,需要自定义 followLayout 设置左对齐,总之会有各种各样的问题,经老开发提醒,UICollectionView 的宽高最好在 外部给定, 即 sizeForItemAtIndexPath 这里返回固定的宽高,然后 不要设置 estimatedItemSize,然后在 自定义Cell里面通过约束布局,
注意 UICollectionView 有默认的 spacing 间距,需要设置为0,才不会出现计算之外的问题,

        UICollectionViewFlowLayout *layout = [UICollectionViewFlowLayout new];
        layout.minimumInteritemSpacing = 0;
        layout.minimumLineSpacing = 0;

即趋势应该是 由外到内 的设置 大小.

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,294评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,493评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,790评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,595评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,718评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,906评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,053评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,797评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,250评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,570评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,711评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,388评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,018评论 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,796评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,023评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,461评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,595评论 2 350

推荐阅读更多精彩内容