RxSwift + UITableView 무한스크롤 구현하기
여러 프로젝트를 진행하다보면 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