I'm trying to build something like a standard Camera Roll grid with like/dislike functionality and faced with an issue of updating only one cell. Is there any way of doing this?
@kronik sure thing! There are actually 2 ways, you can pick whichever makes the most sense. If this answers your question feel free to close the issue 馃檶
Since it sounds like there is only one cell in each section (e.g. one cell per object), you can probably get away w/ reloading the whole section. Something like this:
var selected = false
func cellForItem(at index: Int) -> UICollectionViewCell {
let cell = collectionContext!.dequeueReusableCell(of: MyCell.self, for: self, at: index) as! MyCell
cell.showsSelectedIcon = selected
return cell
}
func didSelectItem(at index: Int) {
selected = !selected
collectionContext?.reload(self)
}
You can do basically the same thing above but for a single cell.
collectionContext?.reload(in: self, at: IndexSet(integer: 0))
If your section controller only has a single cell both of these methods have exactly the same effect.
If you want to control the animations, or batch other changes with the reload, you can do the following:
let context = collectionContext?
context?.performBatch(animated: false, updates: {
context?.reload(self)
})
(there's also a completion block param)
@rnystrom Thank you!
Hey @rnystrom !
Is there an equivalent for just reloading a supplementary view?
Thanks in advance!
Nikita
@nikitaame reloading supplementary views is traditionally kind of tricky. You might be able to use the layout and call invalidateSupplementaryElementsOfKind:atIndexPaths:.
Actually if that works, let me know. I wouldn't mind adding this as an API that section controllers can call.
@ryanolsonk Thanks, I couldn't seem to get the index path of the required supplementary view in a great way so went about it another way.
I kept a height property on the supplementary view which I then changed in the scrollview delegate methods as needed. I then called invalidateLayout on the collection view layout. When returning the size for the supplementary view in the IGListSupplementaryViewSource I used the height property when returning the size.
Hi @rnystrom
Really sorry to bring this topic up again, but I can't seem to call collectionContext?.reload(self) at all anymore. I think it might've been changed recently or is this still the go to function to reload a section?
I just want to update a section just like you showed here in the section controller:
var selected = false
func cellForItem(at index: Int) -> UICollectionViewCell {
let cell = collectionContext!.dequeueReusableCell(of: MyCell.self, for: self, at: index) as! MyCell
cell.showsSelectedIcon = selected
return cell
}
func didSelectItem(at index: Int) {
selected = !selected
collectionContext?.reload(self)
}
@MrMuetze sorry for the churn! We changed this in the 3.0 release, check out this part of the migration guide.
All you need to do is wrap your reload in a batch update, and call reload on the batch context param:
var selected = false
func cellForItem(at index: Int) -> UICollectionViewCell {
let cell = collectionContext!.dequeueReusableCell(of: MyCell.self, for: self, at: index) as! MyCell
cell.showsSelectedIcon = selected
return cell
}
func didSelectItem(at index: Int) {
collectionContext?.performBatch(animated: true, updates: { (batchContext) in
self.selected = !self.selected
batchContext.reload(self)
})
}
@rnystrom thank you very much for that quick reply! That did the trick. I think it's awesome that you keep coming back to these older issues and reply to our questions :] Thanks again!
Hi.
Is it possible for IGListKit to identify changed item contents during adapter.performUpdates() call and update those cells itself, so that I don't need to call batchContext.reload() myself?
@kornerr yup! You need to use IGListBindingSectionController. Check out this guide.
Actually, I got it working by calling adapter.performUpdates upon items' array change (with the help of RxSwift).
Specifically, those Diffable items that are not equal get updated.
I wonder if that's a valid approach.
@kornerr Interesting idea. I'd be curious to see what your approach (using RxSwift) looks like if you'd be willing to share.
@JUSTINMKAUFMAN I cannot share the exact sample with IGListKit, however, I can share the idea implemented in a different sample.
The main concept is 'separation of concerns' by implementing so-called 'coordinator' pattern.
The inserts new row upon each new data item can be seen as performUpdates call.
I hope it's clearer now.
@MrMuetze sorry for the churn! We changed this in the 3.0 release, check out this part of the migration guide.
All you need to do is wrap your reload in a batch update, and call reload on the batch context param:
var selected = false func cellForItem(at index: Int) -> UICollectionViewCell { let cell = collectionContext!.dequeueReusableCell(of: MyCell.self, for: self, at: index) as! MyCell cell.showsSelectedIcon = selected return cell } func didSelectItem(at index: Int) { collectionContext?.performBatch(animated: true, updates: { (batchContext) in self.selected = !self.selected batchContext.reload(self) }) }@rnystrom
How about un-selecting every other cell in this case?
I have mutable models, so I change the value which triggers didUpdate, but the cell does not get updated.
Hi,
I am using
collectionContext?.performBatch(animated: true, updates: { (batchContext) in
self.selected = !self.selected
batchContext.reload(self)
})
to update specific cell, but I saw they call
_queueUpdateWithCollectionView
and all section update => call main thread to much and make app lagging
Most helpful comment
@MrMuetze sorry for the churn! We changed this in the 3.0 release, check out this part of the migration guide.
All you need to do is wrap your reload in a batch update, and call reload on the batch context param: