RxSwift官方实例七(UIPickerView)
代码下载
UIPickerView的Rx实现分析
RxPickerViewDelegateProxy分析
RxCocoa
已经实现了RxPickerViewDelegateProxy
,该类继承DelegateProxy
基类,遵守DelegateProxyType
、UIPickerViewDelegate
协议:
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
基类,遵守DelegateProxyType
、UIPickerViewDataSource
协议: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
是用来处理UIPickerViewDataSource
、UIPickerViewDelegate
协议中定义的带有返回值的函数的。RxCocoa
实现了3种RxPickerViewAdapter
,分别是RxStringPickerViewAdapter
、RxAttributedStringPickerViewAdapter
、RxPickerViewAdapter
。RxPickerViewArrayDataSource分析
RxPickerViewArrayDataSource
是RxStringPickerViewAdapter
、RxAttributedStringPickerViewAdapter
、RxPickerViewAdapter
的根类,遵守UIPickerViewDataSource
、SectionedViewDataSourceType
协议,还定义一个泛型
作为通用数据类型: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
这个类继承自RxPickerViewArrayDataSource
,是RxStringPickerViewAdapter
、RxAttributedStringPickerViewAdapter
、RxPickerViewAdapter
的父类,遵守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
操作,实际相当于直接执行构建Binder
的binding
闭包。 - 将
Event
类型参数observedEvent
转化为Event
作为on
操作的参数。 - 在构建
Binder
的binding
闭包中,将数组赋值给items
属性并刷新整个UIPickerView
。
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
闭包并返回其结果。RxAttributedStringPickerViewAdapter
、RxPickerViewAdapter
的实现与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
并返回一个置空forwardDelegate
的Disposables
: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)
}
}
- 此函数接收一个遵守
RxPickerViewDataSourceType
、UIPickerViewDataSource
、UIPickerViewDelegate
这三个协议的参数adapter
,返回一个(_ source: Source) -> Disposable
绑定数据的闭包 - 构建返回闭包,将原函数参数
adapter
通过setDelegate
函数设置给RxDelegate
代理对象的forwardDelegate
。 - 将原函数参数
adapter
通过原函数参数source
的subscribeProxyDataSource
(见下方分析)函数设置给RxDataSource
代理对象的forwardDelegate
,并在binding
参数的闭包中执行adapter
的func pickerView(_ pickerView: UIPickerView, observedEvent: Event
函数绑定数据。) - 最后将
setDelegate
和subscribeProxyDataSource
函数返回的Disposables
组合成一个Disposables
返回。
itemTitles
,itemAttributedTitles
,items
这三个函数分别是将一个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
序列的元素转化为String
、NSAttributedString
、UIView
的闭包。 - 原函数都是直接构建一个闭包返回,在闭包中将转换原函数
Observable
序列的元素的闭包参数分别构建为RxStringPickerViewAdapter
、RxAttributedStringPickerViewAdapter
、RxPickerViewAdapter
对象,然后调用前面分析的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代理类关联类型Delegate
的dataSource
、是否持有dataSource
的retainDataSource
、数据绑定闭包binding
。 - 首先调用Rx代理类的
proxy
函数获得Rx代理对象,接着调用installForwardDelegate
函数设置Rx代理对象的forwardDelegate
。 - 然后本
Observable
序列执行subscribe
操作进行订阅并在其参数on
闭包中执行binding
闭包。 - 最后将自身订阅和
installForwardDelegate
函数放回的Disposable
组合成一个返回。
构建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?
类型闭包,泛型R2
为Disposable
类型。于是上面数据绑定的代码等价于: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
在刷新时会调用其代理对象(RxPickerViewDelegateProxy
、RxPickerViewDataSourceProxy
)的函数,然后在实现的协议函数中执行其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
通过消息转发实现。 -
modelSelected
将itemSelected
通过flatMap
操作符转化Observable
序列的行列号元素为实际数据。
pickerView1
、pickerView2
、pickerView3
的数据绑定过程是一样的。区别无非是将数据分别绑定到单元格的title
、attributedTitle
、view
。自定义绑定
从对
RxPickerViewArrayDataSource
的分析可以看出RxCocoa
只实现了将单组数据绑定到UIPickerView
单元格的title
、attributedTitle
、view
。这往往满足不了日常的开发需要,所以自定义绑定显得很重要。
实现多组数据绑定 参照
RxCocoa
实现的RxStringPickerViewAdapter
、RxAttributedStringPickerViewAdapter
、RxPickerViewAdapter
实现一个支持多组数据的基类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)
RxCocoa
对func bind(to binder: (Self) -> Result) -> Result
函数的实现:public func bind(to binder: (Self) -> Result) -> Result {
return binder(self)
}
泛型
Result
为Disposable
类型。于是上面数据绑定的代码等价于: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
类型为UIPickerView
的Reactive
实现一个sectionedItems
函数,与RxCocoa
的func 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)
推荐阅读
- ts泛型使用举例
- LSTM网络层详解及其应用实例
- 星际无限|星际无限 | 官方推出Filecoin MinerX奖学金计划,吸引中小型Filecoin矿工
- 1040表格和W-2表格
- Python-类和对象
- SpringBoot整合MongoDB完整实例代码
- 世界脑力锦标赛官方公布2018年世界记忆大师新评定标准!
- MySQL|MySQL 存储过程语法及实例
- thinkphp3.2下实现阿里云视频点播实例(客户端JavaScript上传)
- Servlet原理|Servlet原理 二(Web应用与创建Servlet实例)