├── .swift-version ├── Package.swift ├── ReadmeAssets ├── Screenplay.gif ├── Screenshot.png ├── CardLayoutActions.png ├── CardLayoutOptions.png └── CollectionView_LayoutClass.png ├── HFCardCollectionViewLayoutExample ├── HFCardCollectionViewLayoutExample │ ├── Assets.xcassets │ │ ├── Contents.json │ │ ├── Icons │ │ │ ├── Contents.json │ │ │ ├── Icon1.imageset │ │ │ │ ├── Icon1.pdf │ │ │ │ └── Contents.json │ │ │ ├── Icon2.imageset │ │ │ │ ├── Icon2.pdf │ │ │ │ └── Contents.json │ │ │ ├── Icon3.imageset │ │ │ │ ├── Icon3.pdf │ │ │ │ └── Contents.json │ │ │ ├── Icon4.imageset │ │ │ │ ├── Icon4.pdf │ │ │ │ └── Contents.json │ │ │ ├── Icon5.imageset │ │ │ │ ├── Icon5.pdf │ │ │ │ └── Contents.json │ │ │ └── Icon6.imageset │ │ │ │ ├── Icon6.pdf │ │ │ │ └── Contents.json │ │ └── AppIcon.appiconset │ │ │ └── Contents.json │ ├── Info.plist │ ├── Base.lproj │ │ └── LaunchScreen.storyboard │ ├── AppDelegate.swift │ ├── ExampleCollectionViewCell.swift │ ├── ExampleViewController.swift │ └── MenuTableViewController.swift ├── Podfile ├── Pods │ ├── Target Support Files │ │ ├── HFCardCollectionViewLayout │ │ │ ├── HFCardCollectionViewLayout.modulemap │ │ │ ├── HFCardCollectionViewLayout-dummy.m │ │ │ ├── HFCardCollectionViewLayout-prefix.pch │ │ │ ├── HFCardCollectionViewLayout-umbrella.h │ │ │ ├── HFCardCollectionViewLayout.xcconfig │ │ │ └── Info.plist │ │ └── Pods-HFCardCollectionViewLayoutExample │ │ │ ├── Pods-HFCardCollectionViewLayoutExample.modulemap │ │ │ ├── Pods-HFCardCollectionViewLayoutExample-dummy.m │ │ │ ├── Pods-HFCardCollectionViewLayoutExample-umbrella.h │ │ │ ├── Pods-HFCardCollectionViewLayoutExample.debug.xcconfig │ │ │ ├── Pods-HFCardCollectionViewLayoutExample.release.xcconfig │ │ │ ├── Info.plist │ │ │ ├── Pods-HFCardCollectionViewLayoutExample-acknowledgements.markdown │ │ │ ├── Pods-HFCardCollectionViewLayoutExample-acknowledgements.plist │ │ │ ├── Pods-HFCardCollectionViewLayoutExample-frameworks.sh │ │ │ └── Pods-HFCardCollectionViewLayoutExample-resources.sh │ ├── Manifest.lock │ ├── Local Podspecs │ │ └── HFCardCollectionViewLayout.podspec.json │ └── Pods.xcodeproj │ │ └── project.pbxproj ├── HFCardCollectionViewLayoutExample.xcodeproj │ ├── project.xcworkspace │ │ └── contents.xcworkspacedata │ └── project.pbxproj ├── HFCardCollectionViewLayoutExample.xcworkspace │ └── contents.xcworkspacedata └── Podfile.lock ├── HFCardCollectionViewLayout.xcodeproj ├── project.xcworkspace │ └── contents.xcworkspacedata ├── xcshareddata │ └── xcschemes │ │ └── HFCardCollectionViewLayout.xcscheme └── project.pbxproj ├── HFCardCollectionViewLayout.podspec ├── HFCardCollectionViewLayout ├── HFCardCollectionViewLayout.h └── Info.plist ├── LICENSE ├── .gitignore ├── CHANGELOG.md ├── Source ├── HFCardCollectionViewLayoutDelegate.swift ├── HFCardCollectionView.swift ├── HFCardCollectionViewCell.swift └── HFCardCollectionViewLayout.swift └── README.md /.swift-version: -------------------------------------------------------------------------------- 1 | 3.0 2 | -------------------------------------------------------------------------------- /Package.swift: -------------------------------------------------------------------------------- 1 | import PackageDescription 2 | 3 | let package = Package( 4 | name: "HFCardCollectionViewLayout" 5 | ) -------------------------------------------------------------------------------- /ReadmeAssets/Screenplay.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hfrahmann/HFCardCollectionViewLayout/HEAD/ReadmeAssets/Screenplay.gif -------------------------------------------------------------------------------- /ReadmeAssets/Screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hfrahmann/HFCardCollectionViewLayout/HEAD/ReadmeAssets/Screenshot.png -------------------------------------------------------------------------------- /ReadmeAssets/CardLayoutActions.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hfrahmann/HFCardCollectionViewLayout/HEAD/ReadmeAssets/CardLayoutActions.png -------------------------------------------------------------------------------- /ReadmeAssets/CardLayoutOptions.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hfrahmann/HFCardCollectionViewLayout/HEAD/ReadmeAssets/CardLayoutOptions.png -------------------------------------------------------------------------------- /ReadmeAssets/CollectionView_LayoutClass.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hfrahmann/HFCardCollectionViewLayout/HEAD/ReadmeAssets/CollectionView_LayoutClass.png -------------------------------------------------------------------------------- /HFCardCollectionViewLayoutExample/HFCardCollectionViewLayoutExample/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /HFCardCollectionViewLayoutExample/HFCardCollectionViewLayoutExample/Assets.xcassets/Icons/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /HFCardCollectionViewLayoutExample/Podfile: -------------------------------------------------------------------------------- 1 | # Uncomment this line if you're using Swift 2 | use_frameworks! 3 | 4 | target 'HFCardCollectionViewLayoutExample' do 5 | platform :ios, '10.0' 6 | pod 'HFCardCollectionViewLayout', :path => '../' 7 | end 8 | -------------------------------------------------------------------------------- /HFCardCollectionViewLayout.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /HFCardCollectionViewLayoutExample/Pods/Target Support Files/HFCardCollectionViewLayout/HFCardCollectionViewLayout.modulemap: -------------------------------------------------------------------------------- 1 | framework module HFCardCollectionViewLayout { 2 | umbrella header "HFCardCollectionViewLayout-umbrella.h" 3 | 4 | export * 5 | module * { export * } 6 | } 7 | -------------------------------------------------------------------------------- /HFCardCollectionViewLayoutExample/Pods/Target Support Files/HFCardCollectionViewLayout/HFCardCollectionViewLayout-dummy.m: -------------------------------------------------------------------------------- 1 | #import 2 | @interface PodsDummy_HFCardCollectionViewLayout : NSObject 3 | @end 4 | @implementation PodsDummy_HFCardCollectionViewLayout 5 | @end 6 | -------------------------------------------------------------------------------- /HFCardCollectionViewLayoutExample/HFCardCollectionViewLayoutExample/Assets.xcassets/Icons/Icon1.imageset/Icon1.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hfrahmann/HFCardCollectionViewLayout/HEAD/HFCardCollectionViewLayoutExample/HFCardCollectionViewLayoutExample/Assets.xcassets/Icons/Icon1.imageset/Icon1.pdf -------------------------------------------------------------------------------- /HFCardCollectionViewLayoutExample/HFCardCollectionViewLayoutExample/Assets.xcassets/Icons/Icon2.imageset/Icon2.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hfrahmann/HFCardCollectionViewLayout/HEAD/HFCardCollectionViewLayoutExample/HFCardCollectionViewLayoutExample/Assets.xcassets/Icons/Icon2.imageset/Icon2.pdf -------------------------------------------------------------------------------- /HFCardCollectionViewLayoutExample/HFCardCollectionViewLayoutExample/Assets.xcassets/Icons/Icon3.imageset/Icon3.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hfrahmann/HFCardCollectionViewLayout/HEAD/HFCardCollectionViewLayoutExample/HFCardCollectionViewLayoutExample/Assets.xcassets/Icons/Icon3.imageset/Icon3.pdf -------------------------------------------------------------------------------- /HFCardCollectionViewLayoutExample/HFCardCollectionViewLayoutExample/Assets.xcassets/Icons/Icon4.imageset/Icon4.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hfrahmann/HFCardCollectionViewLayout/HEAD/HFCardCollectionViewLayoutExample/HFCardCollectionViewLayoutExample/Assets.xcassets/Icons/Icon4.imageset/Icon4.pdf -------------------------------------------------------------------------------- /HFCardCollectionViewLayoutExample/HFCardCollectionViewLayoutExample/Assets.xcassets/Icons/Icon5.imageset/Icon5.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hfrahmann/HFCardCollectionViewLayout/HEAD/HFCardCollectionViewLayoutExample/HFCardCollectionViewLayoutExample/Assets.xcassets/Icons/Icon5.imageset/Icon5.pdf -------------------------------------------------------------------------------- /HFCardCollectionViewLayoutExample/HFCardCollectionViewLayoutExample/Assets.xcassets/Icons/Icon6.imageset/Icon6.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hfrahmann/HFCardCollectionViewLayout/HEAD/HFCardCollectionViewLayoutExample/HFCardCollectionViewLayoutExample/Assets.xcassets/Icons/Icon6.imageset/Icon6.pdf -------------------------------------------------------------------------------- /HFCardCollectionViewLayoutExample/HFCardCollectionViewLayoutExample.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /HFCardCollectionViewLayoutExample/Pods/Target Support Files/Pods-HFCardCollectionViewLayoutExample/Pods-HFCardCollectionViewLayoutExample.modulemap: -------------------------------------------------------------------------------- 1 | framework module Pods_HFCardCollectionViewLayoutExample { 2 | umbrella header "Pods-HFCardCollectionViewLayoutExample-umbrella.h" 3 | 4 | export * 5 | module * { export * } 6 | } 7 | -------------------------------------------------------------------------------- /HFCardCollectionViewLayoutExample/Pods/Target Support Files/Pods-HFCardCollectionViewLayoutExample/Pods-HFCardCollectionViewLayoutExample-dummy.m: -------------------------------------------------------------------------------- 1 | #import 2 | @interface PodsDummy_Pods_HFCardCollectionViewLayoutExample : NSObject 3 | @end 4 | @implementation PodsDummy_Pods_HFCardCollectionViewLayoutExample 5 | @end 6 | -------------------------------------------------------------------------------- /HFCardCollectionViewLayoutExample/Pods/Target Support Files/HFCardCollectionViewLayout/HFCardCollectionViewLayout-prefix.pch: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #else 4 | #ifndef FOUNDATION_EXPORT 5 | #if defined(__cplusplus) 6 | #define FOUNDATION_EXPORT extern "C" 7 | #else 8 | #define FOUNDATION_EXPORT extern 9 | #endif 10 | #endif 11 | #endif 12 | 13 | -------------------------------------------------------------------------------- /HFCardCollectionViewLayoutExample/HFCardCollectionViewLayoutExample.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /HFCardCollectionViewLayoutExample/HFCardCollectionViewLayoutExample/Assets.xcassets/Icons/Icon1.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "Icon1.pdf" 6 | } 7 | ], 8 | "info" : { 9 | "version" : 1, 10 | "author" : "xcode" 11 | }, 12 | "properties" : { 13 | "template-rendering-intent" : "template", 14 | "preserves-vector-representation" : true 15 | } 16 | } -------------------------------------------------------------------------------- /HFCardCollectionViewLayoutExample/HFCardCollectionViewLayoutExample/Assets.xcassets/Icons/Icon2.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "Icon2.pdf" 6 | } 7 | ], 8 | "info" : { 9 | "version" : 1, 10 | "author" : "xcode" 11 | }, 12 | "properties" : { 13 | "template-rendering-intent" : "template", 14 | "preserves-vector-representation" : true 15 | } 16 | } -------------------------------------------------------------------------------- /HFCardCollectionViewLayoutExample/HFCardCollectionViewLayoutExample/Assets.xcassets/Icons/Icon3.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "Icon3.pdf" 6 | } 7 | ], 8 | "info" : { 9 | "version" : 1, 10 | "author" : "xcode" 11 | }, 12 | "properties" : { 13 | "template-rendering-intent" : "template", 14 | "preserves-vector-representation" : true 15 | } 16 | } -------------------------------------------------------------------------------- /HFCardCollectionViewLayoutExample/HFCardCollectionViewLayoutExample/Assets.xcassets/Icons/Icon4.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "Icon4.pdf" 6 | } 7 | ], 8 | "info" : { 9 | "version" : 1, 10 | "author" : "xcode" 11 | }, 12 | "properties" : { 13 | "template-rendering-intent" : "template", 14 | "preserves-vector-representation" : true 15 | } 16 | } -------------------------------------------------------------------------------- /HFCardCollectionViewLayoutExample/HFCardCollectionViewLayoutExample/Assets.xcassets/Icons/Icon5.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "Icon5.pdf" 6 | } 7 | ], 8 | "info" : { 9 | "version" : 1, 10 | "author" : "xcode" 11 | }, 12 | "properties" : { 13 | "template-rendering-intent" : "template", 14 | "preserves-vector-representation" : true 15 | } 16 | } -------------------------------------------------------------------------------- /HFCardCollectionViewLayoutExample/HFCardCollectionViewLayoutExample/Assets.xcassets/Icons/Icon6.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "Icon6.pdf" 6 | } 7 | ], 8 | "info" : { 9 | "version" : 1, 10 | "author" : "xcode" 11 | }, 12 | "properties" : { 13 | "template-rendering-intent" : "template", 14 | "preserves-vector-representation" : true 15 | } 16 | } -------------------------------------------------------------------------------- /HFCardCollectionViewLayoutExample/Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - HFCardCollectionViewLayout (0.2.2) 3 | 4 | DEPENDENCIES: 5 | - HFCardCollectionViewLayout (from `../`) 6 | 7 | EXTERNAL SOURCES: 8 | HFCardCollectionViewLayout: 9 | :path: ../ 10 | 11 | SPEC CHECKSUMS: 12 | HFCardCollectionViewLayout: 70073318095967f5fa407147df68dd4696c7aedc 13 | 14 | PODFILE CHECKSUM: 234325b1b12e2e74244c12d2d31fa748e550f0f2 15 | 16 | COCOAPODS: 1.2.0.beta.1 17 | -------------------------------------------------------------------------------- /HFCardCollectionViewLayoutExample/Pods/Manifest.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - HFCardCollectionViewLayout (0.2.2) 3 | 4 | DEPENDENCIES: 5 | - HFCardCollectionViewLayout (from `../`) 6 | 7 | EXTERNAL SOURCES: 8 | HFCardCollectionViewLayout: 9 | :path: ../ 10 | 11 | SPEC CHECKSUMS: 12 | HFCardCollectionViewLayout: 70073318095967f5fa407147df68dd4696c7aedc 13 | 14 | PODFILE CHECKSUM: 234325b1b12e2e74244c12d2d31fa748e550f0f2 15 | 16 | COCOAPODS: 1.2.0.beta.1 17 | -------------------------------------------------------------------------------- /HFCardCollectionViewLayoutExample/Pods/Target Support Files/HFCardCollectionViewLayout/HFCardCollectionViewLayout-umbrella.h: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #else 4 | #ifndef FOUNDATION_EXPORT 5 | #if defined(__cplusplus) 6 | #define FOUNDATION_EXPORT extern "C" 7 | #else 8 | #define FOUNDATION_EXPORT extern 9 | #endif 10 | #endif 11 | #endif 12 | 13 | 14 | FOUNDATION_EXPORT double HFCardCollectionViewLayoutVersionNumber; 15 | FOUNDATION_EXPORT const unsigned char HFCardCollectionViewLayoutVersionString[]; 16 | 17 | -------------------------------------------------------------------------------- /HFCardCollectionViewLayoutExample/Pods/Target Support Files/Pods-HFCardCollectionViewLayoutExample/Pods-HFCardCollectionViewLayoutExample-umbrella.h: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #else 4 | #ifndef FOUNDATION_EXPORT 5 | #if defined(__cplusplus) 6 | #define FOUNDATION_EXPORT extern "C" 7 | #else 8 | #define FOUNDATION_EXPORT extern 9 | #endif 10 | #endif 11 | #endif 12 | 13 | 14 | FOUNDATION_EXPORT double Pods_HFCardCollectionViewLayoutExampleVersionNumber; 15 | FOUNDATION_EXPORT const unsigned char Pods_HFCardCollectionViewLayoutExampleVersionString[]; 16 | 17 | -------------------------------------------------------------------------------- /HFCardCollectionViewLayout.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |s| 2 | s.name = 'HFCardCollectionViewLayout' 3 | s.version = '0.4.2' 4 | s.summary = 'The HFCardCollectionViewLayout provides a card stack layout not quite similar like the apps Reminder and Wallet.' 5 | s.license = 'MIT' 6 | s.homepage = 'https://github.com/hfrahmann/HFCardCollectionViewLayout' 7 | s.ios.deployment_target = '9.0' 8 | s.author = { 9 | 'Hendrik Frahmann' => 'contact@hendrik-frahmann.de' 10 | } 11 | s.source = { 12 | :git => 'https://github.com/hfrahmann/HFCardCollectionViewLayout.git', 13 | :tag => '0.4.2' 14 | } 15 | s.source_files = 'Source/*' 16 | end 17 | -------------------------------------------------------------------------------- /HFCardCollectionViewLayoutExample/Pods/Target Support Files/HFCardCollectionViewLayout/HFCardCollectionViewLayout.xcconfig: -------------------------------------------------------------------------------- 1 | CONFIGURATION_BUILD_DIR = $PODS_CONFIGURATION_BUILD_DIR/HFCardCollectionViewLayout 2 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 3 | HEADER_SEARCH_PATHS = "${PODS_ROOT}/Headers/Private" "${PODS_ROOT}/Headers/Public" 4 | OTHER_SWIFT_FLAGS = $(inherited) "-D" "COCOAPODS" 5 | PODS_BUILD_DIR = $BUILD_DIR 6 | PODS_CONFIGURATION_BUILD_DIR = $PODS_BUILD_DIR/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 7 | PODS_ROOT = ${SRCROOT} 8 | PODS_TARGET_SRCROOT = ${PODS_ROOT}/../.. 9 | PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier} 10 | SKIP_INSTALL = YES 11 | -------------------------------------------------------------------------------- /HFCardCollectionViewLayoutExample/Pods/Local Podspecs/HFCardCollectionViewLayout.podspec.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "HFCardCollectionViewLayout", 3 | "version": "0.2.2", 4 | "summary": "The HFCardCollectionViewLayout provides a card stack layout not quite similar like the apps Reminder and Wallet.", 5 | "license": "MIT", 6 | "homepage": "https://github.com/hfrahmann/HFCardCollectionViewLayout", 7 | "platforms": { 8 | "ios": "9.0" 9 | }, 10 | "authors": { 11 | "Hendrik Frahmann": "contact@hendrik-frahmann.de" 12 | }, 13 | "source": { 14 | "git": "https://github.com/hfrahmann/HFCardCollectionViewLayout.git", 15 | "tag": "0.2.2" 16 | }, 17 | "source_files": "Source/*" 18 | } 19 | -------------------------------------------------------------------------------- /HFCardCollectionViewLayout/HFCardCollectionViewLayout.h: -------------------------------------------------------------------------------- 1 | // 2 | // HFCardCollectionViewLayout.h 3 | // HFCardCollectionViewLayout 4 | // 5 | // Created by David Collado on 23/2/17. 6 | // Copyright © 2017 hendrik-frahmann. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | //! Project version number for HFCardCollectionViewLayout. 12 | FOUNDATION_EXPORT double HFCardCollectionViewLayoutVersionNumber; 13 | 14 | //! Project version string for HFCardCollectionViewLayout. 15 | FOUNDATION_EXPORT const unsigned char HFCardCollectionViewLayoutVersionString[]; 16 | 17 | // In this header, you should import all the public headers of your framework using statements like #import 18 | 19 | 20 | -------------------------------------------------------------------------------- /HFCardCollectionViewLayoutExample/Pods/Target Support Files/Pods-HFCardCollectionViewLayoutExample/Pods-HFCardCollectionViewLayoutExample.debug.xcconfig: -------------------------------------------------------------------------------- 1 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES 2 | FRAMEWORK_SEARCH_PATHS = $(inherited) "$PODS_CONFIGURATION_BUILD_DIR/HFCardCollectionViewLayout" 3 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 4 | LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks' 5 | OTHER_CFLAGS = $(inherited) -iquote "$PODS_CONFIGURATION_BUILD_DIR/HFCardCollectionViewLayout/HFCardCollectionViewLayout.framework/Headers" 6 | OTHER_LDFLAGS = $(inherited) -framework "HFCardCollectionViewLayout" 7 | OTHER_SWIFT_FLAGS = $(inherited) "-D" "COCOAPODS" 8 | PODS_BUILD_DIR = $BUILD_DIR 9 | PODS_CONFIGURATION_BUILD_DIR = $PODS_BUILD_DIR/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 10 | PODS_ROOT = ${SRCROOT}/Pods 11 | -------------------------------------------------------------------------------- /HFCardCollectionViewLayoutExample/Pods/Target Support Files/Pods-HFCardCollectionViewLayoutExample/Pods-HFCardCollectionViewLayoutExample.release.xcconfig: -------------------------------------------------------------------------------- 1 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES 2 | FRAMEWORK_SEARCH_PATHS = $(inherited) "$PODS_CONFIGURATION_BUILD_DIR/HFCardCollectionViewLayout" 3 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 4 | LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks' 5 | OTHER_CFLAGS = $(inherited) -iquote "$PODS_CONFIGURATION_BUILD_DIR/HFCardCollectionViewLayout/HFCardCollectionViewLayout.framework/Headers" 6 | OTHER_LDFLAGS = $(inherited) -framework "HFCardCollectionViewLayout" 7 | OTHER_SWIFT_FLAGS = $(inherited) "-D" "COCOAPODS" 8 | PODS_BUILD_DIR = $BUILD_DIR 9 | PODS_CONFIGURATION_BUILD_DIR = $PODS_BUILD_DIR/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 10 | PODS_ROOT = ${SRCROOT}/Pods 11 | -------------------------------------------------------------------------------- /HFCardCollectionViewLayout/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | $(CURRENT_PROJECT_VERSION) 21 | NSPrincipalClass 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /HFCardCollectionViewLayoutExample/Pods/Target Support Files/HFCardCollectionViewLayout/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIdentifier 10 | ${PRODUCT_BUNDLE_IDENTIFIER} 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | ${PRODUCT_NAME} 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 0.2.2 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | ${CURRENT_PROJECT_VERSION} 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /HFCardCollectionViewLayoutExample/Pods/Target Support Files/Pods-HFCardCollectionViewLayoutExample/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIdentifier 10 | ${PRODUCT_BUNDLE_IDENTIFIER} 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | ${PRODUCT_NAME} 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | ${CURRENT_PROJECT_VERSION} 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016 Hendrik Frahmann 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /HFCardCollectionViewLayoutExample/Pods/Target Support Files/Pods-HFCardCollectionViewLayoutExample/Pods-HFCardCollectionViewLayoutExample-acknowledgements.markdown: -------------------------------------------------------------------------------- 1 | # Acknowledgements 2 | This application makes use of the following third party libraries: 3 | 4 | ## HFCardCollectionViewLayout 5 | 6 | MIT License 7 | 8 | Copyright (c) 2016 Hendrik Frahmann 9 | 10 | Permission is hereby granted, free of charge, to any person obtaining a copy 11 | of this software and associated documentation files (the "Software"), to deal 12 | in the Software without restriction, including without limitation the rights 13 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 14 | copies of the Software, and to permit persons to whom the Software is 15 | furnished to do so, subject to the following conditions: 16 | 17 | The above copyright notice and this permission notice shall be included in all 18 | copies or substantial portions of the Software. 19 | 20 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 21 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 22 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 23 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 24 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 25 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 26 | SOFTWARE. 27 | 28 | Generated by CocoaPods - https://cocoapods.org 29 | -------------------------------------------------------------------------------- /HFCardCollectionViewLayoutExample/HFCardCollectionViewLayoutExample/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | LSRequiresIPhoneOS 22 | 23 | UILaunchStoryboardName 24 | LaunchScreen 25 | UIMainStoryboardFile 26 | Main 27 | UIRequiredDeviceCapabilities 28 | 29 | armv7 30 | 31 | UISupportedInterfaceOrientations 32 | 33 | UIInterfaceOrientationPortrait 34 | UIInterfaceOrientationLandscapeLeft 35 | UIInterfaceOrientationLandscapeRight 36 | 37 | UISupportedInterfaceOrientations~ipad 38 | 39 | UIInterfaceOrientationPortrait 40 | UIInterfaceOrientationPortraitUpsideDown 41 | UIInterfaceOrientationLandscapeLeft 42 | UIInterfaceOrientationLandscapeRight 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | 3 | # Xcode 4 | # 5 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 6 | 7 | ## Build generated 8 | build/ 9 | DerivedData/ 10 | 11 | ## Various settings 12 | *.pbxuser 13 | !default.pbxuser 14 | *.mode1v3 15 | !default.mode1v3 16 | *.mode2v3 17 | !default.mode2v3 18 | *.perspectivev3 19 | !default.perspectivev3 20 | xcuserdata/ 21 | 22 | ## Other 23 | *.moved-aside 24 | *.xcuserstate 25 | 26 | ## Obj-C/Swift specific 27 | *.hmap 28 | *.ipa 29 | *.dSYM.zip 30 | *.dSYM 31 | 32 | ## Playgrounds 33 | timeline.xctimeline 34 | playground.xcworkspace 35 | 36 | # Swift Package Manager 37 | # 38 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies. 39 | # Packages/ 40 | .build/ 41 | 42 | # CocoaPods 43 | # 44 | # We recommend against adding the Pods directory to your .gitignore. However 45 | # you should judge for yourself, the pros and cons are mentioned at: 46 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control 47 | # 48 | # Pods/ 49 | 50 | # Carthage 51 | # 52 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 53 | # Carthage/Checkouts 54 | 55 | Carthage/Build 56 | 57 | # fastlane 58 | # 59 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 60 | # screenshots whenever they are needed. 61 | # For more information about the recommended setup visit: 62 | # https://github.com/fastlane/fastlane/blob/master/fastlane/docs/Gitignore.md 63 | 64 | fastlane/report.xml 65 | fastlane/Preview.html 66 | fastlane/screenshots 67 | fastlane/test_output 68 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | 4 | ## 0.4.2 5 | 6 | - Created HFCardCollectionView class with a helper method 7 | 8 | 9 | ## 0.4.1 10 | 11 | - Fixed UIPanGestureRecognizer misbehaviour 12 | - Added another delete animation 13 | 14 | 15 | ## 0.3.3 16 | 17 | - Added Carthage support (from bitomule) 18 | 19 | 20 | ## 0.3.2 21 | 22 | - Added 'collapse all cards' option 23 | - Fixed a bug of a wrong scale while moving a card. 24 | 25 | 26 | ## 0.3.1 27 | 28 | - Improved shadow of the card cell 29 | 30 | 31 | ## 0.3 32 | 33 | - Added options 'bottomStackedCardsMinimumScale' and 'bottomStackedCardsMaximumScale' 34 | - Added some documentation 35 | 36 | 37 | ## 0.2.2 38 | 39 | - Renamed 'selected' to 'revealed' 40 | 41 | 42 | ## 0.2.1 43 | 44 | - Added option 'scrollStopCardsAtTop' 45 | 46 | 47 | ## 0.2 48 | 49 | - Added the UICollectionViewCell and UICollectionView extension (again) 50 | 51 | 52 | ## 0.1.5 53 | 54 | - Made the HFCardCollectionViewCell and HFCardCollectionViewLayout open instead of public 55 | - Improved the HFCardCollectionViewDelegate 56 | - Package.swift added 57 | - Reordered the source files 58 | 59 | 60 | ## 0.1.4 61 | 62 | - Bugfix for setting the background color or HFCardCollectionViewCell 63 | - Extends the HFCardCollectionViewDelegate 64 | - Removed the HFCardCollectionViewCellDelegate 65 | 66 | 67 | ## 0.1.3 68 | 69 | - HFCardCollectionViewDelegate now inherits from UICollectionViewDelegate 70 | 71 | 72 | ## 0.1.2 73 | 74 | - Renamed option bottomShouldScaleStackedCards to bottomStackedCardsShouldScale 75 | - Improving of the calculation for the moving cell 76 | 77 | 78 | ## 0.1.1 79 | 80 | - Bugfix at scrollShouldSnapCardHead and maximumCardHeight 81 | 82 | 83 | ## 0.1 84 | 85 | - First release 86 | -------------------------------------------------------------------------------- /HFCardCollectionViewLayoutExample/HFCardCollectionViewLayoutExample/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /HFCardCollectionViewLayoutExample/HFCardCollectionViewLayoutExample/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "20x20", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "20x20", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "29x29", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "29x29", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "40x40", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "40x40", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "size" : "60x60", 36 | "scale" : "2x" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "size" : "60x60", 41 | "scale" : "3x" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "size" : "20x20", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "size" : "20x20", 51 | "scale" : "2x" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "size" : "29x29", 56 | "scale" : "1x" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "size" : "29x29", 61 | "scale" : "2x" 62 | }, 63 | { 64 | "idiom" : "ipad", 65 | "size" : "40x40", 66 | "scale" : "1x" 67 | }, 68 | { 69 | "idiom" : "ipad", 70 | "size" : "40x40", 71 | "scale" : "2x" 72 | }, 73 | { 74 | "idiom" : "ipad", 75 | "size" : "76x76", 76 | "scale" : "1x" 77 | }, 78 | { 79 | "idiom" : "ipad", 80 | "size" : "76x76", 81 | "scale" : "2x" 82 | }, 83 | { 84 | "idiom" : "ipad", 85 | "size" : "83.5x83.5", 86 | "scale" : "2x" 87 | }, 88 | { 89 | "idiom" : "ios-marketing", 90 | "size" : "1024x1024", 91 | "scale" : "1x" 92 | } 93 | ], 94 | "info" : { 95 | "version" : 1, 96 | "author" : "xcode" 97 | } 98 | } -------------------------------------------------------------------------------- /HFCardCollectionViewLayoutExample/HFCardCollectionViewLayoutExample/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // HFCardCollectionViewLayoutExample 4 | // 5 | // Created by Hendrik Frahmann on 02.11.16. 6 | // Copyright © 2016 Hendrik Frahmann. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | @UIApplicationMain 12 | class AppDelegate: UIResponder, UIApplicationDelegate { 13 | 14 | var window: UIWindow? 15 | 16 | 17 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool { 18 | // Override point for customization after application launch. 19 | return true 20 | } 21 | 22 | func applicationWillResignActive(_ application: UIApplication) { 23 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 24 | // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game. 25 | } 26 | 27 | func applicationDidEnterBackground(_ application: UIApplication) { 28 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 29 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 30 | } 31 | 32 | func applicationWillEnterForeground(_ application: UIApplication) { 33 | // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background. 34 | } 35 | 36 | func applicationDidBecomeActive(_ application: UIApplication) { 37 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 38 | } 39 | 40 | func applicationWillTerminate(_ application: UIApplication) { 41 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 42 | } 43 | 44 | 45 | } 46 | 47 | -------------------------------------------------------------------------------- /HFCardCollectionViewLayoutExample/Pods/Target Support Files/Pods-HFCardCollectionViewLayoutExample/Pods-HFCardCollectionViewLayoutExample-acknowledgements.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreferenceSpecifiers 6 | 7 | 8 | FooterText 9 | This application makes use of the following third party libraries: 10 | Title 11 | Acknowledgements 12 | Type 13 | PSGroupSpecifier 14 | 15 | 16 | FooterText 17 | MIT License 18 | 19 | Copyright (c) 2016 Hendrik Frahmann 20 | 21 | Permission is hereby granted, free of charge, to any person obtaining a copy 22 | of this software and associated documentation files (the "Software"), to deal 23 | in the Software without restriction, including without limitation the rights 24 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 25 | copies of the Software, and to permit persons to whom the Software is 26 | furnished to do so, subject to the following conditions: 27 | 28 | The above copyright notice and this permission notice shall be included in all 29 | copies or substantial portions of the Software. 30 | 31 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 32 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 33 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 34 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 35 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 36 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 37 | SOFTWARE. 38 | 39 | License 40 | MIT 41 | Title 42 | HFCardCollectionViewLayout 43 | Type 44 | PSGroupSpecifier 45 | 46 | 47 | FooterText 48 | Generated by CocoaPods - https://cocoapods.org 49 | Title 50 | 51 | Type 52 | PSGroupSpecifier 53 | 54 | 55 | StringsTable 56 | Acknowledgements 57 | Title 58 | Acknowledgements 59 | 60 | 61 | -------------------------------------------------------------------------------- /Source/HFCardCollectionViewLayoutDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // HFCardCollectionViewLayoutDelegate.swift 3 | // Pods 4 | // 5 | // Created by Hendrik Frahmann on 17.11.16. 6 | // 7 | // 8 | 9 | import UIKit 10 | 11 | /// Extended delegate. 12 | @objc public protocol HFCardCollectionViewLayoutDelegate : UICollectionViewDelegate { 13 | 14 | /// Asks if the card at the specific index can be revealed. 15 | /// - Parameter collectionViewLayout: The current HFCardCollectionViewLayout. 16 | /// - Parameter canRevealCardAtIndex: Index of the card. 17 | @objc optional func cardCollectionViewLayout(_ collectionViewLayout: HFCardCollectionViewLayout, canRevealCardAtIndex index: Int) -> Bool 18 | 19 | /// Asks if the card at the specific index can be Unrevealed. 20 | /// - Parameter collectionViewLayout: The current HFCardCollectionViewLayout. 21 | /// - Parameter canUnrevealCardAtIndex: Index of the card. 22 | @objc optional func cardCollectionViewLayout(_ collectionViewLayout: HFCardCollectionViewLayout, canUnrevealCardAtIndex index: Int) -> Bool 23 | 24 | /// Feedback when the card at the given index will be revealed. 25 | /// - Parameter collectionViewLayout: The current HFCardCollectionViewLayout. 26 | /// - Parameter didRevealedCardAtIndex: Index of the card. 27 | @objc optional func cardCollectionViewLayout(_ collectionViewLayout: HFCardCollectionViewLayout, willRevealCardAtIndex index: Int) 28 | 29 | /// Feedback when the card at the given index was revealed. 30 | /// - Parameter collectionViewLayout: The current HFCardCollectionViewLayout. 31 | /// - Parameter didRevealedCardAtIndex: Index of the card. 32 | @objc optional func cardCollectionViewLayout(_ collectionViewLayout: HFCardCollectionViewLayout, didRevealCardAtIndex index: Int) 33 | 34 | /// Feedback when the card at the given index will be Unrevealed. 35 | /// - Parameter collectionViewLayout: The current HFCardCollectionViewLayout. 36 | /// - Parameter didUnrevealedCardAtIndex: Index of the card. 37 | @objc optional func cardCollectionViewLayout(_ collectionViewLayout: HFCardCollectionViewLayout, willUnrevealCardAtIndex index: Int) 38 | 39 | /// Feedback when the card at the given index was Unrevealed. 40 | /// - Parameter collectionViewLayout: The current HFCardCollectionViewLayout. 41 | /// - Parameter didUnrevealedCardAtIndex: Index of the card. 42 | @objc optional func cardCollectionViewLayout(_ collectionViewLayout: HFCardCollectionViewLayout, didUnrevealCardAtIndex index: Int) 43 | 44 | } 45 | -------------------------------------------------------------------------------- /Source/HFCardCollectionView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // HFCardCollectionView.swift 3 | // Pods 4 | // 5 | // Created by Hendrik Frahmann on 02.04.17. 6 | // 7 | // 8 | 9 | import UIKit 10 | 11 | class HFCardCollectionView: UICollectionView { 12 | 13 | override open func insertItems(at indexPaths: [IndexPath]) { 14 | if let collectionViewLayout = self.collectionViewLayout as? HFCardCollectionViewLayout { 15 | collectionViewLayout.willInsert(indexPaths: indexPaths) 16 | } 17 | super.insertItems(at: indexPaths) 18 | } 19 | 20 | override open func deleteItems(at indexPaths: [IndexPath]) { 21 | if let collectionViewLayout = self.collectionViewLayout as? HFCardCollectionViewLayout { 22 | collectionViewLayout.willDelete(indexPaths: indexPaths) 23 | } 24 | super.deleteItems(at: indexPaths) 25 | } 26 | 27 | /// Overwritten to prevent default behaviour of 'installsStandardGestureForInteractiveMovement = true'. 28 | /// 29 | /// - Parameter gestureRecognizer: An object whose class descends from the UIGestureRecognizer class. This parameter must not be nil. 30 | override open func addGestureRecognizer(_ gestureRecognizer: UIGestureRecognizer) { 31 | if let collectionViewLayout = self.collectionViewLayout as? HFCardCollectionViewLayout { 32 | let gestureClassName = String(describing: type(of: gestureRecognizer)) 33 | let gestureString = String(describing: gestureRecognizer) 34 | // Prevent default behaviour of 'installsStandardGestureForInteractiveMovement = true' and install a custom reorder gesture recognizer. 35 | if(gestureClassName == "UILongPressGestureRecognizer" && gestureString.range(of: "action=_handleReorderingGesture") != nil) { 36 | collectionViewLayout.installMoveCardsGestureRecognizer() 37 | return 38 | } 39 | } 40 | super.addGestureRecognizer(gestureRecognizer) 41 | } 42 | 43 | 44 | /// Overwritten to ignore the contentOffset change when scrolling is disabled. 45 | /// 46 | /// - Parameter contentOffset: A point (expressed in points) that is offset from the content view’s origin. 47 | /// - Parameter animated: true to animate the transition at a constant velocity to the new offset, false to make the transition immediate. 48 | override open func setContentOffset(_ contentOffset: CGPoint, animated: Bool) { 49 | if self.collectionViewLayout is HFCardCollectionViewLayout { 50 | if(self.isScrollEnabled == true) { 51 | super.setContentOffset(contentOffset, animated: animated) 52 | } 53 | } else { 54 | super.setContentOffset(contentOffset, animated: animated) 55 | } 56 | } 57 | 58 | } 59 | -------------------------------------------------------------------------------- /HFCardCollectionViewLayoutExample/HFCardCollectionViewLayoutExample/ExampleCollectionViewCell.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ExampleCollectionViewCell.swift 3 | // HFCardCollectionViewLayoutExample 4 | // 5 | // Created by Hendrik Frahmann on 02.11.16. 6 | // Copyright © 2016 Hendrik Frahmann. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import QuartzCore 11 | import HFCardCollectionViewLayout 12 | 13 | class ExampleCollectionViewCell: HFCardCollectionViewCell { 14 | 15 | var cardCollectionViewLayout: HFCardCollectionViewLayout? 16 | 17 | @IBOutlet var buttonFlip: UIButton? 18 | @IBOutlet var tableView: UITableView? 19 | @IBOutlet var labelText: UILabel? 20 | @IBOutlet var imageIcon: UIImageView? 21 | 22 | @IBOutlet var backView: UIView? 23 | @IBOutlet var buttonFlipBack: UIButton? 24 | 25 | override func awakeFromNib() { 26 | super.awakeFromNib() 27 | self.buttonFlip?.isHidden = true 28 | self.tableView?.scrollsToTop = false 29 | 30 | self.tableView?.register(UITableViewCell.self, forCellReuseIdentifier: "TableCell") 31 | self.tableView?.dataSource = self 32 | self.tableView?.delegate = self 33 | self.tableView?.allowsSelectionDuringEditing = false 34 | self.tableView?.reloadData() 35 | } 36 | 37 | func cardIsRevealed(_ isRevealed: Bool) { 38 | self.buttonFlip?.isHidden = !isRevealed 39 | self.tableView?.scrollsToTop = isRevealed 40 | } 41 | 42 | @IBAction func buttonFlipAction() { 43 | if let backView = self.backView { 44 | // Same Corner radius like the contentview of the HFCardCollectionViewCell 45 | backView.layer.cornerRadius = self.cornerRadius 46 | backView.layer.masksToBounds = true 47 | 48 | self.cardCollectionViewLayout?.flipRevealedCard(toView: backView) 49 | } 50 | } 51 | 52 | 53 | } 54 | 55 | extension ExampleCollectionViewCell : UITableViewDelegate, UITableViewDataSource { 56 | 57 | func numberOfSections(in tableView: UITableView) -> Int { 58 | return 1 59 | } 60 | 61 | func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { 62 | return 20 63 | } 64 | 65 | func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { 66 | let cell = tableView.dequeueReusableCell(withIdentifier: "TableCell") 67 | cell?.textLabel?.text = "Table Cell #\(indexPath.row)" 68 | cell?.textLabel?.textColor = .white 69 | cell?.backgroundColor = .clear 70 | cell?.selectionStyle = .none 71 | return cell! 72 | } 73 | 74 | func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool { 75 | return true 76 | } 77 | 78 | func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) { 79 | // nothing 80 | } 81 | 82 | func tableView(_ tableView: UITableView, editActionsForRowAt indexPath: IndexPath) -> [UITableViewRowAction]? { 83 | let anAction = UITableViewRowAction(style: .default, title: "An Action") 84 | { 85 | (action, indexPath) -> Void in 86 | // code for action 87 | } 88 | return [anAction] 89 | } 90 | 91 | } 92 | -------------------------------------------------------------------------------- /HFCardCollectionViewLayout.xcodeproj/xcshareddata/xcschemes/HFCardCollectionViewLayout.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 34 | 35 | 45 | 46 | 52 | 53 | 54 | 55 | 56 | 57 | 63 | 64 | 70 | 71 | 72 | 73 | 75 | 76 | 79 | 80 | 81 | -------------------------------------------------------------------------------- /Source/HFCardCollectionViewCell.swift: -------------------------------------------------------------------------------- 1 | // 2 | // HFCardCollectionViewCell.swift 3 | // HFCardCollectionViewLayout 4 | // 5 | // Created by Hendrik Frahmann on 02.11.16. 6 | // Copyright © 2016 Hendrik Frahmann. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import QuartzCore 11 | 12 | /// An UICollectionViewCell for the HFCardCollectionViewLayout. 13 | /// 14 | /// This Cell has no dependency on the HFCardCollectionViewLayout. 15 | /// So you can create your own UICollectionViewCell without extending from this class. 16 | open class HFCardCollectionViewCell: UICollectionViewCell { 17 | 18 | @IBInspectable open var cornerRadius: CGFloat = 10 19 | 20 | private var firstBackgroundColor: UIColor? 21 | 22 | // MARK: Overrides 23 | 24 | /// Overwritten to setup the view 25 | open override func awakeFromNib() { 26 | super.awakeFromNib() 27 | 28 | self.setupLayer(self) 29 | 30 | self.contentView.layer.masksToBounds = true 31 | self.contentView.layer.cornerRadius = self.cornerRadius 32 | self.contentView.clipsToBounds = true 33 | self.contentView.backgroundColor = self.firstBackgroundColor 34 | } 35 | 36 | /// Important for updating the Z index and setting the flag 'isUserInteractionEnabled' 37 | /// 38 | /// - Parameter layoutAttributes: The new layout attributes 39 | override open func apply(_ layoutAttributes: UICollectionViewLayoutAttributes) { 40 | super.apply(layoutAttributes) 41 | if let cardLayoutAttributes = layoutAttributes as? HFCardCollectionViewLayoutAttributes { 42 | self.layer.zPosition = CGFloat(cardLayoutAttributes.zIndex) 43 | self.contentView.isUserInteractionEnabled = cardLayoutAttributes.isRevealed 44 | } else { 45 | self.contentView.isUserInteractionEnabled = true 46 | } 47 | } 48 | 49 | /// Overwritten to pass the backgroundColor to contentView and keep the cell itself transparent. 50 | override open var backgroundColor: UIColor? { 51 | set { 52 | if(self.firstBackgroundColor == nil) { 53 | self.firstBackgroundColor = newValue 54 | } 55 | super.backgroundColor = .clear 56 | self.contentView.backgroundColor = newValue 57 | } 58 | get { 59 | return self.contentView.backgroundColor 60 | } 61 | } 62 | 63 | /// Overwritten to update the shadowPath. 64 | override open var bounds: CGRect { 65 | didSet { 66 | let shadowPath = UIBezierPath(roundedRect: self.bounds, cornerRadius: self.cornerRadius).cgPath 67 | self.layer.shadowPath = shadowPath 68 | } 69 | } 70 | 71 | /// Overwritten to create a better snapshot. 72 | /// 73 | /// The HFCardCollectionViewLayout will create a snapshot of this cell as the moving card view. 74 | /// This Function will recreate the shadows to the snapshotView. 75 | override open func snapshotView(afterScreenUpdates afterUpdates: Bool) -> UIView? { 76 | let snapshotView = UIView(frame: self.frame) 77 | if let snapshotOfContentView = self.contentView.snapshotView(afterScreenUpdates: afterUpdates) { 78 | snapshotView.addSubview(snapshotOfContentView) 79 | } 80 | self.setupLayer(snapshotView) 81 | return snapshotView 82 | } 83 | 84 | // MARK: Private Functions 85 | 86 | private func setupLayer(_ forView: UIView) { 87 | // Shadow can have performance issues on older devices 88 | let shadowPath = UIBezierPath(roundedRect: self.bounds, cornerRadius: self.cornerRadius).cgPath 89 | forView.layer.shadowPath = shadowPath 90 | forView.layer.masksToBounds = false 91 | forView.layer.shadowColor = UIColor(white: 0.0, alpha: 1.0).cgColor 92 | forView.layer.shadowRadius = 2 93 | forView.layer.shadowOpacity = 0.35 94 | forView.layer.shadowOffset = CGSize(width: 0, height: 0) 95 | forView.layer.rasterizationScale = UIScreen.main.scale 96 | forView.layer.shouldRasterize = true 97 | forView.clipsToBounds = false 98 | } 99 | 100 | } 101 | -------------------------------------------------------------------------------- /HFCardCollectionViewLayoutExample/Pods/Target Support Files/Pods-HFCardCollectionViewLayoutExample/Pods-HFCardCollectionViewLayoutExample-frameworks.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | 4 | echo "mkdir -p ${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 5 | mkdir -p "${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 6 | 7 | SWIFT_STDLIB_PATH="${DT_TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" 8 | 9 | install_framework() 10 | { 11 | if [ -r "${BUILT_PRODUCTS_DIR}/$1" ]; then 12 | local source="${BUILT_PRODUCTS_DIR}/$1" 13 | elif [ -r "${BUILT_PRODUCTS_DIR}/$(basename "$1")" ]; then 14 | local source="${BUILT_PRODUCTS_DIR}/$(basename "$1")" 15 | elif [ -r "$1" ]; then 16 | local source="$1" 17 | fi 18 | 19 | local destination="${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 20 | 21 | if [ -L "${source}" ]; then 22 | echo "Symlinked..." 23 | source="$(readlink "${source}")" 24 | fi 25 | 26 | # use filter instead of exclude so missing patterns dont' throw errors 27 | echo "rsync -av --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${source}\" \"${destination}\"" 28 | rsync -av --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${source}" "${destination}" 29 | 30 | local basename 31 | basename="$(basename -s .framework "$1")" 32 | binary="${destination}/${basename}.framework/${basename}" 33 | if ! [ -r "$binary" ]; then 34 | binary="${destination}/${basename}" 35 | fi 36 | 37 | # Strip invalid architectures so "fat" simulator / device frameworks work on device 38 | if [[ "$(file "$binary")" == *"dynamically linked shared library"* ]]; then 39 | strip_invalid_archs "$binary" 40 | fi 41 | 42 | # Resign the code if required by the build settings to avoid unstable apps 43 | code_sign_if_enabled "${destination}/$(basename "$1")" 44 | 45 | # Embed linked Swift runtime libraries. No longer necessary as of Xcode 7. 46 | if [ "${XCODE_VERSION_MAJOR}" -lt 7 ]; then 47 | local swift_runtime_libs 48 | swift_runtime_libs=$(xcrun otool -LX "$binary" | grep --color=never @rpath/libswift | sed -E s/@rpath\\/\(.+dylib\).*/\\1/g | uniq -u && exit ${PIPESTATUS[0]}) 49 | for lib in $swift_runtime_libs; do 50 | echo "rsync -auv \"${SWIFT_STDLIB_PATH}/${lib}\" \"${destination}\"" 51 | rsync -auv "${SWIFT_STDLIB_PATH}/${lib}" "${destination}" 52 | code_sign_if_enabled "${destination}/${lib}" 53 | done 54 | fi 55 | } 56 | 57 | # Signs a framework with the provided identity 58 | code_sign_if_enabled() { 59 | if [ -n "${EXPANDED_CODE_SIGN_IDENTITY}" -a "${CODE_SIGNING_REQUIRED}" != "NO" -a "${CODE_SIGNING_ALLOWED}" != "NO" ]; then 60 | # Use the current code_sign_identitiy 61 | echo "Code Signing $1 with Identity ${EXPANDED_CODE_SIGN_IDENTITY_NAME}" 62 | local code_sign_cmd="/usr/bin/codesign --force --sign ${EXPANDED_CODE_SIGN_IDENTITY} ${OTHER_CODE_SIGN_FLAGS} --preserve-metadata=identifier,entitlements "$1"" 63 | 64 | if [ "${COCOAPODS_PARALLEL_CODE_SIGN}" == "true" ]; then 65 | code_sign_cmd="$code_sign_cmd &" 66 | fi 67 | echo "$code_sign_cmd" 68 | eval "$code_sign_cmd" 69 | fi 70 | } 71 | 72 | # Strip invalid architectures 73 | strip_invalid_archs() { 74 | binary="$1" 75 | # Get architectures for current file 76 | archs="$(lipo -info "$binary" | rev | cut -d ':' -f1 | rev)" 77 | stripped="" 78 | for arch in $archs; do 79 | if ! [[ "${VALID_ARCHS}" == *"$arch"* ]]; then 80 | # Strip non-valid architectures in-place 81 | lipo -remove "$arch" -output "$binary" "$binary" || exit 1 82 | stripped="$stripped $arch" 83 | fi 84 | done 85 | if [[ "$stripped" ]]; then 86 | echo "Stripped $binary of architectures:$stripped" 87 | fi 88 | } 89 | 90 | 91 | if [[ "$CONFIGURATION" == "Debug" ]]; then 92 | install_framework "$BUILT_PRODUCTS_DIR/HFCardCollectionViewLayout/HFCardCollectionViewLayout.framework" 93 | fi 94 | if [[ "$CONFIGURATION" == "Release" ]]; then 95 | install_framework "$BUILT_PRODUCTS_DIR/HFCardCollectionViewLayout/HFCardCollectionViewLayout.framework" 96 | fi 97 | if [ "${COCOAPODS_PARALLEL_CODE_SIGN}" == "true" ]; then 98 | wait 99 | fi 100 | -------------------------------------------------------------------------------- /HFCardCollectionViewLayoutExample/Pods/Target Support Files/Pods-HFCardCollectionViewLayoutExample/Pods-HFCardCollectionViewLayoutExample-resources.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | 4 | mkdir -p "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 5 | 6 | RESOURCES_TO_COPY=${PODS_ROOT}/resources-to-copy-${TARGETNAME}.txt 7 | > "$RESOURCES_TO_COPY" 8 | 9 | XCASSET_FILES=() 10 | 11 | case "${TARGETED_DEVICE_FAMILY}" in 12 | 1,2) 13 | TARGET_DEVICE_ARGS="--target-device ipad --target-device iphone" 14 | ;; 15 | 1) 16 | TARGET_DEVICE_ARGS="--target-device iphone" 17 | ;; 18 | 2) 19 | TARGET_DEVICE_ARGS="--target-device ipad" 20 | ;; 21 | 3) 22 | TARGET_DEVICE_ARGS="--target-device tv" 23 | ;; 24 | *) 25 | TARGET_DEVICE_ARGS="--target-device mac" 26 | ;; 27 | esac 28 | 29 | install_resource() 30 | { 31 | if [[ "$1" = /* ]] ; then 32 | RESOURCE_PATH="$1" 33 | else 34 | RESOURCE_PATH="${PODS_ROOT}/$1" 35 | fi 36 | if [[ ! -e "$RESOURCE_PATH" ]] ; then 37 | cat << EOM 38 | error: Resource "$RESOURCE_PATH" not found. Run 'pod install' to update the copy resources script. 39 | EOM 40 | exit 1 41 | fi 42 | case $RESOURCE_PATH in 43 | *.storyboard) 44 | echo "ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile ${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .storyboard`.storyboardc $RESOURCE_PATH --sdk ${SDKROOT} ${TARGET_DEVICE_ARGS}" 45 | ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .storyboard`.storyboardc" "$RESOURCE_PATH" --sdk "${SDKROOT}" ${TARGET_DEVICE_ARGS} 46 | ;; 47 | *.xib) 48 | echo "ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile ${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .xib`.nib $RESOURCE_PATH --sdk ${SDKROOT} ${TARGET_DEVICE_ARGS}" 49 | ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .xib`.nib" "$RESOURCE_PATH" --sdk "${SDKROOT}" ${TARGET_DEVICE_ARGS} 50 | ;; 51 | *.framework) 52 | echo "mkdir -p ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 53 | mkdir -p "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 54 | echo "rsync -av $RESOURCE_PATH ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 55 | rsync -av "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 56 | ;; 57 | *.xcdatamodel) 58 | echo "xcrun momc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH"`.mom\"" 59 | xcrun momc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodel`.mom" 60 | ;; 61 | *.xcdatamodeld) 62 | echo "xcrun momc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodeld`.momd\"" 63 | xcrun momc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodeld`.momd" 64 | ;; 65 | *.xcmappingmodel) 66 | echo "xcrun mapc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcmappingmodel`.cdm\"" 67 | xcrun mapc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcmappingmodel`.cdm" 68 | ;; 69 | *.xcassets) 70 | ABSOLUTE_XCASSET_FILE="$RESOURCE_PATH" 71 | XCASSET_FILES+=("$ABSOLUTE_XCASSET_FILE") 72 | ;; 73 | *) 74 | echo "$RESOURCE_PATH" 75 | echo "$RESOURCE_PATH" >> "$RESOURCES_TO_COPY" 76 | ;; 77 | esac 78 | } 79 | 80 | mkdir -p "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 81 | rsync -avr --copy-links --no-relative --exclude '*/.svn/*' --files-from="$RESOURCES_TO_COPY" / "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 82 | if [[ "${ACTION}" == "install" ]] && [[ "${SKIP_INSTALL}" == "NO" ]]; then 83 | mkdir -p "${INSTALL_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 84 | rsync -avr --copy-links --no-relative --exclude '*/.svn/*' --files-from="$RESOURCES_TO_COPY" / "${INSTALL_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 85 | fi 86 | rm -f "$RESOURCES_TO_COPY" 87 | 88 | if [[ -n "${WRAPPER_EXTENSION}" ]] && [ "`xcrun --find actool`" ] && [ -n "$XCASSET_FILES" ] 89 | then 90 | # Find all other xcassets (this unfortunately includes those of path pods and other targets). 91 | OTHER_XCASSETS=$(find "$PWD" -iname "*.xcassets" -type d) 92 | while read line; do 93 | if [[ $line != "${PODS_ROOT}*" ]]; then 94 | XCASSET_FILES+=("$line") 95 | fi 96 | done <<<"$OTHER_XCASSETS" 97 | 98 | printf "%s\0" "${XCASSET_FILES[@]}" | xargs -0 xcrun actool --output-format human-readable-text --notices --warnings --platform "${PLATFORM_NAME}" --minimum-deployment-target "${!DEPLOYMENT_TARGET_SETTING_NAME}" ${TARGET_DEVICE_ARGS} --compress-pngs --compile "${BUILT_PRODUCTS_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 99 | fi 100 | -------------------------------------------------------------------------------- /HFCardCollectionViewLayoutExample/HFCardCollectionViewLayoutExample/ExampleViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ExampleViewController.swift 3 | // HFCardCollectionViewLayoutExample 4 | // 5 | // Created by Hendrik Frahmann on 28.10.16. 6 | // Copyright © 2016 Hendrik Frahmann. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import HFCardCollectionViewLayout 11 | 12 | struct CardInfo { 13 | var color: UIColor 14 | var icon: UIImage 15 | } 16 | 17 | class ExampleViewController : UICollectionViewController, HFCardCollectionViewLayoutDelegate { 18 | 19 | var cardCollectionViewLayout: HFCardCollectionViewLayout? 20 | 21 | @IBOutlet var backgroundView: UIView? 22 | @IBOutlet var backgroundNavigationBar: UINavigationBar? 23 | 24 | var cardLayoutOptions: CardLayoutSetupOptions? 25 | var shouldSetupBackgroundView = false 26 | 27 | var cardArray: [CardInfo] = [] 28 | 29 | override func viewDidLoad() { 30 | self.setupExample() 31 | super.viewDidLoad() 32 | } 33 | 34 | // MARK: CollectionView 35 | 36 | func cardCollectionViewLayout(_ collectionViewLayout: HFCardCollectionViewLayout, willRevealCardAtIndex index: Int) { 37 | if let cell = self.collectionView?.cellForItem(at: IndexPath(item: index, section: 0)) as? ExampleCollectionViewCell { 38 | cell.cardCollectionViewLayout = self.cardCollectionViewLayout 39 | cell.cardIsRevealed(true) 40 | } 41 | } 42 | 43 | func cardCollectionViewLayout(_ collectionViewLayout: HFCardCollectionViewLayout, willUnrevealCardAtIndex index: Int) { 44 | if let cell = self.collectionView?.cellForItem(at: IndexPath(item: index, section: 0)) as? ExampleCollectionViewCell { 45 | cell.cardCollectionViewLayout = self.cardCollectionViewLayout 46 | cell.cardIsRevealed(false) 47 | } 48 | } 49 | 50 | override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { 51 | return self.cardArray.count 52 | } 53 | 54 | override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { 55 | let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "CardCell", for: indexPath) as! ExampleCollectionViewCell 56 | cell.backgroundColor = self.cardArray[indexPath.item].color 57 | cell.imageIcon?.image = self.cardArray[indexPath.item].icon 58 | return cell 59 | } 60 | 61 | override func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { 62 | self.cardCollectionViewLayout?.revealCardAt(index: indexPath.item) 63 | } 64 | 65 | override func collectionView(_ collectionView: UICollectionView, moveItemAt sourceIndexPath: IndexPath, to destinationIndexPath: IndexPath) { 66 | let tempItem = self.cardArray[sourceIndexPath.item] 67 | self.cardArray.remove(at: sourceIndexPath.item) 68 | self.cardArray.insert(tempItem, at: destinationIndexPath.item) 69 | } 70 | 71 | // MARK: Actions 72 | 73 | @IBAction func goBackAction() { 74 | _ = self.navigationController?.popViewController(animated: true) 75 | } 76 | 77 | @IBAction func addCardAction() { 78 | let index = 0 79 | let newItem = createCardInfo() 80 | self.cardArray.insert(newItem, at: index) 81 | self.collectionView?.insertItems(at: [IndexPath(item: index, section: 0)]) 82 | 83 | if(self.cardArray.count == 1) { 84 | self.cardCollectionViewLayout?.revealCardAt(index: index) 85 | } 86 | } 87 | 88 | @IBAction func deleteCardAtIndex0orSelected() { 89 | var index = 0 90 | if(self.cardCollectionViewLayout!.revealedIndex >= 0) { 91 | index = self.cardCollectionViewLayout!.revealedIndex 92 | } 93 | self.cardCollectionViewLayout?.flipRevealedCardBack(completion: { 94 | self.cardArray.remove(at: index) 95 | self.collectionView?.deleteItems(at: [IndexPath(item: index, section: 0)]) 96 | }) 97 | } 98 | 99 | // MARK: Private Functions 100 | 101 | private func setupExample() { 102 | if let cardCollectionViewLayout = self.collectionView?.collectionViewLayout as? HFCardCollectionViewLayout { 103 | self.cardCollectionViewLayout = cardCollectionViewLayout 104 | } 105 | if(self.shouldSetupBackgroundView == true) { 106 | self.setupBackgroundView() 107 | } 108 | if let cardLayoutOptions = self.cardLayoutOptions { 109 | self.cardCollectionViewLayout?.firstMovableIndex = cardLayoutOptions.firstMovableIndex 110 | self.cardCollectionViewLayout?.cardHeadHeight = cardLayoutOptions.cardHeadHeight 111 | self.cardCollectionViewLayout?.cardShouldExpandHeadHeight = cardLayoutOptions.cardShouldExpandHeadHeight 112 | self.cardCollectionViewLayout?.cardShouldStretchAtScrollTop = cardLayoutOptions.cardShouldStretchAtScrollTop 113 | self.cardCollectionViewLayout?.cardMaximumHeight = cardLayoutOptions.cardMaximumHeight 114 | self.cardCollectionViewLayout?.bottomNumberOfStackedCards = cardLayoutOptions.bottomNumberOfStackedCards 115 | self.cardCollectionViewLayout?.bottomStackedCardsShouldScale = cardLayoutOptions.bottomStackedCardsShouldScale 116 | self.cardCollectionViewLayout?.bottomCardLookoutMargin = cardLayoutOptions.bottomCardLookoutMargin 117 | self.cardCollectionViewLayout?.spaceAtTopForBackgroundView = cardLayoutOptions.spaceAtTopForBackgroundView 118 | self.cardCollectionViewLayout?.spaceAtTopShouldSnap = cardLayoutOptions.spaceAtTopShouldSnap 119 | self.cardCollectionViewLayout?.spaceAtBottom = cardLayoutOptions.spaceAtBottom 120 | self.cardCollectionViewLayout?.scrollAreaTop = cardLayoutOptions.scrollAreaTop 121 | self.cardCollectionViewLayout?.scrollAreaBottom = cardLayoutOptions.scrollAreaBottom 122 | self.cardCollectionViewLayout?.scrollShouldSnapCardHead = cardLayoutOptions.scrollShouldSnapCardHead 123 | self.cardCollectionViewLayout?.scrollStopCardsAtTop = cardLayoutOptions.scrollStopCardsAtTop 124 | self.cardCollectionViewLayout?.bottomStackedCardsMinimumScale = cardLayoutOptions.bottomStackedCardsMinimumScale 125 | self.cardCollectionViewLayout?.bottomStackedCardsMaximumScale = cardLayoutOptions.bottomStackedCardsMaximumScale 126 | 127 | let count = cardLayoutOptions.numberOfCards 128 | 129 | for index in 0.. CardInfo { 137 | let icons: [UIImage] = [#imageLiteral(resourceName: "Icon1.pdf"), #imageLiteral(resourceName: "Icon2.pdf"), #imageLiteral(resourceName: "Icon3.pdf"), #imageLiteral(resourceName: "Icon4.pdf"), #imageLiteral(resourceName: "Icon5.pdf"), #imageLiteral(resourceName: "Icon6.pdf")] 138 | let icon = icons[Int(arc4random_uniform(6))] 139 | let newItem = CardInfo(color: self.getRandomColor(), icon: icon) 140 | return newItem 141 | } 142 | 143 | private func setupBackgroundView() { 144 | if(self.cardLayoutOptions?.spaceAtTopForBackgroundView == 0) { 145 | self.cardLayoutOptions?.spaceAtTopForBackgroundView = 44 // Height of the NavigationBar in the BackgroundView 146 | } 147 | if let collectionView = self.collectionView { 148 | collectionView.backgroundView = self.backgroundView 149 | self.backgroundNavigationBar?.shadowImage = UIImage() 150 | self.backgroundNavigationBar?.setBackgroundImage(UIImage(), for: .default) 151 | } 152 | } 153 | 154 | private func getRandomColor() -> UIColor{ 155 | let randomRed:CGFloat = CGFloat(drand48()) 156 | let randomGreen:CGFloat = CGFloat(drand48()) 157 | let randomBlue:CGFloat = CGFloat(drand48()) 158 | return UIColor(red: randomRed, green: randomGreen, blue: randomBlue, alpha: 1.0) 159 | } 160 | } 161 | 162 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # HFCardCollectionViewLayout 2 | The HFCardCollectionViewLayout provides a card stack layout not quite similar like the apps Reminder and Wallet. 3 | 4 | Features: 5 | 6 | - Many options, also within the InterfaceBuilder 7 | - Flip animation to have a backview 8 | - Move/Order the cards 9 | - Simple integration (only set the Layout class in the CollectionView) 10 | 11 | ![Screenshot](https://raw.githubusercontent.com/hfrahmann/HFCardCollectionViewLayout/master/ReadmeAssets/Screenshot.png) 12 | ![Screenplay](https://raw.githubusercontent.com/hfrahmann/HFCardCollectionViewLayout/master/ReadmeAssets/Screenplay.gif) 13 | 14 | 15 | ## Installation 16 | 17 | Install it with Cocoapods, Swift Package Manager, Carthage or just use the files inside the **Source** directory. 18 | 19 | 20 | **Cocoapods:** 21 | ``` 22 | pod 'HFCardCollectionViewLayout' 23 | ``` 24 | 25 | 26 | **Carthage:** 27 | ``` 28 | github "hfrahmann/HFCardCollectionViewLayout" 29 | ``` 30 | 31 | 32 | **Swift Package Manager:** 33 | ``` 34 | dependencies: [ 35 | .Package(url: "https://github.com/hfrahmann/HFCardCollectionViewLayout.git") 36 | ] 37 | ``` 38 | 39 | 40 | 41 | ## Implementation 42 | 43 | Just set *HFCardCollectionViewLayout* as the custom layout class in your UICollectionView. 44 | 45 | ![CollectionView_LayoutClass](https://raw.githubusercontent.com/hfrahmann/HFCardCollectionViewLayout/master/ReadmeAssets/CollectionView_LayoutClass.png) 46 | 47 | 48 | There is also a cell class called **HFCardCollectionViewCell** containing rounded corners and a shadow. 49 | But this class has no dependency on the *HFCardCollectionViewLayout*. 50 | It's only there to have a cell that looks like a card. 51 | 52 | **Important: This collectionView layout does support only one section!** 53 | 54 | 55 | ### HFCardCollectionView 56 | 57 | Because of some problems with inserting items while a card is revealed, you have to use the **HFCardCollectionView** instead of the UICollectionView. 58 | Or if you use your own collectionview class, you can copy the lines from the file to your own. 59 | 60 | 61 | ## Delegate 62 | 63 | These are the delegate functions of the **HFCardCollectionViewLayoutDelegate** inherits from *UICollectionViewDelete* so you don't need to connect a further delegate. 64 | 65 | ```swift 66 | /// Asks if the card at the specific index can be revealed. 67 | /// - Parameter collectionViewLayout: The current HFCardCollectionViewLayout. 68 | /// - Parameter canRevealCardAtIndex: Index of the card. 69 | func cardCollectionViewLayout(_ collectionViewLayout: HFCardCollectionViewLayout, canRevealCardAtIndex index: Int) -> Bool 70 | 71 | /// Asks if the card at the specific index can be unrevealed. 72 | /// - Parameter collectionViewLayout: The current HFCardCollectionViewLayout. 73 | /// - Parameter canUnrevealCardAtIndex: Index of the card. 74 | func cardCollectionViewLayout(_ collectionViewLayout: HFCardCollectionViewLayout, canUnrevealCardAtIndex index: Int) -> Bool 75 | 76 | /// Feedback when the card at the given index will be revealed. 77 | /// - Parameter collectionViewLayout: The current HFCardCollectionViewLayout. 78 | /// - Parameter didRevealedCardAtIndex: Index of the card. 79 | func cardCollectionViewLayout(_ collectionViewLayout: HFCardCollectionViewLayout, willRevealCardAtIndex index: Int) 80 | 81 | /// Feedback when the card at the given index was revealed. 82 | /// - Parameter collectionViewLayout: The current HFCardCollectionViewLayout. 83 | /// - Parameter didRevealedCardAtIndex: Index of the card. 84 | func cardCollectionViewLayout(_ collectionViewLayout: HFCardCollectionViewLayout, didRevealCardAtIndex index: Int) 85 | 86 | /// Feedback when the card at the given index will be unrevealed. 87 | /// - Parameter collectionViewLayout: The current HFCardCollectionViewLayout. 88 | /// - Parameter didUnrevealedCardAtIndex: Index of the card. 89 | func cardCollectionViewLayout(_ collectionViewLayout: HFCardCollectionViewLayout, willUnrevealCardAtIndex index: Int) 90 | 91 | /// Feedback when the card at the given index was unrevealed. 92 | /// - Parameter collectionViewLayout: The current HFCardCollectionViewLayout. 93 | /// - Parameter didUnrevealedCardAtIndex: Index of the card. 94 | func cardCollectionViewLayout(_ collectionViewLayout: HFCardCollectionViewLayout, didUnrevealCardAtIndex index: Int) 95 | ``` 96 | 97 | 98 | 99 | ## Options and Actions 100 | 101 | You also have access to the options and (some) actions at the *HFCardCollectionViewLayout* object in the interface builder. 102 | 103 | ![CardLayoutOptions2](https://raw.githubusercontent.com/hfrahmann/HFCardCollectionViewLayout/master/ReadmeAssets/CardLayoutOptions.png) 104 | ![CardLayoutActions](https://raw.githubusercontent.com/hfrahmann/HFCardCollectionViewLayout/master/ReadmeAssets/CardLayoutActions.png) 105 | 106 | These are the public variables of *HFCardCollectionViewLayout*. 107 | 108 | ```swift 109 | /////////////// Public Variables 110 | 111 | /// Only cards with index equal or greater than firstMovableIndex can be moved through the collectionView. 112 | @IBInspectable var firstMovableIndex: Int = 0 113 | 114 | /// Specifies the height that is showing the cardhead when the collectionView is showing all cards. 115 | /// The minimum value is 20. 116 | @IBInspectable var cardHeadHeight: CGFloat = 80 117 | 118 | /// When th collectionView is showing all cards but there are not enough cards to fill the full height, 119 | /// the cardHeadHeight will be expanded to equally fill the height. 120 | @IBInspectable var cardShouldExpandHeadHeight: Bool = true 121 | 122 | /// Stretch the cards when scrolling up 123 | @IBInspectable var cardShouldStretchAtScrollTop: Bool = true 124 | 125 | /// Specifies the maximum height of the cards. 126 | /// But the height can be less if the frame size of collectionView is smaller. 127 | @IBInspectable var cardMaximumHeight: CGFloat = 0 128 | 129 | /// Count of bottom stacked cards when a card is revealed. 130 | /// Value must be between 0 and 10 131 | @IBInspectable var bottomNumberOfStackedCards: Int = 5 132 | 133 | /// All bottom stacked cards are scaled to produce the 3D effect. 134 | @IBInspectable var bottomStackedCardsShouldScale: Bool = true 135 | 136 | /// Specifies the margin for the top margin of a bottom stacked card. 137 | /// Value can be between 0 and 20 138 | @IBInspectable var bottomCardLookoutMargin: CGFloat = 10 139 | 140 | /// An additional topspace to show the top of the collectionViews backgroundView. 141 | @IBInspectable var spaceAtTopForBackgroundView: CGFloat = 0 142 | 143 | /// Snaps the scrollView if the contentOffset is on the 'spaceAtTopForBackgroundView' 144 | @IBInspectable var spaceAtTopShouldSnap: Bool = true 145 | 146 | /// Additional space at the bottom to expand the contentsize of the collectionView. 147 | @IBInspectable var spaceAtBottom: CGFloat = 0 148 | 149 | /// Area the top where to autoscroll while moving a card. 150 | @IBInspectable var scrollAreaTop: CGFloat = 120 151 | 152 | /// Area ot the bottom where to autoscroll while moving a card. 153 | @IBInspectable var scrollAreaBottom: CGFloat = 120 154 | 155 | /// The scrollView should snap the cardhead to the top. 156 | @IBInspectable var scrollShouldSnapCardHead: Bool = false 157 | 158 | /// Cards are stopping at top while scrolling. 159 | @IBInspectable var scrollStopCardsAtTop: Bool = true 160 | 161 | /// All cards are collapsed at bottom. 162 | @IBInspectable var collapseAllCards: Bool = false 163 | 164 | /// Contains the revealed index. 165 | /// ReadOnly. 166 | private(set) var revealedIndex: Int = -1 167 | ``` 168 | 169 | Interface builder actions 170 | ```swift 171 | /////////////// InterfaceBuilder Actions 172 | 173 | 174 | /// Action for the InterfaceBuilder to flip back the revealed card. 175 | @IBAction func flipBackRevealedCardAction() 176 | 177 | /// Action for the InterfaceBuilder to unreveal the revealed card. 178 | @IBAction func unrevealRevealedCardAction() 179 | 180 | /// Action to collapse all cards. 181 | @IBAction func collapseAllCardsAction() 182 | ``` 183 | 184 | Public functions 185 | ```swift 186 | /////////////// Public Functions 187 | 188 | 189 | /// Reveal a card at the given index. 190 | /// 191 | /// - Parameter index: The index of the card. 192 | /// - Parameter completion: An optional completion block. Will be executed the animation is finished. 193 | public func revealCardAt(index: Int, completion: (() -> Void)? = nil) 194 | 195 | /// Unreveal the revealed card 196 | /// 197 | /// - Parameter completion: An optional completion block. Will be executed the animation is finished. 198 | public func unrevealCard(completion: (() -> Void)? = nil) 199 | 200 | 201 | /// Flips the revealed card to the given view. 202 | /// The frame for the view will be the same as the cell 203 | /// 204 | /// - Parameter toView: The view for the backview of te card. 205 | /// - Parameter completion: An optional completion block. Will be executed the animation is finished. 206 | public func flipRevealedCard(toView: UIView, completion: (() -> Void)? = nil) 207 | 208 | 209 | /// Flips the flipped card back to the frontview. 210 | /// 211 | /// - Parameter completion: An optional completion block. Will be executed the animation is finished. 212 | public func flipRevealedCardBack(completion: (() -> Void)? = nil) 213 | ``` 214 | 215 | 216 | ## License 217 | 218 | MIT License 219 | -------------------------------------------------------------------------------- /HFCardCollectionViewLayoutExample/HFCardCollectionViewLayoutExample/MenuTableViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MenuTableViewController.swift 3 | // HFCardCollectionViewLayoutExample 4 | // 5 | // Created by Hendrik Frahmann on 31.10.16. 6 | // Copyright © 2016 Hendrik Frahmann. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | struct CardLayoutSetupOptions { 12 | var firstMovableIndex: Int = 0 13 | var cardHeadHeight: CGFloat = 80 14 | var cardShouldExpandHeadHeight: Bool = true 15 | var cardShouldStretchAtScrollTop: Bool = true 16 | var cardMaximumHeight: CGFloat = 0 17 | var bottomNumberOfStackedCards: Int = 5 18 | var bottomStackedCardsShouldScale: Bool = true 19 | var bottomCardLookoutMargin: CGFloat = 10 20 | var bottomStackedCardsMaximumScale: CGFloat = 1.0 21 | var bottomStackedCardsMinimumScale: CGFloat = 0.94 22 | var spaceAtTopForBackgroundView: CGFloat = 0 23 | var spaceAtTopShouldSnap: Bool = true 24 | var spaceAtBottom: CGFloat = 0 25 | var scrollAreaTop: CGFloat = 120 26 | var scrollAreaBottom: CGFloat = 120 27 | var scrollShouldSnapCardHead: Bool = false 28 | var scrollStopCardsAtTop: Bool = true 29 | 30 | var numberOfCards: Int = 15 31 | } 32 | 33 | class MenuTableViewController: UITableViewController { 34 | 35 | var hideNavigationBar = false 36 | var hideToolBar = false 37 | 38 | var defaults = CardLayoutSetupOptions() 39 | var numberFormatter = NumberFormatter() 40 | 41 | @IBOutlet var textfieldNumberOfCards: UITextField? 42 | @IBOutlet var textfieldFirstMovableIndex: UITextField? 43 | @IBOutlet var textfieldCardHeadHeight: UITextField? 44 | @IBOutlet var switchCardShouldExpandHeadHeight: UISwitch? 45 | @IBOutlet var switchCardShouldStretchAtScrollTop: UISwitch? 46 | @IBOutlet var textfieldCardMaximumHeight: UITextField? 47 | @IBOutlet var textfieldBottomNumberOfStackedCards: UITextField? 48 | @IBOutlet var switchBottomStackedCardsShouldScale: UISwitch? 49 | @IBOutlet var textfieldBottomCardLookoutMargin: UITextField? 50 | @IBOutlet var textfieldSpaceAtTopForBackgroundView: UITextField? 51 | @IBOutlet var textfieldBottomStackedCardsMinimumScale: UITextField? 52 | @IBOutlet var textfieldBottomStackedCardsMaximumScale: UITextField? 53 | @IBOutlet var switchSpaceAtTopShouldSnap: UISwitch? 54 | @IBOutlet var textfieldSpaceAtBottom: UITextField? 55 | @IBOutlet var textfieldScrollAreaTop: UITextField? 56 | @IBOutlet var textfieldScrollAreaBottom: UITextField? 57 | @IBOutlet var switchScrollShouldSnapCardHead: UISwitch? 58 | @IBOutlet var switchScrollStopCardsAtTop: UISwitch? 59 | 60 | override func viewDidLoad() { 61 | self.numberFormatter.locale = Locale(identifier: "en_US") 62 | super.viewDidLoad() 63 | } 64 | 65 | override func viewWillAppear(_ animated: Bool) { 66 | super.viewWillAppear(animated) 67 | self.navigationController?.isNavigationBarHidden = false 68 | self.navigationController?.isToolbarHidden = true 69 | } 70 | 71 | override func viewWillDisappear(_ animated: Bool) { 72 | super.viewWillDisappear(animated) 73 | UIApplication.shared.keyWindow?.endEditing(true) 74 | self.navigationController?.isNavigationBarHidden = self.hideNavigationBar 75 | self.navigationController?.isToolbarHidden = self.hideToolBar 76 | } 77 | 78 | // MARK: Actions 79 | 80 | @IBAction func resetAction() { 81 | self.textfieldNumberOfCards?.text = String(self.defaults.numberOfCards) 82 | self.textfieldFirstMovableIndex?.text = String(self.defaults.firstMovableIndex) 83 | self.textfieldCardHeadHeight?.text = self.stringFromFloat(self.defaults.cardHeadHeight) 84 | self.switchCardShouldExpandHeadHeight?.isOn = self.defaults.cardShouldExpandHeadHeight 85 | self.switchCardShouldStretchAtScrollTop?.isOn = self.defaults.cardShouldStretchAtScrollTop 86 | self.textfieldCardMaximumHeight?.text = self.stringFromFloat(self.defaults.cardMaximumHeight) 87 | self.textfieldBottomNumberOfStackedCards?.text = String(self.defaults.bottomNumberOfStackedCards) 88 | self.switchBottomStackedCardsShouldScale?.isOn = self.defaults.bottomStackedCardsShouldScale 89 | self.textfieldBottomCardLookoutMargin?.text = self.stringFromFloat(self.defaults.bottomCardLookoutMargin) 90 | self.textfieldSpaceAtTopForBackgroundView?.text = self.stringFromFloat(self.defaults.spaceAtTopForBackgroundView) 91 | self.switchSpaceAtTopShouldSnap?.isOn = self.defaults.spaceAtTopShouldSnap 92 | self.textfieldSpaceAtBottom?.text = self.stringFromFloat(self.defaults.spaceAtBottom) 93 | self.textfieldScrollAreaTop?.text = self.stringFromFloat(self.defaults.scrollAreaTop) 94 | self.textfieldScrollAreaBottom?.text = self.stringFromFloat(self.defaults.scrollAreaBottom) 95 | self.switchScrollShouldSnapCardHead?.isOn = self.defaults.scrollShouldSnapCardHead 96 | self.switchScrollStopCardsAtTop?.isOn = self.defaults.scrollStopCardsAtTop 97 | self.textfieldBottomStackedCardsMinimumScale?.text = self.stringFromFloat(self.defaults.bottomStackedCardsMinimumScale) 98 | self.textfieldBottomStackedCardsMaximumScale?.text = self.stringFromFloat(self.defaults.bottomStackedCardsMaximumScale) 99 | } 100 | 101 | // MARK: Navigation 102 | 103 | // In a storyboard-based application, you will often want to do a little preparation before navigation 104 | override func prepare(for segue: UIStoryboardSegue, sender: Any?) { 105 | if let controller = segue.destination as? ExampleViewController { 106 | 107 | var layoutOptions = CardLayoutSetupOptions() 108 | layoutOptions.numberOfCards = self.getIntFromTextfield(self.textfieldNumberOfCards!) 109 | layoutOptions.firstMovableIndex = self.getIntFromTextfield(self.textfieldFirstMovableIndex!) 110 | layoutOptions.cardHeadHeight = self.getFloatFromTextfield(self.textfieldCardHeadHeight!) 111 | layoutOptions.cardShouldExpandHeadHeight = self.switchCardShouldExpandHeadHeight!.isOn 112 | layoutOptions.cardShouldStretchAtScrollTop = self.switchCardShouldStretchAtScrollTop!.isOn 113 | layoutOptions.cardMaximumHeight = self.getFloatFromTextfield(self.textfieldCardMaximumHeight!) 114 | layoutOptions.bottomNumberOfStackedCards = self.getIntFromTextfield(self.textfieldBottomNumberOfStackedCards!) 115 | layoutOptions.bottomStackedCardsShouldScale = self.switchBottomStackedCardsShouldScale!.isOn 116 | layoutOptions.bottomCardLookoutMargin = self.getFloatFromTextfield(self.textfieldBottomCardLookoutMargin!) 117 | layoutOptions.spaceAtTopForBackgroundView = self.getFloatFromTextfield(self.textfieldSpaceAtTopForBackgroundView!) 118 | layoutOptions.spaceAtTopShouldSnap = self.switchSpaceAtTopShouldSnap!.isOn 119 | layoutOptions.spaceAtBottom = self.getFloatFromTextfield(self.textfieldSpaceAtBottom!) 120 | layoutOptions.scrollAreaTop = self.getFloatFromTextfield(self.textfieldScrollAreaTop!) 121 | layoutOptions.scrollAreaBottom = self.getFloatFromTextfield(self.textfieldScrollAreaBottom!) 122 | layoutOptions.scrollShouldSnapCardHead = self.switchScrollShouldSnapCardHead!.isOn 123 | layoutOptions.scrollStopCardsAtTop = self.switchScrollStopCardsAtTop!.isOn 124 | layoutOptions.bottomStackedCardsMinimumScale = self.getFloatFromTextfield(self.textfieldBottomStackedCardsMinimumScale!) 125 | layoutOptions.bottomStackedCardsMaximumScale = self.getFloatFromTextfield(self.textfieldBottomStackedCardsMaximumScale!) 126 | 127 | controller.cardLayoutOptions = layoutOptions 128 | 129 | if(segue.identifier == "AsRootController") { 130 | self.hideNavigationBar = true 131 | self.hideToolBar = true 132 | controller.shouldSetupBackgroundView = true 133 | } 134 | if(segue.identifier == "WithinNavigationController") { 135 | self.hideNavigationBar = false 136 | self.hideToolBar = true 137 | } 138 | if(segue.identifier == "WithNavigationAndToolbar") { 139 | self.hideNavigationBar = false 140 | self.hideToolBar = false 141 | } 142 | } 143 | } 144 | 145 | // MARK: Private functions 146 | 147 | private func getIntFromTextfield(_ textfield: UITextField) -> Int { 148 | if let n = self.numberFormatter.number(from: (textfield.text)!) { 149 | return n.intValue 150 | } 151 | return 0 152 | } 153 | 154 | private func getFloatFromTextfield(_ textfield: UITextField) -> CGFloat { 155 | if let n = self.numberFormatter.number(from: (textfield.text)!) { 156 | return CGFloat(truncating: n) 157 | } 158 | return 0 159 | } 160 | 161 | private func stringFromFloat(_ float: CGFloat) -> String { 162 | return String(Int(float)) 163 | } 164 | 165 | } 166 | -------------------------------------------------------------------------------- /HFCardCollectionViewLayout.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 416D2D671E5F113600D8A570 /* HFCardCollectionViewLayout.h in Headers */ = {isa = PBXBuildFile; fileRef = 416D2D651E5F113600D8A570 /* HFCardCollectionViewLayout.h */; settings = {ATTRIBUTES = (Public, ); }; }; 11 | 416D2D711E5F117500D8A570 /* HFCardCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 416D2D6D1E5F117500D8A570 /* HFCardCollectionViewCell.swift */; }; 12 | 416D2D721E5F117500D8A570 /* HFCardCollectionViewLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = 416D2D6E1E5F117500D8A570 /* HFCardCollectionViewLayout.swift */; }; 13 | 416D2D731E5F117500D8A570 /* HFCardCollectionViewLayoutDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 416D2D6F1E5F117500D8A570 /* HFCardCollectionViewLayoutDelegate.swift */; }; 14 | 416D2D741E5F117500D8A570 /* UICollectionViewExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 416D2D701E5F117500D8A570 /* UICollectionViewExtensions.swift */; }; 15 | /* End PBXBuildFile section */ 16 | 17 | /* Begin PBXFileReference section */ 18 | 416D2D621E5F113600D8A570 /* HFCardCollectionViewLayout.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = HFCardCollectionViewLayout.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 19 | 416D2D651E5F113600D8A570 /* HFCardCollectionViewLayout.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = HFCardCollectionViewLayout.h; sourceTree = ""; }; 20 | 416D2D661E5F113600D8A570 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 21 | 416D2D6D1E5F117500D8A570 /* HFCardCollectionViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = HFCardCollectionViewCell.swift; path = Source/HFCardCollectionViewCell.swift; sourceTree = SOURCE_ROOT; }; 22 | 416D2D6E1E5F117500D8A570 /* HFCardCollectionViewLayout.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = HFCardCollectionViewLayout.swift; path = Source/HFCardCollectionViewLayout.swift; sourceTree = SOURCE_ROOT; }; 23 | 416D2D6F1E5F117500D8A570 /* HFCardCollectionViewLayoutDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = HFCardCollectionViewLayoutDelegate.swift; path = Source/HFCardCollectionViewLayoutDelegate.swift; sourceTree = SOURCE_ROOT; }; 24 | 416D2D701E5F117500D8A570 /* UICollectionViewExtensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = UICollectionViewExtensions.swift; path = Source/UICollectionViewExtensions.swift; sourceTree = SOURCE_ROOT; }; 25 | /* End PBXFileReference section */ 26 | 27 | /* Begin PBXFrameworksBuildPhase section */ 28 | 416D2D5E1E5F113600D8A570 /* Frameworks */ = { 29 | isa = PBXFrameworksBuildPhase; 30 | buildActionMask = 2147483647; 31 | files = ( 32 | ); 33 | runOnlyForDeploymentPostprocessing = 0; 34 | }; 35 | /* End PBXFrameworksBuildPhase section */ 36 | 37 | /* Begin PBXGroup section */ 38 | 416D2D581E5F113600D8A570 = { 39 | isa = PBXGroup; 40 | children = ( 41 | 416D2D641E5F113600D8A570 /* HFCardCollectionViewLayout */, 42 | 416D2D631E5F113600D8A570 /* Products */, 43 | ); 44 | sourceTree = ""; 45 | }; 46 | 416D2D631E5F113600D8A570 /* Products */ = { 47 | isa = PBXGroup; 48 | children = ( 49 | 416D2D621E5F113600D8A570 /* HFCardCollectionViewLayout.framework */, 50 | ); 51 | name = Products; 52 | sourceTree = ""; 53 | }; 54 | 416D2D641E5F113600D8A570 /* HFCardCollectionViewLayout */ = { 55 | isa = PBXGroup; 56 | children = ( 57 | 416D2D6D1E5F117500D8A570 /* HFCardCollectionViewCell.swift */, 58 | 416D2D6E1E5F117500D8A570 /* HFCardCollectionViewLayout.swift */, 59 | 416D2D6F1E5F117500D8A570 /* HFCardCollectionViewLayoutDelegate.swift */, 60 | 416D2D701E5F117500D8A570 /* UICollectionViewExtensions.swift */, 61 | 416D2D651E5F113600D8A570 /* HFCardCollectionViewLayout.h */, 62 | 416D2D661E5F113600D8A570 /* Info.plist */, 63 | ); 64 | path = HFCardCollectionViewLayout; 65 | sourceTree = ""; 66 | }; 67 | /* End PBXGroup section */ 68 | 69 | /* Begin PBXHeadersBuildPhase section */ 70 | 416D2D5F1E5F113600D8A570 /* Headers */ = { 71 | isa = PBXHeadersBuildPhase; 72 | buildActionMask = 2147483647; 73 | files = ( 74 | 416D2D671E5F113600D8A570 /* HFCardCollectionViewLayout.h in Headers */, 75 | ); 76 | runOnlyForDeploymentPostprocessing = 0; 77 | }; 78 | /* End PBXHeadersBuildPhase section */ 79 | 80 | /* Begin PBXNativeTarget section */ 81 | 416D2D611E5F113600D8A570 /* HFCardCollectionViewLayout */ = { 82 | isa = PBXNativeTarget; 83 | buildConfigurationList = 416D2D6A1E5F113600D8A570 /* Build configuration list for PBXNativeTarget "HFCardCollectionViewLayout" */; 84 | buildPhases = ( 85 | 416D2D5D1E5F113600D8A570 /* Sources */, 86 | 416D2D5E1E5F113600D8A570 /* Frameworks */, 87 | 416D2D5F1E5F113600D8A570 /* Headers */, 88 | 416D2D601E5F113600D8A570 /* Resources */, 89 | ); 90 | buildRules = ( 91 | ); 92 | dependencies = ( 93 | ); 94 | name = HFCardCollectionViewLayout; 95 | productName = HFCardCollectionViewLayout; 96 | productReference = 416D2D621E5F113600D8A570 /* HFCardCollectionViewLayout.framework */; 97 | productType = "com.apple.product-type.framework"; 98 | }; 99 | /* End PBXNativeTarget section */ 100 | 101 | /* Begin PBXProject section */ 102 | 416D2D591E5F113600D8A570 /* Project object */ = { 103 | isa = PBXProject; 104 | attributes = { 105 | LastUpgradeCheck = 0820; 106 | ORGANIZATIONNAME = "hendrik-frahmann"; 107 | TargetAttributes = { 108 | 416D2D611E5F113600D8A570 = { 109 | CreatedOnToolsVersion = 8.2.1; 110 | LastSwiftMigration = 0820; 111 | ProvisioningStyle = Automatic; 112 | }; 113 | }; 114 | }; 115 | buildConfigurationList = 416D2D5C1E5F113600D8A570 /* Build configuration list for PBXProject "HFCardCollectionViewLayout" */; 116 | compatibilityVersion = "Xcode 3.2"; 117 | developmentRegion = English; 118 | hasScannedForEncodings = 0; 119 | knownRegions = ( 120 | en, 121 | ); 122 | mainGroup = 416D2D581E5F113600D8A570; 123 | productRefGroup = 416D2D631E5F113600D8A570 /* Products */; 124 | projectDirPath = ""; 125 | projectRoot = ""; 126 | targets = ( 127 | 416D2D611E5F113600D8A570 /* HFCardCollectionViewLayout */, 128 | ); 129 | }; 130 | /* End PBXProject section */ 131 | 132 | /* Begin PBXResourcesBuildPhase section */ 133 | 416D2D601E5F113600D8A570 /* Resources */ = { 134 | isa = PBXResourcesBuildPhase; 135 | buildActionMask = 2147483647; 136 | files = ( 137 | ); 138 | runOnlyForDeploymentPostprocessing = 0; 139 | }; 140 | /* End PBXResourcesBuildPhase section */ 141 | 142 | /* Begin PBXSourcesBuildPhase section */ 143 | 416D2D5D1E5F113600D8A570 /* Sources */ = { 144 | isa = PBXSourcesBuildPhase; 145 | buildActionMask = 2147483647; 146 | files = ( 147 | 416D2D741E5F117500D8A570 /* UICollectionViewExtensions.swift in Sources */, 148 | 416D2D721E5F117500D8A570 /* HFCardCollectionViewLayout.swift in Sources */, 149 | 416D2D711E5F117500D8A570 /* HFCardCollectionViewCell.swift in Sources */, 150 | 416D2D731E5F117500D8A570 /* HFCardCollectionViewLayoutDelegate.swift in Sources */, 151 | ); 152 | runOnlyForDeploymentPostprocessing = 0; 153 | }; 154 | /* End PBXSourcesBuildPhase section */ 155 | 156 | /* Begin XCBuildConfiguration section */ 157 | 416D2D681E5F113600D8A570 /* Debug */ = { 158 | isa = XCBuildConfiguration; 159 | buildSettings = { 160 | ALWAYS_SEARCH_USER_PATHS = NO; 161 | CLANG_ANALYZER_NONNULL = YES; 162 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 163 | CLANG_CXX_LIBRARY = "libc++"; 164 | CLANG_ENABLE_MODULES = YES; 165 | CLANG_ENABLE_OBJC_ARC = YES; 166 | CLANG_WARN_BOOL_CONVERSION = YES; 167 | CLANG_WARN_CONSTANT_CONVERSION = YES; 168 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 169 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 170 | CLANG_WARN_EMPTY_BODY = YES; 171 | CLANG_WARN_ENUM_CONVERSION = YES; 172 | CLANG_WARN_INFINITE_RECURSION = YES; 173 | CLANG_WARN_INT_CONVERSION = YES; 174 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 175 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 176 | CLANG_WARN_UNREACHABLE_CODE = YES; 177 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 178 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 179 | COPY_PHASE_STRIP = NO; 180 | CURRENT_PROJECT_VERSION = 1; 181 | DEBUG_INFORMATION_FORMAT = dwarf; 182 | ENABLE_STRICT_OBJC_MSGSEND = YES; 183 | ENABLE_TESTABILITY = YES; 184 | GCC_C_LANGUAGE_STANDARD = gnu99; 185 | GCC_DYNAMIC_NO_PIC = NO; 186 | GCC_NO_COMMON_BLOCKS = YES; 187 | GCC_OPTIMIZATION_LEVEL = 0; 188 | GCC_PREPROCESSOR_DEFINITIONS = ( 189 | "DEBUG=1", 190 | "$(inherited)", 191 | ); 192 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 193 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 194 | GCC_WARN_UNDECLARED_SELECTOR = YES; 195 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 196 | GCC_WARN_UNUSED_FUNCTION = YES; 197 | GCC_WARN_UNUSED_VARIABLE = YES; 198 | IPHONEOS_DEPLOYMENT_TARGET = 9.0; 199 | MTL_ENABLE_DEBUG_INFO = YES; 200 | ONLY_ACTIVE_ARCH = YES; 201 | SDKROOT = iphoneos; 202 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 203 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 204 | TARGETED_DEVICE_FAMILY = "1,2"; 205 | VERSIONING_SYSTEM = "apple-generic"; 206 | VERSION_INFO_PREFIX = ""; 207 | }; 208 | name = Debug; 209 | }; 210 | 416D2D691E5F113600D8A570 /* Release */ = { 211 | isa = XCBuildConfiguration; 212 | buildSettings = { 213 | ALWAYS_SEARCH_USER_PATHS = NO; 214 | CLANG_ANALYZER_NONNULL = YES; 215 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 216 | CLANG_CXX_LIBRARY = "libc++"; 217 | CLANG_ENABLE_MODULES = YES; 218 | CLANG_ENABLE_OBJC_ARC = YES; 219 | CLANG_WARN_BOOL_CONVERSION = YES; 220 | CLANG_WARN_CONSTANT_CONVERSION = YES; 221 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 222 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 223 | CLANG_WARN_EMPTY_BODY = YES; 224 | CLANG_WARN_ENUM_CONVERSION = YES; 225 | CLANG_WARN_INFINITE_RECURSION = YES; 226 | CLANG_WARN_INT_CONVERSION = YES; 227 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 228 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 229 | CLANG_WARN_UNREACHABLE_CODE = YES; 230 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 231 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 232 | COPY_PHASE_STRIP = NO; 233 | CURRENT_PROJECT_VERSION = 1; 234 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 235 | ENABLE_NS_ASSERTIONS = NO; 236 | ENABLE_STRICT_OBJC_MSGSEND = YES; 237 | GCC_C_LANGUAGE_STANDARD = gnu99; 238 | GCC_NO_COMMON_BLOCKS = YES; 239 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 240 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 241 | GCC_WARN_UNDECLARED_SELECTOR = YES; 242 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 243 | GCC_WARN_UNUSED_FUNCTION = YES; 244 | GCC_WARN_UNUSED_VARIABLE = YES; 245 | IPHONEOS_DEPLOYMENT_TARGET = 9.0; 246 | MTL_ENABLE_DEBUG_INFO = NO; 247 | SDKROOT = iphoneos; 248 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 249 | TARGETED_DEVICE_FAMILY = "1,2"; 250 | VALIDATE_PRODUCT = YES; 251 | VERSIONING_SYSTEM = "apple-generic"; 252 | VERSION_INFO_PREFIX = ""; 253 | }; 254 | name = Release; 255 | }; 256 | 416D2D6B1E5F113600D8A570 /* Debug */ = { 257 | isa = XCBuildConfiguration; 258 | buildSettings = { 259 | CLANG_ENABLE_MODULES = YES; 260 | CODE_SIGN_IDENTITY = ""; 261 | DEFINES_MODULE = YES; 262 | DYLIB_COMPATIBILITY_VERSION = 1; 263 | DYLIB_CURRENT_VERSION = 1; 264 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 265 | INFOPLIST_FILE = HFCardCollectionViewLayout/Info.plist; 266 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 267 | IPHONEOS_DEPLOYMENT_TARGET = 9.0; 268 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 269 | PRODUCT_BUNDLE_IDENTIFIER = "com.hendrik-frahmann.HFCardCollectionViewLayout"; 270 | PRODUCT_NAME = "$(TARGET_NAME)"; 271 | SKIP_INSTALL = YES; 272 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 273 | SWIFT_VERSION = 3.0; 274 | }; 275 | name = Debug; 276 | }; 277 | 416D2D6C1E5F113600D8A570 /* Release */ = { 278 | isa = XCBuildConfiguration; 279 | buildSettings = { 280 | CLANG_ENABLE_MODULES = YES; 281 | CODE_SIGN_IDENTITY = ""; 282 | DEFINES_MODULE = YES; 283 | DYLIB_COMPATIBILITY_VERSION = 1; 284 | DYLIB_CURRENT_VERSION = 1; 285 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 286 | INFOPLIST_FILE = HFCardCollectionViewLayout/Info.plist; 287 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 288 | IPHONEOS_DEPLOYMENT_TARGET = 9.0; 289 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 290 | PRODUCT_BUNDLE_IDENTIFIER = "com.hendrik-frahmann.HFCardCollectionViewLayout"; 291 | PRODUCT_NAME = "$(TARGET_NAME)"; 292 | SKIP_INSTALL = YES; 293 | SWIFT_VERSION = 3.0; 294 | }; 295 | name = Release; 296 | }; 297 | /* End XCBuildConfiguration section */ 298 | 299 | /* Begin XCConfigurationList section */ 300 | 416D2D5C1E5F113600D8A570 /* Build configuration list for PBXProject "HFCardCollectionViewLayout" */ = { 301 | isa = XCConfigurationList; 302 | buildConfigurations = ( 303 | 416D2D681E5F113600D8A570 /* Debug */, 304 | 416D2D691E5F113600D8A570 /* Release */, 305 | ); 306 | defaultConfigurationIsVisible = 0; 307 | defaultConfigurationName = Release; 308 | }; 309 | 416D2D6A1E5F113600D8A570 /* Build configuration list for PBXNativeTarget "HFCardCollectionViewLayout" */ = { 310 | isa = XCConfigurationList; 311 | buildConfigurations = ( 312 | 416D2D6B1E5F113600D8A570 /* Debug */, 313 | 416D2D6C1E5F113600D8A570 /* Release */, 314 | ); 315 | defaultConfigurationIsVisible = 0; 316 | defaultConfigurationName = Release; 317 | }; 318 | /* End XCConfigurationList section */ 319 | }; 320 | rootObject = 416D2D591E5F113600D8A570 /* Project object */; 321 | } 322 | -------------------------------------------------------------------------------- /HFCardCollectionViewLayoutExample/HFCardCollectionViewLayoutExample.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 12520E240E28F70E5C9233D1 /* Pods_HFCardCollectionViewLayoutExample.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8BC84C6ADD77ADD52C0B6EEB /* Pods_HFCardCollectionViewLayoutExample.framework */; }; 11 | B1472D191DCA7CDD007464CA /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B1472D181DCA7CDD007464CA /* QuartzCore.framework */; }; 12 | B1472D1B1DCA8BBE007464CA /* ExampleCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1472D1A1DCA8BBE007464CA /* ExampleCollectionViewCell.swift */; }; 13 | EEBBC63A1DC9E19E00BBC1EE /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = EEBBC6391DC9E19E00BBC1EE /* AppDelegate.swift */; }; 14 | EEBBC63C1DC9E19E00BBC1EE /* ExampleViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = EEBBC63B1DC9E19E00BBC1EE /* ExampleViewController.swift */; }; 15 | EEBBC63F1DC9E19E00BBC1EE /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = EEBBC63D1DC9E19E00BBC1EE /* Main.storyboard */; }; 16 | EEBBC6411DC9E19E00BBC1EE /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = EEBBC6401DC9E19E00BBC1EE /* Assets.xcassets */; }; 17 | EEBBC6441DC9E19F00BBC1EE /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = EEBBC6421DC9E19F00BBC1EE /* LaunchScreen.storyboard */; }; 18 | EEBBC64F1DC9E23700BBC1EE /* MenuTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = EEBBC64E1DC9E23700BBC1EE /* MenuTableViewController.swift */; }; 19 | /* End PBXBuildFile section */ 20 | 21 | /* Begin PBXFileReference section */ 22 | 20F29DBCFC8ECCD0C7119C69 /* Pods-HFCardCollectionViewLayoutExample.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-HFCardCollectionViewLayoutExample.debug.xcconfig"; path = "Pods/Target Support Files/Pods-HFCardCollectionViewLayoutExample/Pods-HFCardCollectionViewLayoutExample.debug.xcconfig"; sourceTree = ""; }; 23 | 896D468DC417110E7A811BB6 /* Pods-HFCardCollectionViewLayoutExample.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-HFCardCollectionViewLayoutExample.release.xcconfig"; path = "Pods/Target Support Files/Pods-HFCardCollectionViewLayoutExample/Pods-HFCardCollectionViewLayoutExample.release.xcconfig"; sourceTree = ""; }; 24 | 8BC84C6ADD77ADD52C0B6EEB /* Pods_HFCardCollectionViewLayoutExample.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_HFCardCollectionViewLayoutExample.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 25 | B1472D181DCA7CDD007464CA /* QuartzCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuartzCore.framework; path = System/Library/Frameworks/QuartzCore.framework; sourceTree = SDKROOT; }; 26 | B1472D1A1DCA8BBE007464CA /* ExampleCollectionViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ExampleCollectionViewCell.swift; sourceTree = ""; }; 27 | EEBBC6361DC9E19E00BBC1EE /* HFCardCollectionViewLayoutExample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = HFCardCollectionViewLayoutExample.app; sourceTree = BUILT_PRODUCTS_DIR; }; 28 | EEBBC6391DC9E19E00BBC1EE /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 29 | EEBBC63B1DC9E19E00BBC1EE /* ExampleViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExampleViewController.swift; sourceTree = ""; }; 30 | EEBBC63E1DC9E19E00BBC1EE /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 31 | EEBBC6401DC9E19E00BBC1EE /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 32 | EEBBC6431DC9E19F00BBC1EE /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 33 | EEBBC6451DC9E19F00BBC1EE /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 34 | EEBBC64E1DC9E23700BBC1EE /* MenuTableViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MenuTableViewController.swift; sourceTree = ""; }; 35 | /* End PBXFileReference section */ 36 | 37 | /* Begin PBXFrameworksBuildPhase section */ 38 | EEBBC6331DC9E19E00BBC1EE /* Frameworks */ = { 39 | isa = PBXFrameworksBuildPhase; 40 | buildActionMask = 2147483647; 41 | files = ( 42 | B1472D191DCA7CDD007464CA /* QuartzCore.framework in Frameworks */, 43 | 12520E240E28F70E5C9233D1 /* Pods_HFCardCollectionViewLayoutExample.framework in Frameworks */, 44 | ); 45 | runOnlyForDeploymentPostprocessing = 0; 46 | }; 47 | /* End PBXFrameworksBuildPhase section */ 48 | 49 | /* Begin PBXGroup section */ 50 | A138CA03463F1B14B9C087DE /* Pods */ = { 51 | isa = PBXGroup; 52 | children = ( 53 | 20F29DBCFC8ECCD0C7119C69 /* Pods-HFCardCollectionViewLayoutExample.debug.xcconfig */, 54 | 896D468DC417110E7A811BB6 /* Pods-HFCardCollectionViewLayoutExample.release.xcconfig */, 55 | ); 56 | name = Pods; 57 | sourceTree = ""; 58 | }; 59 | B1472D171DCA7CDD007464CA /* Frameworks */ = { 60 | isa = PBXGroup; 61 | children = ( 62 | B1472D181DCA7CDD007464CA /* QuartzCore.framework */, 63 | 8BC84C6ADD77ADD52C0B6EEB /* Pods_HFCardCollectionViewLayoutExample.framework */, 64 | ); 65 | name = Frameworks; 66 | sourceTree = ""; 67 | }; 68 | EEBBC62D1DC9E19E00BBC1EE = { 69 | isa = PBXGroup; 70 | children = ( 71 | EEBBC6381DC9E19E00BBC1EE /* HFCardCollectionViewLayoutExample */, 72 | EEBBC6371DC9E19E00BBC1EE /* Products */, 73 | B1472D171DCA7CDD007464CA /* Frameworks */, 74 | A138CA03463F1B14B9C087DE /* Pods */, 75 | ); 76 | sourceTree = ""; 77 | }; 78 | EEBBC6371DC9E19E00BBC1EE /* Products */ = { 79 | isa = PBXGroup; 80 | children = ( 81 | EEBBC6361DC9E19E00BBC1EE /* HFCardCollectionViewLayoutExample.app */, 82 | ); 83 | name = Products; 84 | sourceTree = ""; 85 | }; 86 | EEBBC6381DC9E19E00BBC1EE /* HFCardCollectionViewLayoutExample */ = { 87 | isa = PBXGroup; 88 | children = ( 89 | EEBBC6391DC9E19E00BBC1EE /* AppDelegate.swift */, 90 | EEBBC64E1DC9E23700BBC1EE /* MenuTableViewController.swift */, 91 | EEBBC63B1DC9E19E00BBC1EE /* ExampleViewController.swift */, 92 | B1472D1A1DCA8BBE007464CA /* ExampleCollectionViewCell.swift */, 93 | EEBBC63D1DC9E19E00BBC1EE /* Main.storyboard */, 94 | EEBBC6401DC9E19E00BBC1EE /* Assets.xcassets */, 95 | EEBBC6421DC9E19F00BBC1EE /* LaunchScreen.storyboard */, 96 | EEBBC6451DC9E19F00BBC1EE /* Info.plist */, 97 | ); 98 | path = HFCardCollectionViewLayoutExample; 99 | sourceTree = ""; 100 | }; 101 | /* End PBXGroup section */ 102 | 103 | /* Begin PBXNativeTarget section */ 104 | EEBBC6351DC9E19E00BBC1EE /* HFCardCollectionViewLayoutExample */ = { 105 | isa = PBXNativeTarget; 106 | buildConfigurationList = EEBBC6481DC9E19F00BBC1EE /* Build configuration list for PBXNativeTarget "HFCardCollectionViewLayoutExample" */; 107 | buildPhases = ( 108 | DF784A192065786A9B3017F5 /* [CP] Check Pods Manifest.lock */, 109 | EEBBC6321DC9E19E00BBC1EE /* Sources */, 110 | EEBBC6331DC9E19E00BBC1EE /* Frameworks */, 111 | EEBBC6341DC9E19E00BBC1EE /* Resources */, 112 | 906D250FBF2D46B24EB39BA1 /* [CP] Embed Pods Frameworks */, 113 | 9616E71599BA57C3E0C8514A /* [CP] Copy Pods Resources */, 114 | ); 115 | buildRules = ( 116 | ); 117 | dependencies = ( 118 | ); 119 | name = HFCardCollectionViewLayoutExample; 120 | productName = HFCardCollectionViewLayoutExample; 121 | productReference = EEBBC6361DC9E19E00BBC1EE /* HFCardCollectionViewLayoutExample.app */; 122 | productType = "com.apple.product-type.application"; 123 | }; 124 | /* End PBXNativeTarget section */ 125 | 126 | /* Begin PBXProject section */ 127 | EEBBC62E1DC9E19E00BBC1EE /* Project object */ = { 128 | isa = PBXProject; 129 | attributes = { 130 | LastSwiftUpdateCheck = 0810; 131 | LastUpgradeCheck = 0920; 132 | ORGANIZATIONNAME = "Hendrik Frahmann"; 133 | TargetAttributes = { 134 | EEBBC6351DC9E19E00BBC1EE = { 135 | CreatedOnToolsVersion = 8.1; 136 | DevelopmentTeam = TKESRF7W2Y; 137 | LastSwiftMigration = 0920; 138 | ProvisioningStyle = Automatic; 139 | }; 140 | }; 141 | }; 142 | buildConfigurationList = EEBBC6311DC9E19E00BBC1EE /* Build configuration list for PBXProject "HFCardCollectionViewLayoutExample" */; 143 | compatibilityVersion = "Xcode 3.2"; 144 | developmentRegion = English; 145 | hasScannedForEncodings = 0; 146 | knownRegions = ( 147 | en, 148 | Base, 149 | ); 150 | mainGroup = EEBBC62D1DC9E19E00BBC1EE; 151 | productRefGroup = EEBBC6371DC9E19E00BBC1EE /* Products */; 152 | projectDirPath = ""; 153 | projectRoot = ""; 154 | targets = ( 155 | EEBBC6351DC9E19E00BBC1EE /* HFCardCollectionViewLayoutExample */, 156 | ); 157 | }; 158 | /* End PBXProject section */ 159 | 160 | /* Begin PBXResourcesBuildPhase section */ 161 | EEBBC6341DC9E19E00BBC1EE /* Resources */ = { 162 | isa = PBXResourcesBuildPhase; 163 | buildActionMask = 2147483647; 164 | files = ( 165 | EEBBC6441DC9E19F00BBC1EE /* LaunchScreen.storyboard in Resources */, 166 | EEBBC6411DC9E19E00BBC1EE /* Assets.xcassets in Resources */, 167 | EEBBC63F1DC9E19E00BBC1EE /* Main.storyboard in Resources */, 168 | ); 169 | runOnlyForDeploymentPostprocessing = 0; 170 | }; 171 | /* End PBXResourcesBuildPhase section */ 172 | 173 | /* Begin PBXShellScriptBuildPhase section */ 174 | 906D250FBF2D46B24EB39BA1 /* [CP] Embed Pods Frameworks */ = { 175 | isa = PBXShellScriptBuildPhase; 176 | buildActionMask = 2147483647; 177 | files = ( 178 | ); 179 | inputPaths = ( 180 | ); 181 | name = "[CP] Embed Pods Frameworks"; 182 | outputPaths = ( 183 | ); 184 | runOnlyForDeploymentPostprocessing = 0; 185 | shellPath = /bin/sh; 186 | shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-HFCardCollectionViewLayoutExample/Pods-HFCardCollectionViewLayoutExample-frameworks.sh\"\n"; 187 | showEnvVarsInLog = 0; 188 | }; 189 | 9616E71599BA57C3E0C8514A /* [CP] Copy Pods Resources */ = { 190 | isa = PBXShellScriptBuildPhase; 191 | buildActionMask = 2147483647; 192 | files = ( 193 | ); 194 | inputPaths = ( 195 | ); 196 | name = "[CP] Copy Pods Resources"; 197 | outputPaths = ( 198 | ); 199 | runOnlyForDeploymentPostprocessing = 0; 200 | shellPath = /bin/sh; 201 | shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-HFCardCollectionViewLayoutExample/Pods-HFCardCollectionViewLayoutExample-resources.sh\"\n"; 202 | showEnvVarsInLog = 0; 203 | }; 204 | DF784A192065786A9B3017F5 /* [CP] Check Pods Manifest.lock */ = { 205 | isa = PBXShellScriptBuildPhase; 206 | buildActionMask = 2147483647; 207 | files = ( 208 | ); 209 | inputPaths = ( 210 | ); 211 | name = "[CP] Check Pods Manifest.lock"; 212 | outputPaths = ( 213 | ); 214 | runOnlyForDeploymentPostprocessing = 0; 215 | shellPath = /bin/sh; 216 | shellScript = "diff \"${PODS_ROOT}/../Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n"; 217 | showEnvVarsInLog = 0; 218 | }; 219 | /* End PBXShellScriptBuildPhase section */ 220 | 221 | /* Begin PBXSourcesBuildPhase section */ 222 | EEBBC6321DC9E19E00BBC1EE /* Sources */ = { 223 | isa = PBXSourcesBuildPhase; 224 | buildActionMask = 2147483647; 225 | files = ( 226 | EEBBC64F1DC9E23700BBC1EE /* MenuTableViewController.swift in Sources */, 227 | B1472D1B1DCA8BBE007464CA /* ExampleCollectionViewCell.swift in Sources */, 228 | EEBBC63C1DC9E19E00BBC1EE /* ExampleViewController.swift in Sources */, 229 | EEBBC63A1DC9E19E00BBC1EE /* AppDelegate.swift in Sources */, 230 | ); 231 | runOnlyForDeploymentPostprocessing = 0; 232 | }; 233 | /* End PBXSourcesBuildPhase section */ 234 | 235 | /* Begin PBXVariantGroup section */ 236 | EEBBC63D1DC9E19E00BBC1EE /* Main.storyboard */ = { 237 | isa = PBXVariantGroup; 238 | children = ( 239 | EEBBC63E1DC9E19E00BBC1EE /* Base */, 240 | ); 241 | name = Main.storyboard; 242 | sourceTree = ""; 243 | }; 244 | EEBBC6421DC9E19F00BBC1EE /* LaunchScreen.storyboard */ = { 245 | isa = PBXVariantGroup; 246 | children = ( 247 | EEBBC6431DC9E19F00BBC1EE /* Base */, 248 | ); 249 | name = LaunchScreen.storyboard; 250 | sourceTree = ""; 251 | }; 252 | /* End PBXVariantGroup section */ 253 | 254 | /* Begin XCBuildConfiguration section */ 255 | EEBBC6461DC9E19F00BBC1EE /* Debug */ = { 256 | isa = XCBuildConfiguration; 257 | buildSettings = { 258 | ALWAYS_SEARCH_USER_PATHS = NO; 259 | CLANG_ANALYZER_NONNULL = YES; 260 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 261 | CLANG_CXX_LIBRARY = "libc++"; 262 | CLANG_ENABLE_MODULES = YES; 263 | CLANG_ENABLE_OBJC_ARC = YES; 264 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 265 | CLANG_WARN_BOOL_CONVERSION = YES; 266 | CLANG_WARN_COMMA = YES; 267 | CLANG_WARN_CONSTANT_CONVERSION = YES; 268 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 269 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 270 | CLANG_WARN_EMPTY_BODY = YES; 271 | CLANG_WARN_ENUM_CONVERSION = YES; 272 | CLANG_WARN_INFINITE_RECURSION = YES; 273 | CLANG_WARN_INT_CONVERSION = YES; 274 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 275 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 276 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 277 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 278 | CLANG_WARN_STRICT_PROTOTYPES = YES; 279 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 280 | CLANG_WARN_SUSPICIOUS_MOVES = YES; 281 | CLANG_WARN_UNREACHABLE_CODE = YES; 282 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 283 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 284 | COPY_PHASE_STRIP = NO; 285 | DEBUG_INFORMATION_FORMAT = dwarf; 286 | ENABLE_STRICT_OBJC_MSGSEND = YES; 287 | ENABLE_TESTABILITY = YES; 288 | GCC_C_LANGUAGE_STANDARD = gnu99; 289 | GCC_DYNAMIC_NO_PIC = NO; 290 | GCC_NO_COMMON_BLOCKS = YES; 291 | GCC_OPTIMIZATION_LEVEL = 0; 292 | GCC_PREPROCESSOR_DEFINITIONS = ( 293 | "DEBUG=1", 294 | "$(inherited)", 295 | ); 296 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 297 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 298 | GCC_WARN_UNDECLARED_SELECTOR = YES; 299 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 300 | GCC_WARN_UNUSED_FUNCTION = YES; 301 | GCC_WARN_UNUSED_VARIABLE = YES; 302 | IPHONEOS_DEPLOYMENT_TARGET = 9.0; 303 | MTL_ENABLE_DEBUG_INFO = YES; 304 | ONLY_ACTIVE_ARCH = YES; 305 | SDKROOT = iphoneos; 306 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 307 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 308 | TARGETED_DEVICE_FAMILY = "1,2"; 309 | }; 310 | name = Debug; 311 | }; 312 | EEBBC6471DC9E19F00BBC1EE /* Release */ = { 313 | isa = XCBuildConfiguration; 314 | buildSettings = { 315 | ALWAYS_SEARCH_USER_PATHS = NO; 316 | CLANG_ANALYZER_NONNULL = YES; 317 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 318 | CLANG_CXX_LIBRARY = "libc++"; 319 | CLANG_ENABLE_MODULES = YES; 320 | CLANG_ENABLE_OBJC_ARC = YES; 321 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 322 | CLANG_WARN_BOOL_CONVERSION = YES; 323 | CLANG_WARN_COMMA = YES; 324 | CLANG_WARN_CONSTANT_CONVERSION = YES; 325 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 326 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 327 | CLANG_WARN_EMPTY_BODY = YES; 328 | CLANG_WARN_ENUM_CONVERSION = YES; 329 | CLANG_WARN_INFINITE_RECURSION = YES; 330 | CLANG_WARN_INT_CONVERSION = YES; 331 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 332 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 333 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 334 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 335 | CLANG_WARN_STRICT_PROTOTYPES = YES; 336 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 337 | CLANG_WARN_SUSPICIOUS_MOVES = YES; 338 | CLANG_WARN_UNREACHABLE_CODE = YES; 339 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 340 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 341 | COPY_PHASE_STRIP = NO; 342 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 343 | ENABLE_NS_ASSERTIONS = NO; 344 | ENABLE_STRICT_OBJC_MSGSEND = YES; 345 | GCC_C_LANGUAGE_STANDARD = gnu99; 346 | GCC_NO_COMMON_BLOCKS = YES; 347 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 348 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 349 | GCC_WARN_UNDECLARED_SELECTOR = YES; 350 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 351 | GCC_WARN_UNUSED_FUNCTION = YES; 352 | GCC_WARN_UNUSED_VARIABLE = YES; 353 | IPHONEOS_DEPLOYMENT_TARGET = 9.0; 354 | MTL_ENABLE_DEBUG_INFO = NO; 355 | SDKROOT = iphoneos; 356 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 357 | TARGETED_DEVICE_FAMILY = "1,2"; 358 | VALIDATE_PRODUCT = YES; 359 | }; 360 | name = Release; 361 | }; 362 | EEBBC6491DC9E19F00BBC1EE /* Debug */ = { 363 | isa = XCBuildConfiguration; 364 | baseConfigurationReference = 20F29DBCFC8ECCD0C7119C69 /* Pods-HFCardCollectionViewLayoutExample.debug.xcconfig */; 365 | buildSettings = { 366 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 367 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 368 | CODE_SIGN_STYLE = Automatic; 369 | DEVELOPMENT_TEAM = TKESRF7W2Y; 370 | INFOPLIST_FILE = HFCardCollectionViewLayoutExample/Info.plist; 371 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 372 | PRODUCT_BUNDLE_IDENTIFIER = "de.hendrik-frahmann.HFCardCollectionViewLayoutExample"; 373 | PRODUCT_NAME = "$(TARGET_NAME)"; 374 | PROVISIONING_PROFILE_SPECIFIER = ""; 375 | SWIFT_SWIFT3_OBJC_INFERENCE = On; 376 | SWIFT_VERSION = 4.0; 377 | }; 378 | name = Debug; 379 | }; 380 | EEBBC64A1DC9E19F00BBC1EE /* Release */ = { 381 | isa = XCBuildConfiguration; 382 | baseConfigurationReference = 896D468DC417110E7A811BB6 /* Pods-HFCardCollectionViewLayoutExample.release.xcconfig */; 383 | buildSettings = { 384 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 385 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 386 | CODE_SIGN_STYLE = Automatic; 387 | DEVELOPMENT_TEAM = TKESRF7W2Y; 388 | INFOPLIST_FILE = HFCardCollectionViewLayoutExample/Info.plist; 389 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 390 | PRODUCT_BUNDLE_IDENTIFIER = "de.hendrik-frahmann.HFCardCollectionViewLayoutExample"; 391 | PRODUCT_NAME = "$(TARGET_NAME)"; 392 | PROVISIONING_PROFILE_SPECIFIER = ""; 393 | SWIFT_SWIFT3_OBJC_INFERENCE = On; 394 | SWIFT_VERSION = 4.0; 395 | }; 396 | name = Release; 397 | }; 398 | /* End XCBuildConfiguration section */ 399 | 400 | /* Begin XCConfigurationList section */ 401 | EEBBC6311DC9E19E00BBC1EE /* Build configuration list for PBXProject "HFCardCollectionViewLayoutExample" */ = { 402 | isa = XCConfigurationList; 403 | buildConfigurations = ( 404 | EEBBC6461DC9E19F00BBC1EE /* Debug */, 405 | EEBBC6471DC9E19F00BBC1EE /* Release */, 406 | ); 407 | defaultConfigurationIsVisible = 0; 408 | defaultConfigurationName = Release; 409 | }; 410 | EEBBC6481DC9E19F00BBC1EE /* Build configuration list for PBXNativeTarget "HFCardCollectionViewLayoutExample" */ = { 411 | isa = XCConfigurationList; 412 | buildConfigurations = ( 413 | EEBBC6491DC9E19F00BBC1EE /* Debug */, 414 | EEBBC64A1DC9E19F00BBC1EE /* Release */, 415 | ); 416 | defaultConfigurationIsVisible = 0; 417 | defaultConfigurationName = Release; 418 | }; 419 | /* End XCConfigurationList section */ 420 | }; 421 | rootObject = EEBBC62E1DC9E19E00BBC1EE /* Project object */; 422 | } 423 | -------------------------------------------------------------------------------- /HFCardCollectionViewLayoutExample/Pods/Pods.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 072CA10A611A32FF1BD5A97A921B24D9 /* HFCardCollectionViewLayout-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = FAC687B01081814BEFEA88991197B0BC /* HFCardCollectionViewLayout-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; }; 11 | 3CE3C81FFEFE5F52C3CC15FB5684F101 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CBB3DE36805AF21409EC968A9691732F /* Foundation.framework */; }; 12 | 790F98919A98AB3E2AE7D5B3FD41A905 /* HFCardCollectionViewLayoutDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = A16A63496E99558B4CA6B9D180806AB9 /* HFCardCollectionViewLayoutDelegate.swift */; }; 13 | 7A6E27CC77BF07F42DB5DA9DF2990BA6 /* HFCardCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D41BF607B2DDA5A8DD7AE50E6E83165C /* HFCardCollectionViewCell.swift */; }; 14 | 7F63908EBEA5EE8B1B984B1B5CEB46E1 /* Pods-HFCardCollectionViewLayoutExample-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = 046CED6827F8F476ACC86DD5468CF257 /* Pods-HFCardCollectionViewLayoutExample-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; }; 15 | 8EFA7FE72F4E97A43C553E2BC4C123B4 /* HFCardCollectionViewLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C3E00731B0E31D62A8489277EFEEB67 /* HFCardCollectionViewLayout.swift */; }; 16 | A5E3B8108717879F1E175A7882970651 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CBB3DE36805AF21409EC968A9691732F /* Foundation.framework */; }; 17 | B1BF26FC1E90FD810077D75E /* HFCardCollectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1BF26FB1E90FD810077D75E /* HFCardCollectionView.swift */; }; 18 | CAB4E8075BB566BEB9DEFC2BB0AE1A44 /* HFCardCollectionViewLayout-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = B42049707F2D0F6158127900CD948320 /* HFCardCollectionViewLayout-dummy.m */; }; 19 | F493F895289894F832FE0C88F27A0B15 /* Pods-HFCardCollectionViewLayoutExample-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = 4AAF622BE70472DDFBC8E2489D38DD90 /* Pods-HFCardCollectionViewLayoutExample-dummy.m */; }; 20 | /* End PBXBuildFile section */ 21 | 22 | /* Begin PBXContainerItemProxy section */ 23 | B55B90381C05AD68454AFA5C2682ABA2 /* PBXContainerItemProxy */ = { 24 | isa = PBXContainerItemProxy; 25 | containerPortal = D41D8CD98F00B204E9800998ECF8427E /* Project object */; 26 | proxyType = 1; 27 | remoteGlobalIDString = E1C2F6043B98ABA5417B87102C667275; 28 | remoteInfo = HFCardCollectionViewLayout; 29 | }; 30 | /* End PBXContainerItemProxy section */ 31 | 32 | /* Begin PBXFileReference section */ 33 | 046CED6827F8F476ACC86DD5468CF257 /* Pods-HFCardCollectionViewLayoutExample-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "Pods-HFCardCollectionViewLayoutExample-umbrella.h"; sourceTree = ""; }; 34 | 07D657C1C371416A2A0C47DEFBCAEC04 /* Pods-HFCardCollectionViewLayoutExample-frameworks.sh */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.script.sh; path = "Pods-HFCardCollectionViewLayoutExample-frameworks.sh"; sourceTree = ""; }; 35 | 1C3E00731B0E31D62A8489277EFEEB67 /* HFCardCollectionViewLayout.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = HFCardCollectionViewLayout.swift; sourceTree = ""; }; 36 | 2665BF22FA509A9D04D472D47F29BBDB /* Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 37 | 2871AA1D8F3B5DB60D1FC7F51114E01C /* HFCardCollectionViewLayout.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = HFCardCollectionViewLayout.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 38 | 2F0C5D5262152E6CAC8FBF879E31EDCE /* Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 39 | 3B08DABD74D6D5378A5DDCE853030F53 /* HFCardCollectionViewLayout.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = HFCardCollectionViewLayout.xcconfig; sourceTree = ""; }; 40 | 4AAF622BE70472DDFBC8E2489D38DD90 /* Pods-HFCardCollectionViewLayoutExample-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "Pods-HFCardCollectionViewLayoutExample-dummy.m"; sourceTree = ""; }; 41 | 54312038EC8BFFC3303E3C93C780CEFD /* HFCardCollectionViewLayout-prefix.pch */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "HFCardCollectionViewLayout-prefix.pch"; sourceTree = ""; }; 42 | 89EB551874C33A437A4B3D8A20764101 /* Pods-HFCardCollectionViewLayoutExample.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = "Pods-HFCardCollectionViewLayoutExample.release.xcconfig"; sourceTree = ""; }; 43 | 919E129C9C6AEDB0ADC7AD498746062A /* HFCardCollectionViewLayout.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = "sourcecode.module-map"; path = HFCardCollectionViewLayout.modulemap; sourceTree = ""; }; 44 | 93A4A3777CF96A4AAC1D13BA6DCCEA73 /* Podfile */ = {isa = PBXFileReference; explicitFileType = text.script.ruby; includeInIndex = 1; name = Podfile; path = ../Podfile; sourceTree = SOURCE_ROOT; xcLanguageSpecificationIdentifier = xcode.lang.ruby; }; 45 | A16A63496E99558B4CA6B9D180806AB9 /* HFCardCollectionViewLayoutDelegate.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = HFCardCollectionViewLayoutDelegate.swift; sourceTree = ""; }; 46 | B1258B00B7C713CD4DB0AD7B276F8F50 /* Pods-HFCardCollectionViewLayoutExample-resources.sh */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.script.sh; path = "Pods-HFCardCollectionViewLayoutExample-resources.sh"; sourceTree = ""; }; 47 | B1BF26FB1E90FD810077D75E /* HFCardCollectionView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HFCardCollectionView.swift; sourceTree = ""; }; 48 | B42049707F2D0F6158127900CD948320 /* HFCardCollectionViewLayout-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "HFCardCollectionViewLayout-dummy.m"; sourceTree = ""; }; 49 | B5F36D8799F60A8D7E92CEC996CF92EB /* Pods-HFCardCollectionViewLayoutExample.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = "Pods-HFCardCollectionViewLayoutExample.debug.xcconfig"; sourceTree = ""; }; 50 | C67398A2C41620ED4BB8215FBA4D8EFE /* Pods-HFCardCollectionViewLayoutExample-acknowledgements.markdown */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; path = "Pods-HFCardCollectionViewLayoutExample-acknowledgements.markdown"; sourceTree = ""; }; 51 | CBB3DE36805AF21409EC968A9691732F /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS10.0.sdk/System/Library/Frameworks/Foundation.framework; sourceTree = DEVELOPER_DIR; }; 52 | D41BF607B2DDA5A8DD7AE50E6E83165C /* HFCardCollectionViewCell.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = HFCardCollectionViewCell.swift; sourceTree = ""; }; 53 | D4DDF1440510E0A645063459BBF342BC /* Pods-HFCardCollectionViewLayoutExample-acknowledgements.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = "Pods-HFCardCollectionViewLayoutExample-acknowledgements.plist"; sourceTree = ""; }; 54 | F11EAAD3F1CFFDD7D706ACA402A1C371 /* Pods_HFCardCollectionViewLayoutExample.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_HFCardCollectionViewLayoutExample.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 55 | FAC687B01081814BEFEA88991197B0BC /* HFCardCollectionViewLayout-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "HFCardCollectionViewLayout-umbrella.h"; sourceTree = ""; }; 56 | FFD465A5DC9B5ACE385F3591BD90A298 /* Pods-HFCardCollectionViewLayoutExample.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = "sourcecode.module-map"; path = "Pods-HFCardCollectionViewLayoutExample.modulemap"; sourceTree = ""; }; 57 | /* End PBXFileReference section */ 58 | 59 | /* Begin PBXFrameworksBuildPhase section */ 60 | D5F0304732E1A375415908F1B6AC9FEE /* Frameworks */ = { 61 | isa = PBXFrameworksBuildPhase; 62 | buildActionMask = 2147483647; 63 | files = ( 64 | 3CE3C81FFEFE5F52C3CC15FB5684F101 /* Foundation.framework in Frameworks */, 65 | ); 66 | runOnlyForDeploymentPostprocessing = 0; 67 | }; 68 | D8B57CD8DCCF548A95047D16F3B2EC2D /* Frameworks */ = { 69 | isa = PBXFrameworksBuildPhase; 70 | buildActionMask = 2147483647; 71 | files = ( 72 | A5E3B8108717879F1E175A7882970651 /* Foundation.framework in Frameworks */, 73 | ); 74 | runOnlyForDeploymentPostprocessing = 0; 75 | }; 76 | /* End PBXFrameworksBuildPhase section */ 77 | 78 | /* Begin PBXGroup section */ 79 | 29DCA1BD5EA65F6AABF1763FF25A5265 /* Pods-HFCardCollectionViewLayoutExample */ = { 80 | isa = PBXGroup; 81 | children = ( 82 | 2665BF22FA509A9D04D472D47F29BBDB /* Info.plist */, 83 | FFD465A5DC9B5ACE385F3591BD90A298 /* Pods-HFCardCollectionViewLayoutExample.modulemap */, 84 | C67398A2C41620ED4BB8215FBA4D8EFE /* Pods-HFCardCollectionViewLayoutExample-acknowledgements.markdown */, 85 | D4DDF1440510E0A645063459BBF342BC /* Pods-HFCardCollectionViewLayoutExample-acknowledgements.plist */, 86 | 4AAF622BE70472DDFBC8E2489D38DD90 /* Pods-HFCardCollectionViewLayoutExample-dummy.m */, 87 | 07D657C1C371416A2A0C47DEFBCAEC04 /* Pods-HFCardCollectionViewLayoutExample-frameworks.sh */, 88 | B1258B00B7C713CD4DB0AD7B276F8F50 /* Pods-HFCardCollectionViewLayoutExample-resources.sh */, 89 | 046CED6827F8F476ACC86DD5468CF257 /* Pods-HFCardCollectionViewLayoutExample-umbrella.h */, 90 | B5F36D8799F60A8D7E92CEC996CF92EB /* Pods-HFCardCollectionViewLayoutExample.debug.xcconfig */, 91 | 89EB551874C33A437A4B3D8A20764101 /* Pods-HFCardCollectionViewLayoutExample.release.xcconfig */, 92 | ); 93 | name = "Pods-HFCardCollectionViewLayoutExample"; 94 | path = "Target Support Files/Pods-HFCardCollectionViewLayoutExample"; 95 | sourceTree = ""; 96 | }; 97 | 384F295B9B7C16999BBC2531611857C1 /* Support Files */ = { 98 | isa = PBXGroup; 99 | children = ( 100 | 919E129C9C6AEDB0ADC7AD498746062A /* HFCardCollectionViewLayout.modulemap */, 101 | 3B08DABD74D6D5378A5DDCE853030F53 /* HFCardCollectionViewLayout.xcconfig */, 102 | B42049707F2D0F6158127900CD948320 /* HFCardCollectionViewLayout-dummy.m */, 103 | 54312038EC8BFFC3303E3C93C780CEFD /* HFCardCollectionViewLayout-prefix.pch */, 104 | FAC687B01081814BEFEA88991197B0BC /* HFCardCollectionViewLayout-umbrella.h */, 105 | 2F0C5D5262152E6CAC8FBF879E31EDCE /* Info.plist */, 106 | ); 107 | name = "Support Files"; 108 | path = "HFCardCollectionViewLayoutExample/Pods/Target Support Files/HFCardCollectionViewLayout"; 109 | sourceTree = ""; 110 | }; 111 | 3FF3C2B28A3EE6A89FBA891598AF5508 /* HFCardCollectionViewLayout */ = { 112 | isa = PBXGroup; 113 | children = ( 114 | 90DA7C0E383D971F85355B2CDE812ADE /* Source */, 115 | 384F295B9B7C16999BBC2531611857C1 /* Support Files */, 116 | ); 117 | name = HFCardCollectionViewLayout; 118 | path = ../..; 119 | sourceTree = ""; 120 | }; 121 | 4FFE99151BD282E539E9002587D70D0A /* Development Pods */ = { 122 | isa = PBXGroup; 123 | children = ( 124 | 3FF3C2B28A3EE6A89FBA891598AF5508 /* HFCardCollectionViewLayout */, 125 | ); 126 | name = "Development Pods"; 127 | sourceTree = ""; 128 | }; 129 | 7531C8F8DE19F1AA3C8A7AC97A91DC29 /* iOS */ = { 130 | isa = PBXGroup; 131 | children = ( 132 | CBB3DE36805AF21409EC968A9691732F /* Foundation.framework */, 133 | ); 134 | name = iOS; 135 | sourceTree = ""; 136 | }; 137 | 7DB346D0F39D3F0E887471402A8071AB = { 138 | isa = PBXGroup; 139 | children = ( 140 | 93A4A3777CF96A4AAC1D13BA6DCCEA73 /* Podfile */, 141 | 4FFE99151BD282E539E9002587D70D0A /* Development Pods */, 142 | BC3CA7F9E30CC8F7E2DD044DD34432FC /* Frameworks */, 143 | E11057D728DF1A0CA614502392CB04B8 /* Products */, 144 | E1C4EBB06D13D49B160F339B6DDBECA0 /* Targets Support Files */, 145 | ); 146 | sourceTree = ""; 147 | }; 148 | 90DA7C0E383D971F85355B2CDE812ADE /* Source */ = { 149 | isa = PBXGroup; 150 | children = ( 151 | D41BF607B2DDA5A8DD7AE50E6E83165C /* HFCardCollectionViewCell.swift */, 152 | 1C3E00731B0E31D62A8489277EFEEB67 /* HFCardCollectionViewLayout.swift */, 153 | A16A63496E99558B4CA6B9D180806AB9 /* HFCardCollectionViewLayoutDelegate.swift */, 154 | B1BF26FB1E90FD810077D75E /* HFCardCollectionView.swift */, 155 | ); 156 | path = Source; 157 | sourceTree = ""; 158 | }; 159 | BC3CA7F9E30CC8F7E2DD044DD34432FC /* Frameworks */ = { 160 | isa = PBXGroup; 161 | children = ( 162 | 7531C8F8DE19F1AA3C8A7AC97A91DC29 /* iOS */, 163 | ); 164 | name = Frameworks; 165 | sourceTree = ""; 166 | }; 167 | E11057D728DF1A0CA614502392CB04B8 /* Products */ = { 168 | isa = PBXGroup; 169 | children = ( 170 | 2871AA1D8F3B5DB60D1FC7F51114E01C /* HFCardCollectionViewLayout.framework */, 171 | F11EAAD3F1CFFDD7D706ACA402A1C371 /* Pods_HFCardCollectionViewLayoutExample.framework */, 172 | ); 173 | name = Products; 174 | sourceTree = ""; 175 | }; 176 | E1C4EBB06D13D49B160F339B6DDBECA0 /* Targets Support Files */ = { 177 | isa = PBXGroup; 178 | children = ( 179 | 29DCA1BD5EA65F6AABF1763FF25A5265 /* Pods-HFCardCollectionViewLayoutExample */, 180 | ); 181 | name = "Targets Support Files"; 182 | sourceTree = ""; 183 | }; 184 | /* End PBXGroup section */ 185 | 186 | /* Begin PBXHeadersBuildPhase section */ 187 | 7E51A2A926EE7949AAE4FD1EA1AC105D /* Headers */ = { 188 | isa = PBXHeadersBuildPhase; 189 | buildActionMask = 2147483647; 190 | files = ( 191 | 072CA10A611A32FF1BD5A97A921B24D9 /* HFCardCollectionViewLayout-umbrella.h in Headers */, 192 | ); 193 | runOnlyForDeploymentPostprocessing = 0; 194 | }; 195 | D61D6C4F7E5AC15BD599A447CA8055CE /* Headers */ = { 196 | isa = PBXHeadersBuildPhase; 197 | buildActionMask = 2147483647; 198 | files = ( 199 | 7F63908EBEA5EE8B1B984B1B5CEB46E1 /* Pods-HFCardCollectionViewLayoutExample-umbrella.h in Headers */, 200 | ); 201 | runOnlyForDeploymentPostprocessing = 0; 202 | }; 203 | /* End PBXHeadersBuildPhase section */ 204 | 205 | /* Begin PBXNativeTarget section */ 206 | 1D9676DD5B8F6AABC6C66730FC85E322 /* Pods-HFCardCollectionViewLayoutExample */ = { 207 | isa = PBXNativeTarget; 208 | buildConfigurationList = 7B1C1515171E534D7DD08A568F6263CE /* Build configuration list for PBXNativeTarget "Pods-HFCardCollectionViewLayoutExample" */; 209 | buildPhases = ( 210 | 047C28A02840759B5C22A4609D896D39 /* Sources */, 211 | D5F0304732E1A375415908F1B6AC9FEE /* Frameworks */, 212 | D61D6C4F7E5AC15BD599A447CA8055CE /* Headers */, 213 | ); 214 | buildRules = ( 215 | ); 216 | dependencies = ( 217 | 1CC8B9C75E5A9B2B2A6B1E27AB954823 /* PBXTargetDependency */, 218 | ); 219 | name = "Pods-HFCardCollectionViewLayoutExample"; 220 | productName = "Pods-HFCardCollectionViewLayoutExample"; 221 | productReference = F11EAAD3F1CFFDD7D706ACA402A1C371 /* Pods_HFCardCollectionViewLayoutExample.framework */; 222 | productType = "com.apple.product-type.framework"; 223 | }; 224 | E1C2F6043B98ABA5417B87102C667275 /* HFCardCollectionViewLayout */ = { 225 | isa = PBXNativeTarget; 226 | buildConfigurationList = C000741956A955D75290C78B8C062923 /* Build configuration list for PBXNativeTarget "HFCardCollectionViewLayout" */; 227 | buildPhases = ( 228 | 6FA203D0BBD713600FC1B3832161E89A /* Sources */, 229 | D8B57CD8DCCF548A95047D16F3B2EC2D /* Frameworks */, 230 | 7E51A2A926EE7949AAE4FD1EA1AC105D /* Headers */, 231 | ); 232 | buildRules = ( 233 | ); 234 | dependencies = ( 235 | ); 236 | name = HFCardCollectionViewLayout; 237 | productName = HFCardCollectionViewLayout; 238 | productReference = 2871AA1D8F3B5DB60D1FC7F51114E01C /* HFCardCollectionViewLayout.framework */; 239 | productType = "com.apple.product-type.framework"; 240 | }; 241 | /* End PBXNativeTarget section */ 242 | 243 | /* Begin PBXProject section */ 244 | D41D8CD98F00B204E9800998ECF8427E /* Project object */ = { 245 | isa = PBXProject; 246 | attributes = { 247 | LastSwiftUpdateCheck = 0730; 248 | LastUpgradeCheck = 0920; 249 | TargetAttributes = { 250 | E1C2F6043B98ABA5417B87102C667275 = { 251 | LastSwiftMigration = 0920; 252 | }; 253 | }; 254 | }; 255 | buildConfigurationList = 2D8E8EC45A3A1A1D94AE762CB5028504 /* Build configuration list for PBXProject "Pods" */; 256 | compatibilityVersion = "Xcode 3.2"; 257 | developmentRegion = English; 258 | hasScannedForEncodings = 0; 259 | knownRegions = ( 260 | en, 261 | ); 262 | mainGroup = 7DB346D0F39D3F0E887471402A8071AB; 263 | productRefGroup = E11057D728DF1A0CA614502392CB04B8 /* Products */; 264 | projectDirPath = ""; 265 | projectRoot = ""; 266 | targets = ( 267 | E1C2F6043B98ABA5417B87102C667275 /* HFCardCollectionViewLayout */, 268 | 1D9676DD5B8F6AABC6C66730FC85E322 /* Pods-HFCardCollectionViewLayoutExample */, 269 | ); 270 | }; 271 | /* End PBXProject section */ 272 | 273 | /* Begin PBXSourcesBuildPhase section */ 274 | 047C28A02840759B5C22A4609D896D39 /* Sources */ = { 275 | isa = PBXSourcesBuildPhase; 276 | buildActionMask = 2147483647; 277 | files = ( 278 | F493F895289894F832FE0C88F27A0B15 /* Pods-HFCardCollectionViewLayoutExample-dummy.m in Sources */, 279 | ); 280 | runOnlyForDeploymentPostprocessing = 0; 281 | }; 282 | 6FA203D0BBD713600FC1B3832161E89A /* Sources */ = { 283 | isa = PBXSourcesBuildPhase; 284 | buildActionMask = 2147483647; 285 | files = ( 286 | 7A6E27CC77BF07F42DB5DA9DF2990BA6 /* HFCardCollectionViewCell.swift in Sources */, 287 | CAB4E8075BB566BEB9DEFC2BB0AE1A44 /* HFCardCollectionViewLayout-dummy.m in Sources */, 288 | 8EFA7FE72F4E97A43C553E2BC4C123B4 /* HFCardCollectionViewLayout.swift in Sources */, 289 | 790F98919A98AB3E2AE7D5B3FD41A905 /* HFCardCollectionViewLayoutDelegate.swift in Sources */, 290 | B1BF26FC1E90FD810077D75E /* HFCardCollectionView.swift in Sources */, 291 | ); 292 | runOnlyForDeploymentPostprocessing = 0; 293 | }; 294 | /* End PBXSourcesBuildPhase section */ 295 | 296 | /* Begin PBXTargetDependency section */ 297 | 1CC8B9C75E5A9B2B2A6B1E27AB954823 /* PBXTargetDependency */ = { 298 | isa = PBXTargetDependency; 299 | name = HFCardCollectionViewLayout; 300 | target = E1C2F6043B98ABA5417B87102C667275 /* HFCardCollectionViewLayout */; 301 | targetProxy = B55B90381C05AD68454AFA5C2682ABA2 /* PBXContainerItemProxy */; 302 | }; 303 | /* End PBXTargetDependency section */ 304 | 305 | /* Begin XCBuildConfiguration section */ 306 | 12914D756594D15C6F2CA12FE5F89F1B /* Debug */ = { 307 | isa = XCBuildConfiguration; 308 | buildSettings = { 309 | ALWAYS_SEARCH_USER_PATHS = NO; 310 | CLANG_ANALYZER_NONNULL = YES; 311 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 312 | CLANG_CXX_LIBRARY = "libc++"; 313 | CLANG_ENABLE_MODULES = YES; 314 | CLANG_ENABLE_OBJC_ARC = YES; 315 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 316 | CLANG_WARN_BOOL_CONVERSION = YES; 317 | CLANG_WARN_COMMA = YES; 318 | CLANG_WARN_CONSTANT_CONVERSION = YES; 319 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES; 320 | CLANG_WARN_EMPTY_BODY = YES; 321 | CLANG_WARN_ENUM_CONVERSION = YES; 322 | CLANG_WARN_INFINITE_RECURSION = YES; 323 | CLANG_WARN_INT_CONVERSION = YES; 324 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 325 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 326 | CLANG_WARN_OBJC_ROOT_CLASS = YES; 327 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 328 | CLANG_WARN_STRICT_PROTOTYPES = YES; 329 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 330 | CLANG_WARN_UNREACHABLE_CODE = YES; 331 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 332 | CODE_SIGNING_REQUIRED = NO; 333 | COPY_PHASE_STRIP = NO; 334 | ENABLE_STRICT_OBJC_MSGSEND = YES; 335 | ENABLE_TESTABILITY = YES; 336 | GCC_C_LANGUAGE_STANDARD = gnu99; 337 | GCC_DYNAMIC_NO_PIC = NO; 338 | GCC_NO_COMMON_BLOCKS = YES; 339 | GCC_OPTIMIZATION_LEVEL = 0; 340 | GCC_PREPROCESSOR_DEFINITIONS = ( 341 | "POD_CONFIGURATION_DEBUG=1", 342 | "DEBUG=1", 343 | "$(inherited)", 344 | ); 345 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 346 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 347 | GCC_WARN_ABOUT_RETURN_TYPE = YES; 348 | GCC_WARN_UNDECLARED_SELECTOR = YES; 349 | GCC_WARN_UNINITIALIZED_AUTOS = YES; 350 | GCC_WARN_UNUSED_FUNCTION = YES; 351 | GCC_WARN_UNUSED_VARIABLE = YES; 352 | IPHONEOS_DEPLOYMENT_TARGET = 10.0; 353 | ONLY_ACTIVE_ARCH = YES; 354 | PROVISIONING_PROFILE_SPECIFIER = NO_SIGNING/; 355 | STRIP_INSTALLED_PRODUCT = NO; 356 | SYMROOT = "${SRCROOT}/../build"; 357 | }; 358 | name = Debug; 359 | }; 360 | 59039CA94E93F462878EA27D8A88C6AE /* Release */ = { 361 | isa = XCBuildConfiguration; 362 | baseConfigurationReference = 89EB551874C33A437A4B3D8A20764101 /* Pods-HFCardCollectionViewLayoutExample.release.xcconfig */; 363 | buildSettings = { 364 | "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; 365 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; 366 | "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; 367 | CURRENT_PROJECT_VERSION = 1; 368 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 369 | DEFINES_MODULE = YES; 370 | DYLIB_COMPATIBILITY_VERSION = 1; 371 | DYLIB_CURRENT_VERSION = 1; 372 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 373 | ENABLE_STRICT_OBJC_MSGSEND = YES; 374 | GCC_NO_COMMON_BLOCKS = YES; 375 | INFOPLIST_FILE = "Target Support Files/Pods-HFCardCollectionViewLayoutExample/Info.plist"; 376 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 377 | IPHONEOS_DEPLOYMENT_TARGET = 10.0; 378 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 379 | MACH_O_TYPE = staticlib; 380 | MODULEMAP_FILE = "Target Support Files/Pods-HFCardCollectionViewLayoutExample/Pods-HFCardCollectionViewLayoutExample.modulemap"; 381 | MTL_ENABLE_DEBUG_INFO = NO; 382 | OTHER_LDFLAGS = ""; 383 | OTHER_LIBTOOLFLAGS = ""; 384 | PODS_ROOT = "$(SRCROOT)"; 385 | PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.${PRODUCT_NAME:rfc1034identifier}"; 386 | PRODUCT_NAME = Pods_HFCardCollectionViewLayoutExample; 387 | SDKROOT = iphoneos; 388 | SKIP_INSTALL = YES; 389 | TARGETED_DEVICE_FAMILY = "1,2"; 390 | VERSIONING_SYSTEM = "apple-generic"; 391 | VERSION_INFO_PREFIX = ""; 392 | }; 393 | name = Release; 394 | }; 395 | 7161B9D607CCBE396C693489B0AEB112 /* Release */ = { 396 | isa = XCBuildConfiguration; 397 | baseConfigurationReference = 3B08DABD74D6D5378A5DDCE853030F53 /* HFCardCollectionViewLayout.xcconfig */; 398 | buildSettings = { 399 | "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; 400 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; 401 | "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; 402 | CURRENT_PROJECT_VERSION = 1; 403 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 404 | DEFINES_MODULE = YES; 405 | DYLIB_COMPATIBILITY_VERSION = 1; 406 | DYLIB_CURRENT_VERSION = 1; 407 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 408 | ENABLE_STRICT_OBJC_MSGSEND = YES; 409 | GCC_NO_COMMON_BLOCKS = YES; 410 | GCC_PREFIX_HEADER = "Target Support Files/HFCardCollectionViewLayout/HFCardCollectionViewLayout-prefix.pch"; 411 | INFOPLIST_FILE = "Target Support Files/HFCardCollectionViewLayout/Info.plist"; 412 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 413 | IPHONEOS_DEPLOYMENT_TARGET = 9.0; 414 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 415 | MODULEMAP_FILE = "Target Support Files/HFCardCollectionViewLayout/HFCardCollectionViewLayout.modulemap"; 416 | MTL_ENABLE_DEBUG_INFO = NO; 417 | PRODUCT_NAME = HFCardCollectionViewLayout; 418 | SDKROOT = iphoneos; 419 | SKIP_INSTALL = YES; 420 | SWIFT_SWIFT3_OBJC_INFERENCE = On; 421 | SWIFT_VERSION = 4.0; 422 | TARGETED_DEVICE_FAMILY = "1,2"; 423 | VERSIONING_SYSTEM = "apple-generic"; 424 | VERSION_INFO_PREFIX = ""; 425 | }; 426 | name = Release; 427 | }; 428 | 92B027B0926C28AD73F595226EFBDF0B /* Debug */ = { 429 | isa = XCBuildConfiguration; 430 | baseConfigurationReference = 3B08DABD74D6D5378A5DDCE853030F53 /* HFCardCollectionViewLayout.xcconfig */; 431 | buildSettings = { 432 | "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; 433 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; 434 | "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; 435 | CURRENT_PROJECT_VERSION = 1; 436 | DEBUG_INFORMATION_FORMAT = dwarf; 437 | DEFINES_MODULE = YES; 438 | DYLIB_COMPATIBILITY_VERSION = 1; 439 | DYLIB_CURRENT_VERSION = 1; 440 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 441 | ENABLE_STRICT_OBJC_MSGSEND = YES; 442 | GCC_NO_COMMON_BLOCKS = YES; 443 | GCC_PREFIX_HEADER = "Target Support Files/HFCardCollectionViewLayout/HFCardCollectionViewLayout-prefix.pch"; 444 | INFOPLIST_FILE = "Target Support Files/HFCardCollectionViewLayout/Info.plist"; 445 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 446 | IPHONEOS_DEPLOYMENT_TARGET = 9.0; 447 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 448 | MODULEMAP_FILE = "Target Support Files/HFCardCollectionViewLayout/HFCardCollectionViewLayout.modulemap"; 449 | MTL_ENABLE_DEBUG_INFO = YES; 450 | PRODUCT_NAME = HFCardCollectionViewLayout; 451 | SDKROOT = iphoneos; 452 | SKIP_INSTALL = YES; 453 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 454 | SWIFT_SWIFT3_OBJC_INFERENCE = On; 455 | SWIFT_VERSION = 4.0; 456 | TARGETED_DEVICE_FAMILY = "1,2"; 457 | VERSIONING_SYSTEM = "apple-generic"; 458 | VERSION_INFO_PREFIX = ""; 459 | }; 460 | name = Debug; 461 | }; 462 | CAED8D664B647776A9C45FD40CED9B7F /* Debug */ = { 463 | isa = XCBuildConfiguration; 464 | baseConfigurationReference = B5F36D8799F60A8D7E92CEC996CF92EB /* Pods-HFCardCollectionViewLayoutExample.debug.xcconfig */; 465 | buildSettings = { 466 | "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; 467 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; 468 | "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; 469 | CURRENT_PROJECT_VERSION = 1; 470 | DEBUG_INFORMATION_FORMAT = dwarf; 471 | DEFINES_MODULE = YES; 472 | DYLIB_COMPATIBILITY_VERSION = 1; 473 | DYLIB_CURRENT_VERSION = 1; 474 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 475 | ENABLE_STRICT_OBJC_MSGSEND = YES; 476 | GCC_NO_COMMON_BLOCKS = YES; 477 | INFOPLIST_FILE = "Target Support Files/Pods-HFCardCollectionViewLayoutExample/Info.plist"; 478 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 479 | IPHONEOS_DEPLOYMENT_TARGET = 10.0; 480 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 481 | MACH_O_TYPE = staticlib; 482 | MODULEMAP_FILE = "Target Support Files/Pods-HFCardCollectionViewLayoutExample/Pods-HFCardCollectionViewLayoutExample.modulemap"; 483 | MTL_ENABLE_DEBUG_INFO = YES; 484 | OTHER_LDFLAGS = ""; 485 | OTHER_LIBTOOLFLAGS = ""; 486 | PODS_ROOT = "$(SRCROOT)"; 487 | PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.${PRODUCT_NAME:rfc1034identifier}"; 488 | PRODUCT_NAME = Pods_HFCardCollectionViewLayoutExample; 489 | SDKROOT = iphoneos; 490 | SKIP_INSTALL = YES; 491 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 492 | TARGETED_DEVICE_FAMILY = "1,2"; 493 | VERSIONING_SYSTEM = "apple-generic"; 494 | VERSION_INFO_PREFIX = ""; 495 | }; 496 | name = Debug; 497 | }; 498 | E72E7977875C2D251FC62736BBDDC389 /* Release */ = { 499 | isa = XCBuildConfiguration; 500 | buildSettings = { 501 | ALWAYS_SEARCH_USER_PATHS = NO; 502 | CLANG_ANALYZER_NONNULL = YES; 503 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 504 | CLANG_CXX_LIBRARY = "libc++"; 505 | CLANG_ENABLE_MODULES = YES; 506 | CLANG_ENABLE_OBJC_ARC = YES; 507 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 508 | CLANG_WARN_BOOL_CONVERSION = YES; 509 | CLANG_WARN_COMMA = YES; 510 | CLANG_WARN_CONSTANT_CONVERSION = YES; 511 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES; 512 | CLANG_WARN_EMPTY_BODY = YES; 513 | CLANG_WARN_ENUM_CONVERSION = YES; 514 | CLANG_WARN_INFINITE_RECURSION = YES; 515 | CLANG_WARN_INT_CONVERSION = YES; 516 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 517 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 518 | CLANG_WARN_OBJC_ROOT_CLASS = YES; 519 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 520 | CLANG_WARN_STRICT_PROTOTYPES = YES; 521 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 522 | CLANG_WARN_UNREACHABLE_CODE = YES; 523 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 524 | CODE_SIGNING_REQUIRED = NO; 525 | COPY_PHASE_STRIP = YES; 526 | ENABLE_NS_ASSERTIONS = NO; 527 | ENABLE_STRICT_OBJC_MSGSEND = YES; 528 | GCC_C_LANGUAGE_STANDARD = gnu99; 529 | GCC_NO_COMMON_BLOCKS = YES; 530 | GCC_PREPROCESSOR_DEFINITIONS = ( 531 | "POD_CONFIGURATION_RELEASE=1", 532 | "$(inherited)", 533 | ); 534 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 535 | GCC_WARN_ABOUT_RETURN_TYPE = YES; 536 | GCC_WARN_UNDECLARED_SELECTOR = YES; 537 | GCC_WARN_UNINITIALIZED_AUTOS = YES; 538 | GCC_WARN_UNUSED_FUNCTION = YES; 539 | GCC_WARN_UNUSED_VARIABLE = YES; 540 | IPHONEOS_DEPLOYMENT_TARGET = 10.0; 541 | PROVISIONING_PROFILE_SPECIFIER = NO_SIGNING/; 542 | STRIP_INSTALLED_PRODUCT = NO; 543 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 544 | SYMROOT = "${SRCROOT}/../build"; 545 | VALIDATE_PRODUCT = YES; 546 | }; 547 | name = Release; 548 | }; 549 | /* End XCBuildConfiguration section */ 550 | 551 | /* Begin XCConfigurationList section */ 552 | 2D8E8EC45A3A1A1D94AE762CB5028504 /* Build configuration list for PBXProject "Pods" */ = { 553 | isa = XCConfigurationList; 554 | buildConfigurations = ( 555 | 12914D756594D15C6F2CA12FE5F89F1B /* Debug */, 556 | E72E7977875C2D251FC62736BBDDC389 /* Release */, 557 | ); 558 | defaultConfigurationIsVisible = 0; 559 | defaultConfigurationName = Release; 560 | }; 561 | 7B1C1515171E534D7DD08A568F6263CE /* Build configuration list for PBXNativeTarget "Pods-HFCardCollectionViewLayoutExample" */ = { 562 | isa = XCConfigurationList; 563 | buildConfigurations = ( 564 | CAED8D664B647776A9C45FD40CED9B7F /* Debug */, 565 | 59039CA94E93F462878EA27D8A88C6AE /* Release */, 566 | ); 567 | defaultConfigurationIsVisible = 0; 568 | defaultConfigurationName = Release; 569 | }; 570 | C000741956A955D75290C78B8C062923 /* Build configuration list for PBXNativeTarget "HFCardCollectionViewLayout" */ = { 571 | isa = XCConfigurationList; 572 | buildConfigurations = ( 573 | 92B027B0926C28AD73F595226EFBDF0B /* Debug */, 574 | 7161B9D607CCBE396C693489B0AEB112 /* Release */, 575 | ); 576 | defaultConfigurationIsVisible = 0; 577 | defaultConfigurationName = Release; 578 | }; 579 | /* End XCConfigurationList section */ 580 | }; 581 | rootObject = D41D8CD98F00B204E9800998ECF8427E /* Project object */; 582 | } 583 | -------------------------------------------------------------------------------- /Source/HFCardCollectionViewLayout.swift: -------------------------------------------------------------------------------- 1 | // 2 | // HFCardCollectionViewLayout.swift 3 | // HFCardCollectionViewLayout 4 | // 5 | // Created by Hendrik Frahmann on 02.11.16. 6 | // Copyright © 2016 Hendrik Frahmann. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | /// Layout attributes for the HFCardCollectionViewLayout 12 | open class HFCardCollectionViewLayoutAttributes: UICollectionViewLayoutAttributes { 13 | 14 | /// Specifies if the CardCell is revealed. 15 | public var isRevealed = false 16 | 17 | /// Overwritten to copy also the 'isRevealed' flag. 18 | override open func copy(with zone: NSZone? = nil) -> Any { 19 | let attribute = super.copy(with: zone) as! HFCardCollectionViewLayoutAttributes 20 | attribute.isRevealed = isRevealed 21 | return attribute 22 | } 23 | 24 | } 25 | 26 | /// The HFCardCollectionViewLayout provides a card stack layout not quite similar like the apps Reminder and Wallet. 27 | open class HFCardCollectionViewLayout: UICollectionViewLayout, UIGestureRecognizerDelegate { 28 | 29 | // MARK: Public Variables 30 | 31 | /// Only cards with index equal or greater than firstMovableIndex can be moved through the collectionView. 32 | /// 33 | /// Default: 0 34 | @IBInspectable open var firstMovableIndex: Int = 0 35 | 36 | /// Specifies the height that is showing the cardhead when the collectionView is showing all cards. 37 | /// 38 | /// The minimum value is 20. 39 | /// 40 | /// Default: 80 41 | @IBInspectable open var cardHeadHeight: CGFloat = 80 { 42 | didSet { 43 | if(cardHeadHeight < 20) { 44 | cardHeadHeight = 20 45 | return 46 | } 47 | self.collectionView?.performBatchUpdates({ self.invalidateLayout() }, completion: nil) 48 | } 49 | } 50 | 51 | /// When th collectionView is showing all cards but there are not enough cards to fill the full height, 52 | /// the cardHeadHeight will be expanded to equally fill the height. 53 | /// 54 | /// Default: true 55 | @IBInspectable open var cardShouldExpandHeadHeight: Bool = true { 56 | didSet { 57 | self.collectionView?.performBatchUpdates({ self.invalidateLayout() }, completion: nil) 58 | } 59 | } 60 | 61 | /// Stretch the cards when scrolling up 62 | /// 63 | /// Default: true 64 | @IBInspectable open var cardShouldStretchAtScrollTop: Bool = true { 65 | didSet { 66 | self.collectionView?.performBatchUpdates({ self.invalidateLayout() }, completion: nil) 67 | } 68 | } 69 | 70 | /// Specifies the maximum height of the cards. 71 | /// 72 | /// But the height can be less if the frame size of collectionView is smaller. 73 | /// 74 | /// Default: 0 (no height specified) 75 | @IBInspectable open var cardMaximumHeight: CGFloat = 0 { 76 | didSet { 77 | if(cardMaximumHeight < 0) { 78 | cardMaximumHeight = 0 79 | return 80 | } 81 | self.collectionView?.performBatchUpdates({ self.invalidateLayout() }, completion: nil) 82 | } 83 | } 84 | 85 | /// Count of bottom stacked cards when a card is revealed. 86 | /// 87 | /// Value must be between 0 and 10 88 | /// 89 | /// Default: 5 90 | @IBInspectable open var bottomNumberOfStackedCards: Int = 5 { 91 | didSet { 92 | if(bottomNumberOfStackedCards < 0) { 93 | bottomNumberOfStackedCards = 0 94 | return 95 | } 96 | if(bottomNumberOfStackedCards > 10) { 97 | bottomNumberOfStackedCards = 10 98 | return 99 | } 100 | self.collectionView?.performBatchUpdates({ self.collectionView?.reloadData() }, completion: nil) 101 | } 102 | } 103 | 104 | /// All bottom stacked cards are scaled to produce the 3D effect. 105 | /// 106 | /// Default: true 107 | @IBInspectable open var bottomStackedCardsShouldScale: Bool = true { 108 | didSet { 109 | self.collectionView?.performBatchUpdates({ self.invalidateLayout() }, completion: nil) 110 | } 111 | } 112 | 113 | /// The minimum scale for the bottom cards. 114 | /// 115 | /// Default: 0.94 116 | @IBInspectable open var bottomStackedCardsMinimumScale: CGFloat = 0.94 { 117 | didSet { 118 | if(self.bottomStackedCardsMinimumScale < 0.0) { 119 | self.bottomStackedCardsMinimumScale = 0.0 120 | return 121 | } 122 | self.collectionView?.performBatchUpdates({ self.invalidateLayout() }, completion: nil) 123 | } 124 | } 125 | 126 | /// The maximum scale for the bottom cards. 127 | /// 128 | /// Default: 0.94 129 | @IBInspectable open var bottomStackedCardsMaximumScale: CGFloat = 1.0 { 130 | didSet { 131 | if(self.bottomStackedCardsMaximumScale > 1.0) { 132 | self.bottomStackedCardsMaximumScale = 1.0 133 | return 134 | } 135 | self.collectionView?.performBatchUpdates({ self.invalidateLayout() }, completion: nil) 136 | } 137 | } 138 | 139 | /// Specifies the margin for the top margin of a bottom stacked card. 140 | /// 141 | /// Value can be between 0 and 20 142 | /// 143 | /// Default: 10 144 | @IBInspectable open var bottomCardLookoutMargin: CGFloat = 10 { 145 | didSet { 146 | if(bottomCardLookoutMargin < 0) { 147 | bottomCardLookoutMargin = 0 148 | return 149 | } 150 | if(bottomCardLookoutMargin > 20) { 151 | bottomCardLookoutMargin = 20 152 | return 153 | } 154 | self.collectionView?.performBatchUpdates({ self.invalidateLayout() }, completion: nil) 155 | } 156 | } 157 | 158 | /// An additional topspace to show the top of the collectionViews backgroundView. 159 | /// 160 | /// Default: 0 161 | @IBInspectable open var spaceAtTopForBackgroundView: CGFloat = 0 { 162 | didSet { 163 | if(spaceAtTopForBackgroundView < 0) { 164 | spaceAtTopForBackgroundView = 0 165 | return 166 | } 167 | self.collectionView?.performBatchUpdates({ self.invalidateLayout() }, completion: nil) 168 | } 169 | } 170 | 171 | /// Snaps the scrollView if the contentOffset is on the 'spaceAtTopForBackgroundView' 172 | /// 173 | /// Default: true 174 | @IBInspectable open var spaceAtTopShouldSnap: Bool = true 175 | 176 | /// Additional space at the bottom to expand the contentsize of the collectionView. 177 | /// 178 | /// Default: 0 179 | @IBInspectable open var spaceAtBottom: CGFloat = 0 { 180 | didSet { 181 | self.collectionView?.performBatchUpdates({ self.invalidateLayout() }, completion: nil) 182 | } 183 | } 184 | 185 | /// Area the top where to autoscroll while moving a card. 186 | /// 187 | /// Default 120 188 | @IBInspectable open var scrollAreaTop: CGFloat = 120 { 189 | didSet { 190 | if(scrollAreaTop < 0) { 191 | scrollAreaTop = 0 192 | return 193 | } 194 | } 195 | } 196 | 197 | /// Area ot the bottom where to autoscroll while moving a card. 198 | /// 199 | /// Default 120 200 | @IBInspectable open var scrollAreaBottom: CGFloat = 120 { 201 | didSet { 202 | if(scrollAreaBottom < 0) { 203 | scrollAreaBottom = 0 204 | return 205 | } 206 | } 207 | } 208 | 209 | /// The scrollView should snap the cardhead to the top. 210 | /// 211 | /// Default: false 212 | @IBInspectable open var scrollShouldSnapCardHead: Bool = false 213 | 214 | /// Cards are stopping at top while scrolling. 215 | /// 216 | /// Default: true 217 | @IBInspectable open var scrollStopCardsAtTop: Bool = true { 218 | didSet { 219 | self.collectionView?.performBatchUpdates({ self.invalidateLayout() }, completion: nil) 220 | } 221 | } 222 | 223 | /// All cards are collapsed at bottom. 224 | /// 225 | /// Default: false 226 | @IBInspectable open var collapseAllCards: Bool = false { 227 | didSet { 228 | self.flipRevealedCardBack(completion: { 229 | self.collectionView?.isScrollEnabled = !self.collapseAllCards 230 | var previousRevealedIndex = -1 231 | let collectionViewLayoutDelegate = self.collectionView?.delegate as? HFCardCollectionViewLayoutDelegate 232 | if(self.revealedIndex >= 0) { 233 | previousRevealedIndex = self.revealedIndex 234 | collectionViewLayoutDelegate?.cardCollectionViewLayout?(self, willUnrevealCardAtIndex: self.revealedIndex) 235 | self.revealedIndex = -1 236 | } 237 | self.collectionView?.performBatchUpdates({ self.collectionView?.reloadData() }, completion: {(finished) in 238 | if(previousRevealedIndex >= 0) { 239 | 240 | collectionViewLayoutDelegate?.cardCollectionViewLayout?(self, didUnrevealCardAtIndex: previousRevealedIndex) 241 | } 242 | }) 243 | }) 244 | } 245 | } 246 | 247 | /// Contains the revealed index. 248 | /// ReadOnly. 249 | private(set) open var revealedIndex: Int = -1 250 | 251 | // MARK: Public Actions 252 | 253 | /// Action for the InterfaceBuilder to flip back the revealed card. 254 | @IBAction open func flipBackRevealedCardAction() { 255 | self.flipRevealedCardBack() 256 | } 257 | 258 | /// Action for the InterfaceBuilder to Unreveal the revealed card. 259 | @IBAction open func unrevealRevealedCardAction() { 260 | self.unrevealCard() 261 | } 262 | 263 | /// Action to collapse all cards. 264 | @IBAction open func collapseAllCardsAction() { 265 | self.collapseAllCards = true 266 | } 267 | 268 | // MARK: Public Functions 269 | 270 | /// Reveal a card at the given index. 271 | /// 272 | /// - Parameter index: The index of the card. 273 | /// - Parameter completion: An optional completion block. Will be executed the animation is finished. 274 | open func revealCardAt(index: Int, completion: (() -> Void)? = nil) { 275 | var index = index 276 | let collectionViewLayoutDelegate = self.collectionView?.delegate as? HFCardCollectionViewLayoutDelegate 277 | let oldRevealedIndex = self.revealedIndex 278 | 279 | if(oldRevealedIndex < 0 && index >= 0 && self.collapseAllCards == true) { 280 | index = oldRevealedIndex 281 | self.collapseAllCards = false 282 | return 283 | } 284 | 285 | if ((self.revealedIndex >= 0 && self.revealedIndex == index) || (self.revealedIndex >= 0 && index < 0)) && self.revealedCardIsFlipped == true { 286 | // do nothing, because the card is flipped 287 | } else if self.revealedIndex >= 0 && index >= 0 { 288 | if(self.collectionViewForceUnreveal == false) { 289 | if(collectionViewLayoutDelegate?.cardCollectionViewLayout?(self, canUnrevealCardAtIndex: self.revealedIndex) == false) { 290 | return 291 | } 292 | } 293 | self.collectionViewForceUnreveal = false 294 | if(self.revealedCardIsFlipped == true) { 295 | self.flipRevealedCardBack(completion: { 296 | self.collectionView?.isScrollEnabled = true 297 | self.deinitializeRevealedCard() 298 | collectionViewLayoutDelegate?.cardCollectionViewLayout?(self, willUnrevealCardAtIndex: self.revealedIndex) 299 | self.revealedIndex = -1 300 | self.collectionView?.performBatchUpdates({ self.collectionView?.reloadData() }, completion: { (finished) in 301 | collectionViewLayoutDelegate?.cardCollectionViewLayout?(self, didUnrevealCardAtIndex: oldRevealedIndex) 302 | completion?() 303 | }) 304 | }) 305 | } else { 306 | self.collectionView?.isScrollEnabled = true 307 | self.deinitializeRevealedCard() 308 | collectionViewLayoutDelegate?.cardCollectionViewLayout?(self, willUnrevealCardAtIndex: self.revealedIndex) 309 | self.revealedIndex = -1 310 | self.collectionView?.performBatchUpdates({ self.collectionView?.reloadData() }, completion: { (finished) in 311 | collectionViewLayoutDelegate?.cardCollectionViewLayout?(self, didUnrevealCardAtIndex: oldRevealedIndex) 312 | completion?() 313 | }) 314 | } 315 | } else { 316 | if(index < 0 && self.revealedIndex >= 0) { 317 | self.deinitializeRevealedCard() 318 | collectionViewLayoutDelegate?.cardCollectionViewLayout?(self, willUnrevealCardAtIndex: self.revealedIndex) 319 | } 320 | if index >= 0 { 321 | self.revealedIndex = index 322 | if(collectionViewLayoutDelegate?.cardCollectionViewLayout?(self, canRevealCardAtIndex: index) == false) { 323 | self.revealedIndex = -1 324 | self.collectionView?.isScrollEnabled = true 325 | self.deinitializeRevealedCard() 326 | return 327 | } 328 | collectionViewLayoutDelegate?.cardCollectionViewLayout?(self, willRevealCardAtIndex: index) 329 | _ = self.initializeRevealedCard() 330 | self.collectionView?.isScrollEnabled = false 331 | 332 | self.collectionView?.performBatchUpdates({ self.collectionView?.reloadData() }, completion: { (finished) in 333 | collectionViewLayoutDelegate?.cardCollectionViewLayout?(self, didRevealCardAtIndex: self.revealedIndex) 334 | completion?() 335 | }) 336 | } else if(self.revealedIndex >= 0) { 337 | self.revealedIndex = index 338 | self.collectionView?.isScrollEnabled = false 339 | self.collectionView?.performBatchUpdates({ self.collectionView?.reloadData() }, completion: { (finished) in 340 | collectionViewLayoutDelegate?.cardCollectionViewLayout?(self, didUnrevealCardAtIndex: oldRevealedIndex) 341 | completion?() 342 | }) 343 | } 344 | self.revealedIndex = index 345 | } 346 | } 347 | 348 | /// Unreveal the revealed card 349 | /// 350 | /// - Parameter completion: An optional completion block. Will be executed the animation is finished. 351 | open func unrevealCard(completion: (() -> Void)? = nil) { 352 | if(self.revealedIndex == -1) { 353 | completion?() 354 | } else if(self.revealedCardIsFlipped == true) { 355 | self.flipRevealedCardBack(completion: { 356 | self.collectionViewForceUnreveal = true 357 | self.revealCardAt(index: self.revealedIndex, completion: completion) 358 | }) 359 | } else { 360 | self.collectionViewForceUnreveal = true 361 | self.revealCardAt(index: self.revealedIndex, completion: completion) 362 | } 363 | } 364 | 365 | /// Flips the revealed card to the given view. 366 | /// The frame for the view will be the same as the cell 367 | /// 368 | /// - Parameter toView: The view for the backview of te card. 369 | /// - Parameter completion: An optional completion block. Will be executed the animation is finished. 370 | open func flipRevealedCard(toView: UIView, completion: (() -> Void)? = nil) { 371 | if(self.revealedCardIsFlipped == true) { 372 | return 373 | } 374 | if let cardCell = self.revealedCardCell, self.revealedIndex >= 0 { 375 | toView.removeFromSuperview() 376 | self.revealedCardFlipView = toView 377 | toView.frame = CGRect(x: 0, y: 0, width: cardCell.frame.width, height: cardCell.frame.height) 378 | toView.isHidden = true 379 | cardCell.addSubview(toView) 380 | 381 | self.revealedCardIsFlipped = true 382 | UIApplication.shared.keyWindow?.endEditing(true) 383 | let originalShouldRasterize = cardCell.layer.shouldRasterize 384 | cardCell.layer.shouldRasterize = false 385 | 386 | UIView.transition(with: cardCell, duration: 0.5, options:[.transitionFlipFromRight], animations: { () -> Void in 387 | cardCell.contentView.isHidden = true 388 | toView.isHidden = false 389 | }, completion: { (Bool) -> Void in 390 | cardCell.layer.shouldRasterize = originalShouldRasterize 391 | completion?() 392 | }) 393 | } 394 | } 395 | 396 | /// Flips the flipped card back to the frontview. 397 | /// 398 | /// - Parameter completion: An optional completion block. Will be executed the animation is finished. 399 | open func flipRevealedCardBack(completion: (() -> Void)? = nil) { 400 | if(self.revealedCardIsFlipped == false) { 401 | completion?() 402 | return 403 | } 404 | if let cardCell = self.revealedCardCell { 405 | if let flipView = self.revealedCardFlipView { 406 | let originalShouldRasterize = cardCell.layer.shouldRasterize 407 | UIApplication.shared.keyWindow?.endEditing(true) 408 | cardCell.layer.shouldRasterize = false 409 | 410 | UIView.transition(with: cardCell, duration: 0.5, options:[.transitionFlipFromLeft], animations: { () -> Void in 411 | flipView.isHidden = true 412 | cardCell.contentView.isHidden = false 413 | }, completion: { (Bool) -> Void in 414 | flipView.removeFromSuperview() 415 | cardCell.layer.shouldRasterize = originalShouldRasterize 416 | self.revealedCardFlipView = nil 417 | self.revealedCardIsFlipped = false 418 | completion?() 419 | }) 420 | } 421 | } 422 | } 423 | 424 | open func willInsert(indexPaths: [IndexPath]) { 425 | for indexPath in indexPaths { 426 | if(indexPath.section == 0) { 427 | if(indexPath.item <= self.revealedIndex) { 428 | collectionViewTemporaryTop += self.cardHeadHeight 429 | self.revealedIndex += 1 430 | } 431 | } 432 | } 433 | } 434 | 435 | open func willDelete(indexPaths: [IndexPath]) { 436 | let collectionViewLayoutDelegate = self.collectionView?.delegate as? HFCardCollectionViewLayoutDelegate 437 | for indexPath in indexPaths { 438 | if(indexPath.section == 0) { 439 | if(indexPath.item == self.revealedIndex) { 440 | self.revealedIndex = -1 441 | self.collectionView?.isScrollEnabled = true 442 | self.deinitializeRevealedCard() 443 | collectionViewLayoutDelegate?.cardCollectionViewLayout?(self, willUnrevealCardAtIndex: indexPath.item) 444 | //collectionViewLayoutDelegate?.cardCollectionViewLayout?(self, didUnrevealCardAtIndex: indexPath.item) 445 | } 446 | if(indexPath.item <= self.revealedIndex) { 447 | collectionViewTemporaryTop -= self.cardHeadHeight 448 | self.revealedIndex -= 1 449 | } 450 | } 451 | } 452 | } 453 | 454 | //////////////////////////////////////////////////////////////////////////////////////////////////// 455 | ////////// Private ////////// 456 | //////////////////////////////////////////////////////////////////////////////////////////////////// 457 | 458 | // MARK: Private Variables 459 | 460 | private var collectionViewIsInitialized = false 461 | private var collectionViewItemCount: Int = 0 462 | private var collectionViewTapGestureRecognizer: UITapGestureRecognizer? 463 | private var collectionViewIgnoreBottomContentOffsetChanges: Bool = false 464 | private var collectionViewLastBottomContentOffset: CGFloat = 0 465 | private var collectionViewForceUnreveal: Bool = false 466 | private var collectionViewDeletedIndexPaths = [IndexPath]() 467 | private var collectionViewTemporaryTop: CGFloat = 0 468 | 469 | private var cardCollectionBoundsSize: CGSize = .zero 470 | private var cardCollectionViewLayoutAttributes:[HFCardCollectionViewLayoutAttributes]! 471 | private var cardCollectionBottomCardsSet: [Int] = [] 472 | private var cardCollectionBottomCardsRevealedIndex: CGFloat = 0 473 | private var cardCollectionCellSize: CGSize = .zero 474 | 475 | private var revealedCardCell: UICollectionViewCell? 476 | private var revealedCardPanGestureRecognizer: UIPanGestureRecognizer? 477 | private var revealedCardPanGestureTouchLocationY: CGFloat = 0 478 | private var revealedCardFlipView: UIView? 479 | private var revealedCardIsFlipped: Bool = false 480 | 481 | private var movingCardSelectedIndex: Int = -1 482 | private var movingCardGestureRecognizer: UILongPressGestureRecognizer? 483 | private var movingCardActive: Bool = false 484 | private var movingCardGestureStartLocation: CGPoint = .zero 485 | private var movingCardGestureCurrentLocation: CGPoint = .zero 486 | private var movingCardCenterStart: CGPoint = .zero 487 | private var movingCardSnapshotCell: UIView? 488 | private var movingCardLastTouchedLocation: CGPoint = .zero 489 | private var movingCardLastTouchedIndexPath: IndexPath? 490 | private var movingCardStartIndexPath: IndexPath? 491 | 492 | private var autoscrollDisplayLink: CADisplayLink? 493 | private var autoscrollDirection: HFCardCollectionScrollDirection = .unknown 494 | 495 | // MARK: Private calculated Variable 496 | 497 | private var contentInset: UIEdgeInsets { 498 | get { 499 | if #available(iOS 11, *) { 500 | return self.collectionView!.adjustedContentInset 501 | } else { 502 | return self.collectionView!.contentInset 503 | } 504 | } 505 | } 506 | 507 | private var contentOffsetTop: CGFloat { 508 | get { 509 | return self.collectionView!.contentOffset.y + self.contentInset.top 510 | } 511 | } 512 | 513 | private var bottomCardCount: CGFloat { 514 | return CGFloat(min(self.collectionViewItemCount, min(self.bottomNumberOfStackedCards, self.cardCollectionBottomCardsSet.count))) 515 | } 516 | 517 | private var contentInsetBottom: CGFloat { 518 | if(self.collectionViewIgnoreBottomContentOffsetChanges == true) { 519 | return self.collectionViewLastBottomContentOffset 520 | } 521 | self.collectionViewLastBottomContentOffset = self.contentInset.bottom 522 | return self.contentInset.bottom 523 | } 524 | 525 | private var scalePerCard: CGFloat { 526 | let maximumScale = self.bottomStackedCardsMaximumScale 527 | let minimumScale = (maximumScale < self.bottomStackedCardsMinimumScale) ? maximumScale : self.bottomStackedCardsMinimumScale 528 | return (maximumScale - minimumScale) / CGFloat(self.bottomNumberOfStackedCards) 529 | } 530 | 531 | // MARK: Initialize HFCardCollectionViewLayout 532 | 533 | internal func installMoveCardsGestureRecognizer() { 534 | self.movingCardGestureRecognizer = UILongPressGestureRecognizer(target: self, action: #selector(self.movingCardGestureHandler)) 535 | self.movingCardGestureRecognizer?.minimumPressDuration = 0.49 536 | self.movingCardGestureRecognizer?.delegate = self 537 | self.collectionView?.addGestureRecognizer(self.movingCardGestureRecognizer!) 538 | } 539 | 540 | private func initializeCardCollectionViewLayout() { 541 | self.collectionViewIsInitialized = true 542 | 543 | NotificationCenter.default.addObserver(self, selector: #selector(self.keyboardWillShow(_:)), name: NSNotification.Name.UIKeyboardWillShow, object: nil) 544 | NotificationCenter.default.addObserver(self, selector: #selector(self.keyboardDidHide(_:)), name: NSNotification.Name.UIKeyboardDidHide, object: nil) 545 | 546 | self.collectionViewTapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(self.collectionViewTapGestureHandler)) 547 | self.collectionViewTapGestureRecognizer?.delegate = self 548 | self.collectionView?.addGestureRecognizer(self.collectionViewTapGestureRecognizer!) 549 | } 550 | 551 | @objc func keyboardWillShow(_ notification: Notification) { 552 | self.collectionViewIgnoreBottomContentOffsetChanges = true 553 | } 554 | 555 | @objc func keyboardDidHide(_ notification: Notification) { 556 | self.collectionViewIgnoreBottomContentOffsetChanges = false 557 | } 558 | 559 | // MARK: UICollectionViewLayout Overrides 560 | 561 | /// The width and height of the collection view’s contents. 562 | override open var collectionViewContentSize: CGSize { 563 | get { 564 | let contentHeight = (self.cardHeadHeight * CGFloat(self.collectionViewItemCount)) + self.spaceAtTopForBackgroundView + self.spaceAtBottom 565 | let contentWidth = self.collectionView!.frame.width - (contentInset.left + contentInset.right) 566 | return CGSize.init(width: contentWidth, height: contentHeight) 567 | } 568 | } 569 | 570 | /// Tells the layout object to update the current layout. 571 | override open func prepare() { 572 | super.prepare() 573 | 574 | self.collectionViewItemCount = self.collectionView!.numberOfItems(inSection: 0) 575 | self.cardCollectionCellSize = self.generateCellSize() 576 | 577 | if(self.collectionViewIsInitialized == false) { 578 | self.initializeCardCollectionViewLayout() 579 | } 580 | 581 | self.cardCollectionViewLayoutAttributes = self.generateCardCollectionViewLayoutAttributes() 582 | } 583 | 584 | /// A layout attributes object containing the information to apply to the item’s cell. 585 | override open func layoutAttributesForItem(at indexPath: IndexPath) -> UICollectionViewLayoutAttributes? { 586 | return self.cardCollectionViewLayoutAttributes[indexPath.item] 587 | } 588 | 589 | /// An array of UICollectionViewLayoutAttributes objects representing the layout information for the cells and views. The default implementation returns nil. 590 | /// 591 | /// - Parameter rect: The rectangle 592 | override open func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? { 593 | let attributes = self.cardCollectionViewLayoutAttributes.filter { (layout) -> Bool in 594 | return (layout.frame.intersects(rect)) 595 | } 596 | return attributes 597 | } 598 | 599 | /// true if the collection view requires a layout update or false if the layout does not need to change. 600 | /// 601 | /// - Parameter newBounds: The new bounds of the collection view. 602 | override open func shouldInvalidateLayout(forBoundsChange newBounds: CGRect) -> Bool { 603 | return true 604 | } 605 | 606 | /// The content offset that you want to use instead. 607 | /// 608 | /// - Parameter proposedContentOffset: The proposed point (in the collection view’s content view) at which to stop scrolling. This is the value at which scrolling would naturally stop if no adjustments were made. The point reflects the upper-left corner of the visible content. 609 | /// - Parameter velocity: The current scrolling velocity along both the horizontal and vertical axes. This value is measured in points per second. 610 | override open func targetContentOffset(forProposedContentOffset proposedContentOffset: CGPoint, withScrollingVelocity velocity: CGPoint) -> CGPoint { 611 | let proposedContentOffsetY = proposedContentOffset.y + self.contentInset.top 612 | if(self.spaceAtTopShouldSnap == true && self.spaceAtTopForBackgroundView > 0) { 613 | if(proposedContentOffsetY > 0 && proposedContentOffsetY < self.spaceAtTopForBackgroundView) { 614 | let scrollToTopY = self.spaceAtTopForBackgroundView * 0.5 615 | if(proposedContentOffsetY < scrollToTopY) { 616 | return CGPoint(x: 0, y: 0 - self.contentInset.top) 617 | } else { 618 | return CGPoint(x: 0, y: self.spaceAtTopForBackgroundView - self.contentInset.top) 619 | } 620 | } 621 | } 622 | if(self.scrollShouldSnapCardHead == true && proposedContentOffsetY > self.spaceAtTopForBackgroundView && self.collectionView!.contentSize.height > self.collectionView!.frame.height + self.cardHeadHeight) { 623 | let startIndex = Int((proposedContentOffsetY - self.spaceAtTopForBackgroundView) / self.cardHeadHeight) + 1 624 | let positionToGoUp = self.cardHeadHeight * 0.5 625 | let cardHeadPosition = (proposedContentOffsetY - self.spaceAtTopForBackgroundView).truncatingRemainder(dividingBy: self.cardHeadHeight) 626 | if(cardHeadPosition > positionToGoUp) { 627 | let targetY = (CGFloat(startIndex) * self.cardHeadHeight) + (self.spaceAtTopForBackgroundView - self.contentInset.top) 628 | return CGPoint(x: 0, y: targetY) 629 | } else { 630 | let targetY = (CGFloat(startIndex) * self.cardHeadHeight) - self.cardHeadHeight + (self.spaceAtTopForBackgroundView - self.contentInset.top) 631 | return CGPoint(x: 0, y: targetY) 632 | } 633 | } 634 | return proposedContentOffset 635 | } 636 | 637 | /// This method is called when there is an update with deletes to the collection view. 638 | override open func prepare(forCollectionViewUpdates updateItems: [UICollectionViewUpdateItem]) { 639 | super.prepare(forCollectionViewUpdates: updateItems) 640 | 641 | collectionViewDeletedIndexPaths.removeAll(keepingCapacity: false) 642 | 643 | for update in updateItems { 644 | switch update.updateAction { 645 | case .delete: 646 | collectionViewDeletedIndexPaths.append(update.indexPathBeforeUpdate!) 647 | default: 648 | return 649 | } 650 | } 651 | } 652 | 653 | /// Custom animation for deleting cells. 654 | override open func finalLayoutAttributesForDisappearingItem(at itemIndexPath: IndexPath) -> UICollectionViewLayoutAttributes? { 655 | let attrs = super.finalLayoutAttributesForDisappearingItem(at: itemIndexPath) 656 | 657 | if collectionViewDeletedIndexPaths.contains(itemIndexPath) { 658 | if let attrs = attrs { 659 | attrs.alpha = 0.0 660 | attrs.transform3D = CATransform3DScale(attrs.transform3D, 0.001, 0.001, 1) 661 | } 662 | } 663 | 664 | return attrs 665 | } 666 | 667 | /// Remove deleted indexPaths 668 | override open func finalizeCollectionViewUpdates() { 669 | super.finalizeCollectionViewUpdates() 670 | collectionViewDeletedIndexPaths.removeAll(keepingCapacity: false) 671 | } 672 | 673 | // MARK: Private Functions for UICollectionViewLayout 674 | 675 | @objc internal func collectionViewTapGestureHandler() { 676 | if let tapLocation = self.collectionViewTapGestureRecognizer?.location(in: self.collectionView) { 677 | if let indexPath = self.collectionView?.indexPathForItem(at: tapLocation) { 678 | self.collectionView?.delegate?.collectionView?(self.collectionView!, didSelectItemAt: indexPath) 679 | } 680 | } 681 | } 682 | 683 | private func generateCellSize() -> CGSize { 684 | let width = self.collectionView!.frame.width - (self.contentInset.left + self.contentInset.right) 685 | let maxHeight = self.collectionView!.frame.height - (self.bottomCardLookoutMargin * CGFloat(self.bottomNumberOfStackedCards)) - (self.contentInset.top + self.contentInsetBottom) - 2 686 | let height = (self.cardMaximumHeight == 0 || self.cardMaximumHeight > maxHeight) ? maxHeight : self.cardMaximumHeight 687 | let size = CGSize.init(width: width, height: height) 688 | return size 689 | } 690 | 691 | private func generateCardCollectionViewLayoutAttributes() -> [HFCardCollectionViewLayoutAttributes] { 692 | var cardCollectionViewLayoutAttributes: [HFCardCollectionViewLayoutAttributes] = [] 693 | var shouldReloadAllItems = false 694 | if(self.cardCollectionViewLayoutAttributes != nil && self.collectionViewItemCount == self.cardCollectionViewLayoutAttributes.count) { 695 | cardCollectionViewLayoutAttributes = self.cardCollectionViewLayoutAttributes 696 | } else { 697 | shouldReloadAllItems = true 698 | } 699 | 700 | var startIndex = Int((self.collectionView!.contentOffset.y + self.contentInset.top - self.spaceAtTopForBackgroundView + collectionViewTemporaryTop) / self.cardHeadHeight) - 10 701 | var endBeforeIndex = Int((self.collectionView!.contentOffset.y + self.collectionView!.frame.size.height + collectionViewTemporaryTop) / self.cardHeadHeight) + 5 702 | 703 | if(startIndex < 0) { 704 | startIndex = 0 705 | } 706 | if(endBeforeIndex > self.collectionViewItemCount) { 707 | endBeforeIndex = self.collectionViewItemCount 708 | } 709 | if(shouldReloadAllItems == true) { 710 | startIndex = 0 711 | endBeforeIndex = self.collectionViewItemCount 712 | } 713 | 714 | self.cardCollectionBottomCardsSet = self.generateBottomIndexes() 715 | 716 | var bottomIndex: CGFloat = 0 717 | for itemIndex in startIndex.. [Int] { 742 | if self.revealedIndex < 0 { 743 | if self.collapseAllCards == false { 744 | return [] 745 | } else { 746 | let startIndex: Int = Int((self.contentOffsetTop + collectionViewTemporaryTop) / self.cardHeadHeight) 747 | let endIndex = max(0, startIndex + self.bottomNumberOfStackedCards - 2) 748 | return Array(startIndex...endIndex) 749 | } 750 | } 751 | 752 | let half = Int(self.bottomNumberOfStackedCards / 2) 753 | var minIndex = self.revealedIndex - half 754 | var maxIndex = self.revealedIndex + half 755 | 756 | if minIndex < 0 { 757 | minIndex = 0 758 | maxIndex = self.revealedIndex + half + abs(self.revealedIndex - half) 759 | } else if maxIndex >= self.collectionViewItemCount { 760 | minIndex = (self.collectionViewItemCount - 2 * half) - 1 761 | maxIndex = self.collectionViewItemCount - 1 762 | } 763 | 764 | self.cardCollectionBottomCardsRevealedIndex = 0 765 | 766 | return Array(minIndex...maxIndex).filter({ (value) -> Bool in 767 | if value >= 0 && value != self.revealedIndex { 768 | if(value < self.revealedIndex) { 769 | self.cardCollectionBottomCardsRevealedIndex += 1 770 | } 771 | return true 772 | } 773 | return false 774 | }) 775 | } 776 | 777 | private func generateNonRevealedCardsAttribute(_ attribute: HFCardCollectionViewLayoutAttributes) { 778 | let cardHeadHeight = self.calculateCardHeadHeight() 779 | 780 | let startIndex = Int((self.contentOffsetTop + collectionViewTemporaryTop - self.spaceAtTopForBackgroundView) / cardHeadHeight) 781 | let currentIndex = attribute.indexPath.item 782 | if(currentIndex == self.movingCardSelectedIndex) { 783 | attribute.alpha = 0.0 784 | } else { 785 | attribute.alpha = 1.0 786 | } 787 | 788 | let currentFrame = CGRect(x: 0, y: self.spaceAtTopForBackgroundView + cardHeadHeight * CGFloat(currentIndex), width: self.cardCollectionCellSize.width, height: self.cardCollectionCellSize.height) 789 | 790 | if(self.contentOffsetTop >= 0 && self.contentOffsetTop <= self.spaceAtTopForBackgroundView) { 791 | attribute.frame = currentFrame 792 | } else if(self.contentOffsetTop > self.spaceAtTopForBackgroundView) { 793 | attribute.isHidden = (self.scrollStopCardsAtTop == true && currentIndex < startIndex) 794 | 795 | if(self.movingCardSelectedIndex >= 0 && currentIndex + 1 == self.movingCardSelectedIndex) { 796 | attribute.isHidden = false 797 | } 798 | if (self.scrollStopCardsAtTop == true && ((currentIndex != 0 && currentIndex <= startIndex) || (currentIndex == 0 && (self.contentOffsetTop - self.spaceAtTopForBackgroundView) > 0))) { 799 | var newFrame = currentFrame 800 | newFrame.origin.y = self.contentOffsetTop 801 | attribute.frame = newFrame 802 | } else { 803 | attribute.frame = currentFrame 804 | } 805 | if(attribute.isHidden == true && currentIndex < startIndex - 5) { 806 | attribute.frame = currentFrame 807 | attribute.frame.origin.y = self.collectionView!.frame.height * -1.5 808 | } 809 | } else { 810 | if(self.cardShouldStretchAtScrollTop == true) { 811 | let stretchMultiplier: CGFloat = (1 + (CGFloat(currentIndex + 1) * -0.2)) 812 | var newFrame = currentFrame 813 | newFrame.origin.y = newFrame.origin.y + CGFloat(self.contentOffsetTop * stretchMultiplier) 814 | attribute.frame = newFrame 815 | } else { 816 | attribute.frame = currentFrame 817 | } 818 | } 819 | attribute.isRevealed = false 820 | } 821 | 822 | private func generateRevealedCardAttribute(_ attribute: HFCardCollectionViewLayoutAttributes) { 823 | attribute.isRevealed = true 824 | if(self.collectionViewItemCount == 1) { 825 | attribute.frame = CGRect.init(x: 0, y: self.contentOffsetTop + self.spaceAtTopForBackgroundView + 0.01 , width: self.cardCollectionCellSize.width, height: self.cardCollectionCellSize.height) 826 | } else { 827 | attribute.frame = CGRect.init(x: 0, y: self.contentOffsetTop + 0.01 , width: self.cardCollectionCellSize.width, height: self.cardCollectionCellSize.height) 828 | } 829 | } 830 | 831 | private func generateBottomCardsAttribute(_ attribute: HFCardCollectionViewLayoutAttributes, bottomIndex:inout CGFloat) { 832 | let index = attribute.indexPath.item 833 | let posY = self.cardHeadHeight * CGFloat(index) 834 | let currentFrame = CGRect(x: self.collectionView!.frame.origin.x, y: posY, width: self.cardCollectionCellSize.width, height: self.cardCollectionCellSize.height) 835 | let maxY = self.collectionView!.contentOffset.y + self.collectionView!.frame.height 836 | let contentFrame = CGRect(x: 0, y: self.collectionView!.contentOffset.y, width: self.collectionView!.frame.width, height: maxY) 837 | if self.cardCollectionBottomCardsSet.contains(index) { 838 | let margin: CGFloat = self.bottomCardLookoutMargin 839 | let baseHeight = (self.collectionView!.frame.height + self.collectionView!.contentOffset.y) - self.contentInsetBottom - (margin * self.bottomCardCount) 840 | let scale: CGFloat = self.calculateCardScale(forIndex: bottomIndex) 841 | let yAddition: CGFloat = (self.cardCollectionCellSize.height - (self.cardCollectionCellSize.height * scale)) / 2 842 | let yPos: CGFloat = baseHeight + (bottomIndex * margin) - yAddition 843 | attribute.frame = CGRect.init(x: 0, y: yPos, width: self.cardCollectionCellSize.width, height: self.cardCollectionCellSize.height) 844 | attribute.transform = CGAffineTransform(scaleX: scale, y: scale) 845 | bottomIndex += 1 846 | } else if contentFrame.intersects(currentFrame) { 847 | attribute.isHidden = true 848 | attribute.alpha = 0.0 849 | attribute.frame = CGRect.init(x: 0, y: maxY, width: self.cardCollectionCellSize.width, height: self.cardCollectionCellSize.height) 850 | }else { 851 | attribute.isHidden = true 852 | attribute.alpha = 0.0 853 | attribute.frame = CGRect(x: 0, y: posY, width: cardCollectionCellSize.width, height: cardCollectionCellSize.height) 854 | } 855 | attribute.isRevealed = false 856 | } 857 | 858 | private func calculateCardScale(forIndex index: CGFloat, scaleBehindCard: Bool = false) -> CGFloat { 859 | if(self.bottomStackedCardsShouldScale == true) { 860 | let scalePerCard = self.scalePerCard 861 | let addedDownScale: CGFloat = (scaleBehindCard == true && index < self.bottomCardCount) ? scalePerCard : 0.0 862 | return min(1.0, self.bottomStackedCardsMaximumScale - (((index + 1 - self.bottomCardCount) * -1) * scalePerCard) - addedDownScale) 863 | } 864 | return 1.0 865 | } 866 | 867 | private func calculateCardHeadHeight() -> CGFloat { 868 | var cardHeadHeight = self.cardHeadHeight 869 | if(self.cardShouldExpandHeadHeight == true) { 870 | cardHeadHeight = max(self.cardHeadHeight, (self.collectionView!.frame.height - (self.contentInset.top + self.contentInsetBottom + self.spaceAtTopForBackgroundView)) / CGFloat(self.collectionViewItemCount)) 871 | } 872 | return cardHeadHeight 873 | } 874 | 875 | // MARK: Revealed Card 876 | 877 | private func initializeRevealedCard() -> Bool { 878 | if let cell = self.collectionView?.cellForItem(at: IndexPath(item: self.revealedIndex, section: 0)) { 879 | self.revealedCardCell = cell 880 | self.revealedCardPanGestureRecognizer = UIPanGestureRecognizer(target: self, action: #selector(self.revealedCardPanGestureHandler)) 881 | self.revealedCardPanGestureRecognizer?.delegate = self 882 | self.revealedCardCell?.addGestureRecognizer(self.revealedCardPanGestureRecognizer!) 883 | return true 884 | } 885 | return false 886 | } 887 | 888 | private func deinitializeRevealedCard() { 889 | if self.revealedCardCell != nil && self.revealedCardPanGestureRecognizer != nil { 890 | self.revealedCardCell?.removeGestureRecognizer(self.revealedCardPanGestureRecognizer!) 891 | self.revealedCardPanGestureRecognizer = nil 892 | self.revealedCardCell = nil 893 | } 894 | } 895 | 896 | @objc internal func revealedCardPanGestureHandler() { 897 | if self.collectionViewItemCount == 1 || self.revealedCardIsFlipped == true { 898 | return 899 | } 900 | if let revealedCardPanGestureRecognizer = self.revealedCardPanGestureRecognizer, self.revealedCardCell != nil { 901 | let gestureTouchLocation = revealedCardPanGestureRecognizer.location(in: self.collectionView) 902 | let shiftY: CGFloat = (gestureTouchLocation.y - self.revealedCardPanGestureTouchLocationY > 0) ? gestureTouchLocation.y - self.revealedCardPanGestureTouchLocationY : 0 903 | 904 | switch revealedCardPanGestureRecognizer.state { 905 | case .began: 906 | UIApplication.shared.keyWindow?.endEditing(true) 907 | self.revealedCardPanGestureTouchLocationY = gestureTouchLocation.y 908 | case .changed: 909 | let scaleTarget = self.calculateCardScale(forIndex: self.cardCollectionBottomCardsRevealedIndex, scaleBehindCard: true) 910 | let scaleDiff: CGFloat = 1.0 - scaleTarget 911 | let scale: CGFloat = 1.0 - min(((shiftY * scaleDiff) / (self.collectionView!.frame.height / 2)) , scaleDiff) 912 | let transformY = CGAffineTransform.init(translationX: 0, y: shiftY) 913 | let transformScale = CGAffineTransform.init(scaleX: scale, y: scale) 914 | self.revealedCardCell?.transform = transformY.concatenating(transformScale) 915 | default: 916 | let isNeedReload = (shiftY > self.revealedCardCell!.frame.height / 7) ? true : false 917 | let resetY = (isNeedReload) ? self.collectionView!.frame.height : 0 918 | let scale: CGFloat = (isNeedReload) ? self.calculateCardScale(forIndex: self.cardCollectionBottomCardsRevealedIndex, scaleBehindCard: true) : 1.0 919 | 920 | let transformScale = CGAffineTransform.init(scaleX: scale, y: scale) 921 | let transformY = CGAffineTransform.init(translationX: 0, y: resetY * (1.0 + (1.0 - scale))) 922 | 923 | UIView.animate(withDuration: 0.3, animations: { 924 | self.revealedCardCell?.transform = transformY.concatenating(transformScale) 925 | }, completion: { (finished) in 926 | if isNeedReload && finished { 927 | self.revealCardAt(index: self.revealedIndex) 928 | } 929 | }) 930 | } 931 | } 932 | } 933 | 934 | // MARK: Moving Card 935 | 936 | @objc internal func movingCardGestureHandler() { 937 | let moveUpOffset: CGFloat = 20 938 | 939 | if let movingCardGestureRecognizer = self.movingCardGestureRecognizer { 940 | switch movingCardGestureRecognizer.state { 941 | case .began: 942 | self.movingCardGestureStartLocation = movingCardGestureRecognizer.location(in: self.collectionView) 943 | if let indexPath = self.collectionView?.indexPathForItem(at: self.movingCardGestureStartLocation) { 944 | self.movingCardActive = true 945 | if(indexPath.item < self.firstMovableIndex) { 946 | self.movingCardActive = false 947 | return 948 | } 949 | if let cell = self.collectionView?.cellForItem(at: indexPath) { 950 | self.movingCardStartIndexPath = indexPath 951 | self.movingCardCenterStart = cell.center 952 | self.movingCardSnapshotCell = cell.snapshotView(afterScreenUpdates: false) 953 | self.movingCardSnapshotCell?.frame = cell.frame 954 | self.movingCardSnapshotCell?.alpha = 1.0 955 | self.movingCardSnapshotCell?.layer.zPosition = cell.layer.zPosition 956 | self.collectionView?.insertSubview(self.movingCardSnapshotCell!, aboveSubview: cell) 957 | cell.alpha = 0.0 958 | self.movingCardSelectedIndex = indexPath.item 959 | UIView.animate(withDuration: 0.2, animations: { 960 | self.movingCardSnapshotCell?.frame.origin.y -= moveUpOffset 961 | }) 962 | } 963 | } else { 964 | self.movingCardActive = false 965 | } 966 | case .changed: 967 | if self.movingCardActive == true { 968 | self.movingCardGestureCurrentLocation = movingCardGestureRecognizer.location(in: self.collectionView) 969 | var currentCenter = self.movingCardCenterStart 970 | currentCenter.y += (self.movingCardGestureCurrentLocation.y - self.movingCardGestureStartLocation.y - moveUpOffset) 971 | self.movingCardSnapshotCell?.center = currentCenter 972 | if(self.movingCardGestureCurrentLocation.y > ((self.collectionView!.contentOffset.y + self.collectionView!.frame.height) - self.spaceAtBottom - self.contentInsetBottom - self.scrollAreaBottom)) { 973 | self.setupScrollTimer(direction: .down) 974 | } else if((self.movingCardGestureCurrentLocation.y - self.collectionView!.contentOffset.y) - self.contentInset.top < self.scrollAreaTop) { 975 | self.setupScrollTimer(direction: .up) 976 | } else { 977 | self.invalidateScrollTimer() 978 | } 979 | 980 | var tempIndexPath = self.collectionView?.indexPathForItem(at: self.movingCardGestureCurrentLocation) 981 | if(tempIndexPath == nil) { 982 | tempIndexPath = self.collectionView?.indexPathForItem(at: self.movingCardLastTouchedLocation) 983 | } 984 | 985 | if let currentTouchedIndexPath = tempIndexPath { 986 | self.movingCardLastTouchedLocation = self.movingCardGestureCurrentLocation 987 | if(currentTouchedIndexPath.item < self.firstMovableIndex) { 988 | return 989 | } 990 | if(self.movingCardLastTouchedIndexPath == nil && currentTouchedIndexPath != self.movingCardStartIndexPath!) { 991 | self.movingCardLastTouchedIndexPath = self.movingCardStartIndexPath 992 | } 993 | if(self.self.movingCardLastTouchedIndexPath != nil && self.movingCardLastTouchedIndexPath! != currentTouchedIndexPath) { 994 | let movingCell = self.collectionView?.cellForItem(at: currentTouchedIndexPath) 995 | let movingCellAttr = self.collectionView?.layoutAttributesForItem(at: currentTouchedIndexPath) 996 | 997 | if(movingCell != nil) { 998 | let cardHeadHeight = self.calculateCardHeadHeight() 999 | UIView.animate(withDuration: 0.25, delay: 0, options: .curveEaseOut, animations: { 1000 | movingCell?.frame.origin.y -= cardHeadHeight 1001 | }, completion: { (finished) in 1002 | movingCellAttr?.frame.origin.y -= cardHeadHeight 1003 | }) 1004 | } 1005 | 1006 | self.movingCardSelectedIndex = currentTouchedIndexPath.item 1007 | self.collectionView?.dataSource?.collectionView?(self.collectionView!, moveItemAt: currentTouchedIndexPath, to: self.movingCardLastTouchedIndexPath!) 1008 | UIView.performWithoutAnimation { 1009 | self.collectionView?.moveItem(at: currentTouchedIndexPath, to: self.movingCardLastTouchedIndexPath!) 1010 | } 1011 | 1012 | self.movingCardLastTouchedIndexPath = currentTouchedIndexPath 1013 | if let belowCell = self.collectionView?.cellForItem(at: currentTouchedIndexPath) { 1014 | self.movingCardSnapshotCell?.removeFromSuperview() 1015 | self.collectionView?.insertSubview(self.movingCardSnapshotCell!, belowSubview: belowCell) 1016 | self.movingCardSnapshotCell?.layer.zPosition = belowCell.layer.zPosition 1017 | } else { 1018 | self.collectionView?.sendSubview(toBack: self.movingCardSnapshotCell!) 1019 | } 1020 | } 1021 | } 1022 | } 1023 | case .ended: 1024 | self.invalidateScrollTimer() 1025 | if self.movingCardActive == true { 1026 | var indexPath = self.movingCardStartIndexPath! 1027 | if(self.movingCardLastTouchedIndexPath != nil) { 1028 | indexPath = self.movingCardLastTouchedIndexPath! 1029 | } 1030 | if let cell = self.collectionView?.cellForItem(at: indexPath) { 1031 | UIView.animate(withDuration: 0.2, animations: { 1032 | self.movingCardSnapshotCell?.frame = cell.frame 1033 | }, completion: { (finished) in 1034 | self.movingCardActive = false 1035 | self.movingCardLastTouchedIndexPath = nil 1036 | self.movingCardSelectedIndex = -1 1037 | self.collectionView?.reloadData() 1038 | self.movingCardSnapshotCell?.removeFromSuperview() 1039 | self.movingCardSnapshotCell = nil 1040 | if(self.movingCardStartIndexPath == indexPath) { 1041 | UIView.animate(withDuration: 0, animations: { 1042 | self.invalidateLayout() 1043 | }) 1044 | } 1045 | }) 1046 | } else { 1047 | fallthrough 1048 | } 1049 | } 1050 | case .cancelled: 1051 | self.movingCardActive = false 1052 | self.movingCardLastTouchedIndexPath = nil 1053 | self.movingCardSelectedIndex = -1 1054 | self.collectionView?.reloadData() 1055 | self.movingCardSnapshotCell?.removeFromSuperview() 1056 | self.movingCardSnapshotCell = nil 1057 | self.invalidateLayout() 1058 | default: 1059 | break 1060 | } 1061 | } 1062 | } 1063 | 1064 | // MARK: AutoScroll 1065 | 1066 | enum HFCardCollectionScrollDirection : Int { 1067 | case unknown = 0 1068 | case up 1069 | case down 1070 | } 1071 | 1072 | private func setupScrollTimer(direction: HFCardCollectionScrollDirection) { 1073 | if(self.autoscrollDisplayLink != nil && self.autoscrollDisplayLink!.isPaused == false) { 1074 | if(direction == self.autoscrollDirection) { 1075 | return 1076 | } 1077 | } 1078 | self.invalidateScrollTimer() 1079 | self.autoscrollDisplayLink = CADisplayLink(target: self, selector: #selector(self.autoscrollHandler(displayLink:))) 1080 | self.autoscrollDirection = direction 1081 | self.autoscrollDisplayLink?.add(to: .main, forMode: .commonModes) 1082 | } 1083 | 1084 | private func invalidateScrollTimer() { 1085 | if(self.autoscrollDisplayLink != nil && self.autoscrollDisplayLink!.isPaused == false) { 1086 | self.autoscrollDisplayLink?.invalidate() 1087 | } 1088 | self.autoscrollDisplayLink = nil 1089 | } 1090 | 1091 | @objc internal func autoscrollHandler(displayLink: CADisplayLink) { 1092 | let direction = self.autoscrollDirection 1093 | if(direction == .unknown) { 1094 | return 1095 | } 1096 | 1097 | let scrollMultiplier = self.generateScrollSpeedMultiplier() 1098 | let frameSize = self.collectionView!.frame.size 1099 | let contentSize = self.collectionView!.contentSize 1100 | let contentOffset = self.collectionView!.contentOffset 1101 | let contentInset = self.contentInset 1102 | var distance: CGFloat = CGFloat(rint(scrollMultiplier * displayLink.duration)) 1103 | var translation = CGPoint.zero 1104 | 1105 | switch(direction) { 1106 | case .up: 1107 | distance = -distance 1108 | let minY: CGFloat = 0.0 - contentInset.top 1109 | if (contentOffset.y + distance) <= minY { 1110 | distance = -contentOffset.y - contentInset.top 1111 | } 1112 | translation = CGPoint(x: 0.0, y: distance) 1113 | case .down: 1114 | let maxY: CGFloat = max(contentSize.height, frameSize.height) - frameSize.height + self.contentInsetBottom 1115 | if (contentOffset.y + distance) >= maxY { 1116 | distance = maxY - contentOffset.y 1117 | } 1118 | translation = CGPoint(x: 0.0, y: distance) 1119 | default: 1120 | break 1121 | } 1122 | 1123 | self.collectionView!.contentOffset = self.cgPointAdd(contentOffset, translation) 1124 | self.movingCardGestureHandler() 1125 | } 1126 | 1127 | private func generateScrollSpeedMultiplier() -> Double { 1128 | var multiplier: Double = 250.0 1129 | if let movingCardGestureRecognizer = self.movingCardGestureRecognizer { 1130 | let touchLocation = movingCardGestureRecognizer.location(in: self.collectionView) 1131 | let maxSpeed: CGFloat = 600 1132 | if(self.autoscrollDirection == .up) { 1133 | let touchPosY = min(max(0, self.scrollAreaTop - (touchLocation.y - self.contentOffsetTop)), self.scrollAreaTop) 1134 | multiplier = Double(maxSpeed * (touchPosY / self.scrollAreaTop)) 1135 | } else if(self.autoscrollDirection == .down) { 1136 | let offsetTop = ((self.collectionView!.contentOffset.y + self.collectionView!.frame.height) - self.spaceAtBottom - self.contentInsetBottom - self.scrollAreaBottom) 1137 | let touchPosY = min(max(0, (touchLocation.y - offsetTop)), self.scrollAreaBottom) 1138 | multiplier = Double(maxSpeed * (touchPosY / self.scrollAreaBottom)) 1139 | } 1140 | } 1141 | return multiplier 1142 | } 1143 | 1144 | private func cgPointAdd(_ point1: CGPoint, _ point2: CGPoint) -> CGPoint { 1145 | return CGPoint(x: point1.x + point2.x, y: point1.y + point2.y) 1146 | } 1147 | 1148 | // MARK: UIGestureRecognizerDelegate 1149 | 1150 | /// Return true no card is revealed. 1151 | /// 1152 | /// - Parameter gestureRecognizer: The gesture recognizer. 1153 | public func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool { 1154 | if(gestureRecognizer == self.movingCardGestureRecognizer || gestureRecognizer == self.collectionViewTapGestureRecognizer) { 1155 | if(self.revealedIndex >= 0) { 1156 | return false 1157 | } 1158 | } 1159 | 1160 | if(gestureRecognizer == self.revealedCardPanGestureRecognizer) { 1161 | let velocity = self.revealedCardPanGestureRecognizer?.velocity(in: self.revealedCardPanGestureRecognizer?.view) 1162 | let result = fabs(velocity!.y) > fabs(velocity!.x) 1163 | return result 1164 | } 1165 | return true 1166 | } 1167 | 1168 | } 1169 | --------------------------------------------------------------------------------