UICollectionView로 세로 스크롤 구현하기 (쇼츠, 릴스)

soy·2025년 3월 2일
1

iOS-UIKit

목록 보기
22/22
post-thumbnail

UICollectionView를 사용한 페이징 구현

세로 스크롤(무한 스크롤이라고 칭하겠음)을 구현해야했다!

처음 구현해보는 것이라 방법을 찾아보았는데, UICollectionView 사용하는 방법과 UIPageViewController 사용하는 방법이 있었다.

UICollectionView가 조금은 더 익숙해서 UICollectionView를 사용해서 구현해보고 추후에 UIPageViewController를 적용해봐야겠다고 생각했다.


UICollectionView vs UIPageViewController

1. 구현 접근 방식

UICollectionView:

  • 일반적인 컬렉션 뷰에 isPagingEnabled = true 속성만 추가하면 페이징 기능 구현 가능
  • 개발자가 현재 페이지를 수동으로 계산해야 함
  • 데이터 소스 패턴이 단순하고 익숙함

UIPageViewController:

  • 페이징을 위해 특별히 설계된 컨테이너 뷰 컨트롤러
  • 각 페이지가 별도의 뷰 컨트롤러로 구현됨
  • DataSource를 통해 이전/다음 뷰 컨트롤러를 제공하는 방식으로 동작

2. 성능과 메모리 관리

UICollectionView:

  • 셀 재사용 메커니즘으로 메모리 효율적 관리
  • 많은 수의 페이지도 효율적으로 처리 가능
  • 데이터만 로드하므로 가벼움

UIPageViewController:

  • 전체 뷰 컨트롤러를 로드하므로 리소스 사용량이 더 많을 수 있음
  • 기본적으로 인접한 페이지만 미리 로드함
  • 복잡한 화면일수록 메모리 사용량 차이가 커짐

3. 유연성

UICollectionView:

  • 레이아웃 커스터마이징이 매우 자유로움
  • 페이지 크기, 간격, 스크롤 동작 등 세밀한 조정 가능
  • 수직/수평 페이징 쉽게 전환 가능
  • 페이지 내부에 스크롤뷰를 포함시키는 중첩 스크롤 구현이 더 간단함

UIPageViewController:

  • 페이지 전환 애니메이션이 기본 제공됨(스크롤, 컬, 페이드 등)
  • 페이지 컨트롤(닷 인디케이터)과의 통합이 간편함
  • 각 페이지가 독립적인 뷰 컨트롤러라 생명주기 관리가 명확함

4. 사용 사례

UICollectionView 페이징이 적합한 경우:

  • 동일한 레이아웃의 페이지가 많을 때
  • 데이터 기반 페이지
  • 페이지 내에 복잡한 스크롤 동작이 필요할 때

UIPageViewController가 적합한 경우:

  • 각 페이지의 내용과 레이아웃이 완전히 다를 때
  • 온보딩 화면처럼 페이지 수가 제한적일 때
  • 페이지 전환 애니메이션을 세밀하게 제어해야 할 때
  • 스와이프 제스처 외에 다른 방식의 페이지 전환이 필요할 때

코드

collectionview 설정 코드

private var itemDetailCollectionView: UICollectionView = {
    let layout = UICollectionViewFlowLayout()
    
    layout.scrollDirection = .vertical
    layout.minimumLineSpacing = 0
    
    let collectionView = UICollectionView(frame: .zero, collectionViewLayout: layout)
    
    collectionView.backgroundColor = .clear
    collectionView.showsVerticalScrollIndicator = false
    
    collectionView.isPagingEnabled = true  // 페이징 기능 활성화
    
    return collectionView
}()
  • scrollDirection = .vertical: 세로 방향 페이징 설정
  • isPagingEnabled = true: UIKit의 페이징 동작 활성화
  • minimumLineSpacing = 0: 페이지 간 여백 제거

화면 크기 감지 코드

override func viewDidLayoutSubviews() {
	super.viewDidLayoutSubviews()
	if let layout = itemDetailCollectionView.collectionViewLayout as? UICollectionViewFlowLayout {
		layout.itemSize = itemDetailCollectionView.bounds.size
	}
}

cell의 크기 != 화면 크기 였기 때문에,
viewDidLayoutSubviews에서 view의 size를 측정하지 않으면 올바르게 페이징이 되지 않는 이슈가 있었다.

나의 경우에는 cell의 크기를 화면 크기에 맞춘 것이 아니라, cell 안에도 따로 scrollView가 내장되어 있었기 때문에 해당 메서드를 사용했다.


페이징 감지 코드

itemDetailCollectionView.rx.didEndDecelerating
    .subscribe(onNext: { [weak self] in
        guard let self = self else { return }
        
        let visibleIndexPaths = self.itemDetailCollectionView.indexPathsForVisibleItems
        let sortedIndexPaths = visibleIndexPaths.sorted(by: { $0.item < $1.item })
        
        if let firstIndexPath = sortedIndexPaths.first {
            self.currentIndex = firstIndexPath.item
            self.viewModel.updateCurrentIndex(firstIndexPath.item)
            self.saveRecentItem()
        }
    })
    .disposed(by: disposeBag)

작동 방식:
1. 스크롤이 멈추면(didEndDecelerating) 현재 화면에 보이는 셀들의 IndexPath를 가져옴
2. IndexPath들을 정렬하여 가장 위에 있는 셀을 현재 페이지로 결정
3. 이에 따라 상태 업데이트


결과

스크롤이 잘 작동한다!

profile
soysoisoyysauce~

0개의 댓글