2015-04-08 01:28:38 +03:00
//
// U I C o l l e c t i o n V i e w + R x . s w i f t
// R x C o c o a
//
// C r e a t e d b y K r u n o s l a v Z a h e r o n 4 / 2 / 1 5 .
// C o p y r i g h t ( c ) 2 0 1 5 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 .
//
2015-09-30 15:02:41 +03:00
#if os ( iOS ) || os ( tvOS )
2015-04-08 01:28:38 +03:00
import Foundation
2015-08-01 16:13:26 +03:00
#if ! RX_NO_MODULE
2015-04-10 02:52:51 +03:00
import RxSwift
2015-08-01 16:13:26 +03:00
#endif
2015-05-17 20:07:24 +03:00
import UIKit
2015-04-08 01:28:38 +03:00
2015-08-29 22:20:44 +03:00
// I t e m s
2015-08-10 02:43:30 +03:00
2015-08-29 22:20:44 +03:00
extension UICollectionView {
2015-08-10 02:43:30 +03:00
2015-09-09 12:29:39 +03:00
/* *
Binds sequences of elements to collection view items .
- parameter source : Observable sequence of items .
- parameter cellFactory : Transform between sequence elements and view cells .
- returns : Disposable object that can be used to unbind .
*/
2015-08-29 22:20:44 +03:00
public func rx_itemsWithCellFactory < S : SequenceType , O : ObservableType where O . E = = S >
( source : O )
( cellFactory : ( UICollectionView , Int , S . Generator . Element ) -> UICollectionViewCell )
2015-08-10 02:43:30 +03:00
-> Disposable {
2015-08-29 22:20:44 +03:00
let dataSource = RxCollectionViewReactiveArrayDataSourceSequenceWrapper < S > ( cellFactory : cellFactory )
return self . rx_itemsWithDataSource ( dataSource ) ( source : source )
2015-08-10 02:43:30 +03:00
}
2015-09-09 12:29:39 +03:00
/* *
Binds sequences of elements to collection view items .
- parameter cellIdentifier : Identifier used to dequeue cells .
- parameter source : Observable sequence of items .
- parameter configureCell : Transform between sequence elements and view cells .
- returns : Disposable object that can be used to unbind .
*/
2015-08-29 22:20:44 +03:00
public func rx_itemsWithCellIdentifier < S : SequenceType , Cell : UICollectionViewCell , O : ObservableType where O . E = = S >
( cellIdentifier : String )
( source : O )
( configureCell : ( Int , S . Generator . Element , Cell ) -> Void )
2015-08-10 02:43:30 +03:00
-> Disposable {
2015-08-29 22:20:44 +03:00
let dataSource = RxCollectionViewReactiveArrayDataSourceSequenceWrapper < S > { ( cv , i , item ) in
2015-08-10 02:43:30 +03:00
let indexPath = NSIndexPath ( forItem : i , inSection : 0 )
let cell = cv . dequeueReusableCellWithReuseIdentifier ( cellIdentifier , forIndexPath : indexPath ) as ! Cell
configureCell ( i , item , cell )
return cell
}
2015-08-29 22:20:44 +03:00
return self . rx_itemsWithDataSource ( dataSource ) ( source : source )
}
2015-09-09 12:29:39 +03:00
/* *
Binds sequences of elements to collection view items using a custom reactive data used to perform the transformation .
- parameter dataSource : Data source used to transform elements to view cells .
- parameter source : Observable sequence of items .
- returns : Disposable object that can be used to unbind .
*/
2015-08-29 22:20:44 +03:00
public func rx_itemsWithDataSource < DataSource : protocol < RxCollectionViewDataSourceType , UICollectionViewDataSource > , S : SequenceType , O : ObservableType where DataSource . Element = = S , O . E = = S >
( dataSource : DataSource )
( source : O )
-> Disposable {
return source . subscribeProxyDataSourceForObject ( self , dataSource : dataSource , retainDataSource : false ) { [ weak self ] ( _ : RxCollectionViewDataSourceProxy , event ) -> Void in
guard let collectionView = self else {
return
}
dataSource . collectionView ( collectionView , observedEvent : event )
}
2015-08-10 02:43:30 +03:00
}
}
2015-07-03 21:57:04 +03:00
extension UICollectionView {
2015-09-09 12:29:39 +03:00
/* *
Factory method that enables subclasses to implement their own ` rx_delegate ` .
2015-04-08 01:28:38 +03:00
2015-09-09 12:29:39 +03:00
- returns : Instance of delegate proxy that wraps ` delegate ` .
*/
2015-07-05 12:34:31 +03:00
override func rx_createDelegateProxy ( ) -> RxScrollViewDelegateProxy {
return RxCollectionViewDelegateProxy ( parentObject : self )
2015-04-08 01:28:38 +03:00
}
2015-06-29 01:54:05 +03:00
2015-09-09 12:29:39 +03:00
/* *
Reactive wrapper for ` dataSource ` .
2015-07-05 12:34:31 +03:00
2015-09-09 12:29:39 +03:00
For more information take a look at ` DelegateProxyType ` protocol documentation .
*/
2015-07-05 12:34:31 +03:00
public var rx_dataSource : DelegateProxy {
get {
return proxyForObject ( self ) as RxCollectionViewDataSourceProxy
}
2015-04-08 01:28:38 +03:00
}
2015-09-09 12:29:39 +03:00
/* *
Installs data source as forwarding delegate on ` rx_dataSource ` .
It enables using normal delegate mechanism with reactive delegate mechanism .
- parameter dataSource : Data source object .
- returns : Disposable object that can be used to unbind the data source .
*/
2015-07-05 19:40:06 +03:00
public func rx_setDataSource ( dataSource : UICollectionViewDataSource )
-> Disposable {
let proxy : RxCollectionViewDataSourceProxy = proxyForObject ( self )
2015-07-06 09:35:19 +03:00
return installDelegate ( proxy , delegate : dataSource , retainDelegate : false , onProxyForObject : self )
2015-07-05 19:40:06 +03:00
}
2015-09-09 12:29:39 +03:00
/* *
Reactive wrapper for ` delegate ` message ` collectionView : didSelectItemAtIndexPath : ` .
*/
2015-08-29 22:20:44 +03:00
public var rx_itemSelected : ControlEvent < NSIndexPath > {
let source = rx_delegate . observe ( " collectionView:didSelectItemAtIndexPath: " )
2015-08-10 02:43:30 +03:00
. map { a in
2015-07-05 19:40:06 +03:00
return a [ 1 ] as ! NSIndexPath
}
2015-08-29 22:20:44 +03:00
return ControlEvent ( source : source )
2015-07-03 21:57:04 +03:00
}
2015-09-09 12:29:39 +03:00
/* *
Reactive wrapper for ` delegate ` message ` collectionView : didSelectItemAtIndexPath : ` .
It can be only used when one of the ` rx_itemsWith * ` methods is used to bind observable sequence .
2015-11-28 13:58:58 +03:00
collectionView . rx_modelSelected ( MyModel . self )
. map { . . .
2015-09-09 12:29:39 +03:00
If custom data source is being bound , new ` rx_modelSelected ` wrapper needs to be written also .
public func rx_myModelSelected < T > ( ) -> ControlEvent < T > {
let source : Observable < T > = rx_itemSelected . map { indexPath in
let dataSource : MyDataSource = self . rx_dataSource . forwardToDelegate ( ) as ! MyDataSource
return dataSource . modelAtIndex ( indexPath . item ) !
}
return ControlEvent ( source : source )
}
2015-07-03 21:57:04 +03:00
2015-09-09 12:29:39 +03:00
*/
2015-11-28 13:58:58 +03:00
public func rx_modelSelected < T > ( modelType : T . Type ) -> ControlEvent < T > {
2015-10-19 01:52:11 +03:00
let source : Observable < T > = rx_itemSelected . flatMap { [ weak self ] indexPath -> Observable < T > in
guard let view = self else {
return empty ( )
}
2015-10-19 02:15:09 +03:00
return just ( try view . rx_modelAtIndexPath ( indexPath ) )
2015-10-18 20:23:53 +03:00
}
2015-10-16 22:20:13 +03:00
return ControlEvent ( source : source )
}
2015-10-18 20:23:53 +03:00
/* *
Syncronous helper method for retrieving a model at indexPath through a reactive data source
*/
public func rx_modelAtIndexPath < T > ( indexPath : NSIndexPath ) throws -> T {
let dataSource : RxCollectionViewReactiveArrayDataSource < T > = castOrFatalError ( self . rx_dataSource . forwardToDelegate ( ) , message : " This method only works in case one of the `rx_itemsWith*` methods was used. " )
guard let element = dataSource . modelAtIndex ( indexPath . item ) else {
2015-11-09 02:44:59 +03:00
throw RxCocoaError . ItemsNotYetBound ( object : self )
2015-04-08 01:28:38 +03:00
}
2015-08-29 22:20:44 +03:00
2015-10-18 20:23:53 +03:00
return element
2015-04-08 01:28:38 +03:00
}
2015-07-05 19:40:06 +03:00
}
2015-10-15 04:40:51 +03:00
#endif
#if os ( tvOS )
extension UICollectionView {
2015-10-16 22:20:13 +03:00
2015-10-15 04:40:51 +03:00
/* *
Reactive wrapper for ` delegate ` message ` collectionView : didUpdateFocusInContext : withAnimationCoordinator : ` .
*/
2015-10-18 20:23:53 +03:00
public var rx_didUpdateFocusInContextWithAnimationCoordinator : ControlEvent < ( context : UIFocusUpdateContext , animationCoordinator : UIFocusAnimationCoordinator ) > {
2015-10-16 22:20:13 +03:00
2015-10-15 04:40:51 +03:00
let source = rx_delegate . observe ( " collectionView:didUpdateFocusInContext:withAnimationCoordinator: " )
2015-10-18 20:23:53 +03:00
. map { a -> ( context : UIFocusUpdateContext , animationCoordinator : UIFocusAnimationCoordinator ) in
2015-10-16 22:20:13 +03:00
let context = a [ 1 ] as ! UIFocusUpdateContext
let animationCoordinator = a [ 2 ] as ! UIFocusAnimationCoordinator
return ( context : context , animationCoordinator : animationCoordinator )
}
return ControlEvent ( source : source )
}
2015-10-15 04:40:51 +03:00
}
2015-09-30 15:02:41 +03:00
#endif