InfiniteLayout icon indicating copy to clipboard operation
InfiniteLayout copied to clipboard

Infinitely looping collection view

Open nugmanoff opened this issue 5 years ago • 4 comments

Hello!

First of all wanted to thank you for such an amazing library! :)

I am trying to implement infinitely scrolling collection view with timer, so that it periodically slides to next indexPath.

Currently what I am doing is, I have function that looks like this, and I just call it every like 5 seconds.

func scrollToNextPage() {
     guard let currentPage = collectionView.centeredIndexPath?.row else { return }
     collectionView.scrollToItem(at: IndexPath(item: currentPage + 1, section: 0), at: .centeredHorizontally, animated: true)

But after some time, it throws an error:

'NSInvalidArgumentException', reason: 'attempt to scroll to invalid index path: <NSIndexPath: 0x282bb0980> {length = 2, path = 0 - 162}'

I have also trying to doing like this instead:

collectionView.scrollToItem(at: collectionView.indexPath(from: IndexPath(item: currentPage + 1, section: 0)), at: .centeredHorizontally, animated: true)

But the problem with this one is that when it gets to the last (supposedly last in the raw data source) item, when it scrolls to the first one it does it with animation of scrolling all the items back to the first one, but desired behavior is that it scrolls from the last item to the first with like "swiping right" animation, just ordinary scrolling to next item.

BR

nugmanoff avatar Aug 19 '19 11:08 nugmanoff

Hello! Did you find solution?

voody2506 avatar Sep 12 '19 21:09 voody2506

Hi! You can try something like:

func scrollToNextPage() {
     guard var currentPage = collectionView.centeredIndexPath?.row else { return }
     if currentPage > collectionView.numberOfItems(inSection: 0) - 2 {
     	currentPage = 0
     } else {
     	currentPage = currentPage + 1
     }
     collectionView.scrollToItem(at: IndexPath(item: currentPage, section: 0), at: .centeredHorizontally, animated: true)
}

greenappleball avatar Oct 24 '19 13:10 greenappleball

Basically for this solution you need two things.

Observable.combineLatest(
            viewModel.dataSource.asObservable().filter { !$0.isEmpty },
            rx.viewDidAppear
        ).bind(onNext: { [weak self] _ in
            self?.collectionView.beginShiftCounting()
        })

and then inherit from RxInfiniteCollectionView here is and example. 100% works fine

final class InfiniteCV: RxInfiniteCollectionView {
    var autoScrollConfig: AutoScrollConfig?

    override func scrollViewDidScroll(_ scrollView: UIScrollView) {
        super.scrollViewDidScroll(scrollView)

        resetCounter()
    }

    func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) {
        if !decelerate {
            beginShiftCounting()
        }
    }

    func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
        beginShiftCounting()
    }

    var fakeCounter: EFCounter?

    func resetCounter() {
        guard fakeCounter != nil else { return }

        fakeCounter?.stopCountAtCurrentValue()
        fakeCounter?.reset()
        fakeCounter?.invalidate()
        fakeCounter = nil
    }

    func beginShiftCounting() {
        guard let config = autoScrollConfig, config.direction != .none else { return }
        resetCounter()
        fakeCounter = EFCounter()
        fakeCounter?.updateBlock = { [weak self] _ in
            if let x = self?.contentOffset.x {
                self?.delegate = nil // we must remove delegate to avoid delegate methods calling while we set content offset
                let delta: CGFloat = (config.direction == .left ? 1 : -1) * CGFloat(config.speed)
                self?.contentOffset = CGPoint(x: x + delta, y: 0)
                self?.delegate = self // put delegate back
            }
        }
        fakeCounter?.countFromZeroTo(99)
        fakeCounter?.completionBlock = { [weak self] in
            self?.resetCounter()
            self?.beginShiftCounting()
        }
    }
}

EFCounter is CADisplayLink sugar

astrokin avatar Oct 07 '21 21:10 astrokin

@voody2506 @nugmanoff @greenappleball please find my comment above

astrokin avatar Oct 07 '21 21:10 astrokin