Adds SectionedViewDataSourceType and rx_modelDeselected, rx_deselectedItemAtIndexPath for UICollectionView, and corresponding unit tests.

This commit is contained in:
Krunoslav Zaher 2016-01-10 20:19:30 +01:00
parent c6526e830a
commit eeddf1fe5d
23 changed files with 385 additions and 89 deletions

View File

@ -373,7 +373,6 @@
C83509651C38706E0027C24C /* VirtualSchedulerTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = C835091C1C38706D0027C24C /* VirtualSchedulerTest.swift */; };
C83509661C38706E0027C24C /* RxTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = C835091D1C38706D0027C24C /* RxTest.swift */; };
C83509671C38706E0027C24C /* Foundation+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = C835091F1C38706E0027C24C /* Foundation+Extensions.swift */; };
C83509681C38706E0027C24C /* Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = C83509201C38706E0027C24C /* Info.plist */; };
C83509691C38706E0027C24C /* Recorded+Timeless.swift in Sources */ = {isa = PBXBuildFile; fileRef = C83509211C38706E0027C24C /* Recorded+Timeless.swift */; };
C835096A1C38706E0027C24C /* TestErrors.swift in Sources */ = {isa = PBXBuildFile; fileRef = C83509221C38706E0027C24C /* TestErrors.swift */; };
C835096B1C38706E0027C24C /* XCTest+AllTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C83509231C38706E0027C24C /* XCTest+AllTests.swift */; };
@ -570,6 +569,8 @@
C8554E2D1C3051620052E67D /* PriorityQueue.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8554E291C3051620052E67D /* PriorityQueue.swift */; };
C85BA0591C3878850075D68E /* main.swift in Sources */ = {isa = PBXBuildFile; fileRef = C83508D61C38706D0027C24C /* main.swift */; };
C85BA05A1C3878870075D68E /* PerformanceTools.swift in Sources */ = {isa = PBXBuildFile; fileRef = C83508D71C38706D0027C24C /* PerformanceTools.swift */; };
C860EC951C42E25E00A664B3 /* SectionedViewDataSourceMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8D132521C42DA7F00B59FFF /* SectionedViewDataSourceMock.swift */; };
C860EC961C42E26100A664B3 /* SectionedViewDataSourceMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8D132521C42DA7F00B59FFF /* SectionedViewDataSourceMock.swift */; };
C86409FC1BA593F500D3C4E8 /* Range.swift in Sources */ = {isa = PBXBuildFile; fileRef = C86409FB1BA593F500D3C4E8 /* Range.swift */; };
C86409FD1BA593F500D3C4E8 /* Range.swift in Sources */ = {isa = PBXBuildFile; fileRef = C86409FB1BA593F500D3C4E8 /* Range.swift */; };
C8640A031BA5B12A00D3C4E8 /* Repeat.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8640A021BA5B12A00D3C4E8 /* Repeat.swift */; };
@ -673,6 +674,10 @@
C8C4B4C31C17727000828BD5 /* MessageSentObserver.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8C4B4C01C17727000828BD5 /* MessageSentObserver.swift */; };
C8C4B4C41C17727000828BD5 /* MessageSentObserver.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8C4B4C01C17727000828BD5 /* MessageSentObserver.swift */; };
C8C4B4C51C17727000828BD5 /* MessageSentObserver.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8C4B4C01C17727000828BD5 /* MessageSentObserver.swift */; };
C8D132441C42D15E00B59FFF /* SectionedViewDataSourceType.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8D132431C42D15E00B59FFF /* SectionedViewDataSourceType.swift */; };
C8D132451C42D15E00B59FFF /* SectionedViewDataSourceType.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8D132431C42D15E00B59FFF /* SectionedViewDataSourceType.swift */; };
C8D132461C42D15E00B59FFF /* SectionedViewDataSourceType.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8D132431C42D15E00B59FFF /* SectionedViewDataSourceType.swift */; };
C8D132471C42D15E00B59FFF /* SectionedViewDataSourceType.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8D132431C42D15E00B59FFF /* SectionedViewDataSourceType.swift */; };
C8DB967E1BF7496C0084BD53 /* KVORepresentable.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8DB967D1BF7496C0084BD53 /* KVORepresentable.swift */; };
C8DB967F1BF7496C0084BD53 /* KVORepresentable.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8DB967D1BF7496C0084BD53 /* KVORepresentable.swift */; };
C8DB96801BF7496C0084BD53 /* KVORepresentable.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8DB967D1BF7496C0084BD53 /* KVORepresentable.swift */; };
@ -1601,6 +1606,8 @@
C8C4B4A71C17722400828BD5 /* _RXObjCRuntime.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = _RXObjCRuntime.m; sourceTree = "<group>"; };
C8C4B4A81C17722400828BD5 /* _RXObjCRuntime.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = _RXObjCRuntime.h; sourceTree = "<group>"; };
C8C4B4C01C17727000828BD5 /* MessageSentObserver.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MessageSentObserver.swift; sourceTree = "<group>"; };
C8D132431C42D15E00B59FFF /* SectionedViewDataSourceType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SectionedViewDataSourceType.swift; sourceTree = "<group>"; };
C8D132521C42DA7F00B59FFF /* SectionedViewDataSourceMock.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SectionedViewDataSourceMock.swift; sourceTree = "<group>"; };
C8DB967D1BF7496C0084BD53 /* KVORepresentable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KVORepresentable.swift; sourceTree = "<group>"; };
C8DB96821BF754C80084BD53 /* NSObject+Rx+KVORepresentable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSObject+Rx+KVORepresentable.swift"; sourceTree = "<group>"; };
C8DB96871BF756F40084BD53 /* KVORepresentable+CoreGraphics.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "KVORepresentable+CoreGraphics.swift"; sourceTree = "<group>"; };
@ -2035,6 +2042,7 @@
C8DB96871BF756F40084BD53 /* KVORepresentable+CoreGraphics.swift */,
C8DB968C1BF7595D0084BD53 /* KVORepresentable+Swift.swift */,
C8BCD3F31C14B6D1005F1280 /* NSLayoutConstraint+Rx.swift */,
C8D132431C42D15E00B59FFF /* SectionedViewDataSourceType.swift */,
);
path = Common;
sourceTree = "<group>";
@ -2149,6 +2157,7 @@
C83508D81C38706D0027C24C /* RxCocoaTests */ = {
isa = PBXGroup;
children = (
C8D132511C42DA7F00B59FFF /* TestImplementations */,
C83508D91C38706D0027C24C /* CLLocationManager+RxTests.swift */,
8476A01F1C3D5D580040BA22 /* UIImagePickerController+RxTests.swift */,
C83508DA1C38706D0027C24C /* Control+RxTests+Cocoa.swift */,
@ -2403,6 +2412,14 @@
path = Platform;
sourceTree = "<group>";
};
C8D132511C42DA7F00B59FFF /* TestImplementations */ = {
isa = PBXGroup;
children = (
C8D132521C42DA7F00B59FFF /* SectionedViewDataSourceMock.swift */,
);
path = TestImplementations;
sourceTree = "<group>";
};
C8E3A6FC1C25CE2200643FE6 /* RxTests */ = {
isa = PBXGroup;
children = (
@ -2960,7 +2977,7 @@
isa = PBXProject;
attributes = {
LastSwiftUpdateCheck = 0720;
LastUpgradeCheck = 0710;
LastUpgradeCheck = 0720;
ORGANIZATIONNAME = "Krunoslav Zaher";
TargetAttributes = {
C83508C21C386F6F0027C24C = {
@ -3059,7 +3076,6 @@
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
C83509681C38706E0027C24C /* Info.plist in Resources */,
C83509271C38706E0027C24C /* Info.plist in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
@ -3198,6 +3214,7 @@
C882542A1B8A752B00B02D69 /* UIControl+Rx.swift in Sources */,
C8F6A1451BF0B9B1007DF367 /* NSObject+Rx+RawRepresentable.swift in Sources */,
C8DB967E1BF7496C0084BD53 /* KVORepresentable.swift in Sources */,
C8D132441C42D15E00B59FFF /* SectionedViewDataSourceType.swift in Sources */,
C849EF8B1C3195950048AC4A /* Variable+Driver.swift in Sources */,
C88254341B8A752B00B02D69 /* UITableView+Rx.swift in Sources */,
C88254161B8A752B00B02D69 /* RxCollectionViewReactiveArrayDataSource.swift in Sources */,
@ -3294,6 +3311,7 @@
C8093EF81B8A732E0088E94D /* NSURLSession+Rx.swift in Sources */,
C80DDE9C1BCE69BA006A1832 /* Driver+Operators.swift in Sources */,
C8093F4C1B8A732E0088E94D /* NSSlider+Rx.swift in Sources */,
C8D132451C42D15E00B59FFF /* SectionedViewDataSourceType.swift in Sources */,
C8093EE81B8A732E0088E94D /* ControlTarget.swift in Sources */,
C80D33901B91EF9E0014629D /* Observable+Bind.swift in Sources */,
);
@ -3387,6 +3405,7 @@
C835092C1C38706E0027C24C /* Control+RxTests+UIKit.swift in Sources */,
C83509571C38706E0027C24C /* Observable+CreationTest.swift in Sources */,
C83509321C38706E0027C24C /* DelegateProxyTest.swift in Sources */,
C860EC951C42E25E00A664B3 /* SectionedViewDataSourceMock.swift in Sources */,
C83509431C38706E0027C24C /* MockDisposable.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
@ -3457,6 +3476,7 @@
C83509D01C38752E0027C24C /* RuntimeStateSnapshot.swift in Sources */,
C83509AF1C3874DC0027C24C /* RxTest.swift in Sources */,
C83509F41C38755D0027C24C /* BagTest.swift in Sources */,
C860EC961C42E26100A664B3 /* SectionedViewDataSourceMock.swift in Sources */,
C8350A0F1C3875630027C24C /* Observable+MultipleTest+Zip.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
@ -4080,6 +4100,7 @@
C8F0C0151BBBFBB9001B112F /* UIControl+Rx.swift in Sources */,
C8F6A1481BF0B9B2007DF367 /* NSObject+Rx+RawRepresentable.swift in Sources */,
C8DB96811BF7496C0084BD53 /* KVORepresentable.swift in Sources */,
C8D132471C42D15E00B59FFF /* SectionedViewDataSourceType.swift in Sources */,
C849EF8E1C3195950048AC4A /* Variable+Driver.swift in Sources */,
C8F0C0161BBBFBB9001B112F /* UITableView+Rx.swift in Sources */,
C8F0C0171BBBFBB9001B112F /* RxCollectionViewReactiveArrayDataSource.swift in Sources */,
@ -4169,6 +4190,7 @@
C8F6A1471BF0B9B2007DF367 /* NSObject+Rx+RawRepresentable.swift in Sources */,
C8DB96801BF7496C0084BD53 /* KVORepresentable.swift in Sources */,
C849EF8D1C3195950048AC4A /* Variable+Driver.swift in Sources */,
C8D132461C42D15E00B59FFF /* SectionedViewDataSourceType.swift in Sources */,
D203C4F31BB9C4CA00D02D00 /* RxCollectionViewReactiveArrayDataSource.swift in Sources */,
D203C50B1BB9C53E00D02D00 /* UIScrollView+Rx.swift in Sources */,
C8C4B4AB1C17722400828BD5 /* _RXObjCRuntime.m in Sources */,

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0710"
LastUpgradeVersion = "0720"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0710"
LastUpgradeVersion = "0720"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0710"
LastUpgradeVersion = "0720"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0710"
LastUpgradeVersion = "0720"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0710"
LastUpgradeVersion = "0720"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0710"
LastUpgradeVersion = "0720"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0710"
LastUpgradeVersion = "0720"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0710"
LastUpgradeVersion = "0720"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0710"
LastUpgradeVersion = "0720"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0710"
LastUpgradeVersion = "0720"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0710"
LastUpgradeVersion = "0720"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0710"
LastUpgradeVersion = "0720"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"

View File

@ -0,0 +1,24 @@
//
// SectionedViewDataSourceType.swift
// Rx
//
// Created by Krunoslav Zaher on 1/10/16.
// Copyright © 2016 Krunoslav Zaher. All rights reserved.
//
import Foundation
/**
Data source with access to underlying sectioned model.
*/
public protocol SectionedViewDataSourceType {
/**
Returns model at index path.
In case data source doesn't contain any sections when this method is being called, `RxCocoaError.ItemsNotYetBound(object: self)` is thrown.
- parameter indexPath: Model index path
- returns: Model at index path.
*/
func modelAtIndexPath(indexPath: NSIndexPath) throws -> Any
}

View File

@ -15,7 +15,9 @@ import RxSwift
#endif
// objc monkey business
class _RxCollectionViewReactiveArrayDataSource: NSObject, UICollectionViewDataSource {
class _RxCollectionViewReactiveArrayDataSource
: NSObject
, UICollectionViewDataSource {
func numberOfSectionsInCollectionView(collectionView: UICollectionView) -> Int {
return 1
@ -38,8 +40,9 @@ class _RxCollectionViewReactiveArrayDataSource: NSObject, UICollectionViewDataSo
}
}
class RxCollectionViewReactiveArrayDataSourceSequenceWrapper<S: SequenceType> : RxCollectionViewReactiveArrayDataSource<S.Generator.Element>
, RxCollectionViewDataSourceType {
class RxCollectionViewReactiveArrayDataSourceSequenceWrapper<S: SequenceType>
: RxCollectionViewReactiveArrayDataSource<S.Generator.Element>
, RxCollectionViewDataSourceType {
typealias Element = S
override init(cellFactory: CellFactory) {
@ -61,7 +64,9 @@ class RxCollectionViewReactiveArrayDataSourceSequenceWrapper<S: SequenceType> :
// Please take a look at `DelegateProxyType.swift`
class RxCollectionViewReactiveArrayDataSource<Element> : _RxCollectionViewReactiveArrayDataSource {
class RxCollectionViewReactiveArrayDataSource<Element>
: _RxCollectionViewReactiveArrayDataSource
, SectionedViewDataSourceType {
typealias CellFactory = (UICollectionView, Int, Element) -> UICollectionViewCell
@ -70,6 +75,14 @@ class RxCollectionViewReactiveArrayDataSource<Element> : _RxCollectionViewReacti
func modelAtIndex(index: Int) -> Element? {
return itemModels?[index]
}
func modelAtIndexPath(indexPath: NSIndexPath) throws -> Any {
precondition(indexPath.section == 0)
guard let item = itemModels?[indexPath.item] else {
throw RxCocoaError.ItemsNotYetBound(object: self)
}
return item
}
var cellFactory: CellFactory

View File

@ -15,7 +15,9 @@ import RxSwift
#endif
// objc monkey business
class _RxTableViewReactiveArrayDataSource: NSObject, UITableViewDataSource {
class _RxTableViewReactiveArrayDataSource
: NSObject
, UITableViewDataSource {
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
@ -39,14 +41,15 @@ class _RxTableViewReactiveArrayDataSource: NSObject, UITableViewDataSource {
}
class RxTableViewReactiveArrayDataSourceSequenceWrapper<S: SequenceType> : RxTableViewReactiveArrayDataSource<S.Generator.Element>
, RxTableViewDataSourceType {
class RxTableViewReactiveArrayDataSourceSequenceWrapper<S: SequenceType>
: RxTableViewReactiveArrayDataSource<S.Generator.Element>
, RxTableViewDataSourceType {
typealias Element = S
override init(cellFactory: CellFactory) {
super.init(cellFactory: cellFactory)
}
func tableView(tableView: UITableView, observedEvent: Event<S>) {
switch observedEvent {
case .Next(let value):
@ -60,7 +63,9 @@ class RxTableViewReactiveArrayDataSourceSequenceWrapper<S: SequenceType> : RxTab
}
// Please take a look at `DelegateProxyType.swift`
class RxTableViewReactiveArrayDataSource<Element> : _RxTableViewReactiveArrayDataSource {
class RxTableViewReactiveArrayDataSource<Element>
: _RxTableViewReactiveArrayDataSource
, SectionedViewDataSourceType {
typealias CellFactory = (UITableView, Int, Element) -> UITableViewCell
var itemModels: [Element]? = nil
@ -68,7 +73,15 @@ class RxTableViewReactiveArrayDataSource<Element> : _RxTableViewReactiveArrayDat
func modelAtIndex(index: Int) -> Element? {
return itemModels?[index]
}
func modelAtIndexPath(indexPath: NSIndexPath) throws -> Any {
precondition(indexPath.section == 0)
guard let item = itemModels?[indexPath.item] else {
throw RxCocoaError.ItemsNotYetBound(object: self)
}
return item
}
let cellFactory: CellFactory
init(cellFactory: CellFactory) {

View File

@ -133,27 +133,29 @@ extension UICollectionView {
return ControlEvent(events: source)
}
/**
Reactive wrapper for `delegate` message `collectionView:didSelectItemAtIndexPath:`.
*/
public var rx_itemDeselected: ControlEvent<NSIndexPath> {
let source = rx_delegate.observe("collectionView:didDeselectItemAtIndexPath:")
.map { a in
return a[1] as! NSIndexPath
}
return ControlEvent(events: source)
}
/**
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,
or any other data source conforming to `SectionedViewDataSourceType` protocol.
It can be only used when one of the `rx_itemsWith*` methods is used to bind observable sequence.
```
collectionView.rx_modelSelected(MyModel.self)
.map { ...
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)
}
```
*/
public func rx_modelSelected<T>(modelType: T.Type) -> ControlEvent<T> {
let source: Observable<T> = rx_itemSelected.flatMap { [weak self] indexPath -> Observable<T> in
@ -166,18 +168,39 @@ extension UICollectionView {
return ControlEvent(events: source)
}
/**
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,
or any other data source conforming to `SectionedViewDataSourceType` protocol.
```
collectionView.rx_modelDeselected(MyModel.self)
.map { ...
```
*/
public func rx_modelDeselected<T>(modelType: T.Type) -> ControlEvent<T> {
let source: Observable<T> = rx_itemDeselected.flatMap { [weak self] indexPath -> Observable<T> in
guard let view = self else {
return Observable.empty()
}
return Observable.just(try view.rx_modelAtIndexPath(indexPath))
}
return ControlEvent(events: 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.")
let dataSource: SectionedViewDataSourceType = 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
let element = try dataSource.modelAtIndexPath(indexPath)
return element as! T
}
}
#endif

View File

@ -193,22 +193,13 @@ extension UITableView {
/**
Reactive wrapper for `delegate` message `tableView:didSelectRowAtIndexPath:`.
It can be only used when one of the `rx_itemsWith*` methods is used to bind observable sequence.
It can be only used when one of the `rx_itemsWith*` methods is used to bind observable sequence,
or any other data source conforming to `SectionedViewDataSourceType` protocol.
```
tableView.rx_modelSelected(MyModel.self)
.map { ...
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)
}
```
*/
public func rx_modelSelected<T>(modelType: T.Type) -> ControlEvent<T> {
let source: Observable<T> = rx_itemSelected.flatMap { [weak self] indexPath -> Observable<T> in
@ -225,22 +216,13 @@ extension UITableView {
/**
Reactive wrapper for `delegate` message `tableView:didDeselectRowAtIndexPath:`.
It can be only used when one of the `rx_itemsWith*` methods is used to bind observable sequence.
It can be only used when one of the `rx_itemsWith*` methods is used to bind observable sequence,
or any other data source conforming to `SectionedViewDataSourceType` protocol.
tableView.rx_modelSelected(MyModel.self)
.map { ...
If custom data source is being bound, new `rx_modelSelected` wrapper needs to be written also.
public func rx_myModelDeselected<T>() -> ControlEvent<T> {
let source: Observable<T> = rx_itemDeselected.map { indexPath in
let dataSource: MyDataSource = self.rx_dataSource.forwardToDelegate() as! MyDataSource
return dataSource.modelAtIndex(indexPath.item)!
}
return ControlEvent(source: source)
}
```
tableView.rx_modelDeselected(MyModel.self)
.map { ...
```
*/
public func rx_modelDeselected<T>(modelType: T.Type) -> ControlEvent<T> {
let source: Observable<T> = rx_itemDeselected.flatMap { [weak self] indexPath -> Observable<T> in
@ -255,16 +237,14 @@ extension UITableView {
}
/**
Synchronous helper method for retrieving a model at indexPath through a reactive data source
Synchronous helper method for retrieving a model at indexPath through a reactive data source.
*/
public func rx_modelAtIndexPath<T>(indexPath: NSIndexPath) throws -> T {
let dataSource: RxTableViewReactiveArrayDataSource<T> = castOrFatalError(self.rx_dataSource.forwardToDelegate(), message: "This method only works in case one of the `rx_items*` methods was used.")
let dataSource: SectionedViewDataSourceType = castOrFatalError(self.rx_dataSource.forwardToDelegate(), message: "This method only works in case one of the `rx_items*` methods was used.")
guard let element = dataSource.modelAtIndex(indexPath.item) else {
throw RxCocoaError.ItemsNotYetBound(object: self)
}
return element
let element = try dataSource.modelAtIndexPath(indexPath)
return element as! T
}
}

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0710"
LastUpgradeVersion = "0720"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0710"
LastUpgradeVersion = "0720"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0710"
LastUpgradeVersion = "0720"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"

View File

@ -106,7 +106,7 @@ extension ControlTests {
ensureEventDeallocated(createView) { (view: UICollectionView) in view.rx_modelSelected(Int.self) }
}
func testCollectionView_ModelSelected1() {
func testCollectionView_ModelSelected_itemsWithCellFactory() {
let items: Observable<[Int]> = Observable.just([1, 2, 3])
let layout = UICollectionViewFlowLayout()
@ -137,7 +137,7 @@ extension ControlTests {
s.dispose()
}
func testCollectionView_ModelSelected2() {
func testCollectionView_ModelSelected_itemsWithCellIdentifier() {
let items: Observable<[Int]> = Observable.just([1, 2, 3])
let layout = UICollectionViewFlowLayout()
@ -167,6 +167,90 @@ extension ControlTests {
s.dispose()
dataSourceSubscription.dispose()
}
func testCollectionView_ModelDeselected_itemsWithCellFactory() {
let items: Observable<[Int]> = Observable.just([1, 2, 3])
let layout = UICollectionViewFlowLayout()
let createView: () -> (UICollectionView, Disposable) = {
let collectionView = UICollectionView(frame: CGRectMake(0, 0, 1, 1), collectionViewLayout: layout)
let s = items.bindTo(collectionView.rx_itemsWithCellFactory) { (cv, index: Int, item: Int) -> UICollectionViewCell in
return UICollectionViewCell(frame: CGRectMake(1, 1, 1, 1))
}
return (collectionView, s)
}
let (collectionView, dataSourceSubscription) = createView()
var selectedItem: Int? = nil
let s = collectionView.rx_modelDeselected(Int.self)
.subscribeNext { (item: Int) in
selectedItem = item
}
collectionView.delegate!.collectionView!(collectionView, didDeselectItemAtIndexPath: NSIndexPath(forRow: 1, inSection: 0))
XCTAssertEqual(selectedItem, 2)
dataSourceSubscription.dispose()
s.dispose()
}
func testCollectionView_ModelDeselected_itemsWithCellIdentifier() {
let items: Observable<[Int]> = Observable.just([1, 2, 3])
let layout = UICollectionViewFlowLayout()
let createView: () -> (UICollectionView, Disposable) = {
let collectionView = UICollectionView(frame: CGRectMake(0, 0, 1, 1), collectionViewLayout: layout)
collectionView.registerClass(NSClassFromString("UICollectionViewCell"), forCellWithReuseIdentifier: "a")
let dataSourceSubscription = items.bindTo(collectionView.rx_itemsWithCellIdentifier("a")) { (index: Int, item: Int, cell) in
}
return (collectionView, dataSourceSubscription)
}
let (collectionView, dataSourceSubscription) = createView()
var selectedItem: Int? = nil
let s = collectionView.rx_modelDeselected(Int.self)
.subscribeNext { item in
selectedItem = item
}
collectionView.delegate!.collectionView!(collectionView, didDeselectItemAtIndexPath: NSIndexPath(forRow: 1, inSection: 0))
XCTAssertEqual(selectedItem, 2)
s.dispose()
dataSourceSubscription.dispose()
}
func testCollectionView_modelAtIndexPath_normal() {
let items: Observable<[Int]> = Observable.just([1, 2, 3])
let layout = UICollectionViewFlowLayout()
let createView: () -> (UICollectionView, Disposable) = {
let collectionView = UICollectionView(frame: CGRectMake(0, 0, 1, 1), collectionViewLayout: layout)
collectionView.registerClass(NSClassFromString("UICollectionViewCell"), forCellWithReuseIdentifier: "a")
let dataSource = SectionedViewDataSourceMock()
let dataSourceSubscription = items.bindTo(collectionView.rx_itemsWithDataSource(dataSource))
return (collectionView, dataSourceSubscription)
}
let (collectionView, dataSourceSubscription) = createView()
let model: Int = try! collectionView.rx_modelAtIndexPath(NSIndexPath(forItem: 1, inSection: 0))
XCTAssertEqual(model, 2)
dataSourceSubscription.dispose()
}
}
// UILabel
@ -246,7 +330,7 @@ extension ControlTests {
ensureEventDeallocated(createView) { (view: UITableView) in view.rx_modelSelected(Int.self) }
}
func testTableView_ModelSelected1() {
func testTableView_ModelSelected_rx_itemsWithCellFactory() {
let items: Observable<[Int]> = Observable.just([1, 2, 3])
let createView: () -> (UITableView, Disposable) = {
@ -275,7 +359,7 @@ extension ControlTests {
s.dispose()
}
func testTableView_ModelSelected2() {
func testTableView_ModelSelected_itemsWithCellIdentifier() {
let items: Observable<[Int]> = Observable.just([1, 2, 3])
let createView: () -> (UITableView, Disposable) = {
@ -304,6 +388,86 @@ extension ControlTests {
dataSourceSubscription.dispose()
s.dispose()
}
func testTableView_ModelDeselected_rx_itemsWithCellFactory() {
let items: Observable<[Int]> = Observable.just([1, 2, 3])
let createView: () -> (UITableView, Disposable) = {
let tableView = UITableView(frame: CGRectMake(0, 0, 1, 1))
let dataSourceSubscription = items.bindTo(tableView.rx_itemsWithCellFactory) { (tv, index: Int, item: Int) -> UITableViewCell in
return UITableViewCell(style: .Default, reuseIdentifier: "Identity")
}
return (tableView, dataSourceSubscription)
}
let (tableView, dataSourceSubscription) = createView()
var selectedItem: Int? = nil
let s = tableView.rx_modelDeselected(Int.self)
.subscribeNext { item in
selectedItem = item
}
tableView.delegate!.tableView!(tableView, didDeselectRowAtIndexPath: NSIndexPath(forRow: 1, inSection: 0))
XCTAssertEqual(selectedItem, 2)
dataSourceSubscription.dispose()
s.dispose()
}
func testTableView_ModelDeselected_itemsWithCellIdentifier() {
let items: Observable<[Int]> = Observable.just([1, 2, 3])
let createView: () -> (UITableView, Disposable) = {
let tableView = UITableView(frame: CGRectMake(0, 0, 1, 1))
tableView.registerClass(NSClassFromString("UITableViewCell"), forCellReuseIdentifier: "a")
let dataSourceSubscription = items.bindTo(tableView.rx_itemsWithCellIdentifier("a")) { (index: Int, item: Int, cell) in
}
return (tableView, dataSourceSubscription)
}
let (tableView, dataSourceSubscription) = createView()
var selectedItem: Int? = nil
let s = tableView.rx_modelDeselected(Int.self)
.subscribeNext { item in
selectedItem = item
}
tableView.delegate!.tableView!(tableView, didDeselectRowAtIndexPath: NSIndexPath(forRow: 1, inSection: 0))
XCTAssertEqual(selectedItem, 2)
dataSourceSubscription.dispose()
s.dispose()
}
func testTableView_modelAtIndexPath_normal() {
let items: Observable<[Int]> = Observable.just([1, 2, 3])
let createView: () -> (UITableView, Disposable) = {
let tableView = UITableView(frame: CGRectMake(0, 0, 1, 1))
tableView.registerClass(NSClassFromString("UITableViewCell"), forCellReuseIdentifier: "a")
let dataSource = SectionedViewDataSourceMock()
let dataSourceSubscription = items.bindTo(tableView.rx_itemsWithDataSource(dataSource))
return (tableView, dataSourceSubscription)
}
let (tableView, dataSourceSubscription) = createView()
let model: Int = try! tableView.rx_modelAtIndexPath(NSIndexPath(forItem: 1, inSection: 0))
XCTAssertEqual(model, 2)
dataSourceSubscription.dispose()
}
}
// UIControl

View File

@ -0,0 +1,57 @@
//
// SectionedViewDataSourceMock.swift
// Rx
//
// Created by Krunoslav Zaher on 1/10/16.
// Copyright © 2016 Krunoslav Zaher. All rights reserved.
//
import Foundation
import RxSwift
import RxCocoa
import UIKit
@objc class SectionedViewDataSourceMock
: NSObject
, SectionedViewDataSourceType
, UITableViewDataSource
, UICollectionViewDataSource
, RxTableViewDataSourceType
, RxCollectionViewDataSourceType {
typealias Element = [Int]
var items: [Int]?
override init() {
super.init()
}
func modelAtIndexPath(indexPath: NSIndexPath) throws -> Any {
return items![indexPath.item]
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 0
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
return UITableViewCell()
}
func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 0
}
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
return UICollectionViewCell()
}
func tableView(tableView: UITableView, observedEvent: Event<Element>) {
items = observedEvent.element!
}
func collectionView(collectionView: UICollectionView, observedEvent: Event<Element>) {
items = observedEvent.element!
}
}