Norway


I am having a dataset displayed in a . The dataset is split into sections and each section has a header. Further, each cell has a detail view underneath it that is expanded when the cell is clicked.

For reference:

enter image description here  - LzgOv - ios – How to correctly invalidate layout for supplementary views in UICollectionView

For simplicity, I have implemented the details cells as standard cells that are hidden (height: 0) by default and when the non-detail cell is clicked, the height is set to non-zero value. The cells are updates using invalidateItems(at indexPaths: [IndexPath]) instead of reloading cells in performBatchUpdates(_ updates: (() -> Void)?, completion: ((Bool) -> Void)? = nil) as the animations seems glitchy otherwise.

Now to the problem, the invalidateItems function obviously updates only cells, not like the section header and therefore calling only this function will result in overflowing the section header:

enter image description here  - OPMNC - ios – How to correctly invalidate layout for supplementary views in UICollectionView

After some Googling, I found out that in order to update also the supplementary views, one has to call invalidateSupplementaryElements(ofKind elementKind: String, at indexPaths: [IndexPath]). This might recalculate the section header’s bounds , however results in the content not appearing:

enter image description here  - QCUWh - ios – How to correctly invalidate layout for supplementary views in UICollectionView

This is most likely caused due to the fact that the func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView does not seem to be called.

I would be extremely grateful if somebody could tell me how to correctly supplementary views to the issues above do not happen.

Code:

   override func numberOfSections(in collectionView: UICollectionView) -> Int {
        return dataManager.getSectionCount()
    }

    override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        let count = dataManager.getSectionItemCount(section: section)
        reminder = count % itemsPerWidth
        return count * 2
    }

    override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {

        if isDetailCell(indexPath: indexPath) {
            let cell = collectionView.dequeueReusableCell(withReuseIdentifier: Reusable.CELL_, for: indexPath) as! ServiceCollectionViewCell
            cell.lblName.text = "Americano detail"

            cell.layer.borderWidth = 0.5
            cell.layer.borderColor = UIColor(hexString: "#999999").cgColor
            return cell

        } else {
            let item = indexPath.item > itemsPerWidth ? indexPath.item - (((indexPath.item / itemsPerWidth) / 2) * itemsPerWidth) : indexPath.item
            let product = dataManager.getItem(index: item, section: indexPath.section)

            let cell = collectionView.dequeueReusableCell(withReuseIdentifier: Reusable.CELL_, for: indexPath) as! ServiceCollectionViewCell
            cell.lblName.text = product.name

            cell.layer.borderWidth = 0.5
            cell.layer.borderColor = UIColor(hexString: "#999999").cgColor

            return cell
        }
    }

    override func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView {
        switch kind {
        case UICollectionElementKindSectionHeader:
            if indexPath.section == 0 {
                let header = collectionView.dequeueReusableSupplementaryView(ofKind: UICollectionElementKindSectionHeader, withReuseIdentifier: Reusable.CELL__HEADER_ROOT, for: indexPath) as! ServiceCollectionViewHeaderRoot
                header.lblCategoryName.text = "Section Header"
                header.imgCategoryBackground.af_imageDownloader = imageDownloader
                header.imgCategoryBackground.af_setImage(withURLRequest: ImageHelper.getURL(file: category.backgroundFile!))
                return header
            } else {
                let header = collectionView.dequeueReusableSupplementaryView(ofKind: UICollectionElementKindSectionHeader, withReuseIdentifier: Reusable.CELL__HEADER, for: indexPath) as! ServiceCollectionViewHeader
                header.lblCategoryName.text = "Section Header"
                return header
            }
        default:
            assert(false, "Unexpected element kind")
        }
    }

    // MARK: UICollectionViewDelegate

    func collectionView(_ collectionView: UICollectionView,  collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
        let width = collectionView.frame.size.width / CGFloat(itemsPerWidth)

        if isDetailCell(indexPath: indexPath) {
            if expandedCell == indexPath {
                return CGSize(width: collectionView.frame.size.width, height: width)
            } else {
                return CGSize(width: collectionView.frame.size.width, height: 0)
            }
        } else {
            return CGSize(width: width, height: width)
        }
    }

    func collectionView(_ collectionView: UICollectionView,  collectionViewLayout: UICollectionViewLayout, referenceSizeForHeaderInSection section: Int) -> CGSize {
        if section == 0 {
            return CGSize(width: collectionView.frame.width, height: collectionView.frame.height / 3)
        } else {
            return CGSize(width: collectionView.frame.width, height: heightHeader)
        }
    }

    override func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
        if isDetailCell(indexPath: indexPath) {
            return
        }

        var offset = itemsPerWidth
        if isLastRow(indexPath: indexPath) {
            offset = reminder
        }

        let detailPath = IndexPath(item: indexPath.item + offset, section: indexPath.section)
        let context = UICollectionViewFlowLayoutInvalidationContext()

        let maxItem = collectionView.numberOfItems(inSection: 0) - 1
        var minItem = detailPath.item
        if let expandedCell = expandedCell {
            minItem = min(minItem, expandedCell.item)
        }

        // TODO: optimize this
        var cellIndexPaths = (0 ... maxItem).map { IndexPath(item: $0, section: 0) }

        var supplementaryIndexPaths = (0..<collectionView.numberOfSections).map { IndexPath(item: 0, section: $0)}

        for i in indexPath.section..<collectionView.numberOfSections {
            cellIndexPaths.append(contentsOf: (0 ... collectionView.numberOfItems(inSection: i) - 1).map { IndexPath(item: $0, section: i) })
            //supplementaryIndexPaths.append(IndexPath(item: 0, section: i))
        }

        context.invalidateSupplementaryElements(ofKind: UICollectionElementKindSectionHeader, at: supplementaryIndexPaths)
        context.invalidateItems(at: cellIndexPaths)

        if detailPath == expandedCell {
            expandedCell = nil
        } else {
            expandedCell = detailPath
        }

        UIView.animate(withDuration: 0.25) {
            collectionView.collectionViewLayout.invalidateLayout(with: context)
            collectionView.layoutIfNeeded()
        }
    }



Source link

LEAVE A REPLY

Please enter your comment!
Please enter your name here