sizeForItemAt method is not calling when using cus

2019-07-29 06:37发布

问题:

I am using custom layout for my collection view but if i use custom layout sizeForItemAt method is not calling.

My custom layout:

public class HorizontalCollectionViewLayout : UICollectionViewLayout {
    var itemSize = CGSize(width: 0, height: 0) {
        didSet {
            invalidateLayout()
        }
    }
    private var cellCount = 0
    private var boundsSize = CGSize(width: 0, height: 0)

    public override func prepare() {
        cellCount = self.collectionView!.numberOfItems(inSection: 0)
        boundsSize = self.collectionView!.bounds.size
    }
    public override var collectionViewContentSize: CGSize {
        let verticalItemsCount = Int(floor(boundsSize.height / itemSize.height))
        let horizontalItemsCount = Int(floor(boundsSize.width / itemSize.width))

        let itemsPerPage = verticalItemsCount * horizontalItemsCount
        let numberOfItems = cellCount
        let numberOfPages = Int(ceil(Double(numberOfItems) / Double(itemsPerPage)))

        var size = boundsSize
        size.width = CGFloat(numberOfPages) * boundsSize.width
        return size
    }

    public override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
        var allAttributes = [UICollectionViewLayoutAttributes]()
        if cellCount > 0 {
        for i in 0...(cellCount-1) {
            let indexPath = IndexPath(row: i, section: 0)
            let attr = self.computeLayoutAttributesForCellAt(indexPath: indexPath)
            allAttributes.append(attr)
        }
        }
        return allAttributes
    }

    public override func layoutAttributesForItem(at indexPath: IndexPath) -> UICollectionViewLayoutAttributes? {
        return computeLayoutAttributesForCellAt(indexPath: indexPath)
    }

    public override func shouldInvalidateLayout(forBoundsChange newBounds: CGRect) -> Bool {
        return true
    }

    private func computeLayoutAttributesForCellAt(indexPath:IndexPath)
        -> UICollectionViewLayoutAttributes {
            let row = indexPath.row
            let bounds = self.collectionView!.bounds

            let verticalItemsCount = Int(floor(boundsSize.height / itemSize.height))
            let horizontalItemsCount = Int(floor(boundsSize.width / itemSize.width))
            let itemsPerPage = verticalItemsCount * horizontalItemsCount

            let columnPosition = row % horizontalItemsCount
            let rowPosition = (row/horizontalItemsCount)%verticalItemsCount
            let itemPage = Int(floor(Double(row)/Double(itemsPerPage)))

            let attr = UICollectionViewLayoutAttributes(forCellWith: indexPath)

            var frame = CGRect(x: 0,y: 0,width: 0,height: 0)
            frame.origin.x = CGFloat(itemPage) * bounds.size.width + CGFloat(columnPosition) * itemSize.width
            frame.origin.y = CGFloat(rowPosition) * itemSize.height
            frame.size = itemSize
            attr.frame = frame

            return attr
    }
}

And i am setting this layout at viewDidLoad method:

let itemWidth = 100
let itemHeight = 100
let horizontalCV = HorizontalCollectionViewLayout();
horizontalCV.itemSize = CGSize(width: itemWidth, height: itemHeight)

instagramCollection.collectionViewLayout = horizontalCV
self.instagramCollection.delegate=self
self.instagramCollection.dataSource=self

Other collection view methods are working fine like numberOfItemsInSection or cellForItemAt etc. But I can't set insets or cell spacing for my collection view.

I tried following code, below methods are not calling.

func collectionView(_ collectionView: UICollectionView,
                    layout collectionViewLayout: UICollectionViewLayout,
                    sizeForItemAt indexPath: IndexPath) -> CGSize {
    return CGSize.init(width: (instagramCollection.frame.width / 3.0), height: instagramCollection.frame.height / 2.0)
}


func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets {
    return UIEdgeInsets(top: 10, left: 10, bottom: 10, right: 10)
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat {
    return 10.0
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
    return 10.0
}

Also I tried to set instets and spacing from storyboard also not worked.

My collection view now using zero for all insets and spacing.

How to fix this problem?

回答1:

You should add in your controller implementation of the layout delegate. (you added these methods, but they aren't called)

So, not only UICollectionViewDelegate, UICollectionViewDataSource, but also UICollectionViewDelegateFlowLayout.

Like extension, for example.

extension YourController: UICollectionViewDelegateFlowLayout {
func collectionView(_ collectionView: UICollectionView,
                    layout collectionViewLayout: UICollectionViewLayout,
                    sizeForItemAt indexPath: IndexPath) -> CGSize {
    return CGSize.init(width: (instagramCollection.frame.width / 3.0), height: instagramCollection.frame.height / 2.0)
}

func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets {
    return UIEdgeInsets(top: 10, left: 10, bottom: 10, right: 10)
}

func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat {
    return 10.0
}

func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
    return 10.0
}


回答2:

Since your HorizontalCollectionViewLayout is subclass of UICollectionViewLayout those functions won't get called since they have nothing to do with UICollectionViewLayout

Please notice that the protocol you're implementing inside your controller is UICollectionViewDelegateFlowLayout. This protocol expand the UICollectionViewDelegate protocol.

So if you want those functions to be called just change the class you're inheriting from to UICollectionViewFlowLayout

public class HorizontalCollectionViewLayout : UICollectionViewFlowLayout { [YOUR CLASS] }