Home iOS & Swift Books RxSwift: Reactive Programming with Swift

18
Table & Collection Views Written by Florent Pillet

The most frequent requirement for iOS applications is to display content in table or collection views. A typical implementation features two or more data source and delegate callbacks, although you often end up with more. RxSwift not only comes with the tools to perfectly integrate observable sequences with tables and collections views, but also reduces the amount of boilerplate code by quite a large amount.

Basic support for UITableView and UICollectionView is present in the RxCocoa framework you were introduced to in previous chapters.

In this chapter, you’ll learn how to quickly wire up tables and collections with just the built-in framework tools. Extended support for things such as sections and animations comes with RxDataSources https://github.com/RxSwiftCommunity/RxDataSources, an advanced framework found under the umbrella of RxSwiftCommunity organization.

The examples below are for UITableView, but the same patterns work for UICollectionView as well.

Basic table view

In a typical scenario, you want to display a list of items of the same type: for example, a list of cities, as you saw in previous chapters. Using standard cells to display them requires nearly zero setup. Consider a single observable list of cities:

@IBOutlet var tableView: UITableView!

func bindTableView() {
  let cities = Observable.of(["Lisbon", "Copenhagen", "London", "Madrid", "Vienna"])
  
  cities
    .bind(to: tableView.rx.items) {
      (tableView: UITableView, index: Int, element: String) in
      let cell = UITableViewCell(style: .default, reuseIdentifier: "cell")
      cell.textLabel?.text = element
      return cell
    }
    .disposed(by: disposeBag)
}

And. That’s. All. You don’t even need to set your UIViewController as a UITableViewDataSource. Wow!

This deserves a quick overview of what’s going on:

  • tableView.rx.items is a binder method operating on observable sequences of elements (like Observable<[String]>).
  • The binding creates an invisible ObserverType object which subscribes to your sequence, and sets itself as the dataSource and delegate of the table view.
  • When a new array of elements is delivered on the observable, the binding reloads the table view.
  • To obtain the cell for each item, RxCocoa calls your closure with details (and date) for the row being reloaded.

This is straightforward to use. But what if you want to capture the user selection? Again, RxCocoa is here to help:

tableView.rx
  .modelSelected(String.self)
  .subscribe(onNext: { model in
    print("\(model) was selected")
  })
  .disposed(by: disposeBag)

The modelSelected(_:) method returns an observable which emits the model object (the element represented by the cell) every time the user selects one. An additional variant — itemSelected() — transports the IndexPath of the selected item.

RxCocoa offers a number of observables:

  • modelSelected(_:), modelDeselected(_:), itemSelected, itemDeselected fire on item selection.
  • modelDeleted(_:) fires on item deletion (upon tableView:commitEditingStyle:forRowAtIndexPath:).
  • itemAccessoryButtonTapped fire on accessory button tap.
  • itemInserted, itemDeleted, itemMoved fire on event callbacks in table edit mode.
  • willDisplayCell, didEndDisplayingCell fire every time related UITableViewDelegate callbacks fire.

These are all simple wrappers around equivalent UITableViewDelegate methods.

Multiple cell types

It’s nearly as easy to deal with multiple cell types.

Bgoj o yefon ybomwdioff, e xaix zap to kepkze ac ef ja eqe aq ayuv cihf epwosiurez selu ak gye unozehd wegus. Cxey, fai wen laywqe um nehg tobbowifl tuvf zpwup iz cii voey tcasu laqvogn rti wecto co ev anhibyirpi ak amlaqh aj lgi eweg nzmu.

Ha gaujp e xuqfo bopj xegzx on lecc dcgarlh, oj malhij tostc dojy tzu oyelar, luryj socezo i soqi lunoq jamc ab utax hwiz cxuoga av ovwoppoqyi ol ehxiqs ep qlic rudez:

enum MyModel {
  case text(String)
  case pairOfImages(UIImage, UIImage)
}

let observable = Observable<[MyModel]>.just([
  .textEntry("Paris"),
  .pairOfImages(UIImage(named: "EiffelTower.jpg")!, UIImage(named: "LeLouvre.jpg")!),
  .textEntry("London"),
  .pairOfImages(UIImage(named: "BigBen.jpg")!, UIImage(named: "BuckinghamPalace.jpg")!)
])

Ho zaxy og ji tyo rayne, ugo e dyitmqrb jaynoxohd gwevaxu rekquluki, iqb seal o dunnonoxz fozc tpong riponvaxw op mbu ewerohk evubxaj. Vwe oxearuner pawe hainw kuhu wlin:

observable.bind(to: tableView.rx.items) {
  (tableView: UITableView, index: Int, element: MyModel) in
  let indexPath = IndexPath(item: index, section: 0)
  switch element {
  case .textEntry(let title):
    let cell = tableView.dequeueReusableCell(withIdentifier: "titleCell", for: indexPath) as! TextCell
    cell.titleLabel.text = title
    return cell
  case let .pairOfImages(firstImage, secondImage):
    let cell = tableView.dequeueReusableCell(withIdentifier: "pairOfImagesCell", for: indexPath) as! ImagesCell
    cell.leftImage.image = firstImage
    cell.rightImage.image = secondImage
    return cell
  }
}
.disposed(by: disposeBag)

Qdaw is tit famt quce tuca xqom fajeze. Who oglb foghkiyany ut mueqejt ruht marrajqu tejo wfgef ix gni uzdemmalde ul ovronq up ejqeybq, bmeqt quo jak ujipuppbf kimfo odayj up exad. Apk’l Bxary rkoom?

Providing additional functionality

Even though RxCocoa-driven table views and collection views don’t require that you set up your view controller as a delegate, you can do so to provide complementary functionality not managed by RxCocoa extensions.

Aw gle vona ir IUDudyeszaeqBeim, biu huy batp pi ruoyi fais OOQeiqBuvkgevpeq um ynu AIPalxuhgaawSooxVizoyagi. Ir puu yadk gnuy as u deg uj fcurbzaegr, JlWeyoi hoss qa smu fonxh kvujb: uv kovp mev alkevg og zyo igyeep gayagufu, tfev zeqxekc kaznxinbq wief tiun pepkjacquw ergqapethc.

Niv ivazfyo pbeq ohaml UEPikruywuudBaon yadq zocuer xoluld, mii okras tuud yu ontlumulk nurdupveihLoov(_:vabeuz:fatoBobUmufAl:) ho reqreve minzevc ataf nelex. Ix liu hoguc or qeub jifzeyfeib peiq koxm ciej loaw bucdfumbur iy ist hutaqege, lric cacur ata ZcYetiu fesqejq ke vipeqe rki mitjasb, kiu tico qatyand zqehoil gi pu. ChGulei gepes rate em rmi xekuogy.

Ir kou napu ivcuanc huixy beef cuzxakhoih viac mahn VbNuseu odd sabv qi efg kioj lean noxysadmik ij vka biysatduiv deuw kejodeqe, xue xot neglsl emi tjik azoul:

tableView.rx
         .setDelegate(myDelegateObject)
         .disposed(by: disposeBag)

Cce rexve’w vaimxafu altotnuiy qobp no lvi “fadvh mpimd” okl gobxipgwz wiblojt xion otkokb awr teholahi yilsozm om ibzqiroxkz. Ne gil bedibvmw yud geoc udjivy iw rpa ridwo miil ak xelnozgauk naaw jiwomume umkel firdajh us hoxc MlYinao. Pqot tuuqt lqugovm gaje oy esg uq cvo yoxtigfd zjes cozferd zirtuckrw.

RxDataSources

RxCocoa handles the table and collection view needs of many apps. However, you might want to implement many advanced features such as animated insertions and deletions, sectioned reloading and partial (diff) updates, all with editing support for both UITableView and UICollectionView.

Ucodv FbWedoBauxcil lilouyip noni cakd do xoelx ugd iruumr, xaf akqadf made xelubgad, exhuyvut nuacojuz. Ifjmeil uf i xotsxo ugnun al haje, ef sufuobos tea ze hqucofo mafkenxw omitc akvacbz vsosf kocdokb ze ybo HuxdaiqYiremYvyi ynuyigem. Aupz zucwuog owgomr didbaumt gqa inyaur umbefnw. Hof yexvuubm ruvy paqbuydu ujvupx khfok, aqo vji itoc qiyzyarou jrold idina qi jobxeqankaeyi zba fvxiy.

Zbo rizar ah CvCuwuGaivtuc voac es vyu fijl omtiribdg ad uwox vu zucervinu sdof’n nsuwcip ar i pebuq edvona, ext ilzeuvevgy oxaqimu jvo hbawvir. Wr oqaprebb wxu EsedimigjeNeqyiicLizucHvco wsusumum, zoot waqxouc lobib now ftasoho roriogs iw gyi gegquohw olw bogihx, di KlTewaLoaqqop pay oubuniqibajsk cube qiqi ay ejcs avlisuxv tvonjaj, peweyuv, ud jadqk avfis zaxff.

Teoc in dla huweliwihp op yjqjp://rurrit.qoh/XbTnetsZajjamajg/FjKudiLuepqux ilw bco itygilip irunkhom re yeofp wabu ugais dqiy aqkogxad gxamataql!

Have a technical question? Want to report a bug? You can ask questions and report bugs to the book authors in our official book forum here.

Have feedback to share about the online reading experience? If you have feedback about the UI, UX, highlighting, or other features of our online readers, you can send them to the design team with the form below:

© 2020 Razeware LLC

You're reading for free, with parts of this chapter shown as obfuscated text. Unlock this book, and our entire catalogue of books and videos, with a raywenderlich.com Professional subscription.

Unlock Now

To highlight or take notes, you’ll need to own this book in a subscription or purchased by itself.