RxSwift官方实例七(UIPickerView)

代码下载
UIPickerView的Rx实现分析 RxPickerViewDelegateProxy分析
RxCocoa已经实现了RxPickerViewDelegateProxy,该类继承DelegateProxy基类,遵守DelegateProxyTypeUIPickerViewDelegate协议:

extension UIPickerView: HasDelegate { public typealias Delegate = UIPickerViewDelegate }open class RxPickerViewDelegateProxy : DelegateProxy , DelegateProxyType , UIPickerViewDelegate {/// Typed parent object. public weak private(set) var pickerView: UIPickerView?/// - parameter pickerView: Parent object for delegate proxy. public init(pickerView: ParentObject) { self.pickerView = pickerView super.init(parentObject: pickerView, delegateProxy: RxPickerViewDelegateProxy.self) }// Register known implementationss public static func registerKnownImplementations() { self.register { RxPickerViewDelegateProxy(pickerView: $0) } } }

UIPickerViewDelegate协议的所有函数在这个类中都没有实现,最终会进行消息转发。
RxPickerViewDataSourceProxy分析
RxCocoa已经实现了RxPickerViewDataSourceProxy,该类继承DelegateProxy基类,遵守DelegateProxyTypeUIPickerViewDataSource协议:
extension UIPickerView: HasDataSource { public typealias DataSource = UIPickerViewDataSource }private let pickerViewDataSourceNotSet = PickerViewDataSourceNotSet()final private class PickerViewDataSourceNotSet: NSObject, UIPickerViewDataSource { func numberOfComponents(in pickerView: UIPickerView) -> Int { return 0 }func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int { return 0 } }/// For more information take a look at `DelegateProxyType`. public class RxPickerViewDataSourceProxy : DelegateProxy , DelegateProxyType , UIPickerViewDataSource {/// Typed parent object. public weak private(set) var pickerView: UIPickerView?/// - parameter pickerView: Parent object for delegate proxy. public init(pickerView: ParentObject) { self.pickerView = pickerView super.init(parentObject: pickerView, delegateProxy: RxPickerViewDataSourceProxy.self) }// Register known implementations public static func registerKnownImplementations() { self.register { RxPickerViewDataSourceProxy(pickerView: $0) } }private weak var _requiredMethodsDataSource: UIPickerViewDataSource? = pickerViewDataSourceNotSet// MARK: UIPickerViewDataSource/// Required delegate method implementation. public func numberOfComponents(in pickerView: UIPickerView) -> Int { return (_requiredMethodsDataSource ?? pickerViewDataSourceNotSet).numberOfComponents(in: pickerView) }/// Required delegate method implementation. public func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int { return (_requiredMethodsDataSource ?? pickerViewDataSourceNotSet).pickerView(pickerView, numberOfRowsInComponent: component) }/// For more information take a look at `DelegateProxyType`. public override func setForwardToDelegate(_ forwardToDelegate: UIPickerViewDataSource?, retainDelegate: Bool) { _requiredMethodsDataSource = forwardToDelegate ?? pickerViewDataSourceNotSet super.setForwardToDelegate(forwardToDelegate, retainDelegate: retainDelegate) } }

扩展UIPickerView遵守HasDataSource协议,使RxPickerViewDataSourceProxy满足条件进而实现了DelegateProxyType协议中定义的如下两个函数:
extension DelegateProxyType where ParentObject: HasDataSource, Self.Delegate == ParentObject.DataSource { public static func currentDelegate(for object: ParentObject) -> Delegate? { return object.dataSource }public static func setCurrentDelegate(_ delegate: Delegate?, to object: ParentObject) { object.dataSource = delegate } }

定义PickerViewDataSourceNotSet这个类为UIPickerViewDataSource协议提供默认数据
重写setForwardToDelegate函数,将参数forwardToDelegate存储在私有属性_requiredMethodsDataSource中。
UIPickerViewDataSource协议中的两个函数在这个类中实现,调用属性_requiredMethodsDataSource的方法返回结果。
RxPickerViewAdapter分析
RxPickerViewAdapter是用来处理UIPickerViewDataSourceUIPickerViewDelegate协议中定义的带有返回值的函数的。
RxCocoa实现了3种RxPickerViewAdapter,分别是RxStringPickerViewAdapterRxAttributedStringPickerViewAdapterRxPickerViewAdapter
RxPickerViewArrayDataSource分析 RxPickerViewArrayDataSourceRxStringPickerViewAdapterRxAttributedStringPickerViewAdapterRxPickerViewAdapter的根类,遵守UIPickerViewDataSourceSectionedViewDataSourceType协议,还定义一个泛型作为通用数据类型:
class RxPickerViewArrayDataSource: NSObject, UIPickerViewDataSource, SectionedViewDataSourceType { fileprivate var items: [T] = []func model(at indexPath: IndexPath) throws -> Any { guard items.indices ~= indexPath.row else { throw RxCocoaError.itemsNotYetBound(object: self) } return items[indexPath.row] }func numberOfComponents(in pickerView: UIPickerView) -> Int { return 1 }func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int { return items.count } }

  • items数组属性存储所需数据,问题是他只能存一组数据。
  • 实现UIPickerViewDataSource协议。
RxPickerViewSequenceDataSource分析 RxPickerViewSequenceDataSource这个类继承自RxPickerViewArrayDataSource,是RxStringPickerViewAdapterRxAttributedStringPickerViewAdapterRxPickerViewAdapter的父类,遵守RxPickerViewArrayDataSource
, RxPickerViewDataSourceType协议,定义一个遵守Swift.Sequence协议的泛型
class RxPickerViewSequenceDataSource : RxPickerViewArrayDataSource , RxPickerViewDataSourceType { typealias Element = Sequencefunc pickerView(_ pickerView: UIPickerView, observedEvent: Event) { Binder(self) { dataSource, items in dataSource.items = items pickerView.reloadAllComponents() } .on(observedEvent.map(Array.init)) } }

func pickerView(_ pickerView: UIPickerView, observedEvent: Event)函数的实现:
  • 构建一个Binder并执行on操作,实际相当于直接执行构建Binderbinding闭包。
  • Event类型参数observedEvent转化为Event作为on操作的参数。
  • 在构建Binderbinding闭包中,将数组赋值给items属性并刷新整个UIPickerView
RxStringPickerViewAdapter、RxAttributedStringPickerViewAdapter、RxPickerViewAdapter分析 RxStringPickerViewAdapter是不可被继承的,自身继承自RxPickerViewSequenceDataSource并且遵守UIPickerViewDelegate协议:
final class RxStringPickerViewAdapter : RxPickerViewSequenceDataSource , UIPickerViewDelegate {typealias TitleForRow = (Int, Sequence.Element) -> String? private let titleForRow: TitleForRowinit(titleForRow: @escaping TitleForRow) { self.titleForRow = titleForRow super.init() }func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? { return titleForRow(row, items[row]) } }

属性titleForRow存储将items数组中的元素转化为String类型的闭包。
- (nullable NSString *)pickerView:(UIPickerView *)pickerView titleForRow:(NSInteger)row forComponent:(NSInteger)component函数中执行titleForRow闭包并返回其结果。
RxAttributedStringPickerViewAdapterRxPickerViewAdapter的实现与RxStringPickerViewAdapter类似,不同的是分别存储attributedTitleForRow(将数组中元素转化为NSAttributedString富文本)、viewForRow(将数组中元素转化为UIView视图)的闭包,实现不同的UIPickerViewDelegate协议函数返回执行闭包的返回值:
final class RxAttributedStringPickerViewAdapter: RxPickerViewSequenceDataSource, UIPickerViewDelegate { typealias AttributedTitleForRow = (Int, Sequence.Element) -> NSAttributedString? private let attributedTitleForRow: AttributedTitleForRowinit(attributedTitleForRow: @escaping AttributedTitleForRow) { self.attributedTitleForRow = attributedTitleForRow super.init() }func pickerView(_ pickerView: UIPickerView, attributedTitleForRow row: Int, forComponent component: Int) -> NSAttributedString? { return attributedTitleForRow(row, items[row]) } }final class RxPickerViewAdapter: RxPickerViewSequenceDataSource, UIPickerViewDelegate { typealias ViewForRow = (Int, Sequence.Element, UIView?) -> UIView private let viewForRow: ViewForRowinit(viewForRow: @escaping ViewForRow) { self.viewForRow = viewForRow super.init() }func pickerView(_ pickerView: UIPickerView, viewForRow row: Int, forComponent component: Int, reusing view: UIView?) -> UIView { return viewForRow(row, items[row], view) } }

基于UIPickerView的Reactive扩展分析
定义delegate、dataSource两个函数用于获取Rx代理对象,方便使用:
public var delegate: DelegateProxy { return RxPickerViewDelegateProxy.proxy(for: base) } public var dataSource: DelegateProxy { return RxPickerViewDataSourceProxy.proxy(for: base) }

定义setDelegate函数通过installForwardDelegate函数为Rx代理对象设置forwardDelegate并返回一个置空forwardDelegateDisposables
public func setDelegate(_ delegate: UIPickerViewDelegate) -> Disposable { return RxPickerViewDelegateProxy.installForwardDelegate(delegate, retainDelegate: false, onProxyForObject: self.base) }

public func items(adapter: Adapter) -> (_ source: Source) -> Disposable where Source.Element == Adapter.Element函数,这个函数很重要,数据的绑定最终都会走到这个函数:
public func items(adapter: Adapter) -> (_ source: Source) -> Disposable where Source.Element == Adapter.Element { return { source in let delegateSubscription = self.setDelegate(adapter) let dataSourceSubscription = source.subscribeProxyDataSource(ofObject: self.base, dataSource: adapter, retainDataSource: true, binding: { [weak pickerView = self.base] (_: RxPickerViewDataSourceProxy, event) in guard let pickerView = pickerView else { return } adapter.pickerView(pickerView, observedEvent: event) }) return Disposables.create(delegateSubscription, dataSourceSubscription) } }

  • 此函数接收一个遵守RxPickerViewDataSourceTypeUIPickerViewDataSourceUIPickerViewDelegate这三个协议的参数adapter,返回一个(_ source: Source) -> Disposable绑定数据的闭包
  • 构建返回闭包,将原函数参数adapter通过setDelegate函数设置给RxDelegate代理对象的forwardDelegate
  • 将原函数参数adapter通过原函数参数sourcesubscribeProxyDataSource(见下方分析)函数设置给RxDataSource代理对象的forwardDelegate,并在binding参数的闭包中执行adapterfunc pickerView(_ pickerView: UIPickerView, observedEvent: Event)函数绑定数据。
  • 最后将setDelegatesubscribeProxyDataSource函数返回的Disposables组合成一个Disposables返回。
itemTitlesitemAttributedTitlesitems这三个函数分别是将一个Observable中的数据绑定到UIPickerView的标题、富文本标题、以及view上:
public func itemTitles (_ source: Source) -> (_ titleForRow: @escaping (Int, Sequence.Element) -> String?) -> Disposable where Source.Element == Sequence { return { titleForRow in let adapter = RxStringPickerViewAdapter(titleForRow: titleForRow) return self.items(adapter: adapter)(source) } } public func itemAttributedTitles (_ source: Source) -> (_ attributedTitleForRow: @escaping (Int, Sequence.Element) -> NSAttributedString?) -> Disposable where Source.Element == Sequence { return { attributedTitleForRow in let adapter = RxAttributedStringPickerViewAdapter(attributedTitleForRow: attributedTitleForRow) return self.items(adapter: adapter)(source) } } public func items (_ source: Source) -> (_ viewForRow: @escaping (Int, Sequence.Element, UIView?) -> UIView) -> Disposable where Source.Element == Sequence { return { viewForRow in let adapter = RxPickerViewAdapter(viewForRow: viewForRow) return self.items(adapter: adapter)(source) } }

  • 这三个函数的形式都是一样的,接收一个Observable序列的数据参数,返回一个闭包。
  • 不同的是返回的闭包的参数不一样,分别是将原函数Observable序列的元素转化为StringNSAttributedStringUIView的闭包。
  • 原函数都是直接构建一个闭包返回,在闭包中将转换原函数Observable序列的元素的闭包参数分别构建为RxStringPickerViewAdapterRxAttributedStringPickerViewAdapterRxPickerViewAdapter对象,然后调用前面分析的public func items(adapter: Adapter) -> (_ source: Source) -> Disposable where Source.Element == Adapter.Element函数通过各自的adapter将将原函数Observable序列的元素绑定到UIPickerView并返回Disposable
public func model(at indexPath: IndexPath) throws -> T函数用于获取每个单元格中的数据对象:
public func model(at indexPath: IndexPath) throws -> T { let dataSource: SectionedViewDataSourceType = castOrFatalError(self.dataSource.forwardToDelegate(), message: "This method only works in case one of the `rx.itemTitles, rx.itemAttributedTitles, items(_ source: O)` methods was used.")return castOrFatalError(try dataSource.model(at: indexPath)) }

  • 取出self.dataSource.forwardToDelegate()对象,实际上就是前面设置的adapter对象。
  • 由于adapter对象的基类RxPickerViewArrayDataSource实现了SectionedViewDataSourceType协议中定义的func model(at indexPath: IndexPath) throws -> Any函数,所以返回其调用结果。
使用消息转发的方式实现itemSelected序列,并使用map操作符更改序列元素。modelSelected序列是通过itemSelected序列进行转化而来:
public var itemSelected: ControlEvent<(row: Int, component: Int)> { let source = delegate .methodInvoked(#selector(UIPickerViewDelegate.pickerView(_:didSelectRow:inComponent:))) .map { return (row: try castOrThrow(Int.self, $0[1]), component: try castOrThrow(Int.self, $0[2])) } return ControlEvent(events: source) } public func modelSelected(_ modelType: T.Type) -> ControlEvent { let source = itemSelected.flatMap { [weak view = self.base as UIPickerView] _, component -> Observable in guard let view = view else { return Observable.empty() }let model: [T] = try (0 ..< view.numberOfComponents).map { component in let row = view.selectedRow(inComponent: component) return try view.rx.model(at: IndexPath(row: row, section: component)) }return Observable.just(model) }return ControlEvent(events: source) }

ObservableType扩展分析
func subscribeProxyDataSource(ofObject object: DelegateProxy.ParentObject, dataSource: DelegateProxy.Delegate, retainDataSource: Bool, binding: @escaping (DelegateProxy, Event) -> Void) -> Disposable函数的作用是为Rx代理对象设置forwardDelegate对象和订阅自身时执行binding参数中的闭包相关操作:
extension ObservableType { func subscribeProxyDataSource(ofObject object: DelegateProxy.ParentObject, dataSource: DelegateProxy.Delegate, retainDataSource: Bool, binding: @escaping (DelegateProxy, Event) -> Void) -> Disposable where DelegateProxy.ParentObject: UIView , DelegateProxy.Delegate: AnyObject { let proxy = DelegateProxy.proxy(for: object) let unregisterDelegate = DelegateProxy.installForwardDelegate(dataSource, retainDelegate: retainDataSource, onProxyForObject: object) // this is needed to flush any delayed old state (https://github.com/RxSwiftCommunity/RxDataSources/pull/75) object.layoutIfNeeded()let subscription = self.asObservable() .observeOn(MainScheduler()) .catchError { error in bindingError(error) return Observable.empty() } // source can never end, otherwise it would release the subscriber, and deallocate the data source .concat(Observable.never()) .takeUntil(object.rx.deallocated) .subscribe { [weak object] (event: Event) inif let object = object { assert(proxy === DelegateProxy.currentDelegate(for: object), "Proxy changed from the time it was first set.\nOriginal: \(proxy)\nExisting: \(String(describing: DelegateProxy.currentDelegate(for: object)))") }binding(proxy, event)switch event { case .error(let error): bindingError(error) unregisterDelegate.dispose() case .completed: unregisterDelegate.dispose() default: break } }return Disposables.create { [weak object] in subscription.dispose() object?.layoutIfNeeded() unregisterDelegate.dispose() } } }

  • 这个函数接收三个参数分别是Rx代理对象的持有者object、Rx代理类关联类型DelegatedataSource、是否持有dataSourceretainDataSource、数据绑定闭包binding
  • 首先调用Rx代理类的proxy函数获得Rx代理对象,接着调用installForwardDelegate函数设置Rx代理对象的forwardDelegate
  • 然后本Observable序列执行subscribe操作进行订阅并在其参数on闭包中执行binding闭包。
  • 最后将自身订阅和installForwardDelegate函数放回的Disposable组合成一个返回。
UIPickerView示例 简单的绑定
构建3个UIPickerView分别为pickerView1、pickerView2、pickerView3……
然后构建数据并分别绑定到上面的3个UIPickerView:
// 绑定pickerView1 Observable.just([1, 2, 3, 4, 5, 6, 7, 8, 9]) .bind(to: pickerView1.rx.itemTitles, curriedArgument: { "\($1)" }) .disposed(by: bag) pickerView1.rx.itemSelected .subscribe(onNext: { print("选中了第\($1)列第\($0)行") }) .disposed(by: bag) pickerView1.rx.modelSelected(Int.self) .subscribe(onNext: { print("选中了元素\($0.first ?? 0)") }) .disposed(by: bag)// 绑定pickerView2 Observable.just([1, 2, 3, 4, 5, 6, 7, 8, 9]) .bind(to: pickerView2.rx.itemAttributedTitles, curriedArgument: { NSAttributedString(string: "\($1)", attributes: [NSAttributedString.Key.foregroundColor: UIColor.random]) }) .disposed(by: bag)pickerView2.rx.itemSelected .subscribe(onNext: { print("选中了第\($1)列第\($0)行") }) .disposed(by: bag)pickerView2.rx.modelSelected(Int.self) .subscribe(onNext: { print("选中了元素\($0.first ?? 0)") }) .disposed(by: bag)// 绑定pickerView3 Observable.just(Array(0..<10).map({ (_) -> UIColor in UIColor.random })) .bind(to: pickerView3.rx.items, curriedArgument: { _, color, _ in let view = UIView() view.backgroundColor = color return view }).disposed(by: bag) pickerView3.rx.itemSelected .subscribe(onNext: { print("选中了第\($1)列第\($0)行") }) .disposed(by: bag) pickerView3.rx.modelSelected(UIColor.self) .subscribe(onNext: { print("选中了元素\($0.first ?? UIColor.white)") }) .disposed(by: bag)

数据绑定过程分析
Observable.just([1, 2, 3, 4, 5, 6, 7, 8, 9]) .bind(to: pickerView1.rx.itemTitles, curriedArgument: { "\($1)" }) .disposed(by: bag)

RxCocoa对func bind(to binder: (Self) -> (R1) -> R2, curriedArgument: R1) -> R2函数的实现:
public func bind(to binder: (Self) -> (R1) -> R2, curriedArgument: R1) -> R2 { return binder(self)(curriedArgument) }

泛型R1(Int, Sequence.Element) -> String?类型闭包,泛型R2Disposable类型。于是上面数据绑定的代码等价于:
pickerView1.rx .itemTitles(Observable.just([1, 2, 3, 4, 5, 6, 7, 8, 9]))({ "\($1)" }) .disposed(by: bag)

数据绑定的完整过程:
  • 构建元素是一个[int]类型的原数据Observable序列。
  • 把原数据Observable序列作为参数执行pickerView1.rx.itemTitles函数,返回一个(titleForRow: (Int, Sequence.Element) -> String?) -> Disposable类型的闭包。
  • 将类型为(Int, Sequence.Element) -> String?的参数curriedArgument这个闭包再作为titleForRow参数执行上面的(titleForRow: (Int, Sequence.Element) -> String?) -> Disposable闭包。
  • (titleForRow: (Int, Sequence.Element) -> String?) -> Disposable闭包内部使用titleForRow闭包构建RxStringPickerViewAdapter类型对象adapter
  • 然后用adapter作为参数执行func items(adapter: Adapter) -> (source: Source) -> Disposable函数返回类型为(source: Source) -> Disposable的闭包。
  • 将原数据Observable序列作为参数执行(source: Source) -> Disposable闭包。
  • 闭包(source: Source) -> Disposable内部执行setDelegate函数设置RxPickerViewDelegateProxy类型的forwardDelegate属性。
  • 闭包(source: Source) -> Disposable内部执行subscribeProxyDataSource函数设置RxPickerViewDataSourceProxy类型的forwardDelegate属性。
  • subscribeProxyDataSource函数内部订阅原数据Observable序列将[int]类型的元素作为参数执行adapter对象的func pickerView(pickerView: UIPickerView, observedEvent: Event)函数。
  • 最后func pickerView(pickerView: UIPickerView, observedEvent: Event)函数中将[int]类型的元素保存到adapter对象的items属性中并执行reloadAllComponents刷新UIPickerView
  • UIPickerView在刷新时会调用其代理对象(RxPickerViewDelegateProxyRxPickerViewDataSourceProxy)的函数,然后在实现的协议函数中执行其forwardDelegate属性相应的函数返回数据或者通过消息转发的方式执行forwardDelegate中的函数。
pickerView1.rx.itemSelected .subscribe(onNext: { print("选中了第\($1)列第\($0)行") }) .disposed(by: bag) pickerView1.rx.modelSelected(Int.self) .subscribe(onNext: { print("选中了元素\($0.first ?? 0)") }) .disposed(by: bag)

  • itemSelected通过消息转发实现。
  • modelSelecteditemSelected通过flatMap操作符转化Observable序列的行列号元素为实际数据。
pickerView1pickerView2pickerView3的数据绑定过程是一样的。区别无非是将数据分别绑定到单元格的titleattributedTitleview
自定义绑定
从对RxPickerViewArrayDataSource的分析可以看出RxCocoa只实现了将单组数据绑定到UIPickerView单元格的titleattributedTitleview
这往往满足不了日常的开发需要,所以自定义绑定显得很重要。
实现多组数据绑定 参照RxCocoa实现的RxStringPickerViewAdapterRxAttributedStringPickerViewAdapterRxPickerViewAdapter实现一个支持多组数据的基类BaseSectionedPickerViewAdapter
class BaseSectionedPickerViewAdapter: NSObject, UIPickerViewDataSource, UIPickerViewDelegate, RxPickerViewDataSourceType, SectionedViewDataSourceType { typealias Element = [[CustomStringConvertible]] fileprivate var items: Element = []func model(at indexPath: IndexPath) throws -> Any { items[indexPath.section][indexPath.row] }func numberOfComponents(in pickerView: UIPickerView) -> Int { items.count } func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int { items[component].count }func pickerView(_ pickerView: UIPickerView, observedEvent: Event) { Binder(self) { (adapter, items) in adapter.items = items pickerView.reloadAllComponents() }.on(observedEvent) } }

实现一个简单的SimpleSectionedPickerViewAdapter,实现func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String?函数将数据的description属性简单地绑定到title上:
class SimpleSectionedPickerViewAdapter: BaseSectionedPickerViewAdapter { func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? { return items[component][row].description } }

在页面上搭建一个UIPickerView命名为pickerView,然后进行数据绑定:
Observable.just([Array(0..<10), Array(10..<20), Array(20..<30)]) .bind(to: pickerView.rx.items(adapter: SimpleSectionedPickerViewAdapter())) .disposed(by: bag)

RxCocoafunc bind(to binder: (Self) -> Result) -> Result函数的实现:
public func bind(to binder: (Self) -> Result) -> Result { return binder(self) }

泛型ResultDisposable类型。于是上面数据绑定的代码等价于:
pickerView.rx .items(adapter: SimpleSectionedPickerViewAdapter())(Observable.just([Array(0..<10), Array(10..<20), Array(20..<30)])) .disposed(by: bag)

数据绑定的完整过程:
  • 首先构建一个Observable序列的原数据Observable.just([Array(0..<10), Array(10..<20), Array(20..<30)])
  • 构建一个PickerViewViewAdapter类型对象作为参数执行func items(adapter: Adapter) -> (_ source: Source) -> Disposable函数返回类型为(source: Source) -> Disposable的闭包。
  • 将原数据Observable序列作为参数执行(source: Source) -> Disposable闭包。
  • 接下来的过程与上面的简单绑定过程完全一致。
就这样实现了多组数据的简单绑定,但是实际开发中往往需要控制UIPickerView单元格的宽、高、标题、视图等内容,接下来实现更全面的数据绑定。
实现完整的数据绑定 实现一个SectionedPickerViewAdapter,与Rxcocoa实现的RxPickerViewAdapter类似:
class : BaseSectionedPickerViewAdapter, UIPickerViewDelegate { private var viewForRow: ((UIPickerView, Int, Int, UIView?) -> UIView)? private var titleForRow: ((UIPickerView, Int, Int) -> String)? private var attributedTitleForRow: ((UIPickerView, Int, Int) -> NSAttributedString)? private var widthForComponent: ((UIPickerView, Int) -> CGFloat)? private var heightForComponent: ((UIPickerView, Int) -> CGFloat)?func pickerView(_ pickerView: UIPickerView, viewForRow row: Int, forComponent component: Int, reusing view: UIView?) -> UIView { if let aView = viewForRow { return aView(pickerView, row, component, view) } else { let label = UILabel() label.textAlignment = .center label.font = UIFont.systemFont(ofSize: 21.0) if let aAttributedTitle = attributedTitleForRow { label.attributedText = aAttributedTitle(pickerView, row, component) } else if let aTitle = titleForRow { label.text = aTitle(pickerView, row, component) } else { label.text = items[component][row].description }return label } }func pickerView(_ pickerView: UIPickerView, widthForComponent component: Int) -> CGFloat { if let width = widthForComponent { return width(pickerView, component) }return floor(UIScreen.main.bounds.width/CGFloat(items.count)) }func pickerView(_ pickerView: UIPickerView, rowHeightForComponent component: Int) -> CGFloat { if let aHeight = heightForComponent { return aHeight(pickerView, component) }return 34.0 }init(viewForRow: ((UIPickerView, Int, Int, UIView?) -> UIView)?, widthForComponent: ((UIPickerView, Int) -> CGFloat)? = nil, heightForComponent: ((UIPickerView, Int) -> CGFloat)? = nil, titleForRow: ((UIPickerView, Int, Int) -> String)? = nil, attributedTitleForRow: ((UIPickerView, Int, Int) -> NSAttributedString)? = nil) { super.init() self.viewForRow = viewForRow self.widthForComponent = widthForComponent self.heightForComponent = heightForComponent } }

扩展Base类型为UIPickerViewReactive实现一个sectionedItems函数,与RxCocoafunc items(_ source: Source) -> (_ viewForRow: @escaping (Int, Sequence.Element, UIView?) -> UIView) -> Disposable函数类似:
extension Reactive where Base: UIPickerView { func sectionedItems (_ source: Observable) -> ((viewForRow: (UIPickerView, Int, Int, UIView?) -> UIView, widthForComponent: ((UIPickerView, Int) -> CGFloat)?, heightForComponent: ((UIPickerView, Int) -> CGFloat)?, titleForRow:((UIPickerView, Int, Int) -> String)?, attributedTitleForRow: ((UIPickerView, Int, Int) -> NSAttributedString)?)) -> Disposable { return { arg in let adapter = SectionedPickerViewAdapter(viewForRow: arg.0, widthForComponent: arg.1, heightForComponent: arg.2, titleForRow: arg.3, attributedTitleForRow: arg.4) return items(adapter: adapter)(source) } } }

最后进行数据绑定,其绑定过程与前边分析基本一致:
Observable.just([Array(0..<10), Array(10..<100), Array(100..<1000)]) .bind(to: centerPickerView.rx.sectionedItems, curriedArgument: ({ (_, row, component, _) in let label = UILabel() label.font = UIFont.systemFont(ofSize: 12.0) label.textAlignment = .center label.backgroundColor = UIColor.random label.text = data[component][row].description return label }, { _, component in switch component { case 0: return 40.0 case 1: return 80.0 default: return 120.0 } }, { (_, _) in 50.0 }, nil, nil)) .disposed(by: bag)

【RxSwift官方实例七(UIPickerView)】通过前面对RxCocoa实现UIPickerView绑定的分析,也可以通过如下方式绑定数据:
let adapter = SectionedPickerViewAdapter(viewForRow: { (_, row, component, _) -> UIView in let label = UILabel() label.font = UIFont.systemFont(ofSize: 12.0) label.textAlignment = .center label.backgroundColor = UIColor.random label.text = data[component][row].description return label }, widthForComponent: { _, component in switch component { case 0: return 120.0 case 1: return 80.0 default: return 40.0 } }, heightForComponent: { (_, _) in 40.0 }) Observable.just([Array(0..<10), Array(10..<100), Array(100..<1000)]) .bind(to: bottomPickerView.rx.items(adapter: adapter)) .disposed(by: bag)

    推荐阅读