I have UICollectionViewCompositionalLayout
. In didSelectItemAt
I start downloading the file. Inside the collection view items I have a label showing the percentage of the file downloaded. I use this code for this and to update my cell:
func createDataSource() {
dataSource = UICollectionViewDiffableDataSource<Section, Item>(collectionView: collectionView) { collectionView, indexPath, item in
switch self.sections[indexPath.section].identifier {
case "cell":
let cell = self.configure(DCell.self, with: item, for: indexPath)
if indexPath.row < self.items.count {
let item = self.items[indexPath.row]
switch item.state {
case .downloading: cell.title.text = "\(String(format: "%.f%%", item.progress * 100))"
case .completed: cell.title.text = "Completed"
case .failed: cell.title.text = "Fail"
case .none: break
}
}
return cell
default: return self.configure(DCell.self, with: item, for: indexPath)
}
}
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
// activate the download manager code...
let url = URL(string: "http")!
let downloadManager = DownloadManager()
downloadManager.identifier = indexPath.row+1
downloadManager.collectionId = 0
downloadManager.folderPath = "\(indexPath.row+1)"
let downloadTaskLocal = downloadManager.activate().downloadTask(with: url)
downloadTaskLocal.resume()
// get progress:
downloadManager.onProgress = { [weak self] (row, collection, progress) in
guard let self = self else { return }
DispatchQueue.main.async {
self.items[row - 1].progress = progress
switch progress {
case 1.0: self.items[row - 1].state = .completed
case _: self.items[row - 1].state = .downloading
}
self.reloadItem(indexPath: .init(row: row - 1, section: 0))
}
}
}
func reloadItem(indexPath: IndexPath) {
guard let needReloadItem = dataSource!.itemIdentifier(for: indexPath) else { return }
var snapshot = NSDiffableDataSourceSnapshot<Section, Item>()
snapshot.appendSections(sections)
for section in sections { snapshot.appendItems(section.item, toSection: section) }
dataSource?.apply(snapshot)
snapshot.reloadItems([needReloadItem])
dataSource?.apply(snapshot, animatingDifferences: false)
}
code from downloadManager to get progress:
func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didWriteData bytesWritten: Int64, totalBytesWritten: Int64, totalBytesExpectedToWrite: Int64) {
if totalBytesExpectedToWrite > 0 {
if let onProgress = onProgress { calculateProgress(session: session, completionHandler: onProgress) }
}
}
private func calculateProgress(session : URLSession, completionHandler : @escaping (Int, Int, Float) -> ()) {
session.getTasksWithCompletionHandler { (tasks, uploads, downloads) in
let progress = downloads.map({ (task) -> Float in
if task.countOfBytesExpectedToReceive > 0 { return Float(task.countOfBytesReceived) / Float(task.countOfBytesExpectedToReceive)
} else { return 0.0 }
})
completionHandler(session.getSessionDescription(), Int(session.accessibilityHint!)!, progress.reduce(0.0, +))
}
}
But when my file is downloading and I use this method reloadItem(indexPath: IndexPath)
to update a cell, I am getting some freezes when scrolling the collection view and freezes if I click again on the cell that currently show the downloading progress. My problem is inside this method reloadItem(indexPath: IndexPath)
. But how to fix this problem with cell update?
for @dezinezync
using your code the scroll view freeze was fixed. But if I click again on the cell that currently show the downloading progress I have strange behaviour. In cell class I have this code to show animation:
class DCell: UICollectionViewCell, SelfConfiguringCell {
private func animateScale(to scale: CGFloat, duration: TimeInterval) {
UIView.animate( withDuration: duration, delay: 0, usingSpringWithDamping: 1.0, initialSpringVelocity: 0.5, options: [], animations: {
self.stackView.transform = .init(scaleX: scale, y: scale)
self.textView.transform = .init(scaleX: scale, y: scale)
}, completion: nil)
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
super.touchesBegan(touches, with: event)
animateScale(to: 0.9, duration: 0.4)
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
super.touchesEnded(touches, with: event)
animateScale(to: 1, duration: 0.38)
isSelected.toggle()
}
override func touchesCancelled(_ touches: Set<UITouch>, with event: UIEvent?) {
super.touchesCancelled(touches, with: event)
animateScale(to: 1, duration: 0.38)
isSelected.toggle()
}
}
But if I click again on the cell that currently show the downloading progress my cell sticks. It becomes smaller than others. And when clicked the animation will not appear. didSelectItemAt
not called. But for other cells the animation works fine before the progress starts showing. I don't understand why this happens and didSelectItemAt
not called
Please try the following change:
func reloadItem(indexPath: IndexPath) {
guard let needReloadItem = dataSource!.itemIdentifier(for: indexPath) else { return }
guard var snapshot = dataSource?.snapshot() else { return }
snapshot.reloadItems([needReloadItem])
dataSource?.apply(snapshot, animatingDifferences: false)
}
Once you form your initial snapshot, subsequent changes should typically refer to the same snapshot. This ensures that the DataSource can perform efficient diffs between the snapshot it currently holds and the newly applied one.