2

I am using Collection View for a scene. I created a custom compositional layout which is like down below. However, while scrolling there is an unwanted space between the second part of the cells. It has occurred in different cell types. I checked the spacing or insets but I couldn't figure out the reason.

The compositional layout part :

struct UIHelper {

  static func createLayouts(section: [SectionType], sectionIndex: Int) -> NSCollectionLayoutSection {

      switch section[sectionIndex] {
      
      case .sevenDaysWeather(_):

        // MARK: - Item
        let itemSize = NSCollectionLayoutSize(widthDimension: .absolute(70), heightDimension: .absolute(124))
        let item = NSCollectionLayoutItem(layoutSize: itemSize)

        // MARK: - Group
        let groupSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0), heightDimension: .absolute(124))
        let group = NSCollectionLayoutGroup.horizontal(layoutSize: groupSize, subitems: [item])
        group.interItemSpacing = .fixed(12)

        // MARK: - Section
        let section = NSCollectionLayoutSection(group: group)
        section.orthogonalScrollingBehavior = .continuous

        // MARK: - Header
        let headerSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0), heightDimension: .absolute(24))
        let headerKind = UICollectionView.elementKindSectionHeader
        let headerElement = NSCollectionLayoutBoundarySupplementaryItem(layoutSize: headerSize, elementKind: headerKind, alignment: .top)
  
        section.boundarySupplementaryItems = [headerElement]
        section.contentInsets = NSDirectionalEdgeInsets(top: 12, leading: 16, bottom: 20, trailing: 0)
        return section
  }
}

The collection view part:

  
  func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
    
    let type = sections[indexPath.section]
    
    switch type {
    case .sevenDaysWeather(let viewModels):
      guard let sevenDaysCell = collectionView.dequeueReusableCell(withReuseIdentifier: SevenDaysCollectionViewCell.identifer, for: indexPath) as? SevenDaysCollectionViewCell else { return UICollectionViewCell()}
      sevenDaysCell.configure(with: viewModels[indexPath.row])
      return sevenDaysCell
    }
  }
  
  func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView {
    
    let header = collectionView.dequeueReusableSupplementaryView(ofKind: UICollectionView.elementKindSectionHeader, withReuseIdentifier: HeaderCollectionReusableView.identifier, for: indexPath) as! HeaderCollectionReusableView
    header.configure(section: sections, indexPath: indexPath)
    return header
  }
}

The wanted result: Wanted Result

The current result: Initial state Initial Scrolled state Unwanted space

Edit: Normally I have two more sections in the collection view. In order to make the example more clear I trim those parts. But the logic was the same with the given example.

mzdogan
  • 91
  • 1
  • 12
  • You have a ***LOT*** going on there. Try starting simpler and try to get just *one* section working. If that works, then try adding another section. If you can't get a single section working, the best thing would be to create a [mre]. Use very simple cell layouts and very simple data, so we can take a look at what's happening. – DonMag Apr 10 '22 at 13:35
  • Thank you @DonMag. I try to go simpler as you suggested but can't figure out the reason. I try to make simpler code for you to see. Basically, I use a custom compositional layout for each of the sections. But the idea is the same for all of them. – mzdogan Apr 11 '22 at 07:04
  • Im having the same issue. I noticed this in IOS 15 my device running ios 14 does not have this bug. Not sure what's causing it. I'm digging deeper myself if I find anything ill update you. – Spencer Shelton Jul 04 '22 at 22:57
  • 1
    Hi @SpencerShelton, thanks for the comment. I fixed my problem and posted it as an answer. You can check it and maybe it will help you too. – mzdogan Jul 05 '22 at 10:11

3 Answers3

3

After a while, I realized that the problem was related to the width size of the layout group.

In the initial version that I posted, in the compositional layout group properties, I was using fractionalWidth in group size.

Like this:

// MARK: - Group

let groupSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0), heightDimension: .absolute(124))

After that, I change the group's width to absolute(widthValue). That fixed the unwanted space.

// MARK: - Group

let groupSize = NSCollectionLayoutSize(widthDimension: .absolute(686), heightDimension: .absolute(120))

Result: Initial Initial Position

Scrolled Scrolled Position

Explanation of absolute and fractional can be found in Apple's documentation

Note: I calculated the total widthValue like this:

(numberOfCells * cell widht) + ((numberOfCells - 1) * interSpacingValue)

mzdogan
  • 91
  • 1
  • 12
1

My cells are dynamically sized. So it doesn't work to calculate width beforehand. And .absolute sizing doesn't work.

Here's a simpler way of doing it:

Set 1-item-per-group using the count param. Each item is its own group. And have your items & groups size themselves dynamically using .estimated width. Lastly, NSCollectionLayoutGroup must be set to vertical.

    let itemSize = NSCollectionLayoutSize(widthDimension: .estimated(175), heightDimension: .fractionalHeight(1.0))
    let item = NSCollectionLayoutItem(layoutSize: itemSize)
            
    let groupSize = NSCollectionLayoutSize(widthDimension: .estimated(175), heightDimension: .absolute(44))
    let group = NSCollectionLayoutGroup.vertical(layoutSize: groupSize, subitem: item, count: 1)
    
    let layoutSection = NSCollectionLayoutSection(group: group)
    layoutSection.orthogonalScrollingBehavior = .continuous
    
    return layoutSection
theogood
  • 85
  • 1
  • 1
  • 7
0

I fixed it like this, but I wonder if there are a cleaner way:

private func makeHorizontalSection(environment: NSCollectionLayoutEnvironment) -> NSCollectionLayoutSection {
    // Calculations
    let size = CGSize(width: 100, height: 120)
    let insets: CGFloat = 23
    let itemSpacing: CGFloat = 4
    let availableWidth = environment.container.effectiveContentSize.width - 2 * insets
    let itemAndSpaceWidth = size.width + itemSpacing
    let numberOfColumns = floor(availableWidth / itemAndSpaceWidth)
    let groupWidth: CGFloat = numberOfColumns * itemAndSpaceWidth
    
    // Item
    let itemSize = NSCollectionLayoutSize(widthDimension: .absolute(size.width), heightDimension: .fractionalHeight(1))
    let item = NSCollectionLayoutItem(layoutSize: itemSize)
    
    // Group
    let groupSize = NSCollectionLayoutSize(widthDimension: .absolute(groupWidth), heightDimension: .absolute(size.height))
    let group = NSCollectionLayoutGroup.horizontal(layoutSize: groupSize, subitems: [item])
    group.interItemSpacing = .fixed(itemSpacing)
    
    // Section
    let section = NSCollectionLayoutSection(group: group)
    section.orthogonalScrollingBehavior = .continuous
    section.contentInsets = NSDirectionalEdgeInsets(top: 0, leading: insets, bottom: 0, trailing: insets)
    return section
}
manueGE
  • 1,169
  • 11
  • 14
  • I think I found a cleaner way. We should not have to calculate cell sizes by hand. Instead, let the system do the work with .estimated width. – theogood Oct 11 '22 at 14:12
  • @theogood I really would like to do it that way, but it was not working for me. If you can provide a working code example with your suggestion It would be awesome. – manueGE Oct 15 '22 at 16:41
  • i added an answer. with a relevant code snippet. check it out, and see if it works. my trick is to make items one-to-one with group. – theogood Oct 16 '22 at 22:48