Ariel Congestri
Ariel Congestri

Reputation: 11

Dynamic width horizontal Collection view using compositional layout

I am trying to create a collection view that has a simple cell with text, which means that the width and height of the cells should be dynamic. Since it should be able to be shorter/longer and the number of lines is n (Text comes from the service). As long as The cells width is not bigger than the collection it self, there is no problem. But if a long text should be presented it crashes.

It would only happen using .estimate as size, it wont if the cell take all the possible space

This is how the implementation looks like

Error:

Thread 1: "<UICollectionViewCompositionalLayout: 0x105b08470> ran into an error when computing the layout for section at index 0 in container <_UICollectionLayoutContainer: 0x600001756440 contentSize={353, 500}; contentInsets={0, 0, 0, 0}}>. Container calculated by applying insets ({0, 0, 0, 0}) from sectionInsetsReference \"safeArea\" to collection view frame {{0, 0}, {353, 500}}.\nError: NSCollectionLayoutItem created with invalid combination of spacing and size specified. This group cannot fit even a single item. Inspect the spacing and size of the items in this group and ensure that they fit into the group when its effective size is {333, 480}.\nGroup: <NSCollectionLayoutGroup 0x6000035082c0 size={.containerWidthFactor(1), .estimated(480)}>\n\t layoutDirection: .horizontal\n\t interItemSpacing=.fixed(4)\n\t subitems=\n\t\t <NSCollectionLayoutItem 0x600002620660 size={.estimated(32), .estimated(120)}>"

The collectionView is inside a UIView that works as wrapper and the view is inside a StackView like this:

        stackView.addArrangedSubview(label)
        stackView.addArrangedSubview(collectionViewWrapper)
        stackView.backgroundColor = .white
        collectionViewWrapper.translatesAutoresizingMaskIntoConstraints = false
        let actionGruopViewHeight = collectionViewWrapper.heightAnchor.constraint(equalToConstant: 200)
        NSLayoutConstraint.activate([
            stackView.topAnchor.constraint(equalTo: view.topAnchor, constant: 100),
            stackView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 20),
            stackView.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -20),
            stackView.bottomAnchor.constraint(lessThanOrEqualTo: view.bottomAnchor, constant: -20),
            collectionViewWrapper.widthAnchor.constraint(equalTo: stackView.widthAnchor),
            actionGruopViewHeight
        ])
        view.backgroundColor = .red

        collectionViewWrapper.didLayoutAction =  {
            actionGruopViewHeight.constant = collectionViewWrapper.collectionView.contentSize.height
        }

CollectionView wrapper:

        collectionView.delegate = self
        collectionView.dataSource = self
        collectionView.register(ActionCell.self, forCellWithReuseIdentifier: ActionCell.reuseIdentifier)
        collectionView.translatesAutoresizingMaskIntoConstraints = false
        collectionView.backgroundColor = .green
        addSubview(collectionView)
        NSLayoutConstraint.activate([
            collectionView.topAnchor.constraint(equalTo: topAnchor),
            collectionView.trailingAnchor.constraint(equalTo: trailingAnchor),
            collectionView.leadingAnchor.constraint(equalTo: leadingAnchor),
            collectionView.bottomAnchor.constraint(equalTo: bottomAnchor)
        ])

CollectionViewLayout:

    private lazy var layout = UICollectionViewCompositionalLayout { [weak self] _, _ in
        let itemSize = NSCollectionLayoutSize(widthDimension: .estimated(32), heightDimension: .estimated(120))
        let item = NSCollectionLayoutItem(layoutSize: itemSize)
        let groupSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0), heightDimension: .estimated(480))
        let group = NSCollectionLayoutGroup.horizontal(layoutSize: groupSize, subitems: [item])
        group.interItemSpacing = .fixed(4)
        let section = NSCollectionLayoutSection(group: group)
        section.contentInsets = .init(top: 10, leading: 10, bottom: 10, trailing: 10)
        section.interGroupSpacing = 4
        return section
    }

Cell:

    private lazy var textLabel: UILabel = {
        let textLabel = UILabel()
        textLabel.numberOfLines = 0
        return textLabel
    }()

    override init(frame: CGRect) {
        super.init(frame: frame)
        contentView.addSubview(textLabel)
        textLabel.translatesAutoresizingMaskIntoConstraints = false
        backgroundColor = .gray
        NSLayoutConstraint.activate([
            textLabel.topAnchor.constraint(equalTo: contentView.topAnchor),
            textLabel.trailingAnchor.constraint(equalTo: contentView.trailingAnchor),
            textLabel.leadingAnchor.constraint(equalTo: contentView.leadingAnchor),
            textLabel.bottomAnchor.constraint(equalTo: contentView.bottomAnchor),
        ])
    }

Did I do something wrong? is there a fix/workaround to avoid the crash?

Notice that it would be easy to reproduce with accessibility font sizes, since usually it would need 2 lines

Upvotes: 1

Views: 164

Answers (0)

Related Questions