使用 RxSwift 刷新下一页
RefreshNextPage with RxSwift
我正在使用 RxSwift 拉动刷新和刷新下一页。
目前,这是我目前使用的 viewModel:
public final class MomentViewModel {
// Property
let refreshTrigger = PublishSubject<Void>()
let loadNextPageTrigger = PublishSubject<Void>()
let loading = Variable<Bool>(false)
let posts = Variable<[Post]>([])
var pageIndex: Int = 0
let error = PublishSubject<Swift.Error>()
private let disposeBag = DisposeBag()
public init() {
let refreshRequest = loading.asObservable()
.sample(refreshTrigger)
.flatMap { loading -> Observable<Int> in
if loading {
return Observable.empty()
} else {
return Observable<Int>.create { observer in
self.pageIndex = 0
print("reset page index to 0")
observer.onNext(0)
observer.onCompleted()
return Disposables.create()
}
}
}
.debug("refreshRequest", trimOutput: false)
let nextPageRequest = loading.asObservable()
.sample(loadNextPageTrigger)
.flatMap { loading -> Observable<Int> in
if loading {
return Observable.empty()
} else {
return Observable<Int>.create { [unowned self] observer in
self.pageIndex += 1
print(self.pageIndex)
observer.onNext(self.pageIndex)
observer.onCompleted()
return Disposables.create()
}
}
}
.debug("nextPageRequest", trimOutput: false)
let request = Observable.merge(refreshRequest, nextPageRequest)
.debug("Request", trimOutput: false)
let response = request.flatMapLatest { page in
RxAPIProvider.shared.getPostList(page: page).materialize()
}
.share(replay: 1)
.elements()
.debug("Response", trimOutput: false)
Observable
.combineLatest(request, response, posts.asObservable()) { request, response, posts in
return self.pageIndex == 0 ? response : posts + response
}
.sample(response)
.bind(to: posts)
.disposed(by: disposeBag)
Observable
.merge(request.map{ _ in true },
response.map { _ in false },
error.map { _ in false })
.bind(to: loading)
.disposed(by: disposeBag)
}
}
refreshTrigger
和loadNextPageTrigger
绑定到不同的目标喜欢:
self.tableView.rx_reachedBottom
.map { _ in () }
.bind(to: self.viewModel.loadNextPageTrigger)
.disposed(by: disposeBag)
self.refreshControl.rx.controlEvent(.valueChanged)
.bind(to: self.viewModel.refreshTrigger)
.disposed(by: disposeBag)
self.rx.sentMessage(#selector(UIViewController.viewWillAppear(_:)))
.map { _ in () }
.bind(to: viewModel.refreshTrigger)
.disposed(by: disposeBag)
问题:
当我将 tableView 滚动到底部并触发 loadNextPageTrigger
时,一切正常。
但是,如果下一个请求没有更多的数据,loadnextPageTrigger将被无限触发。
任何帮助将不胜感激。
您可以在此处下载 Demo。
主要思想是检查是否加载了所有元素。我在源代码中发现你加载了 20 个部分,所以这个条件应该可以正常工作 if loadedPosts < 20 then stop loading new part
。在这里,我与您分享基本解决方案,您可以根据需要重构(因为我不擅长RxSwift
):
在 MomentViewModel
你应该声明 属性
private var isAllLoaded = false
如果加载所有值,则设置为 true。然后你应该检查每个 [Post]
响应设置正确的 isAllLoaded
:
Observable
.combineLatest(request, response, posts.asObservable()) { request, response, posts in
self.isAllLoaded = response.count < 20 // here the check
return self.pageIndex == 0 ? response : posts + response
}
.sample(response)
.bind(to: posts)
.disposed(by: disposeBag)
然后在 nextPageRequest
中你应该 return .empty()
observer 如果所有部分都已加载:
if loading {
return Observable.empty()
} else {
guard !self.isAllLoaded else { return Observable.empty() }
return Observable<Int>.create { [unowned self] observer in
self.pageIndex += 1
print(self.pageIndex)
observer.onNext(self.pageIndex)
observer.onCompleted()
return Disposables.create()
}
}
P.S。 MomentViewModel.swift
文件到 copy/paste.
我正在使用 RxSwift 拉动刷新和刷新下一页。
目前,这是我目前使用的 viewModel:
public final class MomentViewModel {
// Property
let refreshTrigger = PublishSubject<Void>()
let loadNextPageTrigger = PublishSubject<Void>()
let loading = Variable<Bool>(false)
let posts = Variable<[Post]>([])
var pageIndex: Int = 0
let error = PublishSubject<Swift.Error>()
private let disposeBag = DisposeBag()
public init() {
let refreshRequest = loading.asObservable()
.sample(refreshTrigger)
.flatMap { loading -> Observable<Int> in
if loading {
return Observable.empty()
} else {
return Observable<Int>.create { observer in
self.pageIndex = 0
print("reset page index to 0")
observer.onNext(0)
observer.onCompleted()
return Disposables.create()
}
}
}
.debug("refreshRequest", trimOutput: false)
let nextPageRequest = loading.asObservable()
.sample(loadNextPageTrigger)
.flatMap { loading -> Observable<Int> in
if loading {
return Observable.empty()
} else {
return Observable<Int>.create { [unowned self] observer in
self.pageIndex += 1
print(self.pageIndex)
observer.onNext(self.pageIndex)
observer.onCompleted()
return Disposables.create()
}
}
}
.debug("nextPageRequest", trimOutput: false)
let request = Observable.merge(refreshRequest, nextPageRequest)
.debug("Request", trimOutput: false)
let response = request.flatMapLatest { page in
RxAPIProvider.shared.getPostList(page: page).materialize()
}
.share(replay: 1)
.elements()
.debug("Response", trimOutput: false)
Observable
.combineLatest(request, response, posts.asObservable()) { request, response, posts in
return self.pageIndex == 0 ? response : posts + response
}
.sample(response)
.bind(to: posts)
.disposed(by: disposeBag)
Observable
.merge(request.map{ _ in true },
response.map { _ in false },
error.map { _ in false })
.bind(to: loading)
.disposed(by: disposeBag)
}
}
refreshTrigger
和loadNextPageTrigger
绑定到不同的目标喜欢:
self.tableView.rx_reachedBottom
.map { _ in () }
.bind(to: self.viewModel.loadNextPageTrigger)
.disposed(by: disposeBag)
self.refreshControl.rx.controlEvent(.valueChanged)
.bind(to: self.viewModel.refreshTrigger)
.disposed(by: disposeBag)
self.rx.sentMessage(#selector(UIViewController.viewWillAppear(_:)))
.map { _ in () }
.bind(to: viewModel.refreshTrigger)
.disposed(by: disposeBag)
问题:
当我将 tableView 滚动到底部并触发 loadNextPageTrigger
时,一切正常。
但是,如果下一个请求没有更多的数据,loadnextPageTrigger将被无限触发。 任何帮助将不胜感激。
您可以在此处下载 Demo。
主要思想是检查是否加载了所有元素。我在源代码中发现你加载了 20 个部分,所以这个条件应该可以正常工作 if loadedPosts < 20 then stop loading new part
。在这里,我与您分享基本解决方案,您可以根据需要重构(因为我不擅长RxSwift
):
在 MomentViewModel
你应该声明 属性
private var isAllLoaded = false
如果加载所有值,则设置为 true。然后你应该检查每个 [Post]
响应设置正确的 isAllLoaded
:
Observable
.combineLatest(request, response, posts.asObservable()) { request, response, posts in
self.isAllLoaded = response.count < 20 // here the check
return self.pageIndex == 0 ? response : posts + response
}
.sample(response)
.bind(to: posts)
.disposed(by: disposeBag)
然后在 nextPageRequest
中你应该 return .empty()
observer 如果所有部分都已加载:
if loading {
return Observable.empty()
} else {
guard !self.isAllLoaded else { return Observable.empty() }
return Observable<Int>.create { [unowned self] observer in
self.pageIndex += 1
print(self.pageIndex)
observer.onNext(self.pageIndex)
observer.onCompleted()
return Disposables.create()
}
}
P.S。 MomentViewModel.swift
文件到 copy/paste.