Compare commits
29 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| d3d5136f3d | |||
| 8691a862bc | |||
| e4378462e2 | |||
| a8b2962c9b | |||
| 31f9b56b3a | |||
| 51473be4c7 | |||
| ee26e28d39 | |||
| f6b339ad96 | |||
| cc916ccd92 | |||
| ae1969e26f | |||
| 42e8d150a2 | |||
| b839a5af47 | |||
| a522cf0c36 | |||
| 294fd763f8 | |||
| 9468de35bd | |||
| e95b550962 | |||
| f8adf9c9a5 | |||
| 29de021e92 | |||
| e011a23f19 | |||
| 12f4c3e966 | |||
| af0c4598be | |||
| a446dc45ff | |||
| f9566b45bb | |||
| 6e74d52f42 | |||
| c125f52695 | |||
| 075fb6c75b | |||
| 03e9dbd3a4 | |||
| 1fbb837941 | |||
| 5746d5a7c4 |
@@ -33,3 +33,4 @@ Carthage
|
||||
# Pods/
|
||||
/Example/InfiniteLayout.xcworkspace
|
||||
/Example/Pods
|
||||
/Example/InfiniteLayout.xcodeproj/project.xcworkspace/xcshareddata
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
@@ -531,6 +597,7 @@
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SWIFT_SWIFT3_OBJC_INFERENCE = Default;
|
||||
SWIFT_VERSION = 4.0;
|
||||
TARGETED_DEVICE_FAMILY = 1;
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
@@ -547,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"/>
|
||||
|
||||
@@ -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>
|
||||
@@ -13,53 +15,414 @@
|
||||
<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="44" width="375" 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>
|
||||
<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>
|
||||
</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="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="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"/>
|
||||
<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="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"/>
|
||||
<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"/>
|
||||
<userDefinedRuntimeAttribute type="number" keyPath="velocityMultiplier">
|
||||
<real key="value" value="500"/>
|
||||
</userDefinedRuntimeAttribute>
|
||||
</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" 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="21" y="788"/>
|
||||
</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="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="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="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="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>
|
||||
@@ -67,15 +430,68 @@
|
||||
<userDefinedRuntimeAttribute type="boolean" keyPath="isItemPagingEnabled" value="YES"/>
|
||||
</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="-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="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" largeTitleDisplayMode="never" 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,29 @@
|
||||
//
|
||||
// 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
|
||||
}
|
||||
|
||||
override func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
|
||||
collectionView.scrollToItem(at: indexPath, at: self.infiniteCollectionView!.infiniteLayout.scrollDirection == .vertical ? .centeredVertically : .centeredHorizontally, animated: true)
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
|
||||
@@ -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,54 @@
|
||||
//
|
||||
// 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 20
|
||||
}
|
||||
|
||||
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: 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 {
|
||||
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)
|
||||
}
|
||||
}
|
||||
@@ -33,7 +33,6 @@
|
||||
<key>UISupportedInterfaceOrientations</key>
|
||||
<array>
|
||||
<string>UIInterfaceOrientationPortrait</string>
|
||||
<string>UIInterfaceOrientationLandscapeLeft</string>
|
||||
</array>
|
||||
</dict>
|
||||
</plist>
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
//
|
||||
// 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: 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?) {
|
||||
guard let indexPath = centeredIndexPath else {
|
||||
return
|
||||
}
|
||||
self.selectedView.update(index: self.infiniteCollectionView.indexPath(from: indexPath).row)
|
||||
}
|
||||
}
|
||||
@@ -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,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)
|
||||
}
|
||||
}
|
||||
@@ -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
@@ -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
|
||||
|
||||
+24
-11
@@ -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.3.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,21 +18,20 @@ 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'
|
||||
# s.screenshots = 'www.example.com/screenshots_1', 'www.example.com/screenshots_2'
|
||||
s.homepage = 'https://github.com/arnauddorgans/InfiniteLayout'
|
||||
s.screenshots = 'https://github.com/arnauddorgans/InfiniteLayout/raw/master/horizontal.gif', 'https://github.com/arnauddorgans/InfiniteLayout/raw/master/vertical.gif', 'https://github.com/arnauddorgans/InfiniteLayout/raw/master/custom.gif', 'https://github.com/arnauddorgans/InfiniteLayout/raw/master/delegate.gif'
|
||||
s.license = { :type => 'MIT', :file => 'LICENSE' }
|
||||
s.author = { 'Arnoymous' => 'ineox@me.com' }
|
||||
s.source = { :git => 'https://github.com/Arnoymous/InfiniteLayout.git', :tag => s.version.to_s }
|
||||
s.author = { 'Arnaud Dorgans' => 'ineox@me.com' }
|
||||
s.source = { :git => 'https://github.com/arnauddorgans/InfiniteLayout.git', :tag => s.version.to_s }
|
||||
s.social_media_url = 'https://twitter.com/arnauddorgans'
|
||||
|
||||
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'
|
||||
|
||||
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'
|
||||
rx.dependency 'RxSwift', '~> 4'
|
||||
rx.dependency 'RxCocoa', '~> 4'
|
||||
rx.dependency 'RxDataSources', '~> 3'
|
||||
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,63 +65,54 @@ 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)
|
||||
}
|
||||
self.updateLayoutIfNeeded()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -129,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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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,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)
|
||||
@@ -127,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
|
||||
}
|
||||
@@ -138,14 +149,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
|
||||
}
|
||||
self.updateContentOffset(offset)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: Paging
|
||||
func collectionViewRect() -> CGRect? {
|
||||
public func collectionViewRect() -> CGRect? {
|
||||
guard let collectionView = self.collectionView else {
|
||||
return nil
|
||||
}
|
||||
@@ -162,7 +171,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 +187,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 +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
|
||||
}
|
||||
@@ -200,16 +209,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 +240,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
|
||||
}
|
||||
self.updateContentOffset(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! }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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])
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,13 @@
|
||||
# InfiniteLayout
|
||||
|
||||
[](https://travis-ci.org/Arnoymous/InfiniteLayout)
|
||||
[](https://travis-ci.org/arnauddorgans/InfiniteLayout)
|
||||
[](http://cocoapods.org/pods/InfiniteLayout)
|
||||
[](http://cocoapods.org/pods/InfiniteLayout)
|
||||
[](http://cocoapods.org/pods/InfiniteLayout)
|
||||
|
||||
<img src="https://github.com/arnauddorgans/InfiniteLayout/raw/master/horizontal.gif" width="250" height="540"><img src="https://github.com/arnauddorgans/InfiniteLayout/raw/master/vertical.gif" width="250" height="540"><img src="https://github.com/arnauddorgans/InfiniteLayout/raw/master/custom.gif" width="250" height="540">
|
||||
|
||||
|
||||
## Example
|
||||
|
||||
To run the example project, clone the repo, and run `pod install` from the Example directory first.
|
||||
@@ -26,10 +29,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 +40,131 @@ 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
|
||||
```
|
||||
|
||||
### Delegate
|
||||
|
||||
<img src="https://github.com/arnauddorgans/InfiniteLayout/raw/master/delegate.gif" width="250" height="540">
|
||||
|
||||
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: { [unowned self] indexPath in
|
||||
self.selectedView.update(index: indexPath.row) // update interface with indexPath
|
||||
}).disposed(by: disposeBag)
|
||||
|
||||
infiniteCollectionView.rx.modelCentered(Int.self)
|
||||
.asDriver()
|
||||
.drive(onNext: { [unowned self] element in
|
||||
self.selectedView.update(index: element) // update interface with model
|
||||
}).disposed(by: disposeBag)
|
||||
```
|
||||
|
||||
## Author
|
||||
|
||||
Arnoymous, ineox@me.com
|
||||
Arnaud Dorgans, arnaud.dorgans@gmail.com
|
||||
|
||||
## License
|
||||
|
||||
|
||||
BIN
Binary file not shown.
|
After Width: | Height: | Size: 2.6 MiB |
Binary file not shown.
|
After Width: | Height: | Size: 572 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 688 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 1.8 MiB |
Reference in New Issue
Block a user