title: UICollectionViewCompositionalLayout's sticky header is jerky
body: <p>I am trying to use the latest UICollectionViewCompositionalLayout to display paginated data using the UICollectionViewDiffableDataSource . I wanted to show a sticky header above the section that would stay on the top always even while loading the next page data from network .. I've noticed the sticky header is not working as expected and instead feels jerky while downloading data in the background and applying a new snapshot.. I was able to reproduce this issue with the sample app shared by Apple <a href="https://developer.apple.com/documentation/uikit/views_and_controls/collection_views/using_collection_view_compositional_layouts_and_diffable_data_sources" rel="noreferrer">here</a> </p>
  
  <p>Here is the code to reproduce the issue:</p>
  
  <pre><code>func layout() -&gt; UICollectionViewLayout {
      let itemSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0),
                                           heightDimension: .fractionalHeight(1.0))
      let item = NSCollectionLayoutItem(layoutSize: itemSize)
  
      let groupSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0),
                                            heightDimension: .absolute(44))
      let group = NSCollectionLayoutGroup.horizontal(layoutSize: groupSize, subitems: [item])
  
      let section = NSCollectionLayoutSection(group: group)
      section.interGroupSpacing = 5
      section.contentInsets = NSDirectionalEdgeInsets(top: 0, leading: 10, bottom: 0, trailing: 10)
  
      let sectionHeader = NSCollectionLayoutBoundarySupplementaryItem(
          layoutSize: NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0),
                                            heightDimension: .estimated(44)),
          elementKind: sectionHeaderElementKind,
          alignment: .top)
      sectionHeader.pinToVisibleBounds = true
      sectionHeader.zIndex = 2
      section.boundarySupplementaryItems = [sectionHeader]
  
      let layout = UICollectionViewCompositionalLayout(section: section)
      return layout
  }
  </code></pre>
  
  <p>The above snippet provides the UICollectionViewCompositionalLayout with section header pinned to visible bounds at the top..</p>
  
  <pre><code>func setUpCollectionView() {
      let collectionView = UICollectionView(frame: self.view.bounds, collectionViewLayout: layout())
      collectionView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
      collectionView.backgroundColor = .systemBackground
      collectionView.register(ListCell.self, forCellWithReuseIdentifier: ListCell.reuseIdentifier)
      collectionView.register(TitleSupplementaryView.self,
                  forSupplementaryViewOfKind: sectionHeaderElementKind,
                  withReuseIdentifier: TitleSupplementaryView.reuseIdentifier)
      self.view.addSubview(collectionView)
      collectionView.refreshControl = refreshControl
      self.collectionView = collectionView
  }
  </code></pre>
  
  <p>In the above code, I am adding the collection view to view controllers view. </p>
  
  <pre><code>func configureDataSource() {
      guard let collectionView = self.collectionView else {
          return
      }
  
      dataSource = UICollectionViewDiffableDataSource&lt;String, Int&gt;(collectionView: collectionView) {
          (collectionView: UICollectionView, indexPath: IndexPath, identifier: Int) -&gt; UICollectionViewCell? in
  
          // Get a cell of the desired kind.
          guard let cell = collectionView.dequeueReusableCell(
              withReuseIdentifier: ListCell.reuseIdentifier,
              for: indexPath) as? ListCell else { fatalError("Cannot create new cell") }
  
          // Populate the cell with our item description.
          cell.label.text = "\(indexPath.section),\(indexPath.item)"
  
          if self.canLoadNextPage(indexpath: indexPath) {
              self.getNextPage()
          }
  
          // Return the cell.
          return cell
      }
  
      dataSource?.supplementaryViewProvider = {
          (collectionView: UICollectionView, kind: String, indexPath: IndexPath) -&gt; UICollectionReusableView? in
  
          // Get a supplementary view of the desired kind.
          guard let headerFooter = collectionView.dequeueReusableSupplementaryView(
              ofKind: kind,
              withReuseIdentifier: TitleSupplementaryView.reuseIdentifier,
              for: indexPath) as? TitleSupplementaryView else { fatalError("Cannot create new header") }
  
          headerFooter.label.text = sectionHeader
          headerFooter.backgroundColor = .lightGray
          headerFooter.layer.borderColor = UIColor.black.cgColor
          headerFooter.layer.borderWidth = 1.0
  
          // Return the view.
          return headerFooter
      }
  
      var snapshot = NSDiffableDataSourceSnapshot&lt;String, Int&gt;()
      snapshot.appendSections([section])
      items = Array(currentOffset..&lt;currentOffset + 2*itemsPerPage)
      currentOffset += 2*itemsPerPage
  
      snapshot.appendItems(items)
  
      serialQueue.async { [weak self] in
          self?.dataSource?.apply(snapshot, animatingDifferences: true)
      }
  }
  </code></pre>
  
  <p>In the above code, I am creating a diffable datasource that builds the cells and header view.. It also initializes the collection view with a snapshot of initial data.. The snapshots are always applied in a background serial queue.</p>
  
  <p>When the content offset is closer towards the end, I am getting the next page of data and applying a new snapshot as you can see here..</p>
  
  <pre><code>func canLoadNextPage(indexpath: IndexPath) -&gt; Bool {
      guard (indexpath.item + 5) &gt; currentOffset else {
          return false
      }
  
      return true
  }
  
  func getNextPage() {
      fetchQueue.asyncAfter(deadline: .now() + 0.5) {
          var snapshot = NSDiffableDataSourceSnapshot&lt;String, Int&gt;()
           snapshot.appendSections([section])
          self.items.append(contentsOf: Array(self.currentOffset..&lt;self.currentOffset + itemsPerPage))
          snapshot.appendItems(self.items)
          self.currentOffset += itemsPerPage
  
          self.serialQueue.async { [weak self] in
              self?.dataSource?.apply(snapshot, animatingDifferences: true)
          }
      }
  }
  </code></pre>
  
  <p><a href="https://i.stack.imgur.com/t2qG4.gif" rel="noreferrer"><img src="https://i.stack.imgur.com/t2qG4.gif" alt="enter image description here"></a></p>
  
  <p>The similar behavior is also observed with Pull To Refresh actions.. </p>
  
  <p>Another thing i've noticed, this doesn't happen when you add data immediately without delay and switching between threads.</p>
  
  <p>Appreciate any help..</p>