| | |
| | | // |
| | | // RefreshModel.swift |
| | | // WanPai |
| | | // |
| | | // Created by 无故事王国 on 2023/7/11. |
| | | // |
| | | // |
| | | // RefreshModel.swift |
| | | // WanPai |
| | | // |
| | | // Created by 无故事王国 on 2023/7/11. |
| | | // |
| | | |
| | | import UIKit |
| | | import MJRefresh |
| | |
| | | |
| | | |
| | | enum RefreshState { |
| | | case refreshing |
| | | case completedRefresh |
| | | case moreLoading |
| | | case completedLoad |
| | | case completedLoadWithNoMoreData |
| | | case refreshing |
| | | case completedRefresh |
| | | case moreLoading |
| | | case completedLoad |
| | | case completedLoadWithNoMoreData |
| | | } |
| | | |
| | | protocol RefreshModelProctol { |
| | | associatedtype T:HandyJSON |
| | | func api()->(Observable<BaseResponse<[T]>>)? |
| | | associatedtype T:HandyJSON |
| | | func api()->(Observable<BaseResponse<[T]>>)? |
| | | } |
| | | protocol RefreshModelInnerProctol { |
| | | associatedtype T:HandyJSON |
| | | func api()->(Observable<BaseResponse<BaseResponseList<T>>>)? |
| | | } |
| | | |
| | | |
| | | class RefreshModel<T:HandyJSON>:RefreshModelProctol{ |
| | | func api() -> (RxSwift.Observable<BaseResponse<[T]>>)? { |
| | | return nil |
| | | } |
| | | func api() -> (RxSwift.Observable<BaseResponse<[T]>>)? {return nil} |
| | | let disposeBag = DisposeBag() |
| | | enum RefreshType {case refresh,load} |
| | | |
| | | let disposeBag = DisposeBag() |
| | | private var handle:UIScrollView! |
| | | |
| | | lazy var refreshSubject = PublishSubject<RefreshState>() |
| | | var page:Int = 0 |
| | | var pageSize:Int = 20 |
| | | private var needRefreshData:Bool = true |
| | | lazy var dataSource = BehaviorRelay<[T]>(value: []) |
| | | |
| | | func configure(_ scrollView:UITableView,needMore:Bool = true){ |
| | | scrollView.mj_header = CustomRefreshHeaer.refreshing(with: refreshData()) |
| | | if needMore{ |
| | | scrollView.mj_footer = CustomRefreshFooter.refreshing(with: loadMoreData()) |
| | | } |
| | | refreshSubject.bind(to: scrollView.rx.handlestatus()).disposed(by: disposeBag) |
| | | handle = scrollView |
| | | } |
| | | |
| | | func configure(_ scrollView:UICollectionView,needMore:Bool = true,needRefreshData:Bool = true){ |
| | | self.needRefreshData = needRefreshData |
| | | scrollView.mj_header = CustomRefreshHeaer.refreshing(with: refreshData()) |
| | | if needMore{ |
| | | scrollView.mj_footer = CustomRefreshFooter.refreshing(with: loadMoreData()) |
| | | } |
| | | refreshSubject.bind(to: scrollView.rx.handlestatus()).disposed(by: disposeBag) |
| | | handle = scrollView |
| | | } |
| | | |
| | | func beginRefresh(){ |
| | | handle.mj_header?.beginRefreshing() |
| | | } |
| | | |
| | | func refreshData() ->(()->Void) { |
| | | return {self.request(status: .refresh)} |
| | | } |
| | | |
| | | func loadMoreData() ->(()->Void) { |
| | | return {self.request(status: .load)} |
| | | } |
| | | |
| | | func request(status:RefreshType){ |
| | | switch status { |
| | | case .refresh: |
| | | self.page = 1 |
| | | self.refreshSubject.onNext(.refreshing) |
| | | case .load: |
| | | self.page += 1 |
| | | self.refreshSubject.onNext(.moreLoading) |
| | | } |
| | | |
| | | api()?.subscribe(onNext: { data in |
| | | if let datas = data.data{ |
| | | switch status{ |
| | | case .refresh: |
| | | self.dataSource.accept(datas) |
| | | self.refreshSubject.onNext(.completedRefresh) |
| | | case .load: |
| | | self.dataSource.accept(self.dataSource.value + datas) |
| | | if datas.count == 0{ |
| | | self.refreshSubject.onNext(.completedLoadWithNoMoreData) |
| | | }else{ |
| | | self.refreshSubject.onNext(.completedLoad) |
| | | } |
| | | } |
| | | }else{ |
| | | self.refreshSubject.onNext(.completedLoadWithNoMoreData) |
| | | } |
| | | }, onError: { error in |
| | | self.refreshSubject.onNext(.completedLoad) |
| | | |
| | | }).disposed(by: disposeBag) |
| | | } |
| | | } |
| | | |
| | | |
| | | enum RefreshType { |
| | | case refresh,load |
| | | } |
| | | class RefreshInnerModel<T:HandyJSON>:RefreshModelInnerProctol{ |
| | | func api() -> (RxSwift.Observable<BaseResponse<BaseResponseList<T>>>)? { |
| | | return nil |
| | | } |
| | | |
| | | let disposeBag = DisposeBag() |
| | | enum RefreshType {case refresh,load} |
| | | |
| | | private var handle:UIScrollView! |
| | | |
| | | lazy var refreshSubject = PublishSubject<RefreshState>() |
| | | var page:Int = 0 |
| | | var pageSize:Int = 20 |
| | | private var needRefreshData:Bool = true |
| | | lazy var dataSource = BehaviorRelay<BaseResponseList<T>?>(value: nil) |
| | | |
| | | func configure(_ scrollView:UITableView,needMore:Bool = true){ |
| | | scrollView.mj_header = CustomRefreshHeaer.refreshing(with: refreshData()) |
| | | if needMore{ |
| | | scrollView.mj_footer = CustomRefreshFooter.refreshing(with: loadMoreData()) |
| | | } |
| | | refreshSubject.bind(to: scrollView.rx.handlestatus()).disposed(by: disposeBag) |
| | | handle = scrollView |
| | | } |
| | | |
| | | func configure(_ scrollView:UICollectionView,needMore:Bool = true,needRefreshData:Bool = true){ |
| | | self.needRefreshData = needRefreshData |
| | | scrollView.mj_header = CustomRefreshHeaer.refreshing(with: refreshData()) |
| | | if needMore{ |
| | | scrollView.mj_footer = CustomRefreshFooter.refreshing(with: loadMoreData()) |
| | | } |
| | | refreshSubject.bind(to: scrollView.rx.handlestatus()).disposed(by: disposeBag) |
| | | handle = scrollView |
| | | } |
| | | |
| | | func beginRefresh(){ |
| | | handle.mj_header?.beginRefreshing() |
| | | } |
| | | |
| | | func refreshData() ->(()->Void) { |
| | | return {self.request(status: .refresh)} |
| | | } |
| | | |
| | | func loadMoreData() ->(()->Void) { |
| | | return {self.request(status: .load)} |
| | | } |
| | | |
| | | func request(status:RefreshType){ |
| | | switch status { |
| | | case .refresh: |
| | | self.page = 1 |
| | | self.refreshSubject.onNext(.refreshing) |
| | | case .load: |
| | | self.page += 1 |
| | | self.refreshSubject.onNext(.moreLoading) |
| | | } |
| | | |
| | | api()?.subscribe(onNext: { data in |
| | | switch status{ |
| | | case .refresh: |
| | | self.dataSource.accept(data.data) |
| | | self.refreshSubject.onNext(.completedRefresh) |
| | | case .load: |
| | | var new = self.dataSource.value?.list ?? [] |
| | | new.append(contentsOf: data.data?.list ?? []) |
| | | var model = self.dataSource.value |
| | | model!.list = new |
| | | self.dataSource.accept(model) |
| | | if data.data?.list.count == 0{ |
| | | self.refreshSubject.onNext(.completedLoadWithNoMoreData) |
| | | }else{ |
| | | self.refreshSubject.onNext(.completedLoad) |
| | | } |
| | | } |
| | | }, onError: { error in |
| | | self.refreshSubject.onNext(.completedLoad) |
| | | |
| | | }).disposed(by: disposeBag) |
| | | } |
| | | |
| | | |
| | | private var handle:UIScrollView! |
| | | |
| | | lazy var refreshSubject = PublishSubject<RefreshState>() |
| | | var page:Int = 0 |
| | | var pageSize:Int = 20 |
| | | private var needRefreshData:Bool = true |
| | | lazy var dataSource = BehaviorRelay<[T]>(value: []) |
| | | |
| | | func configure(_ scrollView:UITableView,needMore:Bool = true){ |
| | | scrollView.mj_header = CustomRefreshHeaer.refreshing(with: refreshData()) |
| | | if needMore{ |
| | | scrollView.mj_footer = CustomRefreshFooter.refreshing(with: loadMoreData()) |
| | | } |
| | | refreshSubject.bind(to: scrollView.rx.handlestatus()).disposed(by: disposeBag) |
| | | handle = scrollView |
| | | } |
| | | |
| | | func configure(_ scrollView:UICollectionView,needMore:Bool = true,needRefreshData:Bool = true){ |
| | | self.needRefreshData = needRefreshData |
| | | scrollView.mj_header = CustomRefreshHeaer.refreshing(with: refreshData()) |
| | | if needMore{ |
| | | scrollView.mj_footer = CustomRefreshFooter.refreshing(with: loadMoreData()) |
| | | } |
| | | refreshSubject.bind(to: scrollView.rx.handlestatus()).disposed(by: disposeBag) |
| | | handle = scrollView |
| | | } |
| | | |
| | | func beginRefresh(){ |
| | | handle.mj_header?.beginRefreshing() |
| | | } |
| | | |
| | | |
| | | func refreshData() ->(()->Void) { |
| | | return {self.request(status: .refresh)} |
| | | } |
| | | |
| | | func loadMoreData() ->(()->Void) { |
| | | return {self.request(status: .load)} |
| | | } |
| | | |
| | | func request(status:RefreshType){ |
| | | switch status { |
| | | case .refresh: |
| | | self.page = 1 |
| | | self.refreshSubject.onNext(.refreshing) |
| | | case .load: |
| | | self.page += 1 |
| | | self.refreshSubject.onNext(.moreLoading) |
| | | } |
| | | |
| | | api()?.subscribe(onNext: { data in |
| | | if let datas = data.data{ |
| | | switch status{ |
| | | case .refresh: |
| | | self.dataSource.accept(datas) |
| | | self.refreshSubject.onNext(.completedRefresh) |
| | | case .load: |
| | | self.dataSource.accept(self.dataSource.value + datas) |
| | | if datas.count == 0{ |
| | | self.refreshSubject.onNext(.completedLoadWithNoMoreData) |
| | | }else{ |
| | | self.refreshSubject.onNext(.completedLoad) |
| | | } |
| | | } |
| | | }else{ |
| | | self.refreshSubject.onNext(.completedLoadWithNoMoreData) |
| | | } |
| | | }, onError: { error in |
| | | self.refreshSubject.onNext(.completedLoad) |
| | | |
| | | }).disposed(by: disposeBag) |
| | | } |
| | | } |
| | | |
| | | class CustomRefreshHeaer:MJRefreshNormalHeader{ |
| | | static func refreshing(with refreshingBlock: @escaping MJRefreshComponentAction) -> MJRefreshNormalHeader? { |
| | | let refreshHeader = MJRefreshNormalHeader(refreshingBlock: refreshingBlock) |
| | | return refreshHeader |
| | | } |
| | | static func refreshing(with refreshingBlock: @escaping MJRefreshComponentAction) -> MJRefreshNormalHeader? { |
| | | let refreshHeader = MJRefreshNormalHeader(refreshingBlock: refreshingBlock) |
| | | return refreshHeader |
| | | } |
| | | } |
| | | |
| | | class CustomRefreshFooter:MJRefreshAutoNormalFooter{ |
| | | static func refreshing(with refreshingBlock: @escaping MJRefreshComponentAction) -> MJRefreshAutoNormalFooter? { |
| | | let refrehFooter = MJRefreshAutoNormalFooter(refreshingBlock: refreshingBlock) |
| | | refrehFooter.isRefreshingTitleHidden = true |
| | | refrehFooter.stateLabel?.isHidden = true |
| | | return refrehFooter |
| | | } |
| | | static func refreshing(with refreshingBlock: @escaping MJRefreshComponentAction) -> MJRefreshAutoNormalFooter? { |
| | | let refrehFooter = MJRefreshAutoNormalFooter(refreshingBlock: refreshingBlock) |
| | | refrehFooter.isRefreshingTitleHidden = true |
| | | refrehFooter.stateLabel?.isHidden = true |
| | | return refrehFooter |
| | | } |
| | | } |
| | | |
| | | extension Reactive where Base : UITableView { |
| | | |
| | | func handlestatus() -> Binder<RefreshState> { |
| | | return Binder(self.base) { (tableView, status) in |
| | | switch status { |
| | | case .moreLoading: |
| | | self.base.mj_footer?.beginRefreshing() |
| | | case .refreshing: |
| | | self.base.reloadData() |
| | | self.base.mj_footer?.resetNoMoreData() |
| | | self.base.mj_header?.beginRefreshing() |
| | | func handlestatus() -> Binder<RefreshState> { |
| | | return Binder(self.base) { (tableView, status) in |
| | | switch status { |
| | | case .moreLoading: |
| | | self.base.mj_footer?.beginRefreshing() |
| | | case .refreshing: |
| | | self.base.reloadData() |
| | | self.base.mj_footer?.resetNoMoreData() |
| | | self.base.mj_header?.beginRefreshing() |
| | | |
| | | case .completedLoadWithNoMoreData: |
| | | DispatchQueue.main.async { |
| | | self.base.reloadData() |
| | | } |
| | | (self.base.mj_footer as? MJRefreshAutoNormalFooter)?.stateLabel?.isHidden = false |
| | | self.base.mj_footer?.endRefreshingWithNoMoreData() |
| | | self.base.mj_header?.endRefreshing() |
| | | case .completedLoad: |
| | | DispatchQueue.main.async { |
| | | self.base.reloadData() |
| | | } |
| | | self.base.mj_footer?.endRefreshing() |
| | | self.base.mj_header?.endRefreshing() |
| | | default: |
| | | DispatchQueue.main.async { |
| | | self.base.reloadData() |
| | | } |
| | | self.base.mj_header?.endRefreshing() |
| | | self.base.mj_footer?.endRefreshing() |
| | | } |
| | | } |
| | | } |
| | | case .completedLoadWithNoMoreData: |
| | | DispatchQueue.main.async { |
| | | self.base.reloadData() |
| | | } |
| | | (self.base.mj_footer as? MJRefreshAutoNormalFooter)?.stateLabel?.isHidden = false |
| | | self.base.mj_footer?.endRefreshingWithNoMoreData() |
| | | self.base.mj_header?.endRefreshing() |
| | | case .completedLoad: |
| | | DispatchQueue.main.async { |
| | | self.base.reloadData() |
| | | } |
| | | self.base.mj_footer?.endRefreshing() |
| | | self.base.mj_header?.endRefreshing() |
| | | default: |
| | | DispatchQueue.main.async { |
| | | self.base.reloadData() |
| | | } |
| | | self.base.mj_header?.endRefreshing() |
| | | self.base.mj_footer?.endRefreshing() |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | extension Reactive where Base : UICollectionView { |
| | | |
| | | func handlestatus() -> Binder<RefreshState> { |
| | | return Binder(self.base) { (tableView, status) in |
| | | switch status { |
| | | case .moreLoading: |
| | | self.base.mj_footer?.beginRefreshing() |
| | | case .refreshing: |
| | | self.base.reloadData() |
| | | self.base.mj_footer?.resetNoMoreData() |
| | | self.base.mj_header?.endRefreshing() |
| | | func handlestatus() -> Binder<RefreshState> { |
| | | return Binder(self.base) { (tableView, status) in |
| | | switch status { |
| | | case .moreLoading: |
| | | self.base.mj_footer?.beginRefreshing() |
| | | case .refreshing: |
| | | self.base.reloadData() |
| | | self.base.mj_footer?.resetNoMoreData() |
| | | self.base.mj_header?.endRefreshing() |
| | | |
| | | case .completedLoadWithNoMoreData: |
| | | DispatchQueue.main.async { |
| | | self.base.reloadData() |
| | | } |
| | | self.base.mj_footer?.endRefreshingWithNoMoreData() |
| | | case .completedLoad: |
| | | DispatchQueue.main.async { |
| | | self.base.reloadData() |
| | | } |
| | | self.base.mj_footer?.endRefreshing() |
| | | default: |
| | | DispatchQueue.main.async { |
| | | self.base.reloadData() |
| | | } |
| | | self.base.mj_header?.endRefreshing() |
| | | self.base.mj_footer?.endRefreshing() |
| | | } |
| | | } |
| | | } |
| | | case .completedLoadWithNoMoreData: |
| | | DispatchQueue.main.async { |
| | | self.base.reloadData() |
| | | } |
| | | self.base.mj_footer?.endRefreshingWithNoMoreData() |
| | | case .completedLoad: |
| | | DispatchQueue.main.async { |
| | | self.base.reloadData() |
| | | } |
| | | self.base.mj_footer?.endRefreshing() |
| | | default: |
| | | DispatchQueue.main.async { |
| | | self.base.reloadData() |
| | | } |
| | | self.base.mj_header?.endRefreshing() |
| | | self.base.mj_footer?.endRefreshing() |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |