[WIP] Implement alternative "Load more"-mechanism for scroll-views (#IOS-272)

This commit is contained in:
Nathan Mattes 2024-05-23 15:23:22 +02:00
parent be85e896da
commit 008691bdf8
2 changed files with 22 additions and 70 deletions

View File

@ -71,15 +71,6 @@ extension FollowerListViewController {
userTableViewCellDelegate: self
)
// setup batch fetch
viewModel.shouldFetch
.receive(on: DispatchQueue.main)
.sink { [weak self] _ in
guard let self = self else { return }
self.viewModel.stateMachine.enter(FollowerListViewModel.State.Loading.self)
}
.store(in: &disposeBag)
// trigger user timeline loading
Publishers.CombineLatest(
viewModel.$domain.removeDuplicates(),
@ -168,19 +159,8 @@ extension FollowerListViewController: DataSourceProvider {
extension FollowerListViewController: UIScrollViewDelegate {
func scrollViewDidScroll(_ scrollView: UIScrollView) {
if scrollView.isDragging || scrollView.isTracking { return }
let frame = scrollView.frame
let contentOffset = scrollView.contentOffset
let contentSize = scrollView.contentSize
let visibleBottomY = contentOffset.y + frame.height
let offset = 2 * frame.height
let fetchThrottleOffsetY = contentSize.height - offset
if visibleBottomY > fetchThrottleOffsetY {
viewModel.shouldFetch.send()
ListBatchFetchViewModel.scrollViewdidScrollToEnd(scrollView) {
viewModel.stateMachine.enter(FollowerListViewModel.State.Loading.self)
}
}
}

View File

@ -10,59 +10,31 @@ import Combine
// ref: Texture.ASBatchFetchingDelegate
final class ListBatchFetchViewModel {
var disposeBag = Set<AnyCancellable>()
// timer running on `common` mode
let timerPublisher = Timer.publish(every: 30.0, on: .main, in: .common)
.autoconnect()
.share()
.eraseToAnyPublisher()
// input
private(set) weak var scrollView: UIScrollView?
let hasMore = CurrentValueSubject<Bool, Never>(true)
// output
let shouldFetch = PassthroughSubject<Void, Never>()
init() {
Publishers.CombineLatest(
hasMore,
timerPublisher
)
.sink { [weak self] hasMore, _ in
guard let self = self else { return }
guard hasMore else { return }
guard let scrollView = self.scrollView else { return }
// skip trigger if user interacting
if scrollView.isDragging || scrollView.isTracking { return }
// send fetch request
if scrollView.contentSize.height < scrollView.frame.height {
self.shouldFetch.send()
} else {
let frame = scrollView.frame
let contentOffset = scrollView.contentOffset
let contentSize = scrollView.contentSize
let visibleBottomY = contentOffset.y + frame.height
let offset = 2 * frame.height
let fetchThrottleOffsetY = contentSize.height - offset
if visibleBottomY > fetchThrottleOffsetY {
self.shouldFetch.send()
}
}
init() {}
static func scrollViewdidScrollToEnd(_ scrollView: UIScrollView, action: () -> Void) {
if scrollView.isDragging || scrollView.isTracking { return }
let frame = scrollView.frame
let contentOffset = scrollView.contentOffset
let contentSize = scrollView.contentSize
let visibleBottomY = contentOffset.y + frame.height
let offset = 2 * frame.height
let fetchThrottleOffsetY = contentSize.height - offset
if visibleBottomY > fetchThrottleOffsetY {
action()
}
.store(in: &disposeBag)
}
}
extension ListBatchFetchViewModel {
func setup(scrollView: UIScrollView) {
self.scrollView = scrollView
}
@available(*, deprecated, message: "Implement `UIScrollViewDelegate` and invoce `scrollViewdidScrollToEnd` for now.")
func setup(scrollView: UIScrollView) {}
}