RxSwift 分页 tableView 靠近底部
RxSwift pagination tableView near bottom
所以我正在我的应用程序中进行分页,但我遇到了一些奇怪的行为:
class ViewController: UIViewController {
func bindRx() {
btn.rx
.tap
.bind(to: viewModel.searchButtonTapped)
.disposed(by: disposeBag)
searchField.rx
.text
.orEmpty
.bind(to: viewModel.searchText)
.disposed(by: disposeBag)
searchField.rx
.searchButtonClicked
.bind(to: viewModel.searchButtonTapped)
.disposed(by: disposeBag)
viewModel.itemsDriver.drive(tableView.rx.items) { (tableView, row, element) -> UITableViewCell in
switch element {
case .post(model: let vm):
let cell = tableView.dequeueReusableCell(withIdentifier: "PostTableViewCell") as! PostTableViewCell
cell.config(viewModel: vm)
return cell
case .promotion(title: _):
let cell = tableView.dequeueReusableCell(withIdentifier: "PromotionTableViewCell") as! PromotionTableViewCell
return cell
}
}.disposed(by: disposeBag)
viewModel
.isLoading
.drive(tableView.rx.animation)
.disposed(by: disposeBag)
tableView.rx
.modelSelected(CellType.self)
.bind(to: viewModel.selectedCell)
.disposed(by: disposeBag)
// tableView.rx
// .willDisplayCell
// .subscribe(onNext: { cell, indexPath in
// let lastItem = self.viewModel.counter - 1
// if indexPath.row == lastItem {
// self.viewModel.loadMore.accept(())
// }
// })
// .disposed(by: disposeBag)
tableView.rx
.reachedBottom()
.debug("botoom!!!!!!!!!")
.bind(to: self.viewModel.loadMore)
.disposed(by: disposeBag)
}
}
public extension Reactive where Base: UIScrollView {
/**
Shows if the bottom of the UIScrollView is reached.
- parameter offset: A threshhold indicating the bottom of the UIScrollView.
- returns: ControlEvent that emits when the bottom of the base UIScrollView is reached.
*/
func reachedBottom(offset: CGFloat = 0.0) -> ControlEvent<Void> {
let source = contentOffset.map { contentOffset in
let visibleHeight = self.base.frame.height - self.base.contentInset.top - self.base.contentInset.bottom
let y = contentOffset.y + self.base.contentInset.top
let threshold = max(offset, self.base.contentSize.height - visibleHeight)
return y >= threshold
}
.distinctUntilChanged()
.filter { [=11=] }
.map { _ in () }
return ControlEvent(events: source)
}
}
final class PostsViewModel {
init(networking: Networking = Networking()) {
self.networking = networking
searchButtonTapped.subscribe(onNext: { [weak self] value in
self?.page = 0
self?.counter = 0
self?.loadMore.accept(())
}).disposed(by: disposeBag)
searchText.subscribe(onNext: { [weak self] value in
self?.params.updateValue(value, forKey: "search_query")
}).disposed(by: disposeBag)
let load = loadMore.scan(0) { (value, _) -> Int in
self.page = self.page + 1
self.params.updateValue(self.page, forKey: "page")
return value + 1
}
let test = load
.flatMapLatest { page in
self.networking.preformNetworkTaskGet(
endPoint: Api.get,
type: Posts.self,
methodType: .get,
param: self.params)
// .debug("call1 ")
}
.scan(into: [Post]()) { current, items in
current.append(contentsOf: items.data)
self.counter = current.count
}
// .debug(" all ")
.share()
let vm = test.map {
self.postsToCellViewModel(posts: [=12=])
}
itemsDriver = vm
.map { [=12=] }
.asDriver(onErrorJustReturn: [])
.debug("2")
}
struct Networking: NetworkType {
func downloadImage(url: String) -> Observable<UIImage> {
return Observable<UIImage>.create { (observer) -> Disposable in
AF.request(url).response{ response in
print(response)
switch response.result {
case .success(let responseData):
observer.onNext(UIImage(data: responseData!)!)
case .failure(let error):
observer.onError(error)
}
}
return Disposables.create()
}
}
func preformNetworkTaskGet<T: Codable>(endPoint: EndpointType, type: T.Type, methodType: MethodsType, param: [String : Any]?) -> Observable<T> {
return Observable<T>.create { (observer) -> Disposable in
if let url = endPoint.baseURL.appendingPathComponent(endPoint.path).absoluteString.removingPercentEncoding {
AF.request(url, method: methodType.method, parameters: param, encoding: URLEncoding.default).responseJSON { (response) in
switch response.result {
case .failure(let error):
observer.onError(error)
case .success(_):
if let data = response.data {
let response = Response.init(data: data)
if let decode = response.decode(type) {
observer.onNext(decode)
} else {
observer.onError(NSError())
}
}
}
}
}
return Disposables.create()
}
}
}
第一次网络调用后,我将进入 if 并触发 self.viewModel.loadMore.accept(()),无需任何滚动。
有什么想法吗?
您可以简单地跳过 reachedBottom Observable 的第一个下一个事件...
tableView.rx.reachedBottom().skip(1)
所以我正在我的应用程序中进行分页,但我遇到了一些奇怪的行为:
class ViewController: UIViewController {
func bindRx() {
btn.rx
.tap
.bind(to: viewModel.searchButtonTapped)
.disposed(by: disposeBag)
searchField.rx
.text
.orEmpty
.bind(to: viewModel.searchText)
.disposed(by: disposeBag)
searchField.rx
.searchButtonClicked
.bind(to: viewModel.searchButtonTapped)
.disposed(by: disposeBag)
viewModel.itemsDriver.drive(tableView.rx.items) { (tableView, row, element) -> UITableViewCell in
switch element {
case .post(model: let vm):
let cell = tableView.dequeueReusableCell(withIdentifier: "PostTableViewCell") as! PostTableViewCell
cell.config(viewModel: vm)
return cell
case .promotion(title: _):
let cell = tableView.dequeueReusableCell(withIdentifier: "PromotionTableViewCell") as! PromotionTableViewCell
return cell
}
}.disposed(by: disposeBag)
viewModel
.isLoading
.drive(tableView.rx.animation)
.disposed(by: disposeBag)
tableView.rx
.modelSelected(CellType.self)
.bind(to: viewModel.selectedCell)
.disposed(by: disposeBag)
// tableView.rx
// .willDisplayCell
// .subscribe(onNext: { cell, indexPath in
// let lastItem = self.viewModel.counter - 1
// if indexPath.row == lastItem {
// self.viewModel.loadMore.accept(())
// }
// })
// .disposed(by: disposeBag)
tableView.rx
.reachedBottom()
.debug("botoom!!!!!!!!!")
.bind(to: self.viewModel.loadMore)
.disposed(by: disposeBag)
}
}
public extension Reactive where Base: UIScrollView {
/**
Shows if the bottom of the UIScrollView is reached.
- parameter offset: A threshhold indicating the bottom of the UIScrollView.
- returns: ControlEvent that emits when the bottom of the base UIScrollView is reached.
*/
func reachedBottom(offset: CGFloat = 0.0) -> ControlEvent<Void> {
let source = contentOffset.map { contentOffset in
let visibleHeight = self.base.frame.height - self.base.contentInset.top - self.base.contentInset.bottom
let y = contentOffset.y + self.base.contentInset.top
let threshold = max(offset, self.base.contentSize.height - visibleHeight)
return y >= threshold
}
.distinctUntilChanged()
.filter { [=11=] }
.map { _ in () }
return ControlEvent(events: source)
}
}
final class PostsViewModel {
init(networking: Networking = Networking()) {
self.networking = networking
searchButtonTapped.subscribe(onNext: { [weak self] value in
self?.page = 0
self?.counter = 0
self?.loadMore.accept(())
}).disposed(by: disposeBag)
searchText.subscribe(onNext: { [weak self] value in
self?.params.updateValue(value, forKey: "search_query")
}).disposed(by: disposeBag)
let load = loadMore.scan(0) { (value, _) -> Int in
self.page = self.page + 1
self.params.updateValue(self.page, forKey: "page")
return value + 1
}
let test = load
.flatMapLatest { page in
self.networking.preformNetworkTaskGet(
endPoint: Api.get,
type: Posts.self,
methodType: .get,
param: self.params)
// .debug("call1 ")
}
.scan(into: [Post]()) { current, items in
current.append(contentsOf: items.data)
self.counter = current.count
}
// .debug(" all ")
.share()
let vm = test.map {
self.postsToCellViewModel(posts: [=12=])
}
itemsDriver = vm
.map { [=12=] }
.asDriver(onErrorJustReturn: [])
.debug("2")
}
struct Networking: NetworkType {
func downloadImage(url: String) -> Observable<UIImage> {
return Observable<UIImage>.create { (observer) -> Disposable in
AF.request(url).response{ response in
print(response)
switch response.result {
case .success(let responseData):
observer.onNext(UIImage(data: responseData!)!)
case .failure(let error):
observer.onError(error)
}
}
return Disposables.create()
}
}
func preformNetworkTaskGet<T: Codable>(endPoint: EndpointType, type: T.Type, methodType: MethodsType, param: [String : Any]?) -> Observable<T> {
return Observable<T>.create { (observer) -> Disposable in
if let url = endPoint.baseURL.appendingPathComponent(endPoint.path).absoluteString.removingPercentEncoding {
AF.request(url, method: methodType.method, parameters: param, encoding: URLEncoding.default).responseJSON { (response) in
switch response.result {
case .failure(let error):
observer.onError(error)
case .success(_):
if let data = response.data {
let response = Response.init(data: data)
if let decode = response.decode(type) {
observer.onNext(decode)
} else {
observer.onError(NSError())
}
}
}
}
}
return Disposables.create()
}
}
}
第一次网络调用后,我将进入 if 并触发 self.viewModel.loadMore.accept(()),无需任何滚动。 有什么想法吗?
您可以简单地跳过 reachedBottom Observable 的第一个下一个事件...
tableView.rx.reachedBottom().skip(1)