Compare commits

..

13 Commits

Author SHA1 Message Date
Arnaud Dorgans a522cf0c36 thread fix 2018-01-04 17:42:22 +01:00
Arnaud Dorgans 294fd763f8 storyboard update 2018-01-04 15:51:40 +01:00
Arnaud Dorgans 9468de35bd thread fix 2018-01-04 15:30:13 +01:00
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
17 changed files with 746 additions and 129 deletions
@@ -17,6 +17,7 @@
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 */
@@ -34,7 +35,7 @@
/* 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>"; };
@@ -51,6 +52,7 @@
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>"; };
@@ -102,9 +104,8 @@
isa = PBXGroup;
children = (
607FACD51AFB9204008FA782 /* AppDelegate.swift */,
D43362D31FF64B9E0040C679 /* Base */,
D43362CC1FF64AD30040C679 /* Custom */,
D4B9816C1FFCCC5B0016C676 /* Picker */,
D4B981781FFD1FEF0016C676 /* Swift */,
D4B981791FFD1FF50016C676 /* Rx */,
607FACD91AFB9204008FA782 /* Main.storyboard */,
607FACDC1AFB9204008FA782 /* Images.xcassets */,
607FACDE1AFB9204008FA782 /* LaunchScreen.xib */,
@@ -184,6 +185,24 @@
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 = (
@@ -325,12 +344,20 @@
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;
@@ -412,6 +439,7 @@
files = (
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 */,
@@ -569,6 +597,7 @@
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_SWIFT3_OBJC_INFERENCE = Default;
SWIFT_VERSION = 4.0;
TARGETED_DEVICE_FAMILY = 1;
};
name = Debug;
};
@@ -585,6 +614,7 @@
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_SWIFT3_OBJC_INFERENCE = Default;
SWIFT_VERSION = 4.0;
TARGETED_DEVICE_FAMILY = 1;
};
name = Release;
};
@@ -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"/>
+122 -27
View File
@@ -1,6 +1,6 @@
<?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="retina3_5" orientation="portrait">
<device id="retina4_0" orientation="portrait">
<adaptation id="fullscreen"/>
</device>
<dependencies>
@@ -15,8 +15,8 @@
<scene sceneID="l4I-9G-XdF">
<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="20" width="320" height="44"/>
<navigationBar key="navigationBar" contentMode="scaleToFill" insetsLayoutMarginsFromSafeArea="NO" largeTitles="YES" id="IdE-R7-9TG">
<rect key="frame" x="0.0" y="20" width="320" height="96"/>
<autoresizingMask key="autoresizingMask"/>
</navigationBar>
<connections>
@@ -32,7 +32,7 @@
<objects>
<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="480"/>
<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"/>
<sections>
@@ -98,14 +98,18 @@
<segue destination="ctO-Ld-yhL" kind="show" id="qss-AL-dgi"/>
</connections>
</tableViewCell>
</cells>
</tableViewSection>
<tableViewSection headerTitle="Delegate" id="lpd-Xl-djA">
<cells>
<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"/>
<rect key="frame" x="0.0" y="160" 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">
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" insetsLayoutMarginsFromSafeArea="NO" text="Horizontal 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"/>
@@ -120,6 +124,30 @@
</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="232" 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"/>
@@ -141,11 +169,11 @@
<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="480"/>
<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="480"/>
<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>
@@ -179,6 +207,9 @@
</cells>
<userDefinedRuntimeAttributes>
<userDefinedRuntimeAttribute type="boolean" keyPath="isItemPagingEnabled" value="YES"/>
<userDefinedRuntimeAttribute type="number" keyPath="velocityMultiplier">
<real key="value" value="500"/>
</userDefinedRuntimeAttribute>
</userDefinedRuntimeAttributes>
<connections>
<outlet property="dataSource" destination="ctO-Ld-yhL" id="bXu-Jz-K7t"/>
@@ -194,14 +225,14 @@
<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"/>
<navigationItem key="navigationItem" title="Custom Layout" largeTitleDisplayMode="never" 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"/>
<point key="canvasLocation" x="21" y="788"/>
</scene>
<!--Infinite Delegate-->
<scene sceneID="HqS-9O-oG4">
@@ -212,11 +243,11 @@
<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="480"/>
<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="288" width="320" height="192"/>
<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"/>
@@ -226,7 +257,7 @@
</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="21" width="200" height="150"/>
<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"/>
@@ -255,9 +286,6 @@
</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="nUW-3u-2O9" id="vHT-UO-3ny"/>
@@ -266,8 +294,11 @@
</connections>
</collectionView>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="n2a-eR-E9T" customClass="CellView" customModule="InfiniteLayout_Example" customModuleProvider="target">
<rect key="frame" x="53.5" y="66.5" width="213" height="213"/>
<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"/>
@@ -279,7 +310,6 @@
<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"/>
<constraint firstItem="n2a-eR-E9T" firstAttribute="width" secondItem="Vhd-CK-34X" secondAttribute="width" multiplier="2:3" id="k1E-14-EKC"/>
</constraints>
</view>
<navigationItem key="navigationItem" title="Picker" id="Op4-F3-Cvs"/>
@@ -290,14 +320,79 @@
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="jlK-LR-KNy" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="22" y="802"/>
<point key="canvasLocation" x="784" y="-664"/>
</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" largeTitleDisplayMode="never" 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="784" y="60"/>
</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="480"/>
<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">
@@ -308,7 +403,7 @@
</collectionViewFlowLayout>
<cells>
<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="133" width="250" height="150"/>
<rect key="frame" x="0.0" y="151" 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="250" height="150"/>
@@ -333,9 +428,6 @@
</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="CU7-Hg-6kG" id="BOZ-dL-WLG"/>
@@ -346,14 +438,14 @@
</collectionViewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="Dsp-JP-Q6i" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="22" y="-599"/>
<point key="canvasLocation" x="21" y="-664"/>
</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="480"/>
<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">
@@ -387,12 +479,15 @@
</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"/>
<navigationItem key="navigationItem" title="Vertical Layout" largeTitleDisplayMode="never" id="2E2-oh-ELb"/>
</collectionViewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="Lvb-qy-gZV" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
@@ -22,4 +22,8 @@ class BaseCollectionViewController: InfiniteCollectionViewController {
override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 20
}
override func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
collectionView.scrollToItem(at: indexPath, at: self.infiniteCollectionView!.infiniteLayout.scrollDirection == .vertical ? .centeredVertically : .centeredHorizontally, animated: true)
}
}
@@ -17,11 +17,7 @@ class CustomViewController: UIViewController {
extension CustomViewController: UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 14
}
func numberOfSections(in collectionView: UICollectionView) -> Int {
return 2
return 20
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
@@ -31,6 +27,13 @@ extension CustomViewController: UICollectionViewDataSource {
}
}
extension CustomViewController: UICollectionViewDelegate {
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
collectionView.scrollToItem(at: indexPath, at: .centeredVertically, animated: true)
}
}
extension CustomViewController: UICollectionViewDelegateFlowLayout {
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
@@ -30,6 +30,13 @@ extension PickerController: UICollectionViewDataSource {
}
}
extension PickerController: UICollectionViewDelegate {
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
collectionView.scrollToItem(at: indexPath, at: .centeredHorizontally, animated: true)
}
}
extension PickerController: InfiniteCollectionViewDelegate {
func infiniteCollectionView(_ infiniteCollectionView: InfiniteCollectionView, didChangeCenteredIndexPath centeredIndexPath: IndexPath?) {
@@ -0,0 +1,45 @@
//
// 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)
infiniteCollectionView.rx.itemSelected
.asDriver()
.drive(onNext: { [unowned self] indexPath in
self.infiniteCollectionView.scrollToItem(at: indexPath, at: .centeredVertically, animated: true)
}).disposed(by: disposeBag)
}
}
+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
+25 -4
View File
@@ -1,10 +1,27 @@
PODS:
- CocoaProxy (0.1.1)
- InfiniteLayout (0.1.3):
- 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:
@@ -12,8 +29,12 @@ EXTERNAL SOURCES:
SPEC CHECKSUMS:
CocoaProxy: 35ab81e24325b33834cffe45a3d1fd48ca67ef3a
InfiniteLayout: 0ab39bf2a4d9ce5c473ebccfc0d3b7278de18ac9
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
+19 -6
View File
@@ -8,8 +8,8 @@
Pod::Spec.new do |s|
s.name = 'InfiniteLayout'
s.version = '0.1.5'
s.summary = 'Horizontal and Vertical infinite scrolling feature for UICollectionView'
s.version = '0.2.1'
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
Horizontal and Vertical infinite scrolling feature 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 @@ Horizontal and Vertical infinite scrolling feature for UICollectionView with Pag
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']
@@ -41,4 +40,18 @@ Horizontal and Vertical infinite scrolling feature for UICollectionView with Pag
# s.public_header_files = 'Pod/Classes/**/*.h'
# s.frameworks = 'UIKit', 'MapKit'
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
@@ -14,23 +14,24 @@ import UIKit
open class InfiniteCollectionView: UICollectionView {
lazy var delegateProxy = InfiniteCollectionViewDelegateProxy(collectionView: self)
lazy var dataSourceProxy = InfiniteCollectionViewDataSourceProxy(collectionView: self)
lazy var delegateProxy = InfiniteCollectionViewDelegateProxy(collectionView: self)
@IBOutlet open var infiniteDelegate: InfiniteCollectionViewDelegate?
open var centeredIndexPath: IndexPath?
open private(set) var centeredIndexPath: IndexPath?
open var preferredCenteredIndexPath: IndexPath? = IndexPath(item: 0, section: 0)
@IBInspectable var isItemPagingEnabled: Bool = false
@IBInspectable var velocityMultiplier: CGFloat = 500 {
didSet {
self.infiniteLayout.velocityMultiplier = velocityMultiplier
}
}
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
@@ -43,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
@@ -59,6 +65,13 @@ 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
}
@@ -72,6 +85,7 @@ open class InfiniteCollectionView: UICollectionView {
public override init(frame: CGRect, collectionViewLayout layout: UICollectionViewLayout) {
super.init(frame: frame, collectionViewLayout: InfiniteCollectionView.infiniteLayout(layout: layout))
sharedInit()
}
required public init?(coder aDecoder: NSCoder) {
@@ -83,45 +97,22 @@ open class InfiniteCollectionView: UICollectionView {
}
}
open override func awakeFromNib() {
super.awakeFromNib()
sharedInit()
}
private func sharedInit() {
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()
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
}
self.infiniteLayout.centerCollectionViewIfNeeded()
}
public func scrollViewWillEndDragging(_ scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer<CGPoint>) {
if isItemPagingEnabled {
self.infiniteLayout.centerCollectionView(withVelocity: velocity, targetContentOffset: targetContentOffset)
}
self.updateLayoutIfNeeded()
}
}
@@ -142,33 +133,74 @@ 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 {
func updateLayoutIfNeeded() {
self.loopCollectionViewIfNeeded()
self.centerCollectionViewIfNeeded()
let preferredVisibleIndexPath = infiniteLayout.preferredVisibleLayoutAttributes()?.indexPath
if self.centeredIndexPath != preferredVisibleIndexPath {
self.centeredIndexPath = preferredVisibleIndexPath
self.infiniteDelegate?.infiniteCollectionView?(self, didChangeCenteredIndexPath: preferredVisibleIndexPath)
}
}
// MARK: Loop
func loopCollectionViewIfNeeded() {
self.infiniteLayout.loopCollectionViewIfNeeded()
}
public func scrollViewDidScroll(_ scrollView: UIScrollView) {
delegateProxy.delegate?.scrollViewDidScroll?(scrollView)
self.updateLayoutIfNeeded()
}
// 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)
}
}
}
@@ -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
}
}
+24 -17
View File
@@ -9,9 +9,9 @@ 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
private let multiplier: CGFloat = 500 // contentOffset multiplier
private var contentSize: CGSize = .zero
@@ -35,9 +35,8 @@ 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() {
@@ -48,7 +47,7 @@ open class InfiniteLayout: UICollectionViewFlowLayout {
return false
}
return (scrollDirection == .horizontal ? self.contentSize.width : self.contentSize.height) >=
InfiniteLayout.minimumContentSize.width
InfiniteLayout.minimumContentSize
}()
super.prepare()
}
@@ -132,6 +131,13 @@ open class InfiniteLayout: UICollectionViewFlowLayout {
}
// MARK: Loop
private func updateContentOffset(_ offset: CGPoint) {
guard let collectionView = self.collectionView else {
return
}
collectionView.contentOffset = offset
collectionView.layoutIfNeeded()
}
private func preferredContentOffset(forContentOffset contentOffset: CGPoint) -> CGPoint {
return rect(from: CGRect(origin: contentOffset, size: .zero), page: self.page(from: .zero, offset: multiplier / 2)).origin
}
@@ -143,9 +149,7 @@ 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
}
self.updateContentOffset(offset)
}
}
@@ -196,7 +200,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
}
@@ -205,7 +209,12 @@ 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.centeredContentOffset(forRect: attributes.frame) else {
return false
@@ -237,18 +246,16 @@ open class InfiniteLayout: UICollectionViewFlowLayout {
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.centeredContentOffset(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
}
self.updateContentOffset(offset)
}
// MARK: Copy
@@ -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])
}
}
+93 -3
View File
@@ -55,11 +55,27 @@ func section(from infiniteSection: Int) -> Int
### Paging
InfiniteCollectionView provide a paging functionality, you can enable it by setting the **isItemPagingEnabled** flag to **true**,
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
### Picker
```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.
@@ -67,9 +83,83 @@ InfiniteCollectionView provide an **infiniteDelegate** protocol used to get the
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