Центрирование подкачки на вертикальной UICollectionView с пользовательской высотой ячейки - PullRequest
0 голосов
/ 01 мая 2020

Я видел много постов, где это решается по горизонтали, однако у меня возникают проблемы с реализацией решения для вертикального представления коллекции.

Ячейки моего представления коллекции заполняют всю ширину, но не всю высоту представления коллекции, поэтому обычная подкачка не работает. Я пытаюсь привязать центр ячеек к центру экрана при прокрутке с помощью пользовательского UICollectionViewFlowLayout. (Аналогично каналу в Instagram, но без «бесплатной» прокрутки, а сообщения располагаются по центру по вертикали)

class FeedLayout: UICollectionViewFlowLayout {

    private var previousOffset: CGFloat = 0
    private var currentPage: Int = 0

    override func targetContentOffset(forProposedContentOffset proposedContentOffset: CGPoint, withScrollingVelocity velocity: CGPoint) -> CGPoint {
        guard let collectionView = collectionView else {
            return super.targetContentOffset(forProposedContentOffset: proposedContentOffset, withScrollingVelocity: velocity)
        }

        let itemsCount = collectionView.numberOfItems(inSection: 0)
        if previousOffset > collectionView.contentOffset.y {
            currentPage = max(currentPage - 1, 0)
        } else if previousOffset < collectionView.contentOffset.y {
            currentPage = min(currentPage + 1, itemsCount - 1)
        }

        let updatedOffset = ((collectionView.frame.height * 0.75) + minimumLineSpacing) * CGFloat(currentPage)
        previousOffset = updatedOffset
        return CGPoint(x: proposedContentOffset.x, y: updatedOffset)
    }

}

1 Ответ

0 голосов
/ 01 мая 2020

Я написал расширение с открытым исходным кодом, которое делает это несколько дней. go.

Я бы настроил его так, чтобы центрировать ячейку (вместо прокрутки ячейка за ячейкой). с новыми ячейками сверху) с этими изменениями:

public extension UICollectionViewFlowLayout {
    override func targetContentOffset(forProposedContentOffset proposedContentOffset: CGPoint, withScrollingVelocity velocity: CGPoint) -> CGPoint {

        guard let collectionView = self.collectionView else {
            let latestOffset = super.targetContentOffset(forProposedContentOffset: proposedContentOffset, withScrollingVelocity: velocity)
            return latestOffset
        }

        // page height used for estimating and calculating paging
        let pageHeight = self.itemSize.height + self.minimumLineSpacing

        // determine total pages
        // collectionView adds an extra self.minimumLineSpacing to the total contentSize.height so this must be removed to get an even division of pages
        let totalPages = (collectionView.contentSize.height - self.minimumLineSpacing) / pageHeight

        // determine current page index
        let visibleRect = CGRect(origin: collectionView.contentOffset, size: collectionView.bounds.size)
        let visiblePoint = self.itemSize.height * 2 > collectionView.visibleSize.height ? CGPoint(x: visibleRect.midX, y: visibleRect.midY) : CGPoint(x: visibleRect.midX, y: visibleRect.midY - (self.itemSize.height / 3))
        let visibleIndexPath = collectionView.indexPathForItem(at: visiblePoint)?.row ?? 0
        let currentIndex = CGFloat(visibleIndexPath)

        // make an estimation of the current page position
        let approximatePage = collectionView.contentOffset.y / pageHeight

        // determine the current page based on velocity
        let currentPage = velocity.y == 0 ? round(approximatePage) : (velocity.y < 0.0 ? floor(approximatePage) : ceil(approximatePage))

        // create custom flickVelocity
        let flickVelocity = velocity.y * 0.5

        // check how many pages the user flicked, if <= 1 then flickedPages should return 0
        let flickedPages = (abs(round(flickVelocity)) <= 1) ? 0 : round(flickVelocity)

        // determine the new vertical offset
        // scroll to top of next/previos cell
//        let newVerticalOffset = ((currentPage + flickedPages) * pageHeight) - collectionView.contentInset.top
        // scroll to center of next/previous cell
        let newVerticalOffset = ((currentPage + flickedPages) * pageHeight) + ((collectionView.visibleSize.height - pageHeight) / 2) - collectionView.contentInset.top

        // determine up or down swipe
        let swipeDirection: CGFloat = flickVelocity > 0 ? 1 : -1

        // determine if we are at the end of beginning of list
        let beyond = newVerticalOffset + pageHeight >= collectionView.contentSize.height || collectionView.contentOffset.y < 0 ? true : false

        // determine if the flick was too small to switch pages
        let stay = abs(newVerticalOffset - collectionView.contentOffset.y) < (self.itemSize.height * 0.4) ? true : false

        // determine if there are multiple pages available to swipe based on current page
        var multipleAvailable = false
        if flickVelocity > 0 {
            multipleAvailable = currentIndex + swipeDirection < totalPages - 1 ? true : false
        } else {
            multipleAvailable = currentIndex + swipeDirection > 0 ? true : false
        }

        // give haptic feedback based on how many cells are scrolled
        if beyond == false && stay == false {
            if abs(flickedPages) > 1 && multipleAvailable {
                TapticGenerator.notification(.success)
            } else {
                TapticGenerator.impact(.medium)
            }
        }

        return CGPoint(x: proposedContentOffset.x, y: newVerticalOffset - collectionView.safeAreaInsets.top)
    }
}
...