swift

Custom ScrollPaging

motosw3600 2022. 6. 5. 14:19

Custom ScrollPaging

  • UICollectionViewFlowLayout의 targetContentOffset을 overriding 하여 paging 설정

targetContentOffset(forProposedContentOffset:, withScrollVelocity:)

  • 파라미터인 forProposedContentOffset, withScrollingVelocity 사용
  • forProposedContentOffset: 스크롤하고 난 뒤의 적절한 offset을 사용
  • withScrollingVelocity: Scroll에 따른 velocity(왼쪽 스크롤 -, 오른쪽 스크롤 +)

CollectionView Paging예시

class SnappingCollectionViewFlowLayout: UICollectionViewFlowLayout {
    override func targetContentOffset(forProposedContentOffset proposedContentOffset: CGPoint,
                                      withScrollingVelocity velocity: CGPoint) -> CGPoint {
        guard let collectionView = self.collectionView else {
            return super.targetContentOffset(forProposedContentOffset: proposedContentOffset,
                                             withScrollingVelocity: velocity)
        }
        let itemWidth = itemSize.width + minimumInteritemSpacing
        
        let contentOffset = collectionView.contentOffset.x - collectionView.adjustedContentInset.left
        let currentOffset = proposedContentOffset.x - collectionView.adjustedContentInset.left
        let maxPage = Int(ceil((collectionView.contentSize.width
                                - collectionView.adjustedContentInset.left
                                - collectionView.adjustedContentInset.right) / itemWidth))
        var page = 0
        let contentPage = Int(round(contentOffset / itemWidth))
        let proposedPage = Int(round(currentOffset / itemWidth))
        if abs(velocity.x) < 2 {
            if abs(velocity.x) < 0.2 {
                page = contentPage
            } else if velocity.x < 0 {
                page = Int(ceil(contentOffset / itemWidth)) > 0 ? Int(ceil(contentOffset / itemWidth)) - 1 : 0
            } else {
                page = contentPage < maxPage ? contentPage + 1 : contentPage
            }
        } else {
            page = proposedPage
        }
        var offsetX = CGFloat(page) * itemWidth - collectionView.adjustedContentInset.left
        let minOffsetX = -collectionView.adjustedContentInset.left
        let maxOffsetX = CGFloat(maxPage) * itemWidth - collectionView.adjustedContentInset.left
        offsetX = max(min(offsetX, maxOffsetX), minOffsetX)
        return CGPoint(x: offsetX, y: proposedContentOffset.y)
    }
}
  • itemWidth: cell의 Size + cell의 spacing이 있을 시 고려(minimumInteritemSpacing)
  • contentOffset: collectionView의 스크롤한 현재 Offset, cell의 왼쪽 마진이 있을시 고려(adjustedContentInset.left)
  • currentOffset: proposedContentOffset을 사용한 스크롤뒤 예상되는 Offset
  • maxPage: 최대 ScrollPage 제한((contentSize - Inset) / cellWidth))
  • contentPage: velocity가 2이하일 경우 paging을 위해 사용
  • proposedPage: proposedContentOffset을 사용해 자연스러운 페이징을 위해 사용
  • offsetX, minOffsetX, maxOffsetX: 스크롤의 최대 범위 지정(paging error방지)
Why Use ContentPage?
velocity가 2 이하일 경우 proposedContentOffset을사 용해 paging을 할 경우 offset의 편차가 적어 페이징이 안되고 툭툭 끊기는 현상 발생

 

실행 화면

 

구현 프로젝트: https://github.com/motosw3600/CustomScrollPaging

'swift' 카테고리의 다른 글

Biometric Authentication  (0) 2022.08.31
DropDown 오픈소스 라이브러리 만들기  (0) 2022.08.10
RxDataSources  (0) 2022.05.07
RxSwift UnitTest 해보기(RxTest, RxNimble)  (0) 2022.03.10
Tuist로 프로젝트 관리해보기  (0) 2022.03.05