Search

Prefetching of Cells on UITableView/UICollectionView

History
UITableView/UICollectionView 에서 Pagenation을 위해 보통 아래 방법을 썼다.
func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) { if indexPath.row == myItemList.count - 1 { fetchNextPage() } }
Swift
복사
보여지는 Cell을 기준으로 List 의 마지막 아이템을 부르기 전에 다음 리스트 호출...
공식?같이 쓰이던 방법이지만 네트워크 상태에 따라 버벅대기 일쑤였다. 아예 추가 로딩을 UI로 구분 지어 놓는 경우도 많았다.
무려 2년전 WWDC 2016 에서 발표된 What's New in UICollectionView in iOS 10 세션에서 처음 소개된 Prefetching Delegate 는 서비스가 iOS 9 부터 지원했어야 했기에 사용하지 않고 있었다.(핑계)
하지만 이번(이라고 쓰고 9개월 전 이라고 읽는다) WWDC 2018에서 What's New in Cocoa Touch 세션을 통해 iOS 12 부터 꽤나 좋아졌다고 하니 알아보고 도입해봤다.
What is
이 방법은 iOS 10 이상부터 사용할 수 있으며 UICollectionViewDataSourcePrefetchingUITableViewDataSourcePrefetching Protocol 로 정의되어 있다.
willDisplay cell 이 실제 보여지는 Cell을 기준으로 한다면 prefetchDataSource 는 이 작업을 백그라운드로 옮겨와 처리하는 것이다.
영상을 보면 실제 보여지는 Cell의 다음 배열을 호출 하는 것을 볼 수 있다.
스크롤을 중단할 경우 이전 Cell의 배열을 호출한다.
UICollectionViewUITableView 모두 같은 방식이기 때문에 UITableView 기준으로 설명하자면,
UITableViewDataSourcePrefetching 엔 크게 두가지 메소드가 있다.
func tableView(_: UITableView, prefetchRowsAt indexPaths: [IndexPath])
Swift
복사
[IndexPath] 에 해당하는 Cell 을 받아온다. 필수적으로 구현해야 하며 이 메소드 안에서 Cell 에 대한 데이터를 불러오면 된다.
func tableView(_ tableView: UITableView, cancelPrefetchingForRowsAt indexPaths: [IndexPath])
Swift
복사
Prefetch 가 필요없어질 경우엔 작업을 취소하는 로직을 구현할 수 있다. 보통 스크롤이 위로 올라갈 때 호출되는데 List 에 대한 데이터를 모두 들고 있지 않게 할 때 구현하면 된다. Optional 하므로 필요없다면 생략하면 된다.
Solution
기존에 willDisplay cell: 을 사용하고 있었다면 제거하고 구현한다.
구현 예는 아래와 같다.
extension YourTableViewController: UITableViewDataSourcePrefetching { public func tableView(_: UITableView, prefetchRowsAt indexPaths: [IndexPath]) { if indexPaths.contains(where: { $0.row >= yourListItems.count - 1 }) { fetchNextPage() } } }
Swift
복사
RxSwift 를 쓴다면
tableView.rx.prefetchRows .flatMap { Observable.from(optional: $0) } .subscribe { [weak self] event in guard let strongSelf = self, let elements = event.element else { return } let needFetch = elements.contains { $0.row >= yourListItems.count - 1 } if needFetch { strongSelf.fetchNextPage() } } .disposed(by: DisposeBag())
Swift
복사
배열을 염두해 두고 구현했다는 점에 주의하자.
iOS 12 의 개선사항
기존에는 cellForRowAt indexPath: 메소드와 동시에 실행되며 CPU에 부담을 주고 있었다.
Cell 을 그리는 작업과 함께 아직 사용하지 않을, 어쩌면 필요하지 않을 수도 있는 데이터를 함께 받아와 CPU에 부담을 주면 그만큼 프레임 저하를 일으킬 가능성이 높아진다.
하지만 iOS 12 를 사용하면 위와 같이 별도로 작업이 진행되며 CPU의 부담을 줄여준다.
Conclusion
iOS 9을 더이상 지원하고 있지 않다면 꼭 구현해주자.
iOS 9을 지원하더라도 버전 분기를 태워 구현할 수 있으니 핑계대지말고 꼭 구현해주자.
난 이미 했다.