본문 바로가기

프로그래밍/iOS,macOS

collection view 에서 load more 처리

목록을 더 읽어와 하단에 추가하는 경우엔 스크롤 관련 이벤트에 따라 그냥 읽어서 붙이면 간단한데..
채팅이나 메신저와 같이 이전 목록을 불러와 기족 목록 상단에 추가하는 경우 스크롤 관련 설정이 조금 더 필요. 

 

스크롤 위치에 따라 더 읽기 작업 시작

연속으로 호출되면 안되므로, 플래그를 두어서 한번만 호출되도록 처리

func scrollViewdidScroll(_ scrollView: UIScrollView) {
    guard scrollView.contentSize.height > scrollView.bounds.height else { return }
    
    if scrollView.contentOffset.y <= 0 {
        if isRunningLoadMore {
            return
        }
        isRunningLoadMore = true
        viewModel.loadMore()
    }
}

 

뷰모델에서 추가 데이터를 읽어와 dataSource 를 업데이트

뷰컨트롤러에 reload 이벤트 전달

enum ReloadType {
    case .normal
    case .loadMore
}

var reloadAction: PublishRelay<ReloadType> = .init()

    .
    .
    .
    
    
func loadMore() {
    // 데이터 로딩 루틴
       .
       .
    insert(data: data)
}

func insert(data: [MyData]) {
    guard !data.isEmpty else { return }
    
    dataSource = data + dataSource
    reloadAction.accept(.loadMore)
}

 

뷰컨트롤러에서 reload 처리

스크롤이 튀는 것을 막기위해 현재 offset 을 저장하고, 강제로 layoutSubviews를 호출해 사이즈가 변경되도록 한다.

viewModel.reloadAction
    .subscribe(onNext: { [weak self] type in
        guard let self = self else { return }
        switch type {
        case .normal:
            UIView.performWithoutAnimation {
                self.collectionView.reloadData()
            }
            
        case .loadMore:
            let currentOffsetY = self.collectionView.contentSize.height - self.collectionView.contentOffset.y
            UIView.performWithoutAnimation {
                self.collectionView.reloadData()
            }
            
            self.collectionView.layoutIfNeeded()
            let offsetY = self.collectionView.contentSize.height - currentOffsetY
            self.collectionView.contentOffset.y = offsetY
            self.isRunningLoadMore = false
        }
    })
    .disposed(by: disposeBag)