UIScrollView+infinity


#1

Доброго времени суток товарищи))

Пытаюсь сделать бесконечный скроллинг на scrollView в обе стороны, использовать collectionView вот вообще намерений нет, да он тут и не поможет, скорее всего

В общем, скроллинг горизонтальный, на нем располагается 3 view, ширина каждой из которых равна ширине scrollView, так наглядней:

lazy var views = (0…2).map { UIView(frame: .init(
origin: .init(x: (scrollView.bounds.width) * CGFloat($0), y: 0),
size: .init(width: scrollView.bounds.width, height: scrollView.bounds.height))) }

эти view переиспользуются, т.е. у той что точно не появится и невинна сейчас меняется x координата

Для себя вижу два способа, это менять положение прямо внутри didScroll, после того как перешли за половину, у меня это получилось примерно так

func scrollViewDidScroll(_ scrollView: UIScrollView) {
        
        let xOffset = scrollView.contentOffset.x

        let direction: ScrollDirection = lastOffsetX > xOffset ? .left : .right
        lastOffsetX = xOffset

        let page = Int(xOffset / scrollView.bounds.width)

        let isHalf: Bool = {
            switch direction {
            case .left:     return abs(scrollView.bounds.width * CGFloat(page) - xOffset) < scrollView.bounds.width / 2
            case .right:    return xOffset - scrollView.bounds.width * CGFloat(page) > scrollView.bounds.width / 2
            }
        }()

        guard isHalf && page != latestPage || direction != latestDirection else { return }
        self.latestPage = page
        self.latestDirection = direction

        let nextPage = (page + 2) % 3

        let view = views[nextPage]

        view.frame.origin.x = scrollView.bounds.width * CGFloat(page + (direction == .left ? -1 : 2))
    }

Либо менять положение при окончании скроллинга

func scrollViewDidEndScrollingAnimation(_ scrollView: UIScrollView) {
        end(scrollView)
    }
    
    func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
        end(scrollView)
    }
    
    func end(_ scrollView: UIScrollView) {
        let page = Int(scrollView.contentOffset.x / scrollView.frame.width)
        
        guard
//            page != latestPage,
            let currentIndex = views.firstIndex(where: { $0.frame.origin.x == scrollView.contentOffset.x })
            else { return }
        
        
        
        latestPage = page
        
        let nextIndex = (currentIndex + 1) % views.count
        let previousIndex = (currentIndex + 2) % views.count
        
        let currentView = views[currentIndex]
        let nextView = views[nextIndex]
        let previousView = views[previousIndex]
        
        let width = scrollView.frame.width
        
        currentView.frame.origin = .init(x: width, y: 0)
        
        
        switch latestDirection {
        case .left:
            previousView.frame.origin = .init(x: width * 2, y: 0)
            nextView.frame.origin = .zero
            
            
            
        case .right:
            nextView.frame.origin = .init(x: width * 2, y: 0)
            previousView.frame.origin = .zero
        }
        
        scrollView.contentOffset = .init(x: width, y: 0)
    }

в первом варианте не очень понимаю как можно подменять contentOffset так, чтобы все красиво встало на свои места, это актуально для обратного скроллинга

Во втором варианте, при быстром скроллинге не можно словить ситуацию, когда когда анимация не закончилась и тогда, при дальнейшем проскролливание вперед все встает, надеюсь понятно определил проблему)

Если кто-то делал что-то похожее и может подсказать, поделиться, послать(только в нужном русле, без ругательств) в нужное место, буду очень признателен

И для второго метода необходимо использовать paging, отличии от первого


#2

В общем, если кому-то интересно, то отказался от использования UISrollView, так как вовремя попалась статья от яндекса https://medium.com/@esskeetit/как-работает-uiscrollview-2e7052032d97

В итоге использовал View c тремя View внутри + UIPanGestureRecognizer + механизмы scrollView из статьи, спасибо Илье Лобанову, со статьей советую всем ознакомиться))