2016-05-13 10:55:21 +03:00
//
// U I P i c k e r V i e w + R x . s w i f t
2016-10-15 15:38:22 +03:00
// R x C o c o a
2016-05-13 10:55:21 +03:00
//
// C r e a t e d b y S e g i i S h u l g a o n 5 / 1 2 / 1 6 .
// C o p y r i g h t © 2 0 1 6 K r u n o s l a v Z a h e r . A l l r i g h t s r e s e r v e d .
//
#if os ( iOS )
import RxSwift
import UIKit
2017-01-08 18:49:42 +03:00
extension Reactive where Base : UIPickerView {
// / R e a c t i v e w r a p p e r f o r ` d e l e g a t e ` .
// / F o r m o r e i n f o r m a t i o n t a k e a l o o k a t ` D e l e g a t e P r o x y T y p e ` p r o t o c o l d o c u m e n t a t i o n .
2017-09-13 18:12:06 +03:00
public var delegate : DelegateProxy < UIPickerView , UIPickerViewDelegate > {
2017-08-29 06:12:46 +03:00
return RxPickerViewDelegateProxy . proxy ( for : base )
2017-01-08 18:49:42 +03:00
}
2017-07-05 19:01:29 +03:00
// / I n s t a l l s d e l e g a t e a s f o r w a r d i n g d e l e g a t e o n ` d e l e g a t e ` .
// / D e l e g a t e w o n ' t b e r e t a i n e d .
// /
// / I t e n a b l e s u s i n g n o r m a l d e l e g a t e m e c h a n i s m w i t h r e a c t i v e d e l e g a t e m e c h a n i s m .
// /
// / - p a r a m e t e r d e l e g a t e : D e l e g a t e o b j e c t .
// / - r e t u r n s : D i s p o s a b l e o b j e c t t h a t c a n b e u s e d t o u n b i n d t h e d e l e g a t e .
public func setDelegate ( _ delegate : UIPickerViewDelegate )
-> Disposable {
return RxPickerViewDelegateProxy . installForwardDelegate ( delegate , retainDelegate : false , onProxyForObject : self . base )
}
2017-07-14 13:13:03 +03:00
/* *
Reactive wrapper for ` dataSource ` .
For more information take a look at ` DelegateProxyType ` protocol documentation .
*/
2017-09-13 18:12:06 +03:00
public var dataSource : DelegateProxy < UIPickerView , UIPickerViewDataSource > {
2017-08-29 06:12:46 +03:00
return RxPickerViewDataSourceProxy . proxy ( for : base )
2017-07-14 13:13:03 +03:00
}
/* *
Reactive wrapper for ` delegate ` message ` pickerView : didSelectRow : inComponent : ` .
*/
2017-07-16 21:25:58 +03:00
public var itemSelected : ControlEvent < ( row : Int , component : Int ) > {
2017-01-08 18:49:42 +03:00
let source = delegate
. methodInvoked ( #selector ( UIPickerViewDelegate . pickerView ( _ : didSelectRow : inComponent : ) ) )
. map {
2017-07-16 21:25:58 +03:00
return ( row : try castOrThrow ( Int . self , $0 [ 1 ] ) , component : try castOrThrow ( Int . self , $0 [ 2 ] ) )
2017-01-08 18:49:42 +03:00
}
return ControlEvent ( events : source )
}
2017-07-05 19:01:29 +03:00
2017-07-14 13:13:03 +03:00
/* *
Reactive wrapper for ` delegate ` message ` pickerView : didSelectRow : inComponent : ` .
It can be only used when one of the ` rx . itemTitles , rx . itemAttributedTitles , items ( _ source : O ) ` methods is used to bind observable sequence ,
or any other data source conforming to a ` ViewDataSourceType ` protocol .
` ` `
pickerView . rx . modelSelected ( MyModel . self )
. map { . . .
` ` `
- parameter modelType : Type of a Model which bound to the dataSource
*/
2017-07-16 21:25:58 +03:00
public func modelSelected < T > ( _ modelType : T . Type ) -> ControlEvent < [ T ] > {
2019-01-05 20:33:16 +03:00
let source = itemSelected . flatMap { [ weak view = self . base as UIPickerView ] _ , component -> Observable < [ T ] > in
2017-07-14 13:13:03 +03:00
guard let view = view else {
return Observable . empty ( )
2017-01-08 18:49:42 +03:00
}
2017-07-16 21:25:58 +03:00
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 )
2017-07-14 13:13:03 +03:00
}
2017-01-08 18:49:42 +03:00
return ControlEvent ( events : source )
}
2017-07-14 13:13:03 +03:00
2017-07-12 17:52:58 +03:00
/* *
Binds sequences of elements to picker view rows .
- parameter source : Observable sequence of items .
- parameter titleForRow : Transform between sequence elements and row titles .
- returns : Disposable object that can be used to unbind .
Example :
let items = Observable . just ( [
" First Item " ,
" Second Item " ,
" Third Item "
] )
items
. bind ( to : pickerView . rx . itemTitles ) { ( row , element ) in
return element . title
}
. disposed ( by : disposeBag )
*/
2019-04-27 13:28:30 +03:00
public func itemTitles < Sequence : Swift . Sequence , Source : ObservableType >
( _ source : Source )
2019-04-24 23:30:36 +03:00
-> ( _ titleForRow : @ escaping ( Int , Sequence . Element ) -> String ? )
2019-04-27 13:28:30 +03:00
-> Disposable where Source . Element = = Sequence {
2017-07-12 17:52:58 +03:00
return { titleForRow in
2019-04-24 23:30:36 +03:00
let adapter = RxStringPickerViewAdapter < Sequence > ( titleForRow : titleForRow )
2017-07-12 17:52:58 +03:00
return self . items ( adapter : adapter ) ( source )
}
}
/* *
Binds sequences of elements to picker view rows .
- parameter source : Observable sequence of items .
- parameter attributedTitleForRow : Transform between sequence elements and row attributed titles .
- returns : Disposable object that can be used to unbind .
Example :
let items = Observable . just ( [
" First Item " ,
" Second Item " ,
" Third Item "
] )
items
. bind ( to : pickerView . rx . itemAttributedTitles ) { ( row , element ) in
return NSAttributedString ( string : element . title )
}
. disposed ( by : disposeBag )
*/
2019-04-27 13:28:30 +03:00
public func itemAttributedTitles < Sequence : Swift . Sequence , Source : ObservableType >
( _ source : Source )
2019-04-24 23:30:36 +03:00
-> ( _ attributedTitleForRow : @ escaping ( Int , Sequence . Element ) -> NSAttributedString ? )
2019-04-27 13:28:30 +03:00
-> Disposable where Source . Element = = Sequence {
2017-07-12 17:52:58 +03:00
return { attributedTitleForRow in
2019-04-24 23:30:36 +03:00
let adapter = RxAttributedStringPickerViewAdapter < Sequence > ( attributedTitleForRow : attributedTitleForRow )
2017-07-12 17:52:58 +03:00
return self . items ( adapter : adapter ) ( source )
}
}
/* *
Binds sequences of elements to picker view rows .
- parameter source : Observable sequence of items .
- parameter viewForRow : Transform between sequence elements and row views .
- returns : Disposable object that can be used to unbind .
Example :
let items = Observable . just ( [
" First Item " ,
" Second Item " ,
" Third Item "
] )
items
. bind ( to : pickerView . rx . items ) { ( row , element , view ) in
guard let myView = view as ? MyView else {
let view = MyView ( )
view . configure ( with : element )
return view
}
myView . configure ( with : element )
return myView
}
. disposed ( by : disposeBag )
*/
2019-04-27 13:28:30 +03:00
public func items < Sequence : Swift . Sequence , Source : ObservableType >
( _ source : Source )
2019-04-24 23:30:36 +03:00
-> ( _ viewForRow : @ escaping ( Int , Sequence . Element , UIView ? ) -> UIView )
2019-04-27 13:28:30 +03:00
-> Disposable where Source . Element = = Sequence {
2017-07-12 17:52:58 +03:00
return { viewForRow in
2019-04-24 23:30:36 +03:00
let adapter = RxPickerViewAdapter < Sequence > ( viewForRow : viewForRow )
2017-07-12 17:52:58 +03:00
return self . items ( adapter : adapter ) ( source )
}
}
2017-07-10 22:32:35 +03:00
/* *
Binds sequences of elements to picker view rows using a custom reactive adapter used to perform the transformation .
This method will retain the adapter for as long as the subscription isn ' t disposed ( result ` Disposable `
being disposed ) .
In case ` source ` observable sequence terminates successfully , the adapter will present latest element
until the subscription isn ' t disposed .
- parameter adapter : Adapter used to transform elements to picker components .
- parameter source : Observable sequence of items .
- returns : Disposable object that can be used to unbind .
*/
2019-04-27 13:28:30 +03:00
public func items < Source : ObservableType ,
2017-07-05 19:01:29 +03:00
Adapter : RxPickerViewDataSourceType & UIPickerViewDataSource & UIPickerViewDelegate > ( adapter : Adapter )
2019-04-27 13:28:30 +03:00
-> ( _ source : Source )
-> Disposable where Source . Element = = Adapter . Element {
2017-07-05 19:01:29 +03:00
return { source in
let delegateSubscription = self . setDelegate ( adapter )
2017-09-13 18:12:06 +03:00
let dataSourceSubscription = source . subscribeProxyDataSource ( ofObject : self . base , dataSource : adapter , retainDataSource : true , binding : { [ weak pickerView = self . base ] ( _ : RxPickerViewDataSourceProxy , event ) in
2017-07-05 19:01:29 +03:00
guard let pickerView = pickerView else { return }
adapter . pickerView ( pickerView , observedEvent : event )
} )
return Disposables . create ( delegateSubscription , dataSourceSubscription )
}
}
2017-07-14 13:13:03 +03:00
/* *
Synchronous helper method for retrieving a model at indexPath through a reactive data source .
*/
2017-07-15 20:18:27 +03:00
public func model < T > ( 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. " )
2017-07-14 13:13:03 +03:00
2017-07-15 20:18:27 +03:00
return castOrFatalError ( try dataSource . model ( at : indexPath ) )
2017-07-14 13:13:03 +03:00
}
2016-05-13 10:55:21 +03:00
}
#endif