VerticalCardSwiper icon indicating copy to clipboard operation
VerticalCardSwiper copied to clipboard

[Feature] Swipe without removing the item from the list

Open james-ff opened this issue 6 years ago • 4 comments

New Issue Checklist

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

james-ff avatar Mar 11 '20 19:03 james-ff

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.

alobanov11 avatar Jul 05 '20 17:07 alobanov11

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.

JoniVR avatar Aug 06 '20 20:08 JoniVR

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.

james-ff avatar Aug 06 '20 21:08 james-ff

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.

sevgjan avatar Aug 07 '20 07:08 sevgjan