I have 2 sections.

The top section contains unchecked items.

The bottom section contains checked items.

I was able to achieve nice move animation between the 2 sections, when I check/ uncheck the item.

enter image description here

The implementation is pretty straightforward by using Diffable Data Source.

enum Section: Hashable {
    case unchecked
    case checked
}

func applySnapshot(_ animatingDifferences: Bool) {
    var snapshot = Snapshot()
    
    snapshot.appendSections([.unchecked])
    
    if !checkedChecklists.isEmpty {
        snapshot.appendSections([.checked])
    }
    
    snapshot.appendItems(uncheckedChecklists, toSection: .unchecked)

    if !checkedChecklists.isEmpty {
        snapshot.appendItems(checkedChecklists, toSection: .checked)
    }
    
    dataSource.apply(snapshot, animatingDifferences: animatingDifferences)
}

func checkedButtonClicked(_ checklistCell: ChecklistCell, _ checked: Bool) {
    guard let indexPath = collectionView.indexPath(for: checklistCell) else { return }
    
    let section = indexPath.section
    let item = indexPath.item
    
    if (section == 0) {
        // Unchecked
        uncheckedChecklists[item].checked = checked
    } else if (section == 1) {
        // Checked
        checkedChecklists[item].checked = checked
    } else {
        precondition(false)
    }
    
    applySnapshot(true)
}

However, for the header at bottom section, instead of displaying "Header with checked item", I would like to display count of checked item. I modify the code as below.

enum Section: Hashable {
    case unchecked
    case checked(Int)
}

func applySnapshot(_ animatingDifferences: Bool) {
    var snapshot = Snapshot()
    
    snapshot.appendSections([.unchecked])
    
    if !checkedChecklists.isEmpty {
        snapshot.appendSections([.checked(checkedChecklists.count)])
    }
    
    snapshot.appendItems(uncheckedChecklists, toSection: .unchecked)

    if !checkedChecklists.isEmpty {
        snapshot.appendItems(checkedChecklists, toSection: .checked(checkedChecklists.count))
    }
    
    dataSource.apply(snapshot, animatingDifferences: animatingDifferences)
}

An additional code was added to display count number in header.

class ChecklistHeader: UICollectionReusableView {

    ...
    
    func update(_ section: NewChecklistViewController.Section) {
        switch section {
        case .unchecked:
            label.text = "Header with unchecked item"
            
        case .checked(let count):
            label.text = "Header with checked item \(count)"
    }
}

private func makeDataSource() -> DataSource {
    let dataSource = DataSource(
        ...
    )

    dataSource.supplementaryViewProvider = { collectionView, kind, indexPath in
        if kind == UICollectionView.elementKindSectionHeader {
            guard let checklistHeader = collectionView.dequeueReusableSupplementaryView(
                ofKind: kind,
                withReuseIdentifier: "header",
                for: indexPath) as? ChecklistHeader else {
                
                return nil
            }
            
            let sectionIdentifier = self.dataSource.snapshot().sectionIdentifiers[indexPath.section]
            if let section = sectionIdentifier as? Section {
                checklistHeader.update(section)
                
            }
            
            checklistHeader.delegate = self
            
            return checklistHeader
        } else if kind == UICollectionView.elementKindSectionFooter {
            ...
        }
        
        return nil
    }
    
    return dataSource
}

Here's the outcome

enter image description here


If you observe carefully

  1. There is no more move animation between sections.
  2. This is because, Diffable Data Source, will remove old bottom section with identifier .check(1), and replace with new bottom section with new identifier .check(2).

I was wondering, how can I retain the move animation between sections, yet able to update the content of header?

0

There are 0 best solutions below