1 minute read

여러 프로젝트를 진행하다보면 UITableView를 이용해 무한스크롤을 구현해야하는 경우가 많습니다.

저도 학교에서 프로젝트들을 진행하였는데 무한스크롤을 사용안하는 프로젝트를 찾기 어려울만큼 가장 보편적으로 사용되는 기능입니다. 이 기능을 구현하기 위해서 우리가 알아야할 것은 UITableView가 언제 스크롤이 바닥에 도달하는지가 쟁점입니다.

복잡할 것 같지만 의외로 간단하게 해결할 수 있습니다. RxSwift를 이용해 무한스크롤을 구현해봅시다

☝🏻UIScrollView+Rx

extension UIScrollView {
    var reachedBottom: Observable<Void> {
        return rx.contentOffset
            .flatMap { [weak self] contentOffset -> Observable<Void> in
                guard let scrollView = self else {
                    return Observable.empty()
                }
                
                let visibleHeight = scrollView.frame.height - scrollView.contentInset.top - scrollView.contentInset.bottom
                let y = contentOffset.y + scrollView.contentInset.top
                let threshold = max(0.0, scrollView.contentSize.height - visibleHeight)
                
                return y > threshold ? Observable.just(()) : Observable.empty()
            }
    }
}

먼저 UITableView에 UIScrollView가 내장되어있고 무한스크롤은 현재 스크롤 된 값을 알아야하기 때문에 UIScrollView를 Extension 해주었습니다. 그리고 reachedBottom 변수로 만약 스크롤 지점이 바닥이라면 Void로 이벤트를 넘겨주고 아니라면 empty()를 주었습니다.

✌🏻UITableView Event 전달

프로젝트는 RxSwift+MVVM으로 구성되어있기 때문에 아래의 코드는 VC에서 VM으로 바인딩을 해주는 코드입니다.

let input = MainViewModel.Input(getPosts: getData.asSignal(onErrorJustReturn: ()),
                                        loadDetail: mainTableView.rx.itemSelected.asSignal(),
                                        postScrap: selectScrap.asSignal(),
                                        getMorePosts: mainTableView.reachedBottom.asSignal(onErrorJustReturn: ()),
                                        getOtherProfile: selectProfile.asSignal())
let output = viewModel.transform(input)

getMorePosts를 보면

mainTableView.reachedBottom.asSignal(onErrorJustReturn: ())

여기서 UITableView가 스크롤의 바닥에 위치한다면 이벤트를 전달한다고 보여지고 있습니다.

👌🏻In ViewModel

input.getMorePosts.asObservable()
            .map { pagination += 1 }
            .flatMap { _ in api.getPosts(pagination)}
            .subscribe(onNext: { data, response in
                print(response)
                switch response {
                case .ok:
                    for i in data!.data {
                        getPostsData.add(element: i)
                    }
                default:
                    result.onNext("서버 오류")
                }
            }).disposed(by: disposeBag)

여기서 getMorePosts의 이벤트를 전달받았다면 불러올 현재 페이지를 +1 시키고 api를 요청합니다. 그렇게 받아온 값들을 getPostsData에 넣어주고 Output으로 보내주면은 pagination이 끝이 납니다..!


출처

https://github.com/RGSSoftware/UIScrollViewReachedBottom