Notice
Recent Posts
Recent Comments
Link
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
Tags
- 리펙터링
- rxswift
- uitableview
- SWIFT
- swift documentation
- 클린 코드
- tableView
- 리펙토링
- UICollectionView
- clean architecture
- ribs
- RxCocoa
- Xcode
- Human interface guide
- 애니메이션
- combine
- UITextView
- uiscrollview
- HIG
- swiftUI
- Observable
- MVVM
- 리팩토링
- ios
- Protocol
- 스위프트
- collectionview
- map
- Refactoring
- Clean Code
Archives
- Today
- Total
김종권의 iOS 앱 개발 알아가기
[iOS - swift] 1. 이미지 캐싱, 이미지 효율적으로 로드 방법, 스크롤에 따라 이미지 로드(tableView, collectionView): scrollViewDidScroll, prefetch, pagination (페이지네이션) 본문
iOS 응용 (swift)
[iOS - swift] 1. 이미지 캐싱, 이미지 효율적으로 로드 방법, 스크롤에 따라 이미지 로드(tableView, collectionView): scrollViewDidScroll, prefetch, pagination (페이지네이션)
jake-kim 2021. 10. 2. 23:141. 이미지 캐싱, 이미지 효율적으로 로드 방법, 스크롤에 따라 이미지 로드(tableView, collectionView): scrollViewDidScroll, prefetch
2. 이미지 캐싱, 이미지 효율적으로 로드 방법(tableView, collectionView): async + NSCache
아이디어
- 스크롤이 느려지고, 앱이 꺼지고 이미지들이 사용자에게 보여질 때 느려질 수 있으므로 캐시를 사용
- 비동기적으로 이미지를 저장하고 cache를 사용
어떻게?
- request(page:1) API 호출 > 10개의 이미지 url 획득
- 10개의 이미지 url만 cell의 model에 먼저 저장 (image는 따로 호출하여 반영)
- image는 최초 10개만 일단 로드하고, 나머지는 아래에서 나오는 내용인 스크롤에 따라 호출하는 로직 사용
이미지 캐싱 or preFetch
- UITableView에서 서버에서 많은 리스트 데이터를 받아올 때 한꺼번에 받아올 경우 device와 서버에 부담이 생기므로 paging별로 쪼개어서 받아오는 테크닉 2가지 방법 존재 > 2가지 모두 사용하여 구현
- 1) scrollViewDidScroll(_:)
- 2) preFetchRow
1) scrollViewDidScroll(_:)
- UITableView가 상속하고 있는 UIScrollView의 delegate인 scrollViewDidScroll(_:) 사용
- 스크롤을 아래로 내릴 때 더 이상 내려갈 곳이 없는 경우, tableView에 addData
- Scroll을 내릴 때 약간의 딜레이가 있는 것처럼 느껴지는 단점 존재
* 실제로는 offset값과 inset값도 고려할것
// 참고
extension ViewController: UICollectionViewDelegate {
func scrollViewDidScroll(_ scrollView: UIScrollView) {
// contentOffset: 스크롤한 길이
// contentInset: collectionView의 테두리 부분과의 여백 (4곳만 존재)
// contentSize: 스크롤 가능한 콘텐츠 사이즈 (주의 - contentInset 값을 합해야, collectionView 전체 콘텐트 사이즈)
let offset = scrollView.contentOffset.x + scrollView.contentInset.left
let width = scrollView.contentSize.width + scrollView.contentInset.left + scrollView.contentInset.right
let scrollRatio = offset / width
}
}
- 코드: TableView는 UIScrollView의 서브클래스이므로 scrollViewDidScroll를 구현
class ViewController: UIViewController {
...
private func execute() {
currentPage += 1
let photoListRequestDTO = PhotoListRequestDTO(page: currentPage)
let endpoint = APIEndpoints.getPhotosInfo(with: photoListRequestDTO)
provider?.request(with: endpoint, completion: { [weak self] result in
switch result {
case .success(let responseDTO):
print(responseDTO)
self?.appendPhotos(with: responseDTO)
case .failure(let error):
print(error)
}
})
}
}
extension ViewController {
func scrollViewDidScroll(_ scrollView: UIScrollView) {
let contentHeight = scrollView.contentSize.height
let yOffset = scrollView.contentOffset.y
let heightRemainBottomHeight = contentHeight - yOffset
let frameHeight = scrollView.frame.size.height
if heightRemainBottomHeight < frameHeight {
execute()
}
}
}
- 주의: 단, scrollViewDidScroll에서 여러번 호출될수 있으므로, API를 호출하기 전에 flag값을 놓고, api가 호출이 끝난 경우만 동작하도록 설정 필요
enum ViewState {
case idle
case isLoading
}
var viewState = ViewState.idle
func execute() {
guard viewState == .idle else { return }
viewState = isLoading
API.request(...) { result in
self.viewState = idle
}
}
2) prefetchRow
- 아이디어: indexPaths에 해당되는 셀에 필요한 데이터를 미리 받아오는 메소드 (아직 보이지 않는 셀들을 미리 시스템에서 구분하여 델리게이트 동작)
- iOS 10+에서 가능한 기능
- prefetchDataSource = self로 위임, prefetchRowsAt 구현
class ViewController: UIViewController {
@IBOutlet weak var tableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
tableView.prefetchDataSource = self
}
}
// MARK: - UITableViewDataSourcePrefetching
extension ViewController: UITableViewDataSourcePrefetching {
func tableView(_ tableView: UITableView, prefetchRowsAt indexPaths: [IndexPath]) {
indexPaths.forEach { viewModel.prefetchImage(at: $0) }
}
}
- collectionView에서의 refetch delegate
// UICollectionViewDataSourcePrefetching
collectionView.prefetchDataSource = self
func collectionView(_ collectionView: UICollectionView, prefetchItemsAt indexPaths: [IndexPath]) {
// Begin asynchronously fetching data for the requested index paths.
for indexPath in indexPaths {
let model = models[indexPath.row]
asyncFetcher.fetchAsync(model.identifier)
}
}
예제 - scroll에 따라 사진 로드
- MVVM 구조 내용
- ViewController에서 UITableViewDiffableDataSource 클로저 저장
- ViewModel에서 데이터가 변경됨에 따라 snapshot을 남기면 ViewController에서 cell업데이트 진행
- scroll에 따라 이미지를 로드해야하므로 ViewController에 ScrollViewDidScroll 델리게이트 구현
> viewModel.loadData() 호출 > viewModel에서 snapshot하여 UITableViewDiffableDataSource클로저 블록에 셀 업데이트 요청 - prefetch되어야 하므로, ViewController에서 prefetchAt 델리게이트 구현
> viewModel.loadImage() 호출 > viewModel에서 snapshot하여 UITableViewDiffableDataSource클로저 블록에 셀 업데이트 요청
* 전체 소스코드: https://github.com/JK0369/PaginationExample
* 참고
- tableView(_:prefetchRowsAt:): https://developer.apple.com/documentation/uikit/uitableviewdatasourceprefetching/1771764-tableview
- prefetch:
https://andreygordeev.com/2017/02/20/uitableview-prefetching/
'iOS 응용 (swift)' 카테고리의 다른 글
Comments