RxSwift/RxCocoa/iOS/UICollectionView+Rx.swift

195 lines
7.2 KiB
Swift
Raw Normal View History

//
// UICollectionView+Rx.swift
// RxCocoa
//
// Created by Krunoslav Zaher on 4/2/15.
// Copyright (c) 2015 Krunoslav Zaher. All rights reserved.
//
#if os(iOS) || os(tvOS)
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
import UIKit
// Items
extension UICollectionView {
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.
*/
public func rx_itemsWithCellFactory<S: SequenceType, O: ObservableType where O.E == S>
(source: O)
(cellFactory: (UICollectionView, Int, S.Generator.Element) -> UICollectionViewCell)
-> Disposable {
let dataSource = RxCollectionViewReactiveArrayDataSourceSequenceWrapper<S>(cellFactory: cellFactory)
return self.rx_itemsWithDataSource(dataSource)(source: source)
}
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.
*/
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)
-> Disposable {
let dataSource = RxCollectionViewReactiveArrayDataSourceSequenceWrapper<S> { (cv, i, item) in
let indexPath = NSIndexPath(forItem: i, inSection: 0)
let cell = cv.dequeueReusableCellWithReuseIdentifier(cellIdentifier, forIndexPath: indexPath) as! Cell
configureCell(i, item, cell)
return cell
}
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.
*/
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-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-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-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-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)
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:`.
*/
public var rx_itemSelected: ControlEvent<NSIndexPath> {
let source = rx_delegate.observe("collectionView:didSelectItemAtIndexPath:")
.map { a in
2015-07-05 19:40:06 +03:00
return a[1] as! NSIndexPath
}
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.
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
*/
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()
}
return just(try view.rx_modelAtIndexPath(indexPath))
}
return ControlEvent(source: source)
}
/**
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 {
throw RxCocoaError.ItemsNotYetBound(object: self)
}
return element
}
2015-07-05 19:40:06 +03:00
}
#endif
#if os(tvOS)
extension UICollectionView {
/**
Reactive wrapper for `delegate` message `collectionView:didUpdateFocusInContext:withAnimationCoordinator:`.
*/
public var rx_didUpdateFocusInContextWithAnimationCoordinator: ControlEvent<(context: UIFocusUpdateContext, animationCoordinator: UIFocusAnimationCoordinator)> {
let source = rx_delegate.observe("collectionView:didUpdateFocusInContext:withAnimationCoordinator:")
.map { a -> (context: UIFocusUpdateContext, animationCoordinator: UIFocusAnimationCoordinator) in
let context = a[1] as! UIFocusUpdateContext
let animationCoordinator = a[2] as! UIFocusAnimationCoordinator
return (context: context, animationCoordinator: animationCoordinator)
}
return ControlEvent(source: source)
}
}
#endif