2

I am having a problem with a collection view using a compositional layout! the Card section supposed to be auto-resizing for the height and it should be scrolling horizontally while the whole collection view vertically as this test project! The issue I am having is that I set an estimated height for the item and the group but this object “_UICollectionViewOrthogonalScrollerEmbeddedScrollView” doesn't resize accordingly, Thus the portion of the cell outside the bounds of “_UICollectionViewOrthogonalScrollerEmbeddedScrollView” doesn't receive events So, If you press the button on the longest card in the project or tried to scroll or touch the portion outside the “_UICollectionViewOrthogonalScrollerEmbeddedScrollView” the cell doesnt receive any events. Check the screenshot attached… Does anybody have an idea how to solve this issue???

Link to the project: https://drive.google.com/file/d/1tksL0qZ1egxy7M4UD8eLMeY61H7U1ou1/view?usp=sharing

enter image description here

This is My Compositional layout code

struct CollectionViewLayout {
static func createLayout(collectionView: UICollectionView) -> UICollectionViewLayout {
    let layout = UICollectionViewCompositionalLayout { (sectionIndex, _: NSCollectionLayoutEnvironment) -> NSCollectionLayoutSection? in
        let sectionLayoutKind = ViewController.Section.allCases[sectionIndex]
        return sectionLayout(for: sectionLayoutKind, collectionView: collectionView)
    }
    return layout
}

static func sectionLayout(for kind: ViewController.Section,
                          collectionView: UICollectionView) -> NSCollectionLayoutSection {
    switch kind {
    case .cards:
        return cardsLayoutSection(collectionView: collectionView)
    case .buttons:
        return buttonsLayoutSection()
    }
}

static func cardsLayoutSection(collectionView: UICollectionView) -> NSCollectionLayoutSection {
    let etimationHeight: CGFloat = 300
    let itemSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0),
                                          heightDimension: .estimated(etimationHeight))
    let item = NSCollectionLayoutItem(layoutSize: itemSize)

    let width = collectionView.frame.width - 40.0 // spacing from left and right 20 each
    let groupSize = NSCollectionLayoutSize(widthDimension: .absolute(width),
                                           heightDimension: .estimated(etimationHeight))
    let group = NSCollectionLayoutGroup.horizontal(layoutSize: groupSize, subitem: item, count: 1)

    let section = NSCollectionLayoutSection(group: group)
    section.contentInsets = NSDirectionalEdgeInsets(top: 0, leading: 20, bottom: 0, trailing: 20)
    section.interGroupSpacing = 10
    section.orthogonalScrollingBehavior = .groupPaging
    return section
}

static func buttonsLayoutSection() -> NSCollectionLayoutSection {
    let layoutSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0),
                                            heightDimension: .estimated(100))
    let item = NSCollectionLayoutItem(layoutSize: layoutSize)
    let group = NSCollectionLayoutGroup.horizontal(layoutSize: layoutSize, subitem: item, count: 1)
    let section = NSCollectionLayoutSection(group: group)
    return section
}

}

And this my cell configurator

class CellConfigurator {
let collectionView: UICollectionView

private let numberOfCards = 3

init(collectionView: UICollectionView) {
    self.collectionView = collectionView
}

var numberOfItems: Int {
    return numberOfCards
}

var activeIndex: Int {
    return 0
}

func cellForButtons(indexPath: IndexPath) -> UICollectionViewCell {
    let cell = collectionView.dequeueReusableCell(withReuseIdentifier: String.init(describing: Buttons.self), for: indexPath)
    return cell
}

func cardCell(indexPath: IndexPath) -> UICollectionViewCell {
    let cell = collectionView.dequeueReusableCell(withReuseIdentifier: String.init(describing: Card.self), for: indexPath) as! Card
    switch indexPath.row {
    case 0:
        cell.heightConstraint.constant = 300
    case 1:
        cell.heightConstraint.constant = 100
    default:
        cell.heightConstraint.constant = 500
    }
    return cell
}

}

Ehab Saifan
  • 290
  • 2
  • 13

1 Answers1

1

After a lot of research it seems an apple bug! Anyways I found a workaround which is subcallsing the collectionView and fix the “_UICollectionViewOrthogonalScrollerEmbeddedScrollView” size and position. I got this form this post UICollectionViewCompositionalLayout bug on iOS 14.3

public final class CollectionView: UICollectionView {
    override public func layoutSubviews() {
        super.layoutSubviews()

        guard #available(iOS 14.3, *) else { return }

        subviews.forEach { subview in
            guard
                let scrollView = subview as? UIScrollView,
                let minY = scrollView.subviews.map(\.frame.origin.y).min(),
                let maxHeight = scrollView.subviews.map(\.frame.height).max(),
                minY > scrollView.frame.minY || maxHeight > scrollView.frame.height
            else { return }

            scrollView.contentInset.top = -minY
            scrollView.frame.origin.y = minY
            scrollView.frame.size.height = maxHeight
        }
    }
}
Ehab Saifan
  • 290
  • 2
  • 13