Compare commits

..

14 Commits

Author SHA1 Message Date
Arnaud Dorgans e95b550962 0.2.0.3 2018-01-04 13:50:42 +01:00
Arnaud Dorgans f8adf9c9a5 readme fix 2018-01-04 13:48:59 +01:00
Arnaud Dorgans 29de021e92 0.2.0.2 2018-01-04 13:42:28 +01:00
Arnaud Dorgans e011a23f19 0.2.0.1 2018-01-04 13:36:29 +01:00
Arnaud Dorgans 12f4c3e966 pod spec 2018-01-04 13:06:38 +01:00
Arnaud Dorgans af0c4598be fix spec 2018-01-04 13:03:59 +01:00
Arnaud Dorgans a446dc45ff subspec fix 2018-01-04 12:38:15 +01:00
Arnaud Dorgans f9566b45bb fix readme 2018-01-04 12:32:10 +01:00
Arnaud Dorgans 6e74d52f42 0.1.7 2018-01-04 12:29:01 +01:00
Arnaud Dorgans c125f52695 fix open var 2018-01-03 11:02:50 +01:00
Arnaud Dorgans 075fb6c75b add exemples & delegate 2018-01-03 10:23:49 +01:00
Arnaud Dorgans 03e9dbd3a4 add CocoaProxy 2017-12-27 23:26:31 +01:00
Arnaud Dorgans 1fbb837941 update 2017-12-25 18:36:20 +01:00
Arnaud Dorgans 5746d5a7c4 Update README.md 2017-12-25 18:30:35 +01:00
25 changed files with 1404 additions and 281 deletions
@@ -9,12 +9,16 @@
/* Begin PBXBuildFile section */
5259B95F4E58F763F6C70333 /* Pods_InfiniteLayout_Example.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A6830D8B970E919B725A0D5E /* Pods_InfiniteLayout_Example.framework */; };
607FACD61AFB9204008FA782 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 607FACD51AFB9204008FA782 /* AppDelegate.swift */; };
607FACD81AFB9204008FA782 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 607FACD71AFB9204008FA782 /* ViewController.swift */; };
607FACD81AFB9204008FA782 /* CustomViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 607FACD71AFB9204008FA782 /* CustomViewController.swift */; };
607FACDB1AFB9204008FA782 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 607FACD91AFB9204008FA782 /* Main.storyboard */; };
607FACDD1AFB9204008FA782 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 607FACDC1AFB9204008FA782 /* Images.xcassets */; };
607FACE01AFB9204008FA782 /* LaunchScreen.xib in Resources */ = {isa = PBXBuildFile; fileRef = 607FACDE1AFB9204008FA782 /* LaunchScreen.xib */; };
607FACEC1AFB9204008FA782 /* Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 607FACEB1AFB9204008FA782 /* Tests.swift */; };
D403973F1FEBFA9D006C41D2 /* Cell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D403973E1FEBFA9D006C41D2 /* Cell.swift */; };
D4162A391FF502A900AC2572 /* CustomLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4162A381FF502A900AC2572 /* CustomLayout.swift */; };
D43362D11FF64B330040C679 /* BaseCollectionViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D43362CF1FF64B330040C679 /* BaseCollectionViewController.swift */; };
D495544E1FFD3D920081225B /* RxBaseCollectionViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D495544D1FFD3D920081225B /* RxBaseCollectionViewController.swift */; };
D4B9816E1FFCCC900016C676 /* PickerController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4B9816D1FFCCC900016C676 /* PickerController.swift */; };
E997A539C8278E7A661C9F91 /* Pods_InfiniteLayout_Tests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C50F79B0DFFDE95D322480B2 /* Pods_InfiniteLayout_Tests.framework */; };
/* End PBXBuildFile section */
@@ -31,11 +35,11 @@
/* Begin PBXFileReference section */
17E9A86BF3432BF233E3D297 /* Pods-InfiniteLayout_Example.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-InfiniteLayout_Example.debug.xcconfig"; path = "Pods/Target Support Files/Pods-InfiniteLayout_Example/Pods-InfiniteLayout_Example.debug.xcconfig"; sourceTree = "<group>"; };
2E68337E21F08944069DC023 /* Pods-InfiniteLayout_Tests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-InfiniteLayout_Tests.release.xcconfig"; path = "Pods/Target Support Files/Pods-InfiniteLayout_Tests/Pods-InfiniteLayout_Tests.release.xcconfig"; sourceTree = "<group>"; };
5DA98D9A451E79A872E96E69 /* InfiniteLayout.podspec */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; name = InfiniteLayout.podspec; path = ../InfiniteLayout.podspec; sourceTree = "<group>"; };
5DA98D9A451E79A872E96E69 /* InfiniteLayout.podspec */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; name = InfiniteLayout.podspec; path = ../InfiniteLayout.podspec; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.ruby; };
607FACD01AFB9204008FA782 /* InfiniteLayout_Example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = InfiniteLayout_Example.app; sourceTree = BUILT_PRODUCTS_DIR; };
607FACD41AFB9204008FA782 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
607FACD51AFB9204008FA782 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
607FACD71AFB9204008FA782 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = "<group>"; };
607FACD71AFB9204008FA782 /* CustomViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomViewController.swift; sourceTree = "<group>"; };
607FACDA1AFB9204008FA782 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; };
607FACDC1AFB9204008FA782 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = "<group>"; };
607FACDF1AFB9204008FA782 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/LaunchScreen.xib; sourceTree = "<group>"; };
@@ -46,6 +50,10 @@
AD3D900D0067605DEAFF148B /* Pods-InfiniteLayout_Example.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-InfiniteLayout_Example.release.xcconfig"; path = "Pods/Target Support Files/Pods-InfiniteLayout_Example/Pods-InfiniteLayout_Example.release.xcconfig"; sourceTree = "<group>"; };
C50F79B0DFFDE95D322480B2 /* Pods_InfiniteLayout_Tests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_InfiniteLayout_Tests.framework; sourceTree = BUILT_PRODUCTS_DIR; };
D403973E1FEBFA9D006C41D2 /* Cell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Cell.swift; sourceTree = "<group>"; };
D4162A381FF502A900AC2572 /* CustomLayout.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomLayout.swift; sourceTree = "<group>"; };
D43362CF1FF64B330040C679 /* BaseCollectionViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BaseCollectionViewController.swift; sourceTree = "<group>"; };
D495544D1FFD3D920081225B /* RxBaseCollectionViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RxBaseCollectionViewController.swift; sourceTree = "<group>"; };
D4B9816D1FFCCC900016C676 /* PickerController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PickerController.swift; sourceTree = "<group>"; };
EF577FD009F0E2F96F1CECC6 /* LICENSE */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; name = LICENSE; path = ../LICENSE; sourceTree = "<group>"; };
F900DDE615FA1056EB58998B /* Pods-InfiniteLayout_Tests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-InfiniteLayout_Tests.debug.xcconfig"; path = "Pods/Target Support Files/Pods-InfiniteLayout_Tests/Pods-InfiniteLayout_Tests.debug.xcconfig"; sourceTree = "<group>"; };
FD6AC573B93C313B5BDFF3FE /* README.md */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = net.daringfireball.markdown; name = README.md; path = ../README.md; sourceTree = "<group>"; };
@@ -96,8 +104,8 @@
isa = PBXGroup;
children = (
607FACD51AFB9204008FA782 /* AppDelegate.swift */,
607FACD71AFB9204008FA782 /* ViewController.swift */,
D403973E1FEBFA9D006C41D2 /* Cell.swift */,
D4B981781FFD1FEF0016C676 /* Swift */,
D4B981791FFD1FF50016C676 /* Rx */,
607FACD91AFB9204008FA782 /* Main.storyboard */,
607FACDC1AFB9204008FA782 /* Images.xcassets */,
607FACDE1AFB9204008FA782 /* LaunchScreen.xib */,
@@ -151,6 +159,50 @@
name = Frameworks;
sourceTree = "<group>";
};
D43362CC1FF64AD30040C679 /* Custom */ = {
isa = PBXGroup;
children = (
607FACD71AFB9204008FA782 /* CustomViewController.swift */,
D4162A381FF502A900AC2572 /* CustomLayout.swift */,
);
name = Custom;
sourceTree = "<group>";
};
D43362D31FF64B9E0040C679 /* Base */ = {
isa = PBXGroup;
children = (
D403973E1FEBFA9D006C41D2 /* Cell.swift */,
D43362CF1FF64B330040C679 /* BaseCollectionViewController.swift */,
);
name = Base;
sourceTree = "<group>";
};
D4B9816C1FFCCC5B0016C676 /* Picker */ = {
isa = PBXGroup;
children = (
D4B9816D1FFCCC900016C676 /* PickerController.swift */,
);
name = Picker;
sourceTree = "<group>";
};
D4B981781FFD1FEF0016C676 /* Swift */ = {
isa = PBXGroup;
children = (
D43362D31FF64B9E0040C679 /* Base */,
D43362CC1FF64AD30040C679 /* Custom */,
D4B9816C1FFCCC5B0016C676 /* Picker */,
);
name = Swift;
sourceTree = "<group>";
};
D4B981791FFD1FF50016C676 /* Rx */ = {
isa = PBXGroup;
children = (
D495544D1FFD3D920081225B /* RxBaseCollectionViewController.swift */,
);
name = Rx;
sourceTree = "<group>";
};
E938139B99B9D91C14707C07 /* Pods */ = {
isa = PBXGroup;
children = (
@@ -291,11 +343,21 @@
);
inputPaths = (
"${SRCROOT}/Pods/Target Support Files/Pods-InfiniteLayout_Example/Pods-InfiniteLayout_Example-frameworks.sh",
"${BUILT_PRODUCTS_DIR}/CocoaProxy/CocoaProxy.framework",
"${BUILT_PRODUCTS_DIR}/Differentiator/Differentiator.framework",
"${BUILT_PRODUCTS_DIR}/InfiniteLayout/InfiniteLayout.framework",
"${BUILT_PRODUCTS_DIR}/RxCocoa/RxCocoa.framework",
"${BUILT_PRODUCTS_DIR}/RxDataSources/RxDataSources.framework",
"${BUILT_PRODUCTS_DIR}/RxSwift/RxSwift.framework",
);
name = "[CP] Embed Pods Frameworks";
outputPaths = (
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/CocoaProxy.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Differentiator.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/InfiniteLayout.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/RxCocoa.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/RxDataSources.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/RxSwift.framework",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
@@ -375,9 +437,13 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
607FACD81AFB9204008FA782 /* ViewController.swift in Sources */,
607FACD81AFB9204008FA782 /* CustomViewController.swift in Sources */,
607FACD61AFB9204008FA782 /* AppDelegate.swift in Sources */,
D495544E1FFD3D920081225B /* RxBaseCollectionViewController.swift in Sources */,
D4162A391FF502A900AC2572 /* CustomLayout.swift in Sources */,
D4B9816E1FFCCC900016C676 /* PickerController.swift in Sources */,
D403973F1FEBFA9D006C41D2 /* Cell.swift in Sources */,
D43362D11FF64B330040C679 /* BaseCollectionViewController.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -1,8 +1,13 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="6214" systemVersion="14A314h" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES">
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="13771" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" colorMatched="YES">
<device id="retina4_7" orientation="portrait">
<adaptation id="fullscreen"/>
</device>
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="6207"/>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="13772"/>
<capability name="Constraints with non-1.0 multipliers" minToolsVersion="5.1"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
@@ -24,7 +29,7 @@
<nil key="highlightedColor"/>
</label>
</subviews>
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstItem="kId-c2-rCX" firstAttribute="centerY" secondItem="iN0-l3-epB" secondAttribute="bottom" multiplier="1/3" constant="1" id="5cJ-9S-tgC"/>
<constraint firstAttribute="centerX" secondItem="kId-c2-rCX" secondAttribute="centerX" id="Koa-jz-hwk"/>
+439 -27
View File
@@ -1,11 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="13771" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES" initialViewController="yGQ-hH-2v6">
<device id="retina5_9" orientation="portrait">
<device id="retina4_0" orientation="portrait">
<adaptation id="fullscreen"/>
</device>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="13772"/>
<capability name="Aspect ratio constraints" minToolsVersion="5.1"/>
<capability name="Constraints with non-1.0 multipliers" minToolsVersion="5.1"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<scenes>
@@ -14,68 +16,478 @@
<objects>
<navigationController id="yGQ-hH-2v6" sceneMemberID="viewController">
<navigationBar key="navigationBar" contentMode="scaleToFill" insetsLayoutMarginsFromSafeArea="NO" id="IdE-R7-9TG">
<rect key="frame" x="0.0" y="44" width="375" height="44"/>
<rect key="frame" x="0.0" y="20" width="320" height="44"/>
<autoresizingMask key="autoresizingMask"/>
</navigationBar>
<connections>
<segue destination="sSL-wr-E9v" kind="relationship" relationship="rootViewController" id="6hU-n3-d5H"/>
<segue destination="pMz-7t-sA5" kind="relationship" relationship="rootViewController" id="AAf-BN-dLc"/>
</connections>
</navigationController>
<placeholder placeholderIdentifier="IBFirstResponder" id="0uD-Xz-jjT" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="-988" y="37"/>
<point key="canvasLocation" x="-1644" y="61"/>
</scene>
<!--View Controller-->
<scene sceneID="Tsa-GF-Rqw">
<!--InfiniteLayout-->
<scene sceneID="fMt-PB-wyT">
<objects>
<collectionViewController id="sSL-wr-E9v" customClass="ViewController" customModule="InfiniteLayout_Example" customModuleProvider="target" sceneMemberID="viewController">
<collectionView key="view" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" dataMode="prototypes" id="BuB-JB-b15" customClass="InfiniteCollectionView" customModule="InfiniteLayout">
<rect key="frame" x="0.0" y="0.0" width="375" height="812"/>
<tableViewController id="pMz-7t-sA5" sceneMemberID="viewController">
<tableView key="view" clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="static" style="plain" separatorStyle="default" rowHeight="-1" estimatedRowHeight="-1" sectionHeaderHeight="28" sectionFooterHeight="28" id="YNN-Jo-hL3">
<rect key="frame" x="0.0" y="0.0" width="320" height="568"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<collectionViewFlowLayout key="collectionViewLayout" scrollDirection="horizontal" minimumLineSpacing="10" minimumInteritemSpacing="10" id="Tcc-xt-sXE">
<size key="itemSize" width="150" height="100"/>
<sections>
<tableViewSection id="wLh-Qy-wV4">
<cells>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" textLabel="VSq-Pr-6ay" style="IBUITableViewCellStyleDefault" id="Ave-Hr-Gmz">
<rect key="frame" x="0.0" y="0.0" width="320" height="44"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="Ave-Hr-Gmz" id="Fri-pG-UG3">
<rect key="frame" x="0.0" y="0.0" width="320" height="43.5"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" insetsLayoutMarginsFromSafeArea="NO" text="Horizontal Layout" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="VSq-Pr-6ay">
<rect key="frame" x="16" y="0.0" width="288" height="43.5"/>
<autoresizingMask key="autoresizingMask"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
</subviews>
</tableViewCellContentView>
<connections>
<segue destination="CU7-Hg-6kG" kind="show" id="yzb-gl-6Xz"/>
</connections>
</tableViewCell>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" textLabel="Efh-hp-Yw4" style="IBUITableViewCellStyleDefault" id="e4W-KK-bkI">
<rect key="frame" x="0.0" y="44" width="320" height="44"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="e4W-KK-bkI" id="WDz-rM-Z2L">
<rect key="frame" x="0.0" y="0.0" width="320" height="43.5"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" insetsLayoutMarginsFromSafeArea="NO" text="Vertical Layout" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="Efh-hp-Yw4">
<rect key="frame" x="16" y="0.0" width="288" height="43.5"/>
<autoresizingMask key="autoresizingMask"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
</subviews>
</tableViewCellContentView>
<connections>
<segue destination="9P2-Ud-D2M" kind="show" id="H8w-EO-laS"/>
</connections>
</tableViewCell>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" textLabel="9We-VT-VYi" style="IBUITableViewCellStyleDefault" id="5Ku-tY-Gtv">
<rect key="frame" x="0.0" y="88" width="320" height="44"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="5Ku-tY-Gtv" id="M7f-Mo-aWc">
<rect key="frame" x="0.0" y="0.0" width="320" height="43.5"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" insetsLayoutMarginsFromSafeArea="NO" text="Custom Layout" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="9We-VT-VYi">
<rect key="frame" x="16" y="0.0" width="288" height="43.5"/>
<autoresizingMask key="autoresizingMask"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
</subviews>
</tableViewCellContentView>
<connections>
<segue destination="ctO-Ld-yhL" kind="show" id="qss-AL-dgi"/>
</connections>
</tableViewCell>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" textLabel="c9c-gc-NDL" style="IBUITableViewCellStyleDefault" id="7Dq-og-5lN">
<rect key="frame" x="0.0" y="132" width="320" height="44"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="7Dq-og-5lN" id="XGb-gg-zbn">
<rect key="frame" x="0.0" y="0.0" width="320" height="43.5"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" insetsLayoutMarginsFromSafeArea="NO" text="Picker" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="c9c-gc-NDL">
<rect key="frame" x="16" y="0.0" width="288" height="43.5"/>
<autoresizingMask key="autoresizingMask"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
</subviews>
</tableViewCellContentView>
<connections>
<segue destination="nUW-3u-2O9" kind="show" id="SPm-WC-Yf4"/>
</connections>
</tableViewCell>
</cells>
</tableViewSection>
<tableViewSection headerTitle="Rx" id="l6q-RG-uyY">
<cells>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" textLabel="i8W-08-UPB" style="IBUITableViewCellStyleDefault" id="1E8-fo-BBx">
<rect key="frame" x="0.0" y="204" width="320" height="44"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="1E8-fo-BBx" id="NMq-kd-IrV">
<rect key="frame" x="0.0" y="0.0" width="320" height="43.5"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" insetsLayoutMarginsFromSafeArea="NO" text="Vertical Layout" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="i8W-08-UPB">
<rect key="frame" x="16" y="0.0" width="288" height="43.5"/>
<autoresizingMask key="autoresizingMask"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
</subviews>
</tableViewCellContentView>
<connections>
<segue destination="LXZ-Bh-cft" kind="show" id="IlB-ib-PBy"/>
</connections>
</tableViewCell>
</cells>
</tableViewSection>
</sections>
<connections>
<outlet property="dataSource" destination="pMz-7t-sA5" id="OC9-mx-dmD"/>
<outlet property="delegate" destination="pMz-7t-sA5" id="nwI-aQ-G7d"/>
</connections>
</tableView>
<navigationItem key="navigationItem" title="InfiniteLayout" id="6dR-xO-LQz"/>
</tableViewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="N5O-nh-h2i" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="-770" y="61"/>
</scene>
<!--Custom Layout-->
<scene sceneID="Nwi-KZ-nuX">
<objects>
<viewController id="ctO-Ld-yhL" customClass="CustomViewController" customModule="InfiniteLayout_Example" customModuleProvider="target" sceneMemberID="viewController">
<layoutGuides>
<viewControllerLayoutGuide type="top" id="pkO-J5-FlB"/>
<viewControllerLayoutGuide type="bottom" id="q9H-d0-4cm"/>
</layoutGuides>
<view key="view" contentMode="scaleToFill" id="C5p-bu-Djh">
<rect key="frame" x="0.0" y="0.0" width="320" height="568"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<collectionView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" showsHorizontalScrollIndicator="NO" showsVerticalScrollIndicator="NO" dataMode="prototypes" translatesAutoresizingMaskIntoConstraints="NO" id="enX-ef-ELn" customClass="InfiniteCollectionView" customModule="InfiniteLayout">
<rect key="frame" x="0.0" y="0.0" width="320" height="568"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<collectionViewLayout key="collectionViewLayout" id="ZR7-Jn-ewG" customClass="CustomLayout" customModule="InfiniteLayout_Example" customModuleProvider="target"/>
<cells>
<collectionViewCell opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" reuseIdentifier="cell" id="w1M-Ws-9fK" customClass="Cell" customModule="InfiniteLayout_Example" customModuleProvider="target">
<rect key="frame" x="0.0" y="0.0" width="50" height="50"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<view key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" insetsLayoutMarginsFromSafeArea="NO">
<rect key="frame" x="0.0" y="0.0" width="50" height="50"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="gVT-2b-OS8" customClass="CellView" customModule="InfiniteLayout_Example" customModuleProvider="target">
<rect key="frame" x="0.0" y="0.0" width="50" height="50"/>
<userDefinedRuntimeAttributes>
<userDefinedRuntimeAttribute type="number" keyPath="styleIndex">
<integer key="value" value="2"/>
</userDefinedRuntimeAttribute>
</userDefinedRuntimeAttributes>
</view>
</subviews>
</view>
<constraints>
<constraint firstItem="gVT-2b-OS8" firstAttribute="top" secondItem="w1M-Ws-9fK" secondAttribute="top" id="9qG-Sx-gVG"/>
<constraint firstAttribute="bottom" secondItem="gVT-2b-OS8" secondAttribute="bottom" id="BOm-tM-pkP"/>
<constraint firstAttribute="trailing" secondItem="gVT-2b-OS8" secondAttribute="trailing" id="HC4-Mv-T4P"/>
<constraint firstItem="gVT-2b-OS8" firstAttribute="leading" secondItem="w1M-Ws-9fK" secondAttribute="leading" id="jH9-f5-fxA"/>
</constraints>
<connections>
<outlet property="cellView" destination="gVT-2b-OS8" id="Em8-Ah-Jzb"/>
</connections>
</collectionViewCell>
</cells>
<userDefinedRuntimeAttributes>
<userDefinedRuntimeAttribute type="boolean" keyPath="isItemPagingEnabled" value="YES"/>
</userDefinedRuntimeAttributes>
<connections>
<outlet property="dataSource" destination="ctO-Ld-yhL" id="bXu-Jz-K7t"/>
<outlet property="delegate" destination="ctO-Ld-yhL" id="Vz4-5J-frZ"/>
</connections>
</collectionView>
</subviews>
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<constraints>
<constraint firstItem="enX-ef-ELn" firstAttribute="top" secondItem="C5p-bu-Djh" secondAttribute="top" id="2nB-Om-PsD"/>
<constraint firstAttribute="bottom" secondItem="enX-ef-ELn" secondAttribute="bottom" id="Tdv-eb-r3X"/>
<constraint firstAttribute="trailing" secondItem="enX-ef-ELn" secondAttribute="trailing" id="ZM0-nc-pUD"/>
<constraint firstItem="enX-ef-ELn" firstAttribute="leading" secondItem="C5p-bu-Djh" secondAttribute="leading" id="uX4-ox-gnL"/>
</constraints>
</view>
<navigationItem key="navigationItem" title="Custom Layout" id="hZl-1w-5Nq"/>
<connections>
<outlet property="infiniteCollectionView" destination="enX-ef-ELn" id="jKo-DT-4uH"/>
</connections>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="4s9-AY-FII" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="-770" y="802"/>
</scene>
<!--Infinite Delegate-->
<scene sceneID="HqS-9O-oG4">
<objects>
<viewController automaticallyAdjustsScrollViewInsets="NO" id="nUW-3u-2O9" customClass="PickerController" customModule="InfiniteLayout_Example" customModuleProvider="target" sceneMemberID="viewController">
<layoutGuides>
<viewControllerLayoutGuide type="top" id="zFz-ZQ-lzW"/>
<viewControllerLayoutGuide type="bottom" id="ZuO-bs-QWL"/>
</layoutGuides>
<view key="view" contentMode="scaleToFill" id="Vhd-CK-34X">
<rect key="frame" x="0.0" y="0.0" width="320" height="568"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<collectionView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" showsHorizontalScrollIndicator="NO" showsVerticalScrollIndicator="NO" dataMode="prototypes" translatesAutoresizingMaskIntoConstraints="NO" id="Yg1-5Z-6FI" customClass="InfiniteCollectionView" customModule="InfiniteLayout">
<rect key="frame" x="0.0" y="341" width="320" height="227"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<collectionViewFlowLayout key="collectionViewLayout" scrollDirection="horizontal" minimumLineSpacing="10" minimumInteritemSpacing="1000" id="PTo-iT-zZX">
<size key="itemSize" width="200" height="150"/>
<size key="headerReferenceSize" width="0.0" height="0.0"/>
<size key="footerReferenceSize" width="0.0" height="0.0"/>
<inset key="sectionInset" minX="0.0" minY="0.0" maxX="10" maxY="0.0"/>
</collectionViewFlowLayout>
<cells>
<collectionViewCell opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" reuseIdentifier="cell" id="7Np-kQ-f93" customClass="Cell" customModule="InfiniteLayout_Example" customModuleProvider="target">
<rect key="frame" x="0.0" y="38.5" width="200" height="150"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<view key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" insetsLayoutMarginsFromSafeArea="NO">
<rect key="frame" x="0.0" y="0.0" width="200" height="150"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="Uz9-xR-d0L" customClass="CellView" customModule="InfiniteLayout_Example" customModuleProvider="target">
<rect key="frame" x="0.0" y="0.0" width="200" height="150"/>
<userDefinedRuntimeAttributes>
<userDefinedRuntimeAttribute type="number" keyPath="styleIndex">
<integer key="value" value="1"/>
</userDefinedRuntimeAttribute>
</userDefinedRuntimeAttributes>
</view>
</subviews>
</view>
<constraints>
<constraint firstItem="Uz9-xR-d0L" firstAttribute="top" secondItem="7Np-kQ-f93" secondAttribute="top" id="2lt-hb-Zl9"/>
<constraint firstItem="Uz9-xR-d0L" firstAttribute="leading" secondItem="7Np-kQ-f93" secondAttribute="leading" id="Hj7-OH-iis"/>
<constraint firstAttribute="trailing" secondItem="Uz9-xR-d0L" secondAttribute="trailing" id="mr0-wj-ftP"/>
<constraint firstAttribute="bottom" secondItem="Uz9-xR-d0L" secondAttribute="bottom" id="t8H-tx-AHw"/>
</constraints>
<connections>
<outlet property="cellView" destination="Uz9-xR-d0L" id="Q84-nb-acR"/>
</connections>
</collectionViewCell>
</cells>
<userDefinedRuntimeAttributes>
<userDefinedRuntimeAttribute type="boolean" keyPath="isItemPagingEnabled" value="YES"/>
</userDefinedRuntimeAttributes>
<connections>
<outlet property="dataSource" destination="nUW-3u-2O9" id="vHT-UO-3ny"/>
<outlet property="delegate" destination="nUW-3u-2O9" id="cXx-OK-csN"/>
<outlet property="infiniteDelegate" destination="nUW-3u-2O9" id="FQk-xA-mjb"/>
</connections>
</collectionView>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="n2a-eR-E9T" customClass="CellView" customModule="InfiniteLayout_Example" customModuleProvider="target">
<rect key="frame" x="26.5" y="98" width="267" height="213.5"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<constraints>
<constraint firstAttribute="width" secondItem="n2a-eR-E9T" secondAttribute="height" multiplier="1.25" id="qUW-wU-jQk"/>
</constraints>
</view>
</subviews>
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<constraints>
<constraint firstItem="n2a-eR-E9T" firstAttribute="height" secondItem="Vhd-CK-34X" secondAttribute="width" multiplier="2:3" id="15l-Xa-cgF"/>
<constraint firstItem="n2a-eR-E9T" firstAttribute="centerY" secondItem="Yg1-5Z-6FI" secondAttribute="top" multiplier="0.6" id="831-T2-ANf"/>
<constraint firstItem="Yg1-5Z-6FI" firstAttribute="height" secondItem="Vhd-CK-34X" secondAttribute="height" multiplier="0.4" id="A7s-lr-k2k"/>
<constraint firstItem="ZuO-bs-QWL" firstAttribute="top" secondItem="Yg1-5Z-6FI" secondAttribute="bottom" id="GmZ-Db-ynH"/>
<constraint firstAttribute="trailing" secondItem="Yg1-5Z-6FI" secondAttribute="trailing" id="PSV-WU-ltt"/>
<constraint firstItem="Yg1-5Z-6FI" firstAttribute="leading" secondItem="Vhd-CK-34X" secondAttribute="leading" id="UQ7-m3-LKy"/>
<constraint firstItem="n2a-eR-E9T" firstAttribute="centerX" secondItem="Vhd-CK-34X" secondAttribute="centerX" id="hN7-qa-9iE"/>
</constraints>
</view>
<navigationItem key="navigationItem" title="Picker" id="Op4-F3-Cvs"/>
<connections>
<outlet property="infiniteCollectionView" destination="Yg1-5Z-6FI" id="dYW-hl-GxB"/>
<outlet property="selectedView" destination="n2a-eR-E9T" id="kE8-NA-C2Q"/>
</connections>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="jlK-LR-KNy" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="22" y="802"/>
</scene>
<!--Rx-->
<scene sceneID="w7X-Gq-cbX">
<objects>
<viewController id="LXZ-Bh-cft" customClass="RxBaseCollectionViewController" customModule="InfiniteLayout_Example" customModuleProvider="target" sceneMemberID="viewController">
<layoutGuides>
<viewControllerLayoutGuide type="top" id="LXw-fB-orN"/>
<viewControllerLayoutGuide type="bottom" id="QOu-Qv-Lfh"/>
</layoutGuides>
<view key="view" contentMode="scaleToFill" id="zqI-fb-q5e">
<rect key="frame" x="0.0" y="0.0" width="320" height="568"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<collectionView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" showsHorizontalScrollIndicator="NO" showsVerticalScrollIndicator="NO" dataMode="prototypes" translatesAutoresizingMaskIntoConstraints="NO" id="ci9-0n-fka" customClass="RxInfiniteCollectionView" customModule="InfiniteLayout">
<rect key="frame" x="0.0" y="0.0" width="320" height="568"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<collectionViewFlowLayout key="collectionViewLayout" minimumLineSpacing="10" minimumInteritemSpacing="1000" id="mgE-mI-m4m">
<size key="itemSize" width="200" height="125"/>
<size key="headerReferenceSize" width="0.0" height="0.0"/>
<size key="footerReferenceSize" width="0.0" height="0.0"/>
<inset key="sectionInset" minX="0.0" minY="0.0" maxX="0.0" maxY="10"/>
</collectionViewFlowLayout>
<cells>
<collectionViewCell opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" reuseIdentifier="cell" id="T9n-br-7do" customClass="Cell" customModule="InfiniteLayout_Example" customModuleProvider="target">
<rect key="frame" x="60" y="0.0" width="200" height="125"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<view key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" insetsLayoutMarginsFromSafeArea="NO">
<rect key="frame" x="0.0" y="0.0" width="200" height="125"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="jBQ-Rn-0uJ" customClass="CellView" customModule="InfiniteLayout_Example" customModuleProvider="target">
<rect key="frame" x="0.0" y="0.0" width="200" height="125"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
</view>
</subviews>
</view>
<constraints>
<constraint firstAttribute="trailing" secondItem="jBQ-Rn-0uJ" secondAttribute="trailing" id="82l-QQ-iZ6"/>
<constraint firstItem="jBQ-Rn-0uJ" firstAttribute="top" secondItem="T9n-br-7do" secondAttribute="top" id="qFb-og-mcO"/>
<constraint firstAttribute="bottom" secondItem="jBQ-Rn-0uJ" secondAttribute="bottom" id="rGV-tl-lnS"/>
<constraint firstItem="jBQ-Rn-0uJ" firstAttribute="leading" secondItem="T9n-br-7do" secondAttribute="leading" id="ugu-X8-5hQ"/>
</constraints>
<connections>
<outlet property="cellView" destination="jBQ-Rn-0uJ" id="kQb-9f-ey8"/>
</connections>
</collectionViewCell>
</cells>
</collectionView>
</subviews>
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<constraints>
<constraint firstItem="ci9-0n-fka" firstAttribute="top" secondItem="zqI-fb-q5e" secondAttribute="top" id="C5M-bI-V1A"/>
<constraint firstItem="ci9-0n-fka" firstAttribute="leading" secondItem="zqI-fb-q5e" secondAttribute="leading" id="ftH-Ue-rBH"/>
<constraint firstItem="ci9-0n-fka" firstAttribute="trailing" secondItem="zqI-fb-q5e" secondAttribute="trailing" id="wy7-Ci-NVK"/>
<constraint firstAttribute="bottom" secondItem="ci9-0n-fka" secondAttribute="bottom" id="yOo-Td-V0c"/>
</constraints>
</view>
<navigationItem key="navigationItem" title="Rx" id="BMY-0d-YCj"/>
<connections>
<outlet property="infiniteCollectionView" destination="ci9-0n-fka" id="fSY-Qh-bhA"/>
</connections>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="TkU-4C-L7I" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="-770" y="-671"/>
</scene>
<!--Horizontal Layout-->
<scene sceneID="0Xz-LA-moA">
<objects>
<collectionViewController id="CU7-Hg-6kG" customClass="BaseCollectionViewController" customModule="InfiniteLayout_Example" customModuleProvider="target" sceneMemberID="viewController">
<collectionView key="view" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" showsHorizontalScrollIndicator="NO" showsVerticalScrollIndicator="NO" dataMode="prototypes" id="Iuy-Ov-jLz" customClass="InfiniteCollectionView" customModule="InfiniteLayout">
<rect key="frame" x="0.0" y="0.0" width="320" height="568"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<collectionViewFlowLayout key="collectionViewLayout" scrollDirection="horizontal" minimumLineSpacing="10" minimumInteritemSpacing="1000" id="i7U-Tf-kfd">
<size key="itemSize" width="250" height="150"/>
<size key="headerReferenceSize" width="0.0" height="0.0"/>
<size key="footerReferenceSize" width="0.0" height="0.0"/>
<inset key="sectionInset" minX="0.0" minY="0.0" maxX="0.0" maxY="0.0"/>
<inset key="sectionInset" minX="0.0" minY="0.0" maxX="10" maxY="0.0"/>
</collectionViewFlowLayout>
<cells>
<collectionViewCell opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" reuseIdentifier="cell" id="Vf0-ro-DM5" customClass="Cell" customModule="InfiniteLayout_Example" customModuleProvider="target">
<rect key="frame" x="0.0" y="0.0" width="150" height="100"/>
<collectionViewCell opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" reuseIdentifier="cell" id="BVQ-cb-bj4" customClass="Cell" customModule="InfiniteLayout_Example" customModuleProvider="target">
<rect key="frame" x="0.0" y="177" width="250" height="150"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<view key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" insetsLayoutMarginsFromSafeArea="NO">
<rect key="frame" x="0.0" y="0.0" width="150" height="100"/>
<rect key="frame" x="0.0" y="0.0" width="250" height="150"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="SWd-w1-K3N" customClass="CellView" customModule="InfiniteLayout_Example" customModuleProvider="target">
<rect key="frame" x="0.0" y="0.0" width="150" height="100"/>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="1wM-ge-YKX" customClass="CellView" customModule="InfiniteLayout_Example" customModuleProvider="target">
<rect key="frame" x="0.0" y="0.0" width="250" height="150"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
</view>
</subviews>
</view>
<constraints>
<constraint firstItem="SWd-w1-K3N" firstAttribute="leading" secondItem="Vf0-ro-DM5" secondAttribute="leading" id="HM6-Rd-74h"/>
<constraint firstItem="SWd-w1-K3N" firstAttribute="top" secondItem="Vf0-ro-DM5" secondAttribute="top" id="jJh-U5-WWC"/>
<constraint firstAttribute="bottom" secondItem="SWd-w1-K3N" secondAttribute="bottom" id="n3V-C9-rAP"/>
<constraint firstAttribute="trailing" secondItem="SWd-w1-K3N" secondAttribute="trailing" id="oPa-mO-RJ6"/>
<constraint firstAttribute="bottom" secondItem="1wM-ge-YKX" secondAttribute="bottom" id="44V-6k-Vxi"/>
<constraint firstAttribute="trailing" secondItem="1wM-ge-YKX" secondAttribute="trailing" id="BO0-dS-ek7"/>
<constraint firstItem="1wM-ge-YKX" firstAttribute="leading" secondItem="BVQ-cb-bj4" secondAttribute="leading" id="O2r-kl-QZo"/>
<constraint firstItem="1wM-ge-YKX" firstAttribute="top" secondItem="BVQ-cb-bj4" secondAttribute="top" id="kT9-T5-w19"/>
</constraints>
<connections>
<outlet property="cellView" destination="SWd-w1-K3N" id="2A6-17-L1b"/>
<outlet property="cellView" destination="1wM-ge-YKX" id="BFc-xc-0VL"/>
</connections>
</collectionViewCell>
</cells>
<userDefinedRuntimeAttributes>
<userDefinedRuntimeAttribute type="boolean" keyPath="isItemPagingEnabled" value="YES"/>
<userDefinedRuntimeAttribute type="number" keyPath="velocityMultiplier">
<real key="value" value="1"/>
</userDefinedRuntimeAttribute>
</userDefinedRuntimeAttributes>
<connections>
<outlet property="dataSource" destination="sSL-wr-E9v" id="weE-ef-YRt"/>
<outlet property="delegate" destination="sSL-wr-E9v" id="hva-cg-vBg"/>
<outlet property="dataSource" destination="CU7-Hg-6kG" id="BOZ-dL-WLG"/>
<outlet property="delegate" destination="CU7-Hg-6kG" id="NKU-4F-wIF"/>
</connections>
</collectionView>
<navigationItem key="navigationItem" id="qS4-kF-4vO"/>
<navigationItem key="navigationItem" title="Horizontal Layout" id="AaD-SQ-gYu"/>
</collectionViewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="bMF-CJ-xJ6" userLabel="First Responder" sceneMemberID="firstResponder"/>
<placeholder placeholderIdentifier="IBFirstResponder" id="Dsp-JP-Q6i" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="-68" y="36.945812807881772"/>
<point key="canvasLocation" x="21" y="-670"/>
</scene>
<!--Vertical Layout-->
<scene sceneID="ccn-9j-1sQ">
<objects>
<collectionViewController id="9P2-Ud-D2M" customClass="BaseCollectionViewController" customModule="InfiniteLayout_Example" customModuleProvider="target" sceneMemberID="viewController">
<collectionView key="view" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" showsHorizontalScrollIndicator="NO" showsVerticalScrollIndicator="NO" dataMode="prototypes" id="iW9-Tg-dTG" customClass="InfiniteCollectionView" customModule="InfiniteLayout">
<rect key="frame" x="0.0" y="0.0" width="320" height="568"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<collectionViewFlowLayout key="collectionViewLayout" minimumLineSpacing="10" minimumInteritemSpacing="1000" id="t85-Nu-Mt1">
<size key="itemSize" width="150" height="100"/>
<size key="headerReferenceSize" width="0.0" height="0.0"/>
<size key="footerReferenceSize" width="0.0" height="0.0"/>
<inset key="sectionInset" minX="0.0" minY="0.0" maxX="0.0" maxY="10"/>
</collectionViewFlowLayout>
<cells>
<collectionViewCell opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" reuseIdentifier="cell" id="OtA-Vt-ejC" customClass="Cell" customModule="InfiniteLayout_Example" customModuleProvider="target">
<rect key="frame" x="85" y="0.0" width="150" height="100"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<view key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" insetsLayoutMarginsFromSafeArea="NO">
<rect key="frame" x="0.0" y="0.0" width="150" height="100"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="fuL-Uj-Mda" customClass="CellView" customModule="InfiniteLayout_Example" customModuleProvider="target">
<rect key="frame" x="0.0" y="0.0" width="150" height="100"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
</view>
</subviews>
</view>
<constraints>
<constraint firstItem="fuL-Uj-Mda" firstAttribute="leading" secondItem="OtA-Vt-ejC" secondAttribute="leading" id="KLH-6Z-vhB"/>
<constraint firstAttribute="trailing" secondItem="fuL-Uj-Mda" secondAttribute="trailing" id="bzB-oG-udJ"/>
<constraint firstAttribute="bottom" secondItem="fuL-Uj-Mda" secondAttribute="bottom" id="fgE-nQ-BGU"/>
<constraint firstItem="fuL-Uj-Mda" firstAttribute="top" secondItem="OtA-Vt-ejC" secondAttribute="top" id="t4I-qE-DtN"/>
</constraints>
<connections>
<outlet property="cellView" destination="fuL-Uj-Mda" id="AN4-RB-ul0"/>
</connections>
</collectionViewCell>
</cells>
<userDefinedRuntimeAttributes>
<userDefinedRuntimeAttribute type="boolean" keyPath="isItemPagingEnabled" value="NO"/>
</userDefinedRuntimeAttributes>
<connections>
<outlet property="dataSource" destination="9P2-Ud-D2M" id="juO-P6-XzS"/>
<outlet property="delegate" destination="9P2-Ud-D2M" id="ns0-fe-DSq"/>
</connections>
</collectionView>
<navigationItem key="navigationItem" title="Vertical Layout" id="2E2-oh-ELb"/>
</collectionViewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="Lvb-qy-gZV" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="22" y="61"/>
</scene>
</scenes>
</document>
@@ -0,0 +1,25 @@
//
// BaseCollectionViewController.swift
// InfiniteLayout_Example
//
// Created by Arnaud Dorgans on 29/12/2017.
// Copyright © 2017 CocoaPods. All rights reserved.
//
import UIKit
import InfiniteLayout
class BaseCollectionViewController: InfiniteCollectionViewController {
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as? Cell else {
fatalError()
}
cell.update(index: self.infiniteCollectionView!.indexPath(from: indexPath).row)
return cell
}
override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 20
}
}
+32 -2
View File
@@ -17,12 +17,30 @@ class Cell: UICollectionViewCell {
}
}
enum CellStyle {
case circular
case `default`
static let all = [circular, `default`]
}
@IBDesignable class CellView: UIView {
let titleLabel = UILabel()
let colors = [#colorLiteral(red: 0.5254901961, green: 0.6901960784, blue: 0.9137254902, alpha: 1), #colorLiteral(red: 0.5254901961, green: 0.6196078431, blue: 0.9137254902, alpha: 1), #colorLiteral(red: 0.6078431373, green: 0.5254901961, blue: 0.9137254902, alpha: 1), #colorLiteral(red: 0.9137254902, green: 0.5254901961, blue: 0.8392156863, alpha: 1), #colorLiteral(red: 0.9137254902, green: 0.5254901961, blue: 0.6, alpha: 1), #colorLiteral(red: 0.9137254902, green: 0.6784313725, blue: 0.5254901961, alpha: 1), #colorLiteral(red: 0.9137254902, green: 0.9058823529, blue: 0.5254901961, alpha: 1), #colorLiteral(red: 0.5254901961, green: 0.9137254902, blue: 0.5921568627, alpha: 1), #colorLiteral(red: 0.5254901961, green: 0.8, blue: 0.9137254902, alpha: 1)]
@IBInspectable var styleIndex: Int {
get { return CellStyle.all.index(of: style)! }
set { style = CellStyle.all[newValue % CellStyle.all.count] }
}
var style: CellStyle = .default {
didSet {
updateStyle()
}
}
override init(frame: CGRect) {
super.init(frame: frame)
sharedInit()
@@ -45,14 +63,26 @@ class Cell: UICollectionViewCell {
self.addSubview(titleLabel)
titleLabel.centerXAnchor.constraint(equalTo: self.centerXAnchor).isActive = true
titleLabel.centerYAnchor.constraint(equalTo: self.centerYAnchor).isActive = true
self.layer.cornerRadius = 8
}
func update(index: Int) {
self.titleLabel.text = String(index + 1)
self.backgroundColor = colors[index % colors.count]
}
func updateStyle() {
switch style {
case .default:
self.layer.cornerRadius = 8
case .circular:
self.layer.cornerRadius = self.frame.height / 2
}
}
override func layoutSubviews() {
super.layoutSubviews()
updateStyle()
}
override func prepareForInterfaceBuilder() {
self.update(index: 0)
+33
View File
@@ -0,0 +1,33 @@
//
// CustomLayout.swift
// InfiniteLayout_Example
//
// Created by Arnaud Dorgans on 28/12/2017.
// Copyright © 2017 CocoaPods. All rights reserved.
//
import UIKit
import InfiniteLayout
class CustomLayout: InfiniteLayout {
let minimumScale: CGFloat = 0.75
let rangeRatio: CGFloat = 1
override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
let attributes = super.layoutAttributesForElements(in: rect).flatMap {
self.copyLayoutAttributes(from: $0)
}
guard let visibleRect = self.visibleCollectionViewRect() else {
return attributes
}
let centeredOffset = CGPoint(x: visibleRect.midX, y: visibleRect.midY)
for attributes in attributes ?? [] {
let diff = self.scrollDirection == .horizontal ? centeredOffset.x - attributes.center.x : centeredOffset.y - attributes.center.y
let scale = max(min(diff / (min(visibleRect.width, visibleRect.height) * rangeRatio), 1), -1)
attributes.transform = attributes.transform.translatedBy(x: abs(scale * (visibleRect.width / 2)), y: 0)
attributes.transform = attributes.transform.rotated(by: scale * (CGFloat.pi / 2))
}
return attributes
}
}
@@ -0,0 +1,51 @@
//
// CustomViewController.swift
// InfiniteLayout
//
// Created by Arnoymous on 12/20/2017.
// Copyright (c) 2017 Arnoymous. All rights reserved.
//
import UIKit
import InfiniteLayout
class CustomViewController: UIViewController {
@IBOutlet weak var infiniteCollectionView: InfiniteCollectionView!
}
extension CustomViewController: UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 14
}
func numberOfSections(in collectionView: UICollectionView) -> Int {
return 2
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! Cell
cell.update(index: self.infiniteCollectionView!.indexPath(from: indexPath).row)
return cell
}
}
extension CustomViewController: UICollectionViewDelegateFlowLayout {
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: 100, height: 100)
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat {
return collectionView.frame.height
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
return 10
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets {
return UIEdgeInsets(top: 0, left: 0, bottom: 10, right: 0)
}
}
-1
View File
@@ -33,7 +33,6 @@
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
</array>
</dict>
</plist>
@@ -0,0 +1,41 @@
//
// PickerController.swift
// InfiniteLayout_Example
//
// Created by Arnaud Dorgans on 03/01/2018.
// Copyright © 2018 CocoaPods. All rights reserved.
//
import UIKit
import InfiniteLayout
class PickerController: UIViewController {
@IBOutlet weak var selectedView: CellView!
@IBOutlet weak var infiniteCollectionView: InfiniteCollectionView!
}
extension PickerController: UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 20
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as? Cell else {
fatalError()
}
cell.update(index: self.infiniteCollectionView.indexPath(from: indexPath).row)
return cell
}
}
extension PickerController: InfiniteCollectionViewDelegate {
func infiniteCollectionView(_ infiniteCollectionView: InfiniteCollectionView, didChangeCenteredIndexPath centeredIndexPath: IndexPath?) {
guard let indexPath = centeredIndexPath else {
return
}
self.selectedView.update(index: self.infiniteCollectionView.indexPath(from: indexPath).row)
}
}
@@ -0,0 +1,39 @@
//
// RxBaseCollectionViewController.swift
// InfiniteLayout_Example
//
// Created by Arnaud Dorgans on 03/01/2018.
// Copyright © 2018 CocoaPods. All rights reserved.
//
import UIKit
import RxSwift
import RxCocoa
import RxDataSources
import InfiniteLayout
class RxBaseCollectionViewController: UIViewController {
@IBOutlet weak var infiniteCollectionView: RxInfiniteCollectionView!
let disposeBag = DisposeBag()
override func viewDidLoad() {
super.viewDidLoad()
Observable.just(Array(0..<20))
.bind(to: infiniteCollectionView.rx.items(cellIdentifier: "cell", cellType: Cell.self, infinite: true)) { _, index, cell in
cell.update(index: index)
}.disposed(by: disposeBag)
infiniteCollectionView.rx.modelCentered(Int.self)
.asDriver()
.drive(onNext: { current in
guard let current = current else {
print("centered: none")
return
}
print("centered: \(current + 1)")
}).disposed(by: disposeBag)
}
}
@@ -1,55 +0,0 @@
//
// ViewController.swift
// InfiniteLayout
//
// Created by Arnoymous on 12/20/2017.
// Copyright (c) 2017 Arnoymous. All rights reserved.
//
import UIKit
import InfiniteLayout
class ViewController: InfiniteCollectionViewController {
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
guard let collectionView = self.collectionView else {
return
}
let cells = collectionView.indexPathsForVisibleItems.sorted()
for i in 0..<cells.count {
guard let cell = collectionView.cellForItem(at: cells[i]) else {
return
}
cell.alpha = 0
UIView.animate(withDuration: 0.3, delay: TimeInterval(i) * 0.05, options: [], animations: {
cell.alpha = 1
}, completion: nil)
}
}
override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 14
}
override func numberOfSections(in collectionView: UICollectionView) -> Int {
return 1
}
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! Cell
cell.update(index: self.infiniteCollectionView!.indexPath(from: indexPath).row)
return cell
}
}
extension ViewController: UICollectionViewDelegateFlowLayout {
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let height: CGFloat = 100
let width: CGFloat = height * 2
return CGSize(width: width, height: height)
}
}
+1
View File
@@ -2,6 +2,7 @@ use_frameworks!
target 'InfiniteLayout_Example' do
pod 'InfiniteLayout', :path => '../'
pod 'InfiniteLayout/Rx', :path => '../'
target 'InfiniteLayout_Tests' do
inherit! :search_paths
+28 -4
View File
@@ -1,16 +1,40 @@
PODS:
- InfiniteLayout (0.1.0)
- CocoaProxy (0.1.1)
- Differentiator (3.0.2)
- InfiniteLayout (0.1.6):
- CocoaProxy (~> 0.1)
- InfiniteLayout/Core (= 0.1.6)
- InfiniteLayout/Core (0.1.6):
- CocoaProxy (~> 0.1)
- InfiniteLayout/Rx (0.1.6):
- CocoaProxy (~> 0.1)
- RxCocoa (~> 4.0)
- RxDataSources (~> 3.0)
- RxSwift (~> 4.0)
- RxCocoa (4.1.0):
- RxSwift (~> 4.0)
- RxDataSources (3.0.2):
- Differentiator (~> 3.0)
- RxCocoa (~> 4.0)
- RxSwift (~> 4.0)
- RxSwift (4.1.0)
DEPENDENCIES:
- InfiniteLayout (from `../`)
- InfiniteLayout/Rx (from `../`)
EXTERNAL SOURCES:
InfiniteLayout:
:path: ../
SPEC CHECKSUMS:
InfiniteLayout: 82a21b8255623e2d72174f3082bef9e576140621
CocoaProxy: 35ab81e24325b33834cffe45a3d1fd48ca67ef3a
Differentiator: a87be69eba49ec4ab460c7671143ee3a9eececfd
InfiniteLayout: 27a10e0a766122807a73740b5e7c7384fcdc37a2
RxCocoa: cc1fec49cdc8fabe645964de7c51c099a36c2aa8
RxDataSources: cb7c31e652a87ebb919da45f716bbb87b3765f6b
RxSwift: 4219941c1244c88002901bd87a69d3aea9ae71f0
PODFILE CHECKSUM: 3a658536624f41ec07c5a7a2c55407b9b8f48528
PODFILE CHECKSUM: 3501aeed91e62112dd38178a52f26b204b97c8c0
COCOAPODS: 1.4.0.beta.2
COCOAPODS: 1.4.0.rc.1
+20 -7
View File
@@ -8,8 +8,8 @@
Pod::Spec.new do |s|
s.name = 'InfiniteLayout'
s.version = '0.1.1'
s.summary = 'Vertical and Horizontal infinite scrolling for UICollectionView'
s.version = '0.2.0.3'
s.summary = 'Horizontal and Vertical infinite scrolling feature for UICollectionView with Paging, NSProxy delegate, Reactive extension'
# This description is used to generate tags and improve search results.
# * Think: What does it do? Why did you write it? What is the focus?
@@ -18,7 +18,7 @@ Pod::Spec.new do |s|
# * Finally, don't worry about the indent, CocoaPods strips it!
s.description = <<-DESC
Vertical and Horizontal infinite scrolling for UICollectionView with Paging and NSProxy delegate
Horizontal and Vertical infinite scrolling feature for UICollectionView with Paging, NSProxy delegate, Reactive extension, SectionModel & AnimatableSectionModel support
DESC
s.homepage = 'https://github.com/Arnoymous/InfiniteLayout'
@@ -30,9 +30,8 @@ Vertical and Horizontal infinite scrolling for UICollectionView with Paging and
s.ios.deployment_target = '8.0'
s.tvos.deployment_target = '9.0'
s.source_files = 'InfiniteLayout/Classes/**/*'
#s.xcconfig = { 'SWIFT_OBJC_BRIDGING_HEADER' => '${POD_ROOT}/InfiniteLayout/BridgeHeader.h' }
#s.xcconfig = { 'SWIFT_OBJC_BRIDGING_HEADER' => '${POD_ROOT}/InfiniteLayout/BridgeHeader.h' }
# s.resource_bundles = {
# 'InfiniteLayout' => ['InfiniteLayout/Assets/*.png']
@@ -40,5 +39,19 @@ Vertical and Horizontal infinite scrolling for UICollectionView with Paging and
# s.public_header_files = 'Pod/Classes/**/*.h'
# s.frameworks = 'UIKit', 'MapKit'
# s.dependency 'AFNetworking', '~> 2.3'
s.dependency 'CocoaProxy', '~> 0.1'
s.default_subspec = 'Core'
s.subspec 'Core' do |core|
core.source_files = 'InfiniteLayout/Classes/**/*'
end
s.subspec 'Rx' do |rx|
rx.dependency 'InfiniteLayout/Core', '~> 0.2'
rx.dependency 'RxSwift', '~> 4.0'
rx.dependency 'RxCocoa', '~> 4.0'
rx.dependency 'RxDataSources', '~> 3.0'
rx.source_files = 'InfiniteLayout/Rx/**/*'
end
end
@@ -7,23 +7,31 @@
import UIKit
@objc public protocol InfiniteCollectionViewDelegate {
@objc optional func infiniteCollectionView(_ infiniteCollectionView: InfiniteCollectionView, didChangeCenteredIndexPath centeredIndexPath: IndexPath?)
}
open class InfiniteCollectionView: UICollectionView {
lazy var delegateProxy = InfiniteCollectionViewDelegateProxy(self, exceptions: ["scrollViewDidScroll:",
"scrollViewWillEndDragging:withVelocity:targetContentOffset:"])
lazy var dataSourceProxy = InfiniteCollectionViewDataSourceProxy(self, exceptions: ["numberOfSectionsInCollectionView:",
"collectionView:numberOfItemsInSection:"])
lazy var dataSourceProxy = InfiniteCollectionViewDataSourceProxy(collectionView: self)
lazy var delegateProxy = InfiniteCollectionViewDelegateProxy(collectionView: self)
@IBInspectable var isItemPagingEnabled: Bool = false
@IBInspectable var velocityMultiplier: CGFloat = 500 {
didSet {
self.infiniteLayout.velocityMultiplier = velocityMultiplier
}
}
@IBOutlet open var infiniteDelegate: InfiniteCollectionViewDelegate?
open private(set) var centeredIndexPath: IndexPath?
open var preferredCenteredIndexPath: IndexPath? = IndexPath(item: 0, section: 0)
var forwardDelegate: Bool { return true }
var _contentSize: CGSize?
override open var delegate: UICollectionViewDelegate? {
get { return super.delegate }
set {
guard forwardDelegate else {
super.delegate = newValue
return
}
guard let newValue = newValue else {
super.delegate = nil
return
@@ -36,9 +44,14 @@ open class InfiniteCollectionView: UICollectionView {
super.delegate = delegate
}
}
override open var dataSource: UICollectionViewDataSource? {
get { return super.dataSource }
set {
guard forwardDelegate else {
super.dataSource = newValue
return
}
guard let newValue = newValue else {
super.dataSource = nil
return
@@ -52,66 +65,58 @@ open class InfiniteCollectionView: UICollectionView {
}
}
@IBInspectable open var isItemPagingEnabled: Bool = false
@IBInspectable open var velocityMultiplier: CGFloat = 1 {
didSet {
self.infiniteLayout.velocityMultiplier = velocityMultiplier
}
}
public var infiniteLayout: InfiniteLayout! {
return self.collectionViewLayout as? InfiniteLayout
}
private static func infiniteLayout(layout: UICollectionViewLayout) -> InfiniteLayout {
guard let infiniteLayout = layout as? InfiniteLayout else {
return InfiniteLayout(layout: layout)
}
return infiniteLayout
}
public override init(frame: CGRect, collectionViewLayout layout: UICollectionViewLayout) {
super.init(frame: frame, collectionViewLayout: InfiniteLayout(layout: layout))
super.init(frame: frame, collectionViewLayout: InfiniteCollectionView.infiniteLayout(layout: layout))
sharedInit()
}
required public init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
let layout = self.collectionViewLayout
if !(layout is InfiniteLayout) {
self.collectionViewLayout = InfiniteLayout(layout: layout)
let infiniteLayout = InfiniteCollectionView.infiniteLayout(layout: self.collectionViewLayout)
if self.collectionViewLayout != infiniteLayout {
self.collectionViewLayout = infiniteLayout
}
}
open override func awakeFromNib() {
super.awakeFromNib()
sharedInit()
}
private func sharedInit() {
delegateProxy.collectionView = self
dataSourceProxy.collectionView = self
self.showsVerticalScrollIndicator = false
self.showsHorizontalScrollIndicator = false
#if os(iOS)
self.scrollsToTop = false
#endif
}
open override func layoutSubviews() {
super.layoutSubviews()
self.centerCollectionViewIfNeeded()
self.loopCollectionViewIfNeeded()
}
}
extension InfiniteCollectionView: UICollectionViewDelegate {
// MARK: Loop
func loopCollectionViewIfNeeded() {
self.infiniteLayout.loopCollectionViewIfNeeded()
}
public func scrollViewDidScroll(_ scrollView: UIScrollView) {
delegateProxy.delegate?.scrollViewDidScroll?(scrollView)
self.loopCollectionViewIfNeeded()
}
// MARK: Paging
func centerCollectionViewIfNeeded() {
guard isItemPagingEnabled,
!self.isDragging && !self.isDecelerating else {
return
}
self.infiniteLayout.centerCollectionViewIfNeeded()
}
public func scrollViewWillEndDragging(_ scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer<CGPoint>) {
if isItemPagingEnabled {
self.infiniteLayout.centerCollectionView(withVelocity: velocity, targetContentOffset: targetContentOffset)
}
}
}
// MARK: DataSource
extension InfiniteCollectionView: UICollectionViewDataSource {
@@ -129,33 +134,70 @@ extension InfiniteCollectionView: UICollectionViewDataSource {
return items
}
private var multiplier: Int {
return InfiniteDataSources.multiplier(estimatedItemSize: self.infiniteLayout.itemSize)
}
public func section(from infiniteSection: Int) -> Int {
return infiniteSection % delegateNumberOfSections
return InfiniteDataSources.section(from: infiniteSection, numberOfSections: delegateNumberOfSections)
}
public func indexPath(from infiniteIndexPath: IndexPath) -> IndexPath {
let items = delegateNumberOfItems(in: infiniteIndexPath.section)
return IndexPath(item: infiniteIndexPath.item % items, section: self.section(from: infiniteIndexPath.section))
}
private var multiplier: Int {
let min = Swift.min(self.infiniteLayout.itemSize.width, self.infiniteLayout.itemSize.height)
let count = ceil(InfiniteLayout.minimumContentSize.width / min)
return Int(count) * delegateNumberOfSections
return InfiniteDataSources.indexPath(from: infiniteIndexPath,
numberOfSections: delegateNumberOfSections,
numberOfItems: delegateNumberOfItems(in: infiniteIndexPath.section))
}
public func numberOfSections(in collectionView: UICollectionView) -> Int {
let delegateNumberOfSections = self.delegateNumberOfSections
return delegateNumberOfSections > 1 ? delegateNumberOfSections * multiplier : delegateNumberOfSections
return InfiniteDataSources.numberOfSections(numberOfSections: delegateNumberOfSections, multiplier: multiplier)
}
public func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
let delegateNumberOfSections = self.delegateNumberOfSections
let delegateNumberOfItems = self.delegateNumberOfItems(in: section)
return delegateNumberOfSections > 1 ? delegateNumberOfItems : delegateNumberOfItems * multiplier
return InfiniteDataSources.numberOfItemsInSection(numberOfItemsInSection: delegateNumberOfItems(in: section),
numberOfSections: delegateNumberOfSections,
multiplier: multiplier)
}
public func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
fatalError("collectionView dataSource is required")
}
}
extension InfiniteCollectionView: UICollectionViewDelegate {
// MARK: Loop
func loopCollectionViewIfNeeded() {
self.infiniteLayout.loopCollectionViewIfNeeded()
self.centerCollectionViewIfNeeded()
}
public func scrollViewDidScroll(_ scrollView: UIScrollView) {
delegateProxy.delegate?.scrollViewDidScroll?(scrollView)
self.loopCollectionViewIfNeeded()
let preferredVisibleIndexPath = infiniteLayout.preferredVisibleLayoutAttributes()?.indexPath
if self.centeredIndexPath != preferredVisibleIndexPath {
self.centeredIndexPath = preferredVisibleIndexPath
self.infiniteDelegate?.infiniteCollectionView?(self, didChangeCenteredIndexPath: preferredVisibleIndexPath)
}
}
// MARK: Paging
func centerCollectionViewIfNeeded() {
guard isItemPagingEnabled,
!self.isDragging && !self.isDecelerating else {
return
}
guard self._contentSize != self.contentSize else {
return
}
self._contentSize = self.contentSize
self.infiniteLayout.centerCollectionViewIfNeeded(indexPath: self.preferredCenteredIndexPath)
}
public func scrollViewWillEndDragging(_ scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer<CGPoint>) {
if isItemPagingEnabled {
self.infiniteLayout.centerCollectionView(withVelocity: velocity, targetContentOffset: targetContentOffset)
}
}
}
@@ -1,19 +0,0 @@
//
// InfiniteCollectionViewProxy.h
// InfiniteLayout
//
// Created by Arnaud Dorgans on 21/12/2017.
//
#import <Foundation/Foundation.h>
@interface _InfiniteCollectionViewProxy<T: id<NSObject>> : NSProxy <UICollectionViewDelegate, UICollectionViewDataSource>
-(nonnull instancetype)init:(nullable T)collectionView exceptions:(nonnull NSArray<NSString*>*)exceptions;
@property (nonatomic, weak, nullable) T collectionView;
@property (nonatomic, weak, nullable) T delegate;
@property (nonatomic, nonnull, retain) NSArray* exceptions;
@end
@@ -1,52 +0,0 @@
//
// InfiniteCollectionViewProxy.m
// InfiniteLayout
//
// Created by Arnaud Dorgans on 21/12/2017.
//
#import "InfiniteCollectionViewProxy.h"
@implementation _InfiniteCollectionViewProxy
-(instancetype)init:(nullable id)collectionView exceptions:(nonnull NSArray<NSString*>*)exceptions {
self.collectionView = collectionView;
self.exceptions = exceptions;
return self;
}
- (BOOL)respondsToSelector:(SEL)aSelector
{
return ([self.collectionView respondsToSelector:aSelector] || [self.delegate respondsToSelector:aSelector]);
}
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
NSObject *delegateForResonse = [self.delegate respondsToSelector:aSelector] ? self.delegate : self.collectionView;
NSMethodSignature *signature = [delegateForResonse respondsToSelector:aSelector] ? [delegateForResonse methodSignatureForSelector:aSelector] : nil;
return signature;
}
- (void)forwardInvocation:(NSInvocation *)invocation
{
NSString *selectorName = NSStringFromSelector(invocation.selector);
NSArray<id<NSObject>> *delegates = @[self.delegate, self.collectionView];
if ([self.exceptions containsObject:selectorName]) {
delegates = [[delegates reverseObjectEnumerator] allObjects];
}
for (int i = 0; i < delegates.count; i++) {
if ([delegates[i] respondsToSelector:invocation.selector]) {
[self invokeInvocation:invocation onDelegate:delegates[i]];
return;
}
}
}
- (void)invokeInvocation:(NSInvocation *)invocation onDelegate:(id<NSObject>)delegate
{
if ([delegate respondsToSelector:invocation.selector]) {
[invocation invokeWithTarget:delegate];
}
}
@end
@@ -0,0 +1,85 @@
//
// Proxy.swift
// InfiniteLayout
//
// Created by Arnaud Dorgans on 20/12/2017.
//
import UIKit
import CocoaProxy
class InfiniteCollectionViewProxy<T: NSObjectProtocol>: CocoaProxy {
var collectionView: InfiniteCollectionView! {
get { return self.proxies.first as? InfiniteCollectionView }
set {
if !self.proxies.isEmpty {
self.proxies.removeFirst()
}
self.proxies.insert(newValue, at: 0)
}
}
var delegate: T? {
get {
guard self.proxies.count > 1 else {
return nil
}
return self.proxies.last as? T
} set {
while self.proxies.count > 1 {
self.proxies.removeLast()
}
guard let delegate = newValue else {
return
}
self.proxies.append(delegate)
}
}
override func proxies(for aSelector: Selector) -> [NSObjectProtocol] {
return super.proxies(for: aSelector).reversed()
}
init(collectionView: InfiniteCollectionView) {
super.init(proxies: [])
self.collectionView = collectionView
}
}
class InfiniteCollectionViewDelegateProxy: InfiniteCollectionViewProxy<UICollectionViewDelegate>, UICollectionViewDelegate {
override func proxies(for aSelector: Selector) -> [NSObjectProtocol] {
return super.proxies(for: aSelector)
.first { proxy in
guard !(aSelector == #selector(UIScrollViewDelegate.scrollViewDidScroll(_:)) ||
aSelector == #selector(UIScrollViewDelegate.scrollViewWillEndDragging(_:withVelocity:targetContentOffset:))) else {
return proxy is InfiniteCollectionView
}
return true
}.flatMap { [$0] } ?? []
}
}
class InfiniteCollectionViewDataSourceProxy: InfiniteCollectionViewProxy<UICollectionViewDataSource>, UICollectionViewDataSource {
override func proxies(for aSelector: Selector) -> [NSObjectProtocol] {
return super.proxies(for: aSelector)
.first { proxy in
guard !(aSelector == #selector(UICollectionViewDataSource.numberOfSections(in:)) ||
aSelector == #selector(UICollectionViewDataSource.collectionView(_:numberOfItemsInSection:))) else {
return proxy is InfiniteCollectionView
}
return true
}.flatMap { [$0] } ?? []
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return self.collectionView.collectionView(collectionView, numberOfItemsInSection: section)
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
return self.delegate?.collectionView(collectionView, cellForItemAt: indexPath) ??
self.collectionView.collectionView(collectionView, cellForItemAt: indexPath)
}
}
@@ -0,0 +1,33 @@
//
// InfiniteDataSources.swift
// InfiniteLayout
//
// Created by Arnaud Dorgans on 03/01/2018.
//
import UIKit
class InfiniteDataSources {
static func section(from infiniteSection: Int, numberOfSections: Int) -> Int {
return infiniteSection % numberOfSections
}
static func indexPath(from infiniteIndexPath: IndexPath, numberOfSections: Int, numberOfItems: Int) -> IndexPath {
return IndexPath(item: infiniteIndexPath.item % numberOfItems, section: self.section(from: infiniteIndexPath.section, numberOfSections: numberOfSections))
}
static func multiplier(estimatedItemSize: CGSize) -> Int {
let min = Swift.min(estimatedItemSize.width, estimatedItemSize.height)
let count = ceil(InfiniteLayout.minimumContentSize / min)
return Int(count)
}
static func numberOfSections(numberOfSections: Int, multiplier: Int) -> Int {
return numberOfSections > 1 ? numberOfSections * multiplier : numberOfSections
}
static func numberOfItemsInSection(numberOfItemsInSection: Int, numberOfSections: Int, multiplier: Int) -> Int {
return numberOfSections > 1 ? numberOfItemsInSection : numberOfItemsInSection * multiplier
}
}
+41 -25
View File
@@ -9,7 +9,7 @@ import UIKit
open class InfiniteLayout: UICollectionViewFlowLayout {
public var velocityMultiplier: CGFloat = 500 // used to simulate paging
public var velocityMultiplier: CGFloat = 1 // used to simulate paging
private let multiplier: CGFloat = 100 // contentOffset multiplier
@@ -35,16 +35,20 @@ open class InfiniteLayout: UICollectionViewFlowLayout {
self.footerReferenceSize = layout.footerReferenceSize
}
static var minimumContentSize: CGSize {
let max = Swift.max(UIScreen.main.bounds.width, UIScreen.main.bounds.height) * 4
return CGSize(width: max, height: max)
static var minimumContentSize: CGFloat {
return max(UIScreen.main.bounds.width, UIScreen.main.bounds.height) * 4
}
override open func prepare() {
let collectionViewContentSize = super.collectionViewContentSize
self.contentSize = CGSize(width: collectionViewContentSize.width, height: collectionViewContentSize.height)
self.isEnabled = (scrollDirection == .horizontal ? self.contentSize.width : self.contentSize.height) >=
InfiniteLayout.minimumContentSize.width
self.isEnabled = {
guard let collectionView = self.collectionView, collectionView.bounds != .zero else {
return false
}
return (scrollDirection == .horizontal ? self.contentSize.width : self.contentSize.height) >=
InfiniteLayout.minimumContentSize
}()
super.prepare()
}
@@ -119,7 +123,7 @@ open class InfiniteLayout: UICollectionViewFlowLayout {
}
private func layoutAttributes(from layoutAttributes: UICollectionViewLayoutAttributes, page: CGPoint) -> UICollectionViewLayoutAttributes! {
guard let attributes = layoutAttributes.copy() as? UICollectionViewLayoutAttributes else {
guard let attributes = self.copyLayoutAttributes(layoutAttributes) else {
return nil
}
attributes.frame = rect(from: attributes.frame, page: page)
@@ -138,14 +142,12 @@ open class InfiniteLayout: UICollectionViewFlowLayout {
let page = self.pageIndex(from: self.page(for: collectionView.contentOffset))
let offset = self.preferredContentOffset(forContentOffset: collectionView.contentOffset)
if (page < 2 || page > self.multiplier - 2) && collectionView.contentOffset != offset {
DispatchQueue.main.async {
collectionView.contentOffset = offset
}
collectionView.contentOffset = offset
}
}
// MARK: Paging
func collectionViewRect() -> CGRect? {
public func collectionViewRect() -> CGRect? {
guard let collectionView = self.collectionView else {
return nil
}
@@ -162,7 +164,7 @@ open class InfiniteLayout: UICollectionViewFlowLayout {
return visibleRect
}
func visibleCollectionViewRect() -> CGRect? {
public func visibleCollectionViewRect() -> CGRect? {
guard let collectionView = self.collectionView,
var collectionViewRect = self.collectionViewRect() else {
return nil
@@ -178,10 +180,10 @@ open class InfiniteLayout: UICollectionViewFlowLayout {
}
return (self.layoutAttributesForElements(in: CGRect(origin: offset ?? collectionView.contentOffset, size: collectionView.frame.size)) ?? [])
.sorted(by: { lhs, rhs in
guard let lhs = self.centerredContentOffset(forRect: lhs.frame) else {
guard let lhs = self.centeredContentOffset(forRect: lhs.frame) else {
return false
}
guard let rhs = self.centerredContentOffset(forRect: rhs.frame) else {
guard let rhs = self.centeredContentOffset(forRect: rhs.frame) else {
return true
}
let value: (CGPoint)->CGFloat = {
@@ -191,7 +193,7 @@ open class InfiniteLayout: UICollectionViewFlowLayout {
})
}
public func preferredVisibleLayoutAttributes(at offset: CGPoint? = nil, velocity: CGPoint = .zero, targetOffset: CGPoint? = nil) -> UICollectionViewLayoutAttributes? {
public func preferredVisibleLayoutAttributes(at offset: CGPoint? = nil, velocity: CGPoint = .zero, targetOffset: CGPoint? = nil, indexPath: IndexPath? = nil) -> UICollectionViewLayoutAttributes? {
guard let currentOffset = self.collectionView?.contentOffset else {
return nil
}
@@ -200,16 +202,21 @@ open class InfiniteLayout: UICollectionViewFlowLayout {
}
let velocity = self.scrollDirection == .horizontal ? velocity.x : velocity.y
let targetDirection = direction(targetOffset ?? currentOffset)
return self.visibleLayoutAttributes(at: offset)
let attributes = self.visibleLayoutAttributes(at: offset)
if let indexPath = indexPath,
let attributes = attributes.first(where: { $0.indexPath == indexPath }) {
return attributes
}
return attributes
.first { attributes in
guard let offset = self.centerredContentOffset(forRect: attributes.frame) else {
guard let offset = self.centeredContentOffset(forRect: attributes.frame) else {
return false
}
return direction(offset) == targetDirection || velocity == 0
}
}
func centerredContentOffset(forRect rect: CGRect) -> CGPoint? {
func centeredContentOffset(forRect rect: CGRect) -> CGPoint? {
guard let collectionView = self.collectionView,
let collectionRect = self.collectionViewRect() else {
return nil
@@ -226,23 +233,32 @@ open class InfiniteLayout: UICollectionViewFlowLayout {
y: self.scrollDirection == .vertical ? collectionView.contentOffset.y + velocity.y * velocityMultiplier : targetContentOffset.pointee.y)
guard let preferredAttributes = self.preferredVisibleLayoutAttributes(at: newTarget, velocity: velocity, targetOffset: targetContentOffset.pointee),
let offset = self.centerredContentOffset(forRect: preferredAttributes.frame) else {
let offset = self.centeredContentOffset(forRect: preferredAttributes.frame) else {
return
}
targetContentOffset.pointee = offset
}
public func centerCollectionViewIfNeeded() {
public func centerCollectionViewIfNeeded(indexPath: IndexPath? = nil) {
guard let collectionView = self.collectionView, self.isEnabled else {
return
}
guard let preferredAttributes = self.preferredVisibleLayoutAttributes(),
let offset = self.centerredContentOffset(forRect: preferredAttributes.frame),
guard let preferredAttributes = self.preferredVisibleLayoutAttributes(indexPath: indexPath),
let offset = self.centeredContentOffset(forRect: preferredAttributes.frame),
collectionView.contentOffset != offset else {
return
}
DispatchQueue.main.async {
collectionView.contentOffset = offset
}
collectionView.contentOffset = offset
}
// MARK: Copy
public func copyLayoutAttributes(_ attributes: UICollectionViewLayoutAttributes) -> UICollectionViewLayoutAttributes? {
return attributes.copy() as? UICollectionViewLayoutAttributes
}
public func copyLayoutAttributes(from array: [UICollectionViewLayoutAttributes]) -> [UICollectionViewLayoutAttributes] {
return array.map { self.copyLayoutAttributes($0) }
.filter { $0 != nil }
.map { $0! }
}
}
-16
View File
@@ -1,16 +0,0 @@
//
// Proxy.swift
// InfiniteLayout
//
// Created by Arnaud Dorgans on 20/12/2017.
//
import UIKit
class InfiniteCollectionViewDelegateProxy: _InfiniteCollectionViewProxy<UICollectionViewDelegate> {
}
class InfiniteCollectionViewDataSourceProxy: _InfiniteCollectionViewProxy<UICollectionViewDataSource> {
}
@@ -0,0 +1,94 @@
//
// InfiniteCollectionView+Rx.swift
// InfiniteLayout
//
// Created by Arnaud Dorgans on 03/01/2018.
//
import UIKit
import RxSwift
import RxCocoa
import RxDataSources
class RxInfiniteCollectionViewDelegate: DelegateProxy<InfiniteCollectionView, InfiniteCollectionViewDelegate>, DelegateProxyType, InfiniteCollectionViewDelegate {
init(infiniteCollectionView: InfiniteCollectionView) {
super.init(parentObject: infiniteCollectionView, delegateProxy: RxInfiniteCollectionViewDelegate.self)
}
static func registerKnownImplementations() {
RxInfiniteCollectionViewDelegate.register {
RxInfiniteCollectionViewDelegate(infiniteCollectionView: $0)
}
}
static func currentDelegate(for object: InfiniteCollectionView) -> InfiniteCollectionViewDelegate? {
return object.infiniteDelegate
}
static func setCurrentDelegate(_ delegate: InfiniteCollectionViewDelegate?, to object: InfiniteCollectionView) {
object.infiniteDelegate = delegate
}
}
public extension Reactive where Base: InfiniteCollectionView {
private var infiniteDelegate: RxInfiniteCollectionViewDelegate {
return RxInfiniteCollectionViewDelegate.proxy(for: self.base)
}
public var itemCentered: ControlEvent<IndexPath?> {
let source = infiniteDelegate.sentMessage(#selector(InfiniteCollectionViewDelegate.infiniteCollectionView(_:didChangeCenteredIndexPath:)))
.map { $0.last as? IndexPath }
return ControlEvent(events: source)
}
public func modelCentered<T>(_ type: T.Type) -> ControlEvent<T?> {
let source = itemCentered
.map { indexPath -> T? in
return try indexPath.flatMap { try self.model(at: $0) }
}
return ControlEvent(events: source)
}
}
public extension Reactive where Base: RxInfiniteCollectionView {
public func items<S: Sequence, O: ObservableType>
(infinite: Bool)
-> (_ source: O)
-> (_ cellFactory: @escaping (UICollectionView, Int, S.Iterator.Element) -> UICollectionViewCell)
-> Disposable where O.E == S {
return { source in
guard infinite else {
return self.items(source)
}
return { cellFactory in
let dataSource = RxInfiniteCollectionViewSectionedReloadDataSource<SectionModel<Int, S.Iterator.Element>>(configureCell: { _, collectionView, indexPath, element in
return cellFactory(collectionView, indexPath.item, element)
})
return self.items(dataSource: dataSource)(source.map { [SectionModel(model: 0, items: $0.map { $0 })] })
}
}
}
public func items<S: Sequence, Cell: UICollectionViewCell, O : ObservableType>
(cellIdentifier: String, cellType: Cell.Type = Cell.self, infinite: Bool)
-> (_ source: O)
-> (_ configureCell: @escaping (Int, S.Iterator.Element, Cell) -> Void)
-> Disposable where O.E == S {
guard infinite else {
return self.items(cellIdentifier: cellIdentifier, cellType: cellType)
}
return { source in
return { configureCell in
let dataSource = RxInfiniteCollectionViewSectionedReloadDataSource<SectionModel<Int, S.Iterator.Element>>(configureCell: { _, collectionView, indexPath, element in
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: cellIdentifier, for: indexPath) as! Cell
configureCell(indexPath.item, element, cell)
return cell
})
return self.items(dataSource: dataSource)(source.map { [SectionModel(model: 0, items: $0.map { $0 })] })
}
}
}
}
@@ -0,0 +1,40 @@
//
// RxInfiniteCollectionView.swift
// InfiniteLayout
//
// Created by Arnaud Dorgans on 03/01/2018.
//
import UIKit
import RxSwift
import RxCocoa
open class RxInfiniteCollectionView: InfiniteCollectionView {
let disposeBag = DisposeBag()
override var forwardDelegate: Bool {
return false
}
public override init(frame: CGRect, collectionViewLayout layout: UICollectionViewLayout) {
super.init(frame: frame, collectionViewLayout: layout)
setupRx()
}
required public init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
open override func awakeFromNib() {
super.awakeFromNib()
setupRx()
}
func setupRx() {
self.rx.setDelegate(self)
.disposed(by: disposeBag)
}
}
@@ -0,0 +1,97 @@
//
// RxInfiniteCollectionViewDelegate.swift
// InfiniteLayout
//
// Created by Arnaud Dorgans on 03/01/2018.
//
import UIKit
import RxDataSources
open class RxInfiniteCollectionViewSectionedReloadDataSource<S: SectionModelType>: RxCollectionViewSectionedReloadDataSource<S> {
open override subscript(section: Int) -> S {
let section = InfiniteDataSources.section(from: section, numberOfSections: sectionModels.count)
return self.sectionModels[section]
}
open override subscript(indexPath: IndexPath) -> I {
get {
let indexPath = InfiniteDataSources.indexPath(from: indexPath,
numberOfSections: sectionModels.count,
numberOfItems: self[indexPath.section].items.count)
return super[indexPath]
} set {
let indexPath = InfiniteDataSources.indexPath(from: indexPath,
numberOfSections: sectionModels.count,
numberOfItems: self[indexPath.section].items.count)
super[indexPath] = newValue
}
}
private func multiplier(for collectionView: UICollectionView) -> Int {
guard let layout = collectionView.collectionViewLayout as? UICollectionViewFlowLayout else {
fatalError()
}
return InfiniteDataSources.multiplier(estimatedItemSize: layout.itemSize)
}
open override func numberOfSections(in collectionView: UICollectionView) -> Int {
return InfiniteDataSources.numberOfSections(numberOfSections: self.sectionModels.count,
multiplier: self.multiplier(for: collectionView))
}
open override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return InfiniteDataSources.numberOfItemsInSection(numberOfItemsInSection: self[section].items.count,
numberOfSections: self.sectionModels.count,
multiplier: self.multiplier(for: collectionView))
}
open override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
return configureCell(self, collectionView, indexPath, self[indexPath])
}
}
open class RxInfiniteCollectionViewSectionedAnimatedDataSource<S: AnimatableSectionModelType>: RxCollectionViewSectionedAnimatedDataSource<S> {
open override subscript(section: Int) -> S {
let section = InfiniteDataSources.section(from: section, numberOfSections: sectionModels.count)
return self.sectionModels[section]
}
open override subscript(indexPath: IndexPath) -> I {
get {
let indexPath = InfiniteDataSources.indexPath(from: indexPath,
numberOfSections: sectionModels.count,
numberOfItems: self[indexPath.section].items.count)
return super[indexPath]
} set {
let indexPath = InfiniteDataSources.indexPath(from: indexPath,
numberOfSections: sectionModels.count,
numberOfItems: self[indexPath.section].items.count)
super[indexPath] = newValue
}
}
private func multiplier(for collectionView: UICollectionView) -> Int {
guard let layout = collectionView.collectionViewLayout as? UICollectionViewFlowLayout else {
fatalError()
}
return InfiniteDataSources.multiplier(estimatedItemSize: layout.itemSize)
}
open override func numberOfSections(in collectionView: UICollectionView) -> Int {
return InfiniteDataSources.numberOfSections(numberOfSections: self.sectionModels.count,
multiplier: self.multiplier(for: collectionView))
}
open override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return InfiniteDataSources.numberOfItemsInSection(numberOfItemsInSection: self[section].items.count,
numberOfSections: self.sectionModels.count,
multiplier: self.multiplier(for: collectionView))
}
open override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
return configureCell(self, collectionView, indexPath, self[indexPath])
}
}
+123 -4
View File
@@ -26,10 +26,10 @@ pod 'InfiniteLayout'
@IBOutlet weak var collectionView: InfiniteCollectionView!
```
InfiniteCollectionView don't need any other delegate or dataSource,
just use UICollectionViewDataSource and UICollectionViewDelegate as you'll use it in any other UICollectionView.
InfiniteCollectionView doesn't need any other delegate or dataSource,
just use UICollectionViewDataSource and UICollectionViewDelegate in the same way as you'll use it in any other UICollectionView.
InfiniteLayout provide 3 class for infinite scrolling:
InfiniteLayout provides 3 classes for infinite scrolling:
**InfiniteLayout**: an UICollectionViewFlowLayout
@@ -37,10 +37,129 @@ InfiniteLayout provide 3 class for infinite scrolling:
**InfiniteCollectionViewController**: an UICollectionViewController with InfiniteCollectionView
### IndexPath
InfiniteCollectionView may create fake indexPath,
To get the real indexPath call
```swift
func indexPath(from infiniteIndexPath: IndexPath) -> IndexPath
```
To get the real section call
```swift
func section(from infiniteSection: Int) -> Int
```
### Paging
InfiniteCollectionView provide a paging functionality, you can enable it by setting the **isItemPagingEnabled** flag to **true**
```swift
self.infiniteCollectionView.isItemPagingEnabled = true
```
When the **isItemPagingEnabled** flag is enabled you can adjust the deceleration rate by setting the **velocityMultiplier**, the more the value is high, the more the deceleration is long
```swift
self.infiniteCollectionView.velocityMultiplier = 1 // like scrollView with paging (default value)
self.infiniteCollectionView.velocityMultiplier = 500 // like scrollView without paging
```
When the **isItemPagingEnabled** flag is enabled you can set a **preferredCenteredIndexPath**, this value is used to calculate the preferred visible cell to center each time the collectionView will change its contentSize
```swift
self.infiniteCollectionView.preferredCenteredIndexPath = [0, 0] // center the cell at [0, 0] if visible (default value)
self.infiniteCollectionView.preferredCenteredIndexPath = nil // center the closest cell from center
```
### Centered IndexPath
InfiniteCollectionView provide an **infiniteDelegate** protocol used to get the centered IndexPath, usefull if you want to use an InfiniteCollectionView like a Picker.
```swift
func infiniteCollectionView(_ infiniteCollectionView: InfiniteCollectionView, didChangeCenteredIndexPath centeredIndexPath: IndexPath?)
```
### Rx
InfiniteCollectionView provide a subspec **InfiniteLayout/Rx**
```ruby
pod 'InfiniteLayout/Rx'
```
To use InfiniteCollectionView with RxSwift without conflicts between NSProxy
Use **RxInfiniteCollectionView** instead of **InfiniteCollectionView**
```swift
@IBOutlet weak var collectionView: RxInfiniteCollectionView!
```
RxInfiniteCollectionView provides 2 dataSources for SectionModel:
**RxInfiniteCollectionViewSectionedReloadDataSource** and **RxInfiniteCollectionViewSectionedAnimatedDataSource**
#### Binding:
Without sections:
```swift
// automatic cell dequeue
Observable.just(Array(0..<2))
.bind(to: infiniteCollectionView.rx.items(cellIdentifier: "cell", cellType: Cell.self, infinite: true)) { row, element, cell in
cell.update(index: row) // update your cell
}.disposed(by: disposeBag)
// custom cell dequeue
Observable.just(Array(0..<2))
.bind(to: infiniteCollectionView.rx.items(infinite: true)) { collectionView, row, element in
let indexPath = IndexPath(row: row, section: 0)
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! Cell // dequeue your cell
cell.update(index: row) // update your cell
return cell
}.disposed(by: disposeBag)
```
With sections:
```swift
let dataSource = RxInfiniteCollectionViewSectionedReloadDataSource<SectionModel<Int, Int>>(configureCell: { dataSource, collectionView, indexPath, element in
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! Cell // dequeue your cell
cell.update(index: indexPath.row) // update your cell
return cell
})
Observable.just([
SectionModel(model: 0, items: Array(0..<2)),
SectionModel(model: 1, items: Array(0..<10))
])
.bind(to: infiniteCollectionView.rx.items(dataSource: dataSource))
.disposed(by: disposeBag)
```
for animations just use **RxInfiniteCollectionViewSectionedAnimatedDataSource** & **AnimatableSectionModel**
#### Centered IndexPath:
RxInfiniteCollectionView provide Reactive extension for **itemCentered** & **modelCentered**
```swift
infiniteCollectionView.rx.itemCentered
.asDriver()
.drive(onNext: { indexPath in
self.selectedView.update(index: indexPath.row) // update interface with indexPath
}).disposed(by: disposeBag)
infiniteCollectionView.rx.modelCentered(Int.self)
.asDriver()
.drive(onNext: { element in
self.selectedView.update(index: element) // update interface with model
}).disposed(by: disposeBag)
```
## Author
Arnoymous, ineox@me.com
Arnoymous, arnaud.dorgans@gmail.com
## License