UICollectionView Self Sizing Cells with Auto Layout

前端 未结 16 1954
野性不改
野性不改 2020-11-22 05:58

I\'m trying to get self sizing UICollectionViewCells working with Auto Layout, but I can\'t seem to get the cells to size themselves to the content. I\'m having

16条回答
  •  一整个雨季
    2020-11-22 06:05

    I tried using estimatedItemSize but there were a bunch of bugs when inserting and deleting cells if the estimatedItemSize was not exactly equal to the cell's height. i stopped setting estimatedItemSize and implemented dynamic cell's by using a prototype cell. here's how that's done:

    create this protocol:

    protocol SizeableCollectionViewCell {
        func fittedSize(forConstrainedSize size: CGSize)->CGSize
    }
    

    implement this protocol in your custom UICollectionViewCell:

    class YourCustomCollectionViewCell: UICollectionViewCell, SizeableCollectionViewCell {
    
        @IBOutlet private var mTitle: UILabel!
        @IBOutlet private var mDescription: UILabel!
        @IBOutlet private var mContentView: UIView!
        @IBOutlet private var mTitleTopConstraint: NSLayoutConstraint!
        @IBOutlet private var mDesciptionBottomConstraint: NSLayoutConstraint!
    
        func fittedSize(forConstrainedSize size: CGSize)->CGSize {
    
            let fittedSize: CGSize!
    
            //if height is greatest value, then it's dynamic, so it must be calculated
            if size.height == CGFLoat.greatestFiniteMagnitude {
    
                var height: CGFloat = 0
    
                /*now here's where you want to add all the heights up of your views.
                  apple provides a method called sizeThatFits(size:), but it's not 
                  implemented by default; except for some concrete subclasses such 
                  as UILabel, UIButton, etc. search to see if the classes you use implement 
                  it. here's how it would be used:
                */
                height += mTitle.sizeThatFits(size).height
                height += mDescription.sizeThatFits(size).height
                height += mCustomView.sizeThatFits(size).height    //you'll have to implement this in your custom view
    
                //anything that takes up height in the cell has to be included, including top/bottom margin constraints
                height += mTitleTopConstraint.constant
                height += mDescriptionBottomConstraint.constant
    
                fittedSize = CGSize(width: size.width, height: height)
            }
            //else width is greatest value, if not, you did something wrong
            else {
                //do the same thing that's done for height but with width, remember to include leading/trailing margins in calculations
            }
    
            return fittedSize
        }
    }
    

    now make your controller conform to UICollectionViewDelegateFlowLayout, and in it, have this field:

    class YourViewController: UIViewController, UICollectionViewDelegateFlowLayout {
        private var mCustomCellPrototype = UINib(nibName: , bundle: nil).instantiate(withOwner: nil, options: nil).first as! SizeableCollectionViewCell
    }
    

    it will be used as a prototype cell to bind data to and then determine how that data affected the dimension that you want to be dynamic

    finally, the UICollectionViewDelegateFlowLayout's collectionView(:layout:sizeForItemAt:) has to be implemented:

    class YourViewController: UIViewController, UICollectionViewDelegateFlowLayout, UICollectionViewDataSource {
    
        private var mDataSource: [CustomModel]
    
        func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath)->CGSize {
    
            //bind the prototype cell with the data that corresponds to this index path
            mCustomCellPrototype.bind(model: mDataSource[indexPath.row])    //this is the same method you would use to reconfigure the cells that you dequeue in collectionView(:cellForItemAt:). i'm calling it bind
    
            //define the dimension you want constrained
            let width = UIScreen.main.bounds.size.width - 20    //the width you want your cells to be
            let height = CGFloat.greatestFiniteMagnitude    //height has the greatest finite magnitude, so in this code, that means it will be dynamic
            let constrainedSize = CGSize(width: width, height: height)
    
            //determine the size the cell will be given this data and return it
            return mCustomCellPrototype.fittedSize(forConstrainedSize: constrainedSize)
        }
    }
    

    and that's it. Returning the cell's size in collectionView(:layout:sizeForItemAt:) in this way preventing me from having to use estimatedItemSize, and inserting and deleting cells works perfectly.

提交回复
热议问题