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 )
#if ! RX_NO_MODULE
import RxSwift
#endif
import UIKit
2017-01-08 18:49:42 +03:00
extension UIPickerView {
2016-10-23 20:26:22 +03:00
2017-01-08 18:49:42 +03:00
// / F a c t o r y m e t h o d t h a t e n a b l e s s u b c l a s s e s t o i m p l e m e n t t h e i r o w n ` d e l e g a t e ` .
// /
// / - r e t u r n s : I n s t a n c e o f d e l e g a t e p r o x y t h a t w r a p s ` d e l e g a t e ` .
public func createRxDelegateProxy ( ) -> RxPickerViewDelegateProxy {
return RxPickerViewDelegateProxy ( parentObject : self )
}
2017-07-05 19:01:29 +03:00
/* *
Factory method that enables subclasses to implement their own ` rx . dataSource ` .
- returns : Instance of delegate proxy that wraps ` dataSource ` .
*/
public func createRxDataSourceProxy ( ) -> RxPickerViewDataSourceProxy {
return RxPickerViewDataSourceProxy ( parentObject : self )
}
2016-05-13 10:55:21 +03:00
}
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 .
public var delegate : DelegateProxy {
return RxPickerViewDelegateProxy . proxyForObject ( base )
}
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 .
*/
public var dataSource : DelegateProxy {
return RxPickerViewDataSourceProxy . proxyForObject ( base )
}
/* *
Reactive wrapper for ` delegate ` message ` pickerView : didSelectRow : inComponent : ` .
*/
2017-01-08 18:49:42 +03:00
public var itemSelected : ControlEvent < ( Int , Int ) > {
let source = delegate
. methodInvoked ( #selector ( UIPickerViewDelegate . pickerView ( _ : didSelectRow : inComponent : ) ) )
. map {
return ( try castOrThrow ( Int . self , $0 [ 1 ] ) , try castOrThrow ( Int . self , $0 [ 2 ] ) )
}
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
*/
public func modelSelected < T > ( _ modelType : T . Type ) -> ControlEvent < T > {
let source = itemSelected . flatMap { [ weak view = self . base as UIPickerView ] ( row , _ ) -> Observable < T > in
guard let view = view else {
return Observable . empty ( )
}
return Observable . just ( try view . rx . model ( at : row ) )
}
return ControlEvent ( events : source )
}
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 )
*/
public func itemTitles < S : Sequence , O : ObservableType >
( _ source : O )
-> ( _ titleForRow : @ escaping ( Int , S . Iterator . Element ) -> String ? )
-> Disposable where O . E = = S {
return { titleForRow in
let adapter = RxStringPickerViewAdapter < S > ( titleForRow : titleForRow )
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 )
*/
public func itemAttributedTitles < S : Sequence , O : ObservableType >
( _ source : O )
-> ( _ attributedTitleForRow : @ escaping ( Int , S . Iterator . Element ) -> NSAttributedString ? )
-> Disposable where O . E = = S {
return { attributedTitleForRow in
let adapter = RxAttributedStringPickerViewAdapter < S > ( attributedTitleForRow : attributedTitleForRow )
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 )
*/
public func items < S : Sequence , O : ObservableType >
( _ source : O )
-> ( _ viewForRow : @ escaping ( Int , S . Iterator . Element , UIView ? ) -> UIView )
-> Disposable where O . E = = S {
return { viewForRow in
let adapter = RxPickerViewAdapter < S > ( viewForRow : viewForRow )
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 .
*/
2017-07-05 19:01:29 +03:00
public func items < O : ObservableType ,
Adapter : RxPickerViewDataSourceType & UIPickerViewDataSource & UIPickerViewDelegate > ( adapter : Adapter )
-> ( _ source : O )
-> Disposable where O . E = = 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 )
}
}
2017-07-14 13:13:03 +03:00
/* *
Synchronous helper method for retrieving a model at indexPath through a reactive data source .
*/
public func model < T > ( at index : Int ) throws -> T {
let dataSource : ViewDataSourceType = 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 : index ) )
}
2016-05-13 10:55:21 +03:00
}
#endif