ios – UICollectionView glitches when eradicating sections utilizing Compositional Layouts and diffable knowledge sources

0
66


I am experiencing an issue when rendering detachable sections in my assortment views. The setup is as follows:

The gathering view has two sections. The primary one incorporates just one cell, which when tapped triggers a snapshot replace that removes the sections from the gathering view. The second part incorporates a number of cells scrolling orthogonally.

The issue happens when scrolling the second part, after which tapping the one cell on the primary one.

The conduct I count on is that the primary part can be eliminated, and the part would slide into the primary’s place in a clean animation.

What I am seeing is the orthogonally scrolling part flickering and leaping till the animation is full.

Listed here are some examples of what I might count on, and what occurs.

Anticipated (with out scrolling) Sudden (precise end result)
Expected Unexpected

This is a code snippet able to drop on a Playground to check (Xcode 15.3, iOS 17). Please word that it is simply an instance I attempted to distill from the actual code, which is extra advanced.

import UIKit
import PlaygroundSupport

var onTap = {
    removeSection()
}

// Rainbow coloured cells
last class Cell: UICollectionViewCell {
    override func prepareForReuse() {
        contentView.backgroundColor = .clear
    }

    func configure(with colour: UIColor) {
        contentView.backgroundColor = colour
    }
}

// The cell that when tapped, triggers the part elimination (word that choice is not being dealt with by way of didSelectItem.
last class RemovableCell: UICollectionViewCell {
    override init(body: CGRect) {
        tremendous.init(body: body)
        contentView.backgroundColor = .systemCyan
        contentView.addGestureRecognizer(UITapGestureRecognizer(goal: self, motion: #selector(faucet)))
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been applied")
    }

    @objc
    func faucet() {
        onTap()
    }
}

// Registrations for the cells
let cellRegistration = UICollectionView.CellRegistration<Cell, Int> { cell, indexPath, _ in
    let colours: [UIColor] = [.red, .orange, .yellow, .green, .blue]
    cell.configure(with: colours[indexPath.item % colors.count])
}
let removableCellRegistration = UICollectionView.CellRegistration<RemovableCell, Int> { cell, indexPath, _ in }

let removableSection: NSCollectionLayoutSection = {
    let itemSize = NSCollectionLayoutSize(
        widthDimension: .fractionalWidth(1),
        heightDimension: .estimated(120))
    let merchandise = NSCollectionLayoutItem(layoutSize: itemSize)

    let group = NSCollectionLayoutGroup.horizontal(layoutSize: itemSize, subitems: [item])
    let part = NSCollectionLayoutSection(group: group)

    return part
}()

// Sections definitions
let orthogonallyScrollingSection: NSCollectionLayoutSection = {
    let itemSize = NSCollectionLayoutSize(
        widthDimension: .fractionalWidth(1),
        heightDimension: .fractionalHeight(1))
    let merchandise = NSCollectionLayoutItem(layoutSize: itemSize)

    let groupSize = NSCollectionLayoutSize(
        widthDimension: .estimated(100),
        heightDimension: .fractionalHeight(0.2))
    let group = NSCollectionLayoutGroup.horizontal(layoutSize: groupSize, subitems: [item])

    let part = NSCollectionLayoutSection(group: group)
    part.orthogonalScrollingBehavior = .steady

    return part
}()

// Assortment view development, please do not thoughts the mess.
var removeSectionAvailable = true
let structure = UICollectionViewCompositionalLayout { part, _ in
    if part == 0, removeSectionAvailable {
        removableSection
    } else {
        orthogonallyScrollingSection
    }
}
let body = CGRect(x: 0, y: 0, width: 300, peak: 500)
let view = UICollectionView(body: body, collectionViewLayout: structure)
let dataSource = UICollectionViewDiffableDataSource<Int, Int>(collectionView: view) { collectionView, indexPath, itemIdentifier in
    if indexPath.part == 0, removeSectionAvailable {
        return collectionView.dequeueConfiguredReusableCell(
            utilizing: removableCellRegistration,
            for: indexPath,
            merchandise: itemIdentifier)
    } else {
        return collectionView.dequeueConfiguredReusableCell(
            utilizing: cellRegistration,
            for: indexPath,
            merchandise: itemIdentifier
        )
    }
}

var snapshot = NSDiffableDataSourceSnapshot<Int, Int>()
snapshot.appendSections([0, 1])
snapshot.appendItems([0], toSection: 0)
snapshot.appendItems([1, 2, 3, 4, 5, 6], toSection: 1)
dataSource.apply(snapshot)

func removeSection() {
    removeSectionAvailable = false
    snapshot.deleteSections([0])
    dataSource.apply(snapshot)
}

// Playground setup
PlaygroundPage.present.liveView = view
PlaygroundPage.present.needsIndefiniteExecution = true

All ideas are appreciated!