[Feature] Swipe without removing the item from the list
james-ff opened this issue · comments
New Issue Checklist
- I read the documentation.
- I searched for existing GitHub issues.
Is your feature request related to a problem? Please describe.
If I do not remove the swiped item from the datasource an error occurs
[UICollectionView] Invalid update: invalid number of items in section 0. The number of items contained in an existing section after the update (5) must be equal to the number of items contained in that section before the update (5), plus or minus the number of items inserted or deleted from that section (0 inserted, 1 deleted) and plus or minus the number of items moved into or out of that section (0 moved in, 0 moved out). - will perform reloadData. UICollectionView instance: <VerticalCardSwiper.VerticalCardSwiperView: 0x7fd828086000; baseClass = UICollectionView; frame = (0 0; 414 673); clipsToBounds = YES; userInteractionEnabled = NO; gestureRecognizers = <NSArray: 0x600001be2c40>; layer = <CALayer: 0x600001590c20>; contentOffset: {-20, -40}; contentSize: {374, 2875}; adjustedContentInset: {40, 20, 90, 20}; layout: <VerticalCardSwiper.VerticalCardSwiperFlowLayout: 0x7fd82757c8d0>; dataSource: <VerticalCardSwiper.VerticalCardSwiper: 0x7fd82757c0c0; frame = (0 0; 414 673); autoresize = RM+BM; layer = <CALayer: 0x6000015904c0>>>; currentUpdate: [UICollectionViewUpdate - 0x7fd82bd21980: old:<UICollectionViewData: 0x600002d89180> new<UICollectionViewData: 0x600002d07800> items:<(
"D(0,0)"
)>]
Describe the solution you'd like
I would like to configure the cardSwiper view to not remove an item when it is swiped (instead trigger an action), and later on when that action finishes I can remove the item programmatically.
Describe alternatives you've considered
I've considered re-adding the removed item immediately, but It feels like this would be a hack solution, which might cause strange animations
Additional context
none
Hi @james-ff, I had the same error. It was a stupid mistake:
private var films = [Film]() {
didSet { self.cardSwiper.reloadData() }
}
...
func willSwipeCardAway(card: CardCell, index: Int, swipeDirection: SwipeDirection) {
guard index < self.films.count else { return }
self.films.remove(at: index)
}
I just removed didSet and it worked.
Do you want the card to swipe away without having to update the datasource to reflect this? Or just block the card from swiping away?
To me personally it seems weird to want your datasource out of sync with the actual displayed data, I think this is why Apple does this too? I'm just forwarding the UICollectionView
for the most part anyways.
Blocking the card from swiping away matches my intent here.
- The idea being that swiping one direction could remove an item, and swiping the other would bring up a confirmation view.
- The card would then slide back into position while a confirmation view appears and stay there if cancelled
- In the case of confirmation the card could then be removed programmatically
Hope that clarifies it. In the meantime I add the item back in if the confirmation is cancelled.
Hi @james-ff ,
I was able to achieve this by modifying internal func didSwipeAway
method in VerticalCardSwiper.swift class, from:
internal func didSwipeAway(cell: CardCell, swipeDirection direction: SwipeDirection) {
if let indexPathToRemove = self.verticalCardSwiperView.indexPath(for: cell) {
swipedCard = nil
self.verticalCardSwiperView.performBatchUpdates({
self.verticalCardSwiperView.deleteItems(at: [indexPathToRemove])
}, completion: { [weak self] _ in
self?.verticalCardSwiperView.collectionViewLayout.invalidateLayout()
self?.verticalCardSwiperView.isUserInteractionEnabled = true
self?.delegate?.didSwipeCardAway?(card: cell, index: indexPathToRemove.row, swipeDirection: direction)
})
}
}
to:
internal func didSwipeAway(cell: CardCell, swipeDirection direction: SwipeDirection) {
if let indexPathToRemove = self.verticalCardSwiperView.indexPath(for: cell) {
swipedCard = nil
self.verticalCardSwiperView.performBatchUpdates({
if direction == .left {
self.verticalCardSwiperView.deleteItems(at: [indexPathToRemove])
}
}, completion: { [weak self] _ in
self?.verticalCardSwiperView.collectionViewLayout.invalidateLayout()
self?.verticalCardSwiperView.isUserInteractionEnabled = true
self?.delegate?.didSwipeCardAway?(card: cell, index: indexPathToRemove.row, swipeDirection: direction)
})
}
}
Hope this helps.