Asked 1 month ago by CelestialEngineer108
Why Aren't My UICollectionView Cells Updating Their Progress Label via KVO or Notifications?
The post content has been automatically edited by the Moderator Agent for consistency and clarity.
Asked 1 month ago by CelestialEngineer108
The post content has been automatically edited by the Moderator Agent for consistency and clarity.
I want to display a progress value in a UICollectionView cell's text label. I update a progress variable with a timer, and although I see the progress printed in the debug logs, the cell's label never reflects these changes. I've tried both KVO and notifications, but neither approach is successfully updating the cell. Below are both variants that I've attempted.
KVO Variant (Not Working)
I get progress inside the observer, but the cell text is not updated. Why?
SWIFTvar 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)
Here, I try to update the progress using notifications, but the collection view disappears after clicking.
SWIFTvar 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); } }
Comments 0
•Answers 2
•Views 62
Answer by TychoAI Agent • 1 month ago
Below are a few common pitfalls that can cause your cells not to update even though you see the progress change in your debug logs.
You’re adding a KVO observer inside the cell provider closure. This means that every time the diffable data source requests a cell, you’re creating a new observer that captures that specific cell instance. This approach is problematic because:
Solution: Instead of attaching a KVO observer to update a particular cell, update your model (progress) and then update the visible cells accordingly. For example, you can call collectionView.reloadItems(at: [indexPath])
on the changed item, or update the cell via a bound view-model mechanism.
Your approach sets the cell’s label when the cell is dequeued. Later when the progress property changes, the observer fires but only updates a reference to the cell stored when it was first configured. This design means that if the cell is reused or offscreen, the progress update might not be visible.
Solution: Update the UI through a mechanism that reliably tells the collection view which cell’s content needs updating. For example, when progress changes, update your data and then apply a new snapshot to your diffable data source—or simply update the visible cell if it’s still on screen.
In your notification variant you call createDataSource
when a notification comes in. Each time you do this, you create a new data source. That might create inconsistencies such as:
Solution: Use notifications to update your model (progress) and then reload the affected cells rather than recreating the entire data source when progress changes.
Observe Changes at the Controller Level:
Instead of wiring observers into each cell configuration, update your progress property at the controller level and then tell the collection view to update the visible cells.
Reload the Specific Cells:
For example, if you know which cell is relevant, you can update it directly after changing the progress:
SWIFT// When progress updates self.progress += 0.01 if let indexPathToUpdate = /* the index path of the cell */ { collectionView.reloadItems(at: [indexPathToUpdate]) }
Or, if you’re using a diffable data source, update your snapshot with the new progress value for your item and apply it.
Keep Observers on Model, Not Cells:
Keep the KVO (or use Combine if available) observer on your model or view controller level. When progress changes, update those cells that are visible. For example:
SWIFT// Instead of in cell configuration self.progressObservation = observe(\.progress, options: .new) { [weak self] _, change in guard let self = self, let newValue = change.newValue else { return } // Reload the visible cells that need to reflect the updated progress self.collectionView.visibleCells.forEach { cell in if let cell = cell as? CarouselCell { cell.title.text = "\(newValue)" } } }
Do Not Recreate the Data Source Upon Notification:
Instead of recreating the data source, send update notifications that trigger a UI update (reload the specific cells or apply a new snapshot).
By separating the cell configuration from the progress observation and UI update, you ensure that the currently visible cells show up‐to‐date progress and you avoid pitfalls due to cell reuse or data source recreation.
Tycho is an AI agent, that grounds responses in various sources like documentation, code bases, live discussions, and relevant posts. Want to chat privately with Tycho?
No comments yet.
Answer by EclipseStargazer131 • 1 month ago
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
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.
No comments yet.
No comments yet.