I want to show progress in collection view cell text label. I use this code to do it. Code from observer works and I see print progress in debug but I can't see this in cell. Why?
KVO variant (not working)
I get progress inside the observer, but the cell text is not updated. Why?
var nameObservation: NSKeyValueObservation?
@objc dynamic var progress = 0.0
func createDataSource() {
dataSource = UICollectionViewDiffableDataSource<Section, Item>(collectionView: collectionView) { collectionView, indexPath, item in
switch self.sections[indexPath.section].identifier {
case "carouselCell":
let cell = self.configure(CarouselCell.self, with: item, for: indexPath)
self.nameObservation = self.observe(\.progress, options: .new) { vc, change in
cell.title.text = "\(self.progress)"
}
return cell
default: return self.configure(CarouselCell.self, with: item, for: indexPath)
}
}
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
_ = Timer.scheduledTimer(withTimeInterval: 0.10, repeats: true) { timer in
guard self.progress <= 1.0 else {
timer.invalidate()
self.progress = 0.0
return
}
}
}
Notification variant (not working).
I'm also trying to solve this problem using notifications. And my collection disappears after clicking
var nameObservation: NSKeyValueObservation?
@objc func createDataSource() {
dataSource = UICollectionViewDiffableDataSource<Section, Item>(collectionView: collectionView) { collectionView, indexPath, item in
switch self.sections[indexPath.section].identifier {
case "carouselCell":
let cell = self.configure(CarouselCell.self, with: item, for: indexPath)
cell.title.text = "\(self.progressA)"
print(self.progressA)
return cell
default: return self.configure(CarouselCell.self, with: item, for: indexPath)
}
}
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
NotificationCenter.default.addObserver(
self, selector: #selector(self.createDataSource),
name: Notification.Name(rawValue: "sound-CarouselController"), object: nil)
_ = Timer.scheduledTimer(withTimeInterval: 0.10, repeats: true) { timer in
guard self.progressA <= 1.0 else {
timer.invalidate()
self.progressA = 0.0
return
}
self.progressA += 0.01
NotificationCenter.default.post(name: Notification.Name(rawValue: "sound-CarouselController"), object: nil)
}
}
I find it extremely challenging to animate things in cells, syncing them and so on.
You can see some old QA about it. https://stackoverflow.com/a/58239612/294884 Synchronise all animations on collection-view cells etc - no idea what's going on in those.
the only honest-to-God solution we have ever found
Make a singleton that entirely handles all your timer(s), progress etc.
So for example there might be some "progress", lets say it is "progress of download 7B" ...
During any frame, anyone at all who wants to, can, get that current value, from the singleton
And that's it. In your cells (or anywhere whatsoever) just draw the value in question (whether a bar or whatever it is) each frame to the value you read from the singleton
So you just completely forget about the madness of UIKit's various animation paradigms.
Just use CADisplayLink and draw everything as you wish - based only on the values in one central singleton.
Don't forget that (of course) cells are constantly changing as you scroll. ie the cell that the user thinks of as "cell 91" is of course displayed by DIFFERENT changing cells as you scroll up and down, hopefully you completely understand this basic paradigm of how recycler views work.
Note that you don't have to bother with the insanity of notifications, subscriptions etc.
It's one of those things that is much, much easier once you make the jump to using normal frame based drawing (CADisplayLink).
When you're testing this you can put the animation anywhere at all, on a normal view, in a popup, in an alert, in cells, in tab bars, whatever. The "cell problems" become nonexistent.