This is the 20th day of my participation in the Challenge. For details, see:More article challenges

Realize the function of pull – down refresh and pull – up loading

In yesterday’s code, we made a network request and data return for the first page of the leaderboard via RxSwift, and then used the data to drive the page loading.

Of course, only 1 page loading, for a pagination function of the page is not meaningful, do a good pulldown and pull up function is very important.

Here we need to analyze several points:

  • What controls are integrated to do pull-down refresh and pull-up load?

    • MJRefresh, although the control is written by OC, but the invocation and encapsulation are relatively complete, can be used by new veterans.

  • Pull down refresh logic:

    • The drop-down refresh is to reset the page to page 1 and reset the footer’s state.

    • Make a network request for the data on page 1, assign the data to the dataSource dataSource, and let it drive the page.

    • Network request completed, note that success or failure should end the pull-down refresh state.

    • After requesting the first page, we need to determine whether there is a next page, and keep the display and state of foot:

      • If curPage is less than pageCount, there is a next page. If curPage is less than pageCount, there is a next page. If curPage is less than pageCount, there is a next page.

  • Pull-up loads more logic:
    • The pull-up load is to increase the number of pages on a page by 1.

    • Make a network request for the data on page + 1, and merge the obtained data with the previous dataSourc, note that the merge, rather than directly assign, let it drive the page.

    • Network request completed, note that either success or failure should end the pull-up to load more state.

    • After requesting the first page + 1 page, we need to determine whether there is a next page, and keep the display and state of foot:

      • If curPage is less than pageCount, there is a next page. If curPage is less than pageCount, there is a next page. If curPage is less than pageCount, there is a next page.

Ok, the above analysis is done, so modify the code according to this idea, please pay attention to the code comment oh:

import UIKit import RxSwift import RxCocoa import NSObject_Rx import Moya import MJRefresh class RxSwiftCoinRankListController: BaseViewController {/ / / lazy loading tableView private lazy var tableView = UITableView (frame: .zero, style:.plain) private var dataSource: Int = 1 private var dataSource: BehaviorRelay<[CoinRank]> = BehaviorRelay(value: []) override func viewDidLoad() { super.viewDidLoad() setupTableView() } private func setupTableView() { /// Set tableFooterView tableView. TableFooterView = UIView () / / / set the proxy tableView. Rx. SetDelegate (self) disposed (by: Rx.disposebag) /// set the header refresh control tableView.mj_header = MJRefreshNormalHeader() tableView.mj_header? .beginRefreshing { [weak self] in self? RefreshAction ()} / / / set the tail refresh control tableView. Mj_footer = MJRefreshBackNormalFooter () tableView. Mj_footer? .beginRefreshing { [weak self] in self? LoadMoreAction ()} / / / simple layout the addSubview (tableView) tableView. SNP. MakeConstraints {make in the make. Edges. EqualTo (view) } /// dataSource. AsDriver (onErrorJustReturn: []) .drive(tableView.rx.items) { (tableView, row, coinRank) in if let cell = tableView.dequeueReusableCell(withIdentifier: "Cell") { cell.textLabel?.text = coinRank.username cell.detailTextLabel?.text = coinRank.coinCount?.toString return cell  }else { let cell = UITableViewCell(style: .subtitle, reuseIdentifier: "Cell") cell.textLabel?.text = coinRank.username cell.detailTextLabel?.text = coinRank.coinCount?.toString return cell }  } .disposed(by: Rx. DisposeBag)}} the extension RxSwiftCoinRankListController {/ / / the drop-down refresh behavior private func refreshAction () { resetCurrentPageAndMjFooter() getCoinRank(page: Private func loadMoreAction() {page = page + 1 getCoinRank(page: The parameters of the page)} / / / the drop-down and reset behavior private func resetCurrentPageAndMjFooter () {page = 1 self. TableView. Mj_footer?. IsHidden = False self.tableView.mj_footer?.resetNomoreData ()} private func getCoinRank() Int) {myprovider.rx.request (myservice.coinrank (page)) /// turn model.map (BaseModel< page < coinRank >>.self) /// $0.data.datas.map {$0.data.data} /// unpack.compactMap {$0} /// Transform operation.asObservable() .subscribe {event == 1; // subscribe {event == 1; // subscribe {event == 1; // subscribe {event == 1; self.tableView.mj_header?.endRefreshing() : self.tableView.mj_footer?.endRefreshing() switch event { case .success(let pageModel): / / / unpack data if the let datas. = pageModel datas {/ / / on the page of value judgment is a drop-down or pull, do data processing, in order to facilitate comment here, not using the ternary operator if page = = 1 {/ / / the drop-down do assignment operation Self.datasource. Accept (datas)}else {self.datasource. Accept (self.datasource. Value + datas)}} /// Unpack curPage with pageCount if let curPage = pagemodel.curpage, Let pageCount = pagemodel.pagecount {if curPage == pageCount {if curPage == pageCount { Self. TableView. Mj_footer?. EndRefreshingWithNoMoreData ()}} case. The error (_) : / / / the error of processing break}}. Don't do disposed (by: rx.disposeBag) } } extension RxSwiftCoinRankListController: UITableViewDelegate {}Copy the code

The above code has completed the complete comment, here is a separate:

Private Var dataSource: BehaviorRelay<[CoinRank]> = dataSource of BehaviorRelay(value: []).

Instead of the usual dataSource being an array, here we use the BehaviorRelay in RxSwift, which is both a sequence and an observer and can assign to the data. Sequences can be turned into specialized sequence drivers, and they can drive the tableView, and they can do assignment, so they can assign and merge data that’s requested by the network, which is really critical in my code above.

So what’s the next step?

In fact, the above code runs without any problems, just not RxSwifty, not the rX.xxxx callback feel.

My personal understanding is that sometimes can not patronize the face of the matter, first to ensure that the function is no problem, then consider the expansion and depth. Mastering basic knowledge and skills is the cornerstone.

Also, while writing the code above, I was thinking about how to bind the tableView with a value to change the UI state of the header and footer through the state.

This is actually consistent with the principle of declarative UI writing, UI = f(state).

Tomorrow to continue

I continued around MJRefresh with pull-down refresh and pull-up loading, considering a layer of it with RxSwift for better Rx programming.

Why DO I stick to a simple list: many pages in Android apps are lists. If you write one, the rest can be written and reused in the same way.

Come on, everybody.