├── Circular.png ├── Vertical.png ├── Horizontal.png ├── VerticalSelected.png ├── CentralizedVertical.png ├── CentralizedHorizontal.png ├── VPCollectionViewLayoutExample ├── VPCollectionViewLayoutExample │ ├── Assets.xcassets │ │ ├── Contents.json │ │ ├── sample-1.imageset │ │ │ ├── sample-1.jpeg │ │ │ └── Contents.json │ │ ├── sample-10.imageset │ │ │ ├── sample-10.jpg │ │ │ └── Contents.json │ │ ├── sample-2.imageset │ │ │ ├── sample-2.jpg │ │ │ └── Contents.json │ │ ├── sample-3.imageset │ │ │ ├── sample-3.jpg │ │ │ └── Contents.json │ │ ├── sample-4.imageset │ │ │ ├── sample-4.jpg │ │ │ └── Contents.json │ │ ├── sample-5.imageset │ │ │ ├── sample-5.jpg │ │ │ └── Contents.json │ │ ├── sample-6.imageset │ │ │ ├── sample-6.jpg │ │ │ └── Contents.json │ │ ├── sample-7.imageset │ │ │ ├── sample-7.jpg │ │ │ └── Contents.json │ │ ├── sample-8.imageset │ │ │ ├── sample-8.jpg │ │ │ └── Contents.json │ │ ├── sample-9.imageset │ │ │ ├── sample-9.jpg │ │ │ └── Contents.json │ │ └── AppIcon.appiconset │ │ │ └── Contents.json │ ├── SampleCollectionViewCell.swift │ ├── Info.plist │ ├── Base.lproj │ │ ├── LaunchScreen.storyboard │ │ └── Main.storyboard │ ├── AppDelegate.swift │ ├── ViewController.swift │ └── SampleCollectionViewCell.xib ├── VPCollectionViewLayoutExampleTests │ ├── Info.plist │ └── VPCollectionViewLayoutExampleTests.swift ├── VPCollectionViewLayoutExampleUITests │ ├── Info.plist │ └── VPCollectionViewLayoutExampleUITests.swift └── VPCollectionViewLayoutExample.xcodeproj │ └── project.pbxproj ├── LICENSE ├── .gitignore ├── README.md └── VPCollectionViewLayout └── VPCollectionViewLayout.swift /Circular.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/varunpm1/VPCollectionViewLayout/HEAD/Circular.png -------------------------------------------------------------------------------- /Vertical.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/varunpm1/VPCollectionViewLayout/HEAD/Vertical.png -------------------------------------------------------------------------------- /Horizontal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/varunpm1/VPCollectionViewLayout/HEAD/Horizontal.png -------------------------------------------------------------------------------- /VerticalSelected.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/varunpm1/VPCollectionViewLayout/HEAD/VerticalSelected.png -------------------------------------------------------------------------------- /CentralizedVertical.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/varunpm1/VPCollectionViewLayout/HEAD/CentralizedVertical.png -------------------------------------------------------------------------------- /CentralizedHorizontal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/varunpm1/VPCollectionViewLayout/HEAD/CentralizedHorizontal.png -------------------------------------------------------------------------------- /VPCollectionViewLayoutExample/VPCollectionViewLayoutExample/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /VPCollectionViewLayoutExample/VPCollectionViewLayoutExample/Assets.xcassets/sample-1.imageset/sample-1.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/varunpm1/VPCollectionViewLayout/HEAD/VPCollectionViewLayoutExample/VPCollectionViewLayoutExample/Assets.xcassets/sample-1.imageset/sample-1.jpeg -------------------------------------------------------------------------------- /VPCollectionViewLayoutExample/VPCollectionViewLayoutExample/Assets.xcassets/sample-10.imageset/sample-10.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/varunpm1/VPCollectionViewLayout/HEAD/VPCollectionViewLayoutExample/VPCollectionViewLayoutExample/Assets.xcassets/sample-10.imageset/sample-10.jpg -------------------------------------------------------------------------------- /VPCollectionViewLayoutExample/VPCollectionViewLayoutExample/Assets.xcassets/sample-2.imageset/sample-2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/varunpm1/VPCollectionViewLayout/HEAD/VPCollectionViewLayoutExample/VPCollectionViewLayoutExample/Assets.xcassets/sample-2.imageset/sample-2.jpg -------------------------------------------------------------------------------- /VPCollectionViewLayoutExample/VPCollectionViewLayoutExample/Assets.xcassets/sample-3.imageset/sample-3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/varunpm1/VPCollectionViewLayout/HEAD/VPCollectionViewLayoutExample/VPCollectionViewLayoutExample/Assets.xcassets/sample-3.imageset/sample-3.jpg -------------------------------------------------------------------------------- /VPCollectionViewLayoutExample/VPCollectionViewLayoutExample/Assets.xcassets/sample-4.imageset/sample-4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/varunpm1/VPCollectionViewLayout/HEAD/VPCollectionViewLayoutExample/VPCollectionViewLayoutExample/Assets.xcassets/sample-4.imageset/sample-4.jpg -------------------------------------------------------------------------------- /VPCollectionViewLayoutExample/VPCollectionViewLayoutExample/Assets.xcassets/sample-5.imageset/sample-5.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/varunpm1/VPCollectionViewLayout/HEAD/VPCollectionViewLayoutExample/VPCollectionViewLayoutExample/Assets.xcassets/sample-5.imageset/sample-5.jpg -------------------------------------------------------------------------------- /VPCollectionViewLayoutExample/VPCollectionViewLayoutExample/Assets.xcassets/sample-6.imageset/sample-6.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/varunpm1/VPCollectionViewLayout/HEAD/VPCollectionViewLayoutExample/VPCollectionViewLayoutExample/Assets.xcassets/sample-6.imageset/sample-6.jpg -------------------------------------------------------------------------------- /VPCollectionViewLayoutExample/VPCollectionViewLayoutExample/Assets.xcassets/sample-7.imageset/sample-7.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/varunpm1/VPCollectionViewLayout/HEAD/VPCollectionViewLayoutExample/VPCollectionViewLayoutExample/Assets.xcassets/sample-7.imageset/sample-7.jpg -------------------------------------------------------------------------------- /VPCollectionViewLayoutExample/VPCollectionViewLayoutExample/Assets.xcassets/sample-8.imageset/sample-8.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/varunpm1/VPCollectionViewLayout/HEAD/VPCollectionViewLayoutExample/VPCollectionViewLayoutExample/Assets.xcassets/sample-8.imageset/sample-8.jpg -------------------------------------------------------------------------------- /VPCollectionViewLayoutExample/VPCollectionViewLayoutExample/Assets.xcassets/sample-9.imageset/sample-9.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/varunpm1/VPCollectionViewLayout/HEAD/VPCollectionViewLayoutExample/VPCollectionViewLayoutExample/Assets.xcassets/sample-9.imageset/sample-9.jpg -------------------------------------------------------------------------------- /VPCollectionViewLayoutExample/VPCollectionViewLayoutExample/Assets.xcassets/sample-2.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "sample-2.jpg", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /VPCollectionViewLayoutExample/VPCollectionViewLayoutExample/Assets.xcassets/sample-3.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "sample-3.jpg", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /VPCollectionViewLayoutExample/VPCollectionViewLayoutExample/Assets.xcassets/sample-4.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "sample-4.jpg", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /VPCollectionViewLayoutExample/VPCollectionViewLayoutExample/Assets.xcassets/sample-5.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "sample-5.jpg", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /VPCollectionViewLayoutExample/VPCollectionViewLayoutExample/Assets.xcassets/sample-6.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "sample-6.jpg", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /VPCollectionViewLayoutExample/VPCollectionViewLayoutExample/Assets.xcassets/sample-7.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "sample-7.jpg", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /VPCollectionViewLayoutExample/VPCollectionViewLayoutExample/Assets.xcassets/sample-8.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "sample-8.jpg", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /VPCollectionViewLayoutExample/VPCollectionViewLayoutExample/Assets.xcassets/sample-9.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "sample-9.jpg", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /VPCollectionViewLayoutExample/VPCollectionViewLayoutExample/Assets.xcassets/sample-1.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "sample-1.jpeg", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /VPCollectionViewLayoutExample/VPCollectionViewLayoutExample/Assets.xcassets/sample-10.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "sample-10.jpg", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /VPCollectionViewLayoutExample/VPCollectionViewLayoutExample/SampleCollectionViewCell.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SampleCollectionViewCell.swift 3 | // VPCollectionViewLayoutExample 4 | // 5 | // Created by Varun P M on 05/11/17. 6 | // Copyright © 2017 Varun P M. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class SampleCollectionViewCell: UICollectionViewCell { 12 | @IBOutlet weak var cellTitleLabel: UILabel! 13 | @IBOutlet weak var cellImageView: UIImageView! 14 | 15 | override func awakeFromNib() { 16 | super.awakeFromNib() 17 | // Initialization code 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /VPCollectionViewLayoutExample/VPCollectionViewLayoutExampleTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /VPCollectionViewLayoutExample/VPCollectionViewLayoutExampleUITests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Varun P M 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 | -------------------------------------------------------------------------------- /VPCollectionViewLayoutExample/VPCollectionViewLayoutExampleTests/VPCollectionViewLayoutExampleTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // VPCollectionViewLayoutExampleTests.swift 3 | // VPCollectionViewLayoutExampleTests 4 | // 5 | // Created by Varun P M on 05/11/17. 6 | // Copyright © 2017 Varun P M. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | @testable import VPCollectionViewLayoutExample 11 | 12 | class VPCollectionViewLayoutExampleTests: XCTestCase { 13 | 14 | override func setUp() { 15 | super.setUp() 16 | // Put setup code here. This method is called before the invocation of each test method in the class. 17 | } 18 | 19 | override func tearDown() { 20 | // Put teardown code here. This method is called after the invocation of each test method in the class. 21 | super.tearDown() 22 | } 23 | 24 | func testExample() { 25 | // This is an example of a functional test case. 26 | // Use XCTAssert and related functions to verify your tests produce the correct results. 27 | } 28 | 29 | func testPerformanceExample() { 30 | // This is an example of a performance test case. 31 | self.measure { 32 | // Put the code you want to measure the time of here. 33 | } 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /VPCollectionViewLayoutExample/VPCollectionViewLayoutExampleUITests/VPCollectionViewLayoutExampleUITests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // VPCollectionViewLayoutExampleUITests.swift 3 | // VPCollectionViewLayoutExampleUITests 4 | // 5 | // Created by Varun P M on 05/11/17. 6 | // Copyright © 2017 Varun P M. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | 11 | class VPCollectionViewLayoutExampleUITests: XCTestCase { 12 | 13 | override func setUp() { 14 | super.setUp() 15 | 16 | // Put setup code here. This method is called before the invocation of each test method in the class. 17 | 18 | // In UI tests it is usually best to stop immediately when a failure occurs. 19 | continueAfterFailure = false 20 | // UI tests must launch the application that they test. Doing this in setup will make sure it happens for each test method. 21 | XCUIApplication().launch() 22 | 23 | // In UI tests it’s important to set the initial state - such as interface orientation - required for your tests before they run. The setUp method is a good place to do this. 24 | } 25 | 26 | override func tearDown() { 27 | // Put teardown code here. This method is called after the invocation of each test method in the class. 28 | super.tearDown() 29 | } 30 | 31 | func testExample() { 32 | // Use recording to get started writing UI tests. 33 | // Use XCTAssert and related functions to verify your tests produce the correct results. 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /VPCollectionViewLayoutExample/VPCollectionViewLayoutExample/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 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 | # Xcode 2 | # 3 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 4 | 5 | ## Build generated 6 | build/ 7 | DerivedData/ 8 | 9 | ## Various settings 10 | *.pbxuser 11 | !default.pbxuser 12 | *.mode1v3 13 | !default.mode1v3 14 | *.mode2v3 15 | !default.mode2v3 16 | *.perspectivev3 17 | !default.perspectivev3 18 | xcuserdata/ 19 | 20 | ## Other 21 | *.moved-aside 22 | *.xccheckout 23 | *.xcscmblueprint 24 | 25 | ## Obj-C/Swift specific 26 | *.hmap 27 | *.ipa 28 | *.dSYM.zip 29 | *.dSYM 30 | 31 | ## Playgrounds 32 | timeline.xctimeline 33 | playground.xcworkspace 34 | 35 | # Swift Package Manager 36 | # 37 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies. 38 | # Packages/ 39 | # Package.pins 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://docs.fastlane.tools/best-practices/source-control/#source-control 63 | 64 | fastlane/report.xml 65 | fastlane/Preview.html 66 | fastlane/screenshots 67 | fastlane/test_output 68 | -------------------------------------------------------------------------------- /VPCollectionViewLayoutExample/VPCollectionViewLayoutExample/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 | -------------------------------------------------------------------------------- /VPCollectionViewLayoutExample/VPCollectionViewLayoutExample/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 | } -------------------------------------------------------------------------------- /VPCollectionViewLayoutExample/VPCollectionViewLayoutExample/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // VPCollectionViewLayoutExample 4 | // 5 | // Created by Varun P M on 05/11/17. 6 | // Copyright © 2017 Varun P M. 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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # VPCollectionViewLayout 2 | 3 | VPCollectionViewLayout is a helper class for displaying collection view cells in different flow layouts. The UI can be customized in different ways. See `Usage` for more info. 4 | 5 | ## Requirements 6 | 7 | 1. iOS 9.0 or later. 8 | 2. ARC memory management. 9 | 10 | ## Usage 11 | 12 | Drag and drop the folder `VPCollectionViewLayout` into your project. Add a collection view wherever needed in your xib or storyboard and change the flow layout class to custom and enter the name of the class as `VPCollectionViewLayout`. 13 | 14 | The currently supported layouts are - vertical, horizontal, centralizedVertical, centralizedHorizontal and circular (more detail explanation mentioned within the class). 15 | 16 | 17 | # ![Screenshot](/Vertical.png) 18 | # ![Screenshot](/Horizontal.png) 19 | # ![Screenshot](/CentralizedVertical.png) 20 | # ![Screenshot](/CentralizedHorizontal.png) 21 | # ![Screenshot](/VerticalSelected.png) 22 | # ![Screenshot](/Circular.png) 23 | 24 | ## Contributing 25 | **Type - 1** 26 | 27 | 1. Fork it! 28 | 2. Create your feature branch: `git checkout -b my-new-feature` 29 | 3. Commit your changes: `git commit -am 'Add some feature'` 30 | 4. Push to the branch: `git push origin my-new-feature` 31 | 5. Submit a pull request :D 32 | 33 | **Type - 2** 34 | 35 | 1. Create an issue/explain the needed flowlayout 36 | 2. I'll try to add the necessry feature 37 | 3. You can download the update code :P 38 | 39 | ## History 40 | 41 | ### Version 0.2.1 42 | 43 | Added sample images and set default layout to be vertical 44 | 45 | ### Version 0.2.0 46 | 47 | Added support for circular flow layout 48 | 49 | ### Version 0.1.0 50 | 51 | Added support for 4 types of flow layout display as well as selection animation handled with animation. 52 | 53 | 54 | ## License 55 | MIT License 56 | 57 | Copyright (c) 2017 Varun P M 58 | 59 | Permission is hereby granted, free of charge, to any person obtaining a copy 60 | of this software and associated documentation files (the "Software"), to deal 61 | in the Software without restriction, including without limitation the rights 62 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 63 | copies of the Software, and to permit persons to whom the Software is 64 | furnished to do so, subject to the following conditions: 65 | 66 | The above copyright notice and this permission notice shall be included in all 67 | copies or substantial portions of the Software. 68 | 69 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 70 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 71 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 72 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 73 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 74 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 75 | SOFTWARE. 76 | -------------------------------------------------------------------------------- /VPCollectionViewLayoutExample/VPCollectionViewLayoutExample/ViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.swift 3 | // VPCollectionViewLayoutExample 4 | // 5 | // Created by Varun P M on 05/11/17. 6 | // Copyright © 2017 Varun P M. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class ViewController: UIViewController { 12 | 13 | @IBOutlet weak var sampleCollectionView: UICollectionView! 14 | 15 | override func viewDidLoad() { 16 | super.viewDidLoad() 17 | // Do any additional setup after loading the view, typically from a nib. 18 | 19 | sampleCollectionView.register(UINib(nibName: String(describing: SampleCollectionViewCell.self), bundle: nil), forCellWithReuseIdentifier: String(describing: SampleCollectionViewCell.self)) 20 | } 21 | 22 | override func didReceiveMemoryWarning() { 23 | super.didReceiveMemoryWarning() 24 | // Dispose of any resources that can be recreated. 25 | } 26 | } 27 | 28 | extension ViewController: UICollectionViewDataSource, UICollectionViewDelegateFlowLayout { 29 | func numberOfSections(in collectionView: UICollectionView) -> Int { 30 | return 1 31 | } 32 | 33 | func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { 34 | return 10 35 | } 36 | 37 | func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { 38 | let cell = collectionView.dequeueReusableCell(withReuseIdentifier: String(describing: SampleCollectionViewCell.self), for: indexPath) as! SampleCollectionViewCell 39 | 40 | // cell.cellTitleLabel.text = "\(indexPath.item + 1)" 41 | cell.cellImageView.image = UIImage(named: "sample-\(indexPath.item + 1)") 42 | 43 | return cell 44 | } 45 | 46 | func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { 47 | (collectionView.collectionViewLayout as! VPCollectionViewLayout).didSelectCell(atIndexPath: indexPath) 48 | } 49 | 50 | func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize { 51 | return CGSize(width: 150, height: 150) 52 | } 53 | 54 | func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat { 55 | return 20 56 | } 57 | 58 | func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat { 59 | return 10 60 | } 61 | 62 | func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets { 63 | return UIEdgeInsets(top: 25, left: 25, bottom: 25, right: 25) 64 | } 65 | } 66 | 67 | -------------------------------------------------------------------------------- /VPCollectionViewLayoutExample/VPCollectionViewLayoutExample/Base.lproj/Main.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 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /VPCollectionViewLayoutExample/VPCollectionViewLayoutExample/SampleCollectionViewCell.xib: -------------------------------------------------------------------------------- 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 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /VPCollectionViewLayout/VPCollectionViewLayout.swift: -------------------------------------------------------------------------------- 1 | // 2 | // VPCollectionViewLayout.swift 3 | // VPCollectionViewLayoutExample 4 | // Version 0.1.0 5 | // 6 | // Created by Varun P M on 05/11/17. 7 | // Copyright © 2017 Varun P M. All rights reserved. 8 | // 9 | // 10 | // MIT License 11 | // 12 | // Copyright (c) 2017 Varun P M 13 | // 14 | // Permission is hereby granted, free of charge, to any person obtaining a copy 15 | // of this software and associated documentation files (the "Software"), to deal 16 | // in the Software without restriction, including without limitation the rights 17 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 18 | // copies of the Software, and to permit persons to whom the Software is 19 | // furnished to do so, subject to the following conditions: 20 | // 21 | // The above copyright notice and this permission notice shall be included in all 22 | // copies or substantial portions of the Software. 23 | // 24 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 25 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 26 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 27 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 28 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 29 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 30 | // SOFTWARE. 31 | 32 | import UIKit 33 | 34 | class VPCollectionViewLayout: UICollectionViewFlowLayout { 35 | // Enum that contains all possible combination of layout type 36 | enum CollectionViewLayoutType { 37 | /// If vertical flow, then the cells will be placed horizontally first until it fits the current collectionView's width. If the cell doesn't fit, then it'll be moved to next row and same flow follows for remaining cells. If there is a new section, then the cell will be forced to the next row forcefully. 38 | case vertical 39 | 40 | /// If horizontal flow, then the cells will be placed vertically first until it fits the current collectionView's height. If the cell doesn't fit, then it'll be moved to next column and same flow follows for remaining cells. If there is a new section, then the cell will be forced to the next column forcefully. 41 | case horizontal 42 | 43 | /// If centralized flow, then the cells will be placed centered and vertically and will be adjusted to accomodate as much as possible. 44 | /// - Note: The cell's height for all the cell within a section has to be constant for this to look good. 45 | case centralizedVertical 46 | 47 | /// If centralized flow, then the cells will be placed centered and horizontally and will be adjusted to accomodate as much as possible. 48 | /// - Note: The cell's width for all the cell within a section has to be constant for this to look good. 49 | case centralizedHorizontal 50 | 51 | /// If circular flow, then the cells will be placed circularly and will be adjusted to accomodate as much as possible. 52 | /// - Note: This will place the cell in round table format and doesn't rotate the cell itself. Also this layout assumes that all the cell's are of same size. Different sized cells are not supported. 53 | case circular 54 | } 55 | 56 | /// Set the required layout type from the defined set of types. Defaults to vertical 57 | var layoutType: CollectionViewLayoutType = .vertical 58 | 59 | /// The radius to be used for circular layout. Defaults to 100 60 | var circularRadius: CGFloat = 100 61 | 62 | // This variable is used if a cell has to be expanded when selecting pushing other nearby cells maintaining the same spacing 63 | private var selectedIndexPath: IndexPath? 64 | 65 | // This variable is used only if `selectedIndexPath` is set. This variable takes values by which the size has to be increased or decreased for the selected cell. The values should be in range between 0 (decrease to 0) to +ve (increase by any value) 66 | private var selectedCellExpandPercentage: CGSize = CGSize(width: 1, height: 1) 67 | 68 | // Stored calculated attributes for caching purpose 69 | private var layoutAttributes: [UICollectionViewLayoutAttributes] = [] 70 | private var maxContentWidth: CGFloat = 0 71 | private var maxContentHeight: CGFloat = 0 72 | 73 | override func prepare() { 74 | layoutAttributes.removeAll() 75 | calculateLayoutAttributes() 76 | } 77 | 78 | override var collectionViewContentSize: CGSize { 79 | return CGSize(width: maxContentWidth, height: maxContentHeight) 80 | } 81 | 82 | /// Call this method when selection of cell should expand the cell with/without animation 83 | /// 84 | /// - Parameters: 85 | /// - indexPath: the indexpath of the cell whose size has to be increased or decreased. If set to nil, the size will return to default size if there are any previously selected cells. 86 | /// - percentageIncrease: the size increase in percentage/100 format for the selected cell. If width and height are 1 & 1, then default size. If greater than 1, then it's increased size and vice-versa. Defaults to 1.25 each (125% of the current size). 87 | /// - shouldAnimate: optional bool variable to animate selection if needed. Defaults to `true`. 88 | func didSelectCell(atIndexPath indexPath: IndexPath?, percentageIncrease: CGSize = CGSize(width: 1.25, height: 1.25), shouldAnimate: Bool = true) { 89 | selectedIndexPath = indexPath 90 | selectedCellExpandPercentage = percentageIncrease 91 | 92 | if shouldAnimate { 93 | collectionView?.performBatchUpdates({ [weak self] in 94 | self?.invalidateLayout() 95 | }, completion: nil) 96 | } 97 | else { 98 | invalidateLayout() 99 | } 100 | } 101 | 102 | // Calculate the attributes when invalidating layout 103 | private func calculateLayoutAttributes() { 104 | guard let collectionView = collectionView else { 105 | return 106 | } 107 | 108 | maxContentWidth = 0 109 | maxContentHeight = 0 110 | 111 | var adjustedInsets: CGFloat = 0 112 | if #available(iOS 11, *) { 113 | adjustedInsets = collectionView.adjustedContentInset.top 114 | } 115 | 116 | let adjustedCollectionViewHeight = collectionView.bounds.size.height - adjustedInsets 117 | var startXPosition: CGFloat = 0 118 | var startYPosition: CGFloat = 0 119 | var availableWidth: CGFloat = collectionView.bounds.size.width 120 | var availableHeight: CGFloat = adjustedCollectionViewHeight 121 | var angle: CGFloat = 0 122 | 123 | for sectionIndex in stride(from: 0, to: collectionView.numberOfSections, by: 1) { 124 | // Fetch the default values for spacing, if set 125 | var defaultSectionInsets: UIEdgeInsets = sectionInset 126 | var defaultInterItemSpacing: CGFloat = minimumInteritemSpacing 127 | var defaultLineSpacing: CGFloat = minimumLineSpacing 128 | 129 | if let delagteFlowLayout = collectionView.delegate as? UICollectionViewDelegateFlowLayout { 130 | // Get the insets from the controller/view if the insets delegate has been implemented 131 | if delagteFlowLayout.responds(to: #selector(delagteFlowLayout.collectionView(_:layout:insetForSectionAt:))) { 132 | defaultSectionInsets = delagteFlowLayout.collectionView!(collectionView, layout: self, insetForSectionAt: sectionIndex) 133 | } 134 | 135 | // Get the inter item spacing from the controller/view if the inter item spacing delegate has been implemented 136 | if delagteFlowLayout.responds(to: #selector(delagteFlowLayout.collectionView(_:layout:minimumInteritemSpacingForSectionAt:))) { 137 | defaultInterItemSpacing = delagteFlowLayout.collectionView!(collectionView, layout: self, minimumInteritemSpacingForSectionAt: sectionIndex) 138 | } 139 | 140 | // Get the line spacing from the controller/view if the line spacing delegate has been implemented 141 | if delagteFlowLayout.responds(to: #selector(delagteFlowLayout.collectionView(_:layout:minimumLineSpacingForSectionAt:))) { 142 | defaultLineSpacing = delagteFlowLayout.collectionView!(collectionView, layout: self, minimumLineSpacingForSectionAt: sectionIndex) 143 | } 144 | } 145 | 146 | // Update content variables based on layout type 147 | switch layoutType { 148 | case .vertical, .centralizedVertical: 149 | startYPosition += defaultSectionInsets.top 150 | startXPosition = defaultSectionInsets.left 151 | maxContentHeight += defaultSectionInsets.top 152 | availableWidth = (collectionView.bounds.size.width - defaultSectionInsets.left - defaultSectionInsets.right) 153 | case .horizontal, .centralizedHorizontal: 154 | startXPosition += defaultSectionInsets.left 155 | startYPosition = defaultSectionInsets.top 156 | maxContentWidth += defaultSectionInsets.left 157 | availableHeight = (adjustedCollectionViewHeight - defaultSectionInsets.top - defaultSectionInsets.bottom) 158 | case .circular: 159 | angle = 2 * CGFloat.pi / CGFloat(collectionView.numberOfItems(inSection: sectionIndex)) 160 | } 161 | 162 | for itemIndex in stride(from: 0, to: collectionView.numberOfItems(inSection: sectionIndex), by: 1) { 163 | let indexPath = IndexPath(item: itemIndex, section: sectionIndex) 164 | let layoutAttribute = UICollectionViewLayoutAttributes(forCellWith: indexPath) 165 | var itemSize: CGSize = CGSize.zero 166 | 167 | // Get the item size from the controller/view if the itemSize delegate has been implemented 168 | if let delagteFlowLayout = collectionView.delegate as? UICollectionViewDelegateFlowLayout { 169 | if delagteFlowLayout.responds(to: #selector(delagteFlowLayout.collectionView(_:layout:sizeForItemAt:))) { 170 | itemSize = delagteFlowLayout.collectionView!(collectionView, layout: self, sizeForItemAt: indexPath) 171 | 172 | if selectedIndexPath == indexPath { 173 | layoutAttribute.frame.size = CGSize(width: itemSize.width * selectedCellExpandPercentage.width, height: itemSize.height * selectedCellExpandPercentage.width) 174 | } 175 | else { 176 | layoutAttribute.frame.size = itemSize 177 | } 178 | } 179 | } 180 | 181 | // Calculate the proper layout for each cases 182 | switch layoutType { 183 | case .vertical, .centralizedVertical: 184 | if layoutType == .centralizedVertical { 185 | // Check if the cell can be placed in the same row. If not, then increase Y position for height related values and update frame 186 | availableWidth -= layoutAttribute.frame.size.width 187 | 188 | // Remove padding only if it's not a first cell 189 | if itemIndex != 0 { 190 | availableWidth -= defaultInterItemSpacing 191 | } 192 | 193 | if availableWidth >= 0 { 194 | // Loop through all attributes in reverse order to identify attribute that is in same row. Then reposition all attributes centered and place the current attribute again 195 | for attribute in layoutAttributes.reversed() { 196 | if attribute.frame.origin.y != startYPosition { 197 | break 198 | } 199 | 200 | // Adjust the attribute that lie in same line so as to make it center aligned 201 | attribute.frame.origin.x -= (defaultInterItemSpacing + layoutAttribute.frame.size.width) / 2 202 | } 203 | 204 | // If not first index, then get X value of last attribute and add spacing to get next X position. Else get X position w.r.t collectionView width 205 | if itemIndex != 0 { 206 | startXPosition = layoutAttributes.last!.frame.maxX + defaultInterItemSpacing 207 | } 208 | else { 209 | startXPosition = (collectionView.bounds.size.width - layoutAttribute.frame.size.width) / 2 210 | } 211 | } 212 | else { 213 | // Move to next row and set necessary values 214 | availableWidth = collectionView.bounds.size.width - defaultSectionInsets.left - defaultSectionInsets.right - layoutAttribute.frame.size.width 215 | startXPosition = (collectionView.bounds.size.width - layoutAttribute.frame.size.width) / 2 216 | startYPosition = maxContentHeight + defaultLineSpacing 217 | } 218 | } 219 | else { 220 | if (startXPosition + defaultInterItemSpacing + layoutAttribute.frame.size.width + defaultSectionInsets.right) <= collectionView.bounds.size.width { 221 | // Add spacing only if it's not a first cell 222 | if itemIndex != 0 { 223 | startXPosition += defaultInterItemSpacing 224 | } 225 | } 226 | else { 227 | startXPosition = defaultSectionInsets.left 228 | startYPosition = maxContentHeight + defaultLineSpacing 229 | } 230 | } 231 | 232 | // Update frame origin 233 | layoutAttribute.frame.origin = CGPoint(x: startXPosition, y: startYPosition) 234 | startXPosition = layoutAttribute.frame.maxX 235 | 236 | // Get max height for collectionView 237 | maxContentHeight = max(maxContentHeight, layoutAttribute.frame.maxY) 238 | 239 | // If the item is the last item, then add the bottom insets for the section 240 | if itemIndex == (collectionView.numberOfItems(inSection: sectionIndex) - 1) { 241 | maxContentHeight += defaultSectionInsets.bottom 242 | startYPosition = maxContentHeight 243 | } 244 | 245 | // Width doesn't increase here 246 | maxContentWidth = collectionView.bounds.size.width 247 | case .horizontal, .centralizedHorizontal: 248 | if layoutType == .centralizedHorizontal { 249 | // Check if the cell can be placed in the same column. If not, then increase X position for width related values and update frame 250 | availableHeight -= layoutAttribute.frame.size.height 251 | 252 | // Remove padding only if it's not a first cell 253 | if itemIndex != 0 { 254 | availableHeight -= defaultInterItemSpacing 255 | } 256 | 257 | if availableHeight >= 0 { 258 | // Loop through all attributes in reverse order to identify attribute that is in same column. Then reposition all attributes centered and place the current attribute again 259 | for attribute in layoutAttributes.reversed() { 260 | if attribute.frame.origin.x != startXPosition { 261 | break 262 | } 263 | 264 | // Adjust the attribute that lie in same line so as to make it center aligned 265 | attribute.frame.origin.y -= (defaultInterItemSpacing + layoutAttribute.frame.size.height) / 2 266 | } 267 | 268 | // If not first index, then get Y value of last attribute and add spacing to get next Y position. Else get Y position w.r.t collectionView height 269 | if itemIndex != 0 { 270 | startYPosition = layoutAttributes.last!.frame.maxY + defaultInterItemSpacing 271 | } 272 | else { 273 | startYPosition = (adjustedCollectionViewHeight - layoutAttribute.frame.size.height) / 2 274 | } 275 | } 276 | else { 277 | // Move to next column and set necessary values 278 | availableHeight = adjustedCollectionViewHeight - defaultSectionInsets.top - defaultSectionInsets.bottom - layoutAttribute.frame.size.height 279 | startYPosition = (adjustedCollectionViewHeight - layoutAttribute.frame.size.height) / 2 280 | startXPosition = maxContentWidth + defaultLineSpacing 281 | } 282 | } 283 | else { 284 | // Check if the cell can be placed in the same column. If not, then increase X position for width related values and update frame 285 | if (startYPosition + defaultInterItemSpacing + layoutAttribute.frame.size.height + defaultSectionInsets.bottom) <= adjustedCollectionViewHeight { 286 | // Add spacing only if it's not a first cell 287 | if itemIndex != 0 { 288 | startYPosition += defaultInterItemSpacing 289 | } 290 | } 291 | else { 292 | startYPosition = defaultSectionInsets.top 293 | startXPosition = maxContentWidth + defaultLineSpacing 294 | } 295 | } 296 | 297 | // Update frame origin 298 | layoutAttribute.frame.origin = CGPoint(x: startXPosition, y: startYPosition) 299 | startYPosition = layoutAttribute.frame.maxY 300 | 301 | // Get max width for collectionView 302 | maxContentWidth = max(maxContentWidth, layoutAttribute.frame.maxX) 303 | 304 | // If the item is the last item, then add the bottom insets for the section 305 | if itemIndex == (collectionView.numberOfItems(inSection: sectionIndex) - 1) { 306 | maxContentWidth += defaultSectionInsets.right 307 | startXPosition = maxContentWidth 308 | } 309 | 310 | // Height doesn't increase here 311 | maxContentHeight = adjustedCollectionViewHeight 312 | case .circular: 313 | // Since max content width would be diameter of circle + left and right section insets + cell's size (left and right cell will be clipped to half since placed at center) 314 | maxContentWidth = 2 * circularRadius + layoutAttribute.frame.size.width + defaultSectionInsets.left + defaultSectionInsets.right 315 | 316 | // Since max content height would be diameter of circle + top and bottom section insets + cell's size (top and bottom cell will be clipped to half since placed at center) 317 | maxContentHeight = 2 * circularRadius + layoutAttribute.frame.size.height + defaultSectionInsets.top + defaultSectionInsets.bottom 318 | 319 | // If the selected index path is the same as the current index path, then the cell is expanded. So get the increased size to calculate padding 320 | let increasedPaddingSize: CGSize = CGSize(width: (layoutAttribute.frame.size.width - itemSize.width) / 2, height: (layoutAttribute.frame.size.height - itemSize.height) / 2) 321 | 322 | // Calculate the position for the layout attribute 323 | let actualAngle = CGFloat(itemIndex) * angle 324 | let xPositionPadding = circularRadius + defaultSectionInsets.left - increasedPaddingSize.width 325 | let yPositionPadding = circularRadius + defaultSectionInsets.top - increasedPaddingSize.height 326 | layoutAttribute.frame.origin = CGPoint(x: xPositionPadding + circularRadius * cos(actualAngle), y: yPositionPadding + circularRadius * sin(actualAngle)) 327 | } 328 | 329 | layoutAttributes.append(layoutAttribute) 330 | } 331 | } 332 | } 333 | 334 | override func shouldInvalidateLayout(forBoundsChange newBounds: CGRect) -> Bool { 335 | return false 336 | } 337 | 338 | override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? { 339 | var matchingLayoutAttributes = [UICollectionViewLayoutAttributes]() 340 | 341 | for attribute in layoutAttributes { 342 | if attribute.frame.intersects(rect) { 343 | matchingLayoutAttributes.append(attribute) 344 | } 345 | } 346 | 347 | return matchingLayoutAttributes 348 | } 349 | } 350 | -------------------------------------------------------------------------------- /VPCollectionViewLayoutExample/VPCollectionViewLayoutExample.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 48; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 8B33BD191FAEFE81002046F2 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8B33BD181FAEFE81002046F2 /* AppDelegate.swift */; }; 11 | 8B33BD1B1FAEFE81002046F2 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8B33BD1A1FAEFE81002046F2 /* ViewController.swift */; }; 12 | 8B33BD1E1FAEFE81002046F2 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 8B33BD1C1FAEFE81002046F2 /* Main.storyboard */; }; 13 | 8B33BD201FAEFE81002046F2 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 8B33BD1F1FAEFE81002046F2 /* Assets.xcassets */; }; 14 | 8B33BD231FAEFE81002046F2 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 8B33BD211FAEFE81002046F2 /* LaunchScreen.storyboard */; }; 15 | 8B33BD2E1FAEFE81002046F2 /* VPCollectionViewLayoutExampleTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8B33BD2D1FAEFE81002046F2 /* VPCollectionViewLayoutExampleTests.swift */; }; 16 | 8B33BD391FAEFE81002046F2 /* VPCollectionViewLayoutExampleUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8B33BD381FAEFE81002046F2 /* VPCollectionViewLayoutExampleUITests.swift */; }; 17 | 8B33BD481FAEFF10002046F2 /* VPCollectionViewLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8B33BD471FAEFF10002046F2 /* VPCollectionViewLayout.swift */; }; 18 | 8B33BD4B1FAF01A1002046F2 /* SampleCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8B33BD491FAF01A1002046F2 /* SampleCollectionViewCell.swift */; }; 19 | 8B33BD4C1FAF01A1002046F2 /* SampleCollectionViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 8B33BD4A1FAF01A1002046F2 /* SampleCollectionViewCell.xib */; }; 20 | /* End PBXBuildFile section */ 21 | 22 | /* Begin PBXContainerItemProxy section */ 23 | 8B33BD2A1FAEFE81002046F2 /* PBXContainerItemProxy */ = { 24 | isa = PBXContainerItemProxy; 25 | containerPortal = 8B33BD0D1FAEFE81002046F2 /* Project object */; 26 | proxyType = 1; 27 | remoteGlobalIDString = 8B33BD141FAEFE81002046F2; 28 | remoteInfo = VPCollectionViewLayoutExample; 29 | }; 30 | 8B33BD351FAEFE81002046F2 /* PBXContainerItemProxy */ = { 31 | isa = PBXContainerItemProxy; 32 | containerPortal = 8B33BD0D1FAEFE81002046F2 /* Project object */; 33 | proxyType = 1; 34 | remoteGlobalIDString = 8B33BD141FAEFE81002046F2; 35 | remoteInfo = VPCollectionViewLayoutExample; 36 | }; 37 | /* End PBXContainerItemProxy section */ 38 | 39 | /* Begin PBXFileReference section */ 40 | 8B33BD151FAEFE81002046F2 /* VPCollectionViewLayoutExample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = VPCollectionViewLayoutExample.app; sourceTree = BUILT_PRODUCTS_DIR; }; 41 | 8B33BD181FAEFE81002046F2 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 42 | 8B33BD1A1FAEFE81002046F2 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; 43 | 8B33BD1D1FAEFE81002046F2 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 44 | 8B33BD1F1FAEFE81002046F2 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 45 | 8B33BD221FAEFE81002046F2 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 46 | 8B33BD241FAEFE81002046F2 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 47 | 8B33BD291FAEFE81002046F2 /* VPCollectionViewLayoutExampleTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = VPCollectionViewLayoutExampleTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 48 | 8B33BD2D1FAEFE81002046F2 /* VPCollectionViewLayoutExampleTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VPCollectionViewLayoutExampleTests.swift; sourceTree = ""; }; 49 | 8B33BD2F1FAEFE81002046F2 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 50 | 8B33BD341FAEFE81002046F2 /* VPCollectionViewLayoutExampleUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = VPCollectionViewLayoutExampleUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 51 | 8B33BD381FAEFE81002046F2 /* VPCollectionViewLayoutExampleUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VPCollectionViewLayoutExampleUITests.swift; sourceTree = ""; }; 52 | 8B33BD3A1FAEFE81002046F2 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 53 | 8B33BD471FAEFF10002046F2 /* VPCollectionViewLayout.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VPCollectionViewLayout.swift; sourceTree = ""; }; 54 | 8B33BD491FAF01A1002046F2 /* SampleCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SampleCollectionViewCell.swift; sourceTree = ""; }; 55 | 8B33BD4A1FAF01A1002046F2 /* SampleCollectionViewCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = SampleCollectionViewCell.xib; sourceTree = ""; }; 56 | /* End PBXFileReference section */ 57 | 58 | /* Begin PBXFrameworksBuildPhase section */ 59 | 8B33BD121FAEFE81002046F2 /* Frameworks */ = { 60 | isa = PBXFrameworksBuildPhase; 61 | buildActionMask = 2147483647; 62 | files = ( 63 | ); 64 | runOnlyForDeploymentPostprocessing = 0; 65 | }; 66 | 8B33BD261FAEFE81002046F2 /* Frameworks */ = { 67 | isa = PBXFrameworksBuildPhase; 68 | buildActionMask = 2147483647; 69 | files = ( 70 | ); 71 | runOnlyForDeploymentPostprocessing = 0; 72 | }; 73 | 8B33BD311FAEFE81002046F2 /* Frameworks */ = { 74 | isa = PBXFrameworksBuildPhase; 75 | buildActionMask = 2147483647; 76 | files = ( 77 | ); 78 | runOnlyForDeploymentPostprocessing = 0; 79 | }; 80 | /* End PBXFrameworksBuildPhase section */ 81 | 82 | /* Begin PBXGroup section */ 83 | 8B33BD0C1FAEFE81002046F2 = { 84 | isa = PBXGroup; 85 | children = ( 86 | 8B33BD171FAEFE81002046F2 /* VPCollectionViewLayoutExample */, 87 | 8B33BD2C1FAEFE81002046F2 /* VPCollectionViewLayoutExampleTests */, 88 | 8B33BD371FAEFE81002046F2 /* VPCollectionViewLayoutExampleUITests */, 89 | 8B33BD161FAEFE81002046F2 /* Products */, 90 | ); 91 | sourceTree = ""; 92 | }; 93 | 8B33BD161FAEFE81002046F2 /* Products */ = { 94 | isa = PBXGroup; 95 | children = ( 96 | 8B33BD151FAEFE81002046F2 /* VPCollectionViewLayoutExample.app */, 97 | 8B33BD291FAEFE81002046F2 /* VPCollectionViewLayoutExampleTests.xctest */, 98 | 8B33BD341FAEFE81002046F2 /* VPCollectionViewLayoutExampleUITests.xctest */, 99 | ); 100 | name = Products; 101 | sourceTree = ""; 102 | }; 103 | 8B33BD171FAEFE81002046F2 /* VPCollectionViewLayoutExample */ = { 104 | isa = PBXGroup; 105 | children = ( 106 | 8B33BD181FAEFE81002046F2 /* AppDelegate.swift */, 107 | 8B33BD461FAEFEE8002046F2 /* VPCollectionViewLayout */, 108 | 8B33BD1A1FAEFE81002046F2 /* ViewController.swift */, 109 | 8B33BD491FAF01A1002046F2 /* SampleCollectionViewCell.swift */, 110 | 8B33BD4A1FAF01A1002046F2 /* SampleCollectionViewCell.xib */, 111 | 8B33BD1C1FAEFE81002046F2 /* Main.storyboard */, 112 | 8B33BD1F1FAEFE81002046F2 /* Assets.xcassets */, 113 | 8B33BD211FAEFE81002046F2 /* LaunchScreen.storyboard */, 114 | 8B33BD241FAEFE81002046F2 /* Info.plist */, 115 | ); 116 | path = VPCollectionViewLayoutExample; 117 | sourceTree = ""; 118 | }; 119 | 8B33BD2C1FAEFE81002046F2 /* VPCollectionViewLayoutExampleTests */ = { 120 | isa = PBXGroup; 121 | children = ( 122 | 8B33BD2D1FAEFE81002046F2 /* VPCollectionViewLayoutExampleTests.swift */, 123 | 8B33BD2F1FAEFE81002046F2 /* Info.plist */, 124 | ); 125 | path = VPCollectionViewLayoutExampleTests; 126 | sourceTree = ""; 127 | }; 128 | 8B33BD371FAEFE81002046F2 /* VPCollectionViewLayoutExampleUITests */ = { 129 | isa = PBXGroup; 130 | children = ( 131 | 8B33BD381FAEFE81002046F2 /* VPCollectionViewLayoutExampleUITests.swift */, 132 | 8B33BD3A1FAEFE81002046F2 /* Info.plist */, 133 | ); 134 | path = VPCollectionViewLayoutExampleUITests; 135 | sourceTree = ""; 136 | }; 137 | 8B33BD461FAEFEE8002046F2 /* VPCollectionViewLayout */ = { 138 | isa = PBXGroup; 139 | children = ( 140 | 8B33BD471FAEFF10002046F2 /* VPCollectionViewLayout.swift */, 141 | ); 142 | name = VPCollectionViewLayout; 143 | path = ../../VPCollectionViewLayout; 144 | sourceTree = ""; 145 | }; 146 | /* End PBXGroup section */ 147 | 148 | /* Begin PBXNativeTarget section */ 149 | 8B33BD141FAEFE81002046F2 /* VPCollectionViewLayoutExample */ = { 150 | isa = PBXNativeTarget; 151 | buildConfigurationList = 8B33BD3D1FAEFE81002046F2 /* Build configuration list for PBXNativeTarget "VPCollectionViewLayoutExample" */; 152 | buildPhases = ( 153 | 8B33BD111FAEFE81002046F2 /* Sources */, 154 | 8B33BD121FAEFE81002046F2 /* Frameworks */, 155 | 8B33BD131FAEFE81002046F2 /* Resources */, 156 | ); 157 | buildRules = ( 158 | ); 159 | dependencies = ( 160 | ); 161 | name = VPCollectionViewLayoutExample; 162 | productName = VPCollectionViewLayoutExample; 163 | productReference = 8B33BD151FAEFE81002046F2 /* VPCollectionViewLayoutExample.app */; 164 | productType = "com.apple.product-type.application"; 165 | }; 166 | 8B33BD281FAEFE81002046F2 /* VPCollectionViewLayoutExampleTests */ = { 167 | isa = PBXNativeTarget; 168 | buildConfigurationList = 8B33BD401FAEFE81002046F2 /* Build configuration list for PBXNativeTarget "VPCollectionViewLayoutExampleTests" */; 169 | buildPhases = ( 170 | 8B33BD251FAEFE81002046F2 /* Sources */, 171 | 8B33BD261FAEFE81002046F2 /* Frameworks */, 172 | 8B33BD271FAEFE81002046F2 /* Resources */, 173 | ); 174 | buildRules = ( 175 | ); 176 | dependencies = ( 177 | 8B33BD2B1FAEFE81002046F2 /* PBXTargetDependency */, 178 | ); 179 | name = VPCollectionViewLayoutExampleTests; 180 | productName = VPCollectionViewLayoutExampleTests; 181 | productReference = 8B33BD291FAEFE81002046F2 /* VPCollectionViewLayoutExampleTests.xctest */; 182 | productType = "com.apple.product-type.bundle.unit-test"; 183 | }; 184 | 8B33BD331FAEFE81002046F2 /* VPCollectionViewLayoutExampleUITests */ = { 185 | isa = PBXNativeTarget; 186 | buildConfigurationList = 8B33BD431FAEFE81002046F2 /* Build configuration list for PBXNativeTarget "VPCollectionViewLayoutExampleUITests" */; 187 | buildPhases = ( 188 | 8B33BD301FAEFE81002046F2 /* Sources */, 189 | 8B33BD311FAEFE81002046F2 /* Frameworks */, 190 | 8B33BD321FAEFE81002046F2 /* Resources */, 191 | ); 192 | buildRules = ( 193 | ); 194 | dependencies = ( 195 | 8B33BD361FAEFE81002046F2 /* PBXTargetDependency */, 196 | ); 197 | name = VPCollectionViewLayoutExampleUITests; 198 | productName = VPCollectionViewLayoutExampleUITests; 199 | productReference = 8B33BD341FAEFE81002046F2 /* VPCollectionViewLayoutExampleUITests.xctest */; 200 | productType = "com.apple.product-type.bundle.ui-testing"; 201 | }; 202 | /* End PBXNativeTarget section */ 203 | 204 | /* Begin PBXProject section */ 205 | 8B33BD0D1FAEFE81002046F2 /* Project object */ = { 206 | isa = PBXProject; 207 | attributes = { 208 | LastSwiftUpdateCheck = 0900; 209 | LastUpgradeCheck = 0900; 210 | ORGANIZATIONNAME = "Varun P M"; 211 | TargetAttributes = { 212 | 8B33BD141FAEFE81002046F2 = { 213 | CreatedOnToolsVersion = 9.0; 214 | ProvisioningStyle = Manual; 215 | }; 216 | 8B33BD281FAEFE81002046F2 = { 217 | CreatedOnToolsVersion = 9.0; 218 | ProvisioningStyle = Automatic; 219 | TestTargetID = 8B33BD141FAEFE81002046F2; 220 | }; 221 | 8B33BD331FAEFE81002046F2 = { 222 | CreatedOnToolsVersion = 9.0; 223 | ProvisioningStyle = Automatic; 224 | TestTargetID = 8B33BD141FAEFE81002046F2; 225 | }; 226 | }; 227 | }; 228 | buildConfigurationList = 8B33BD101FAEFE81002046F2 /* Build configuration list for PBXProject "VPCollectionViewLayoutExample" */; 229 | compatibilityVersion = "Xcode 8.0"; 230 | developmentRegion = en; 231 | hasScannedForEncodings = 0; 232 | knownRegions = ( 233 | en, 234 | Base, 235 | ); 236 | mainGroup = 8B33BD0C1FAEFE81002046F2; 237 | productRefGroup = 8B33BD161FAEFE81002046F2 /* Products */; 238 | projectDirPath = ""; 239 | projectRoot = ""; 240 | targets = ( 241 | 8B33BD141FAEFE81002046F2 /* VPCollectionViewLayoutExample */, 242 | 8B33BD281FAEFE81002046F2 /* VPCollectionViewLayoutExampleTests */, 243 | 8B33BD331FAEFE81002046F2 /* VPCollectionViewLayoutExampleUITests */, 244 | ); 245 | }; 246 | /* End PBXProject section */ 247 | 248 | /* Begin PBXResourcesBuildPhase section */ 249 | 8B33BD131FAEFE81002046F2 /* Resources */ = { 250 | isa = PBXResourcesBuildPhase; 251 | buildActionMask = 2147483647; 252 | files = ( 253 | 8B33BD231FAEFE81002046F2 /* LaunchScreen.storyboard in Resources */, 254 | 8B33BD201FAEFE81002046F2 /* Assets.xcassets in Resources */, 255 | 8B33BD4C1FAF01A1002046F2 /* SampleCollectionViewCell.xib in Resources */, 256 | 8B33BD1E1FAEFE81002046F2 /* Main.storyboard in Resources */, 257 | ); 258 | runOnlyForDeploymentPostprocessing = 0; 259 | }; 260 | 8B33BD271FAEFE81002046F2 /* Resources */ = { 261 | isa = PBXResourcesBuildPhase; 262 | buildActionMask = 2147483647; 263 | files = ( 264 | ); 265 | runOnlyForDeploymentPostprocessing = 0; 266 | }; 267 | 8B33BD321FAEFE81002046F2 /* Resources */ = { 268 | isa = PBXResourcesBuildPhase; 269 | buildActionMask = 2147483647; 270 | files = ( 271 | ); 272 | runOnlyForDeploymentPostprocessing = 0; 273 | }; 274 | /* End PBXResourcesBuildPhase section */ 275 | 276 | /* Begin PBXSourcesBuildPhase section */ 277 | 8B33BD111FAEFE81002046F2 /* Sources */ = { 278 | isa = PBXSourcesBuildPhase; 279 | buildActionMask = 2147483647; 280 | files = ( 281 | 8B33BD1B1FAEFE81002046F2 /* ViewController.swift in Sources */, 282 | 8B33BD481FAEFF10002046F2 /* VPCollectionViewLayout.swift in Sources */, 283 | 8B33BD4B1FAF01A1002046F2 /* SampleCollectionViewCell.swift in Sources */, 284 | 8B33BD191FAEFE81002046F2 /* AppDelegate.swift in Sources */, 285 | ); 286 | runOnlyForDeploymentPostprocessing = 0; 287 | }; 288 | 8B33BD251FAEFE81002046F2 /* Sources */ = { 289 | isa = PBXSourcesBuildPhase; 290 | buildActionMask = 2147483647; 291 | files = ( 292 | 8B33BD2E1FAEFE81002046F2 /* VPCollectionViewLayoutExampleTests.swift in Sources */, 293 | ); 294 | runOnlyForDeploymentPostprocessing = 0; 295 | }; 296 | 8B33BD301FAEFE81002046F2 /* Sources */ = { 297 | isa = PBXSourcesBuildPhase; 298 | buildActionMask = 2147483647; 299 | files = ( 300 | 8B33BD391FAEFE81002046F2 /* VPCollectionViewLayoutExampleUITests.swift in Sources */, 301 | ); 302 | runOnlyForDeploymentPostprocessing = 0; 303 | }; 304 | /* End PBXSourcesBuildPhase section */ 305 | 306 | /* Begin PBXTargetDependency section */ 307 | 8B33BD2B1FAEFE81002046F2 /* PBXTargetDependency */ = { 308 | isa = PBXTargetDependency; 309 | target = 8B33BD141FAEFE81002046F2 /* VPCollectionViewLayoutExample */; 310 | targetProxy = 8B33BD2A1FAEFE81002046F2 /* PBXContainerItemProxy */; 311 | }; 312 | 8B33BD361FAEFE81002046F2 /* PBXTargetDependency */ = { 313 | isa = PBXTargetDependency; 314 | target = 8B33BD141FAEFE81002046F2 /* VPCollectionViewLayoutExample */; 315 | targetProxy = 8B33BD351FAEFE81002046F2 /* PBXContainerItemProxy */; 316 | }; 317 | /* End PBXTargetDependency section */ 318 | 319 | /* Begin PBXVariantGroup section */ 320 | 8B33BD1C1FAEFE81002046F2 /* Main.storyboard */ = { 321 | isa = PBXVariantGroup; 322 | children = ( 323 | 8B33BD1D1FAEFE81002046F2 /* Base */, 324 | ); 325 | name = Main.storyboard; 326 | sourceTree = ""; 327 | }; 328 | 8B33BD211FAEFE81002046F2 /* LaunchScreen.storyboard */ = { 329 | isa = PBXVariantGroup; 330 | children = ( 331 | 8B33BD221FAEFE81002046F2 /* Base */, 332 | ); 333 | name = LaunchScreen.storyboard; 334 | sourceTree = ""; 335 | }; 336 | /* End PBXVariantGroup section */ 337 | 338 | /* Begin XCBuildConfiguration section */ 339 | 8B33BD3B1FAEFE81002046F2 /* Debug */ = { 340 | isa = XCBuildConfiguration; 341 | buildSettings = { 342 | ALWAYS_SEARCH_USER_PATHS = NO; 343 | CLANG_ANALYZER_NONNULL = YES; 344 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 345 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 346 | CLANG_CXX_LIBRARY = "libc++"; 347 | CLANG_ENABLE_MODULES = YES; 348 | CLANG_ENABLE_OBJC_ARC = YES; 349 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 350 | CLANG_WARN_BOOL_CONVERSION = YES; 351 | CLANG_WARN_COMMA = YES; 352 | CLANG_WARN_CONSTANT_CONVERSION = YES; 353 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 354 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 355 | CLANG_WARN_EMPTY_BODY = YES; 356 | CLANG_WARN_ENUM_CONVERSION = YES; 357 | CLANG_WARN_INFINITE_RECURSION = YES; 358 | CLANG_WARN_INT_CONVERSION = YES; 359 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 360 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 361 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 362 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 363 | CLANG_WARN_STRICT_PROTOTYPES = YES; 364 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 365 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 366 | CLANG_WARN_UNREACHABLE_CODE = YES; 367 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 368 | CODE_SIGN_IDENTITY = "iPhone Developer"; 369 | COPY_PHASE_STRIP = NO; 370 | DEBUG_INFORMATION_FORMAT = dwarf; 371 | ENABLE_STRICT_OBJC_MSGSEND = YES; 372 | ENABLE_TESTABILITY = YES; 373 | GCC_C_LANGUAGE_STANDARD = gnu11; 374 | GCC_DYNAMIC_NO_PIC = NO; 375 | GCC_NO_COMMON_BLOCKS = YES; 376 | GCC_OPTIMIZATION_LEVEL = 0; 377 | GCC_PREPROCESSOR_DEFINITIONS = ( 378 | "DEBUG=1", 379 | "$(inherited)", 380 | ); 381 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 382 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 383 | GCC_WARN_UNDECLARED_SELECTOR = YES; 384 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 385 | GCC_WARN_UNUSED_FUNCTION = YES; 386 | GCC_WARN_UNUSED_VARIABLE = YES; 387 | IPHONEOS_DEPLOYMENT_TARGET = 11.0; 388 | MTL_ENABLE_DEBUG_INFO = YES; 389 | ONLY_ACTIVE_ARCH = YES; 390 | SDKROOT = iphoneos; 391 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 392 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 393 | }; 394 | name = Debug; 395 | }; 396 | 8B33BD3C1FAEFE81002046F2 /* Release */ = { 397 | isa = XCBuildConfiguration; 398 | buildSettings = { 399 | ALWAYS_SEARCH_USER_PATHS = NO; 400 | CLANG_ANALYZER_NONNULL = YES; 401 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 402 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 403 | CLANG_CXX_LIBRARY = "libc++"; 404 | CLANG_ENABLE_MODULES = YES; 405 | CLANG_ENABLE_OBJC_ARC = YES; 406 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 407 | CLANG_WARN_BOOL_CONVERSION = YES; 408 | CLANG_WARN_COMMA = YES; 409 | CLANG_WARN_CONSTANT_CONVERSION = YES; 410 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 411 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 412 | CLANG_WARN_EMPTY_BODY = YES; 413 | CLANG_WARN_ENUM_CONVERSION = YES; 414 | CLANG_WARN_INFINITE_RECURSION = YES; 415 | CLANG_WARN_INT_CONVERSION = YES; 416 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 417 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 418 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 419 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 420 | CLANG_WARN_STRICT_PROTOTYPES = YES; 421 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 422 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 423 | CLANG_WARN_UNREACHABLE_CODE = YES; 424 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 425 | CODE_SIGN_IDENTITY = "iPhone Developer"; 426 | COPY_PHASE_STRIP = NO; 427 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 428 | ENABLE_NS_ASSERTIONS = NO; 429 | ENABLE_STRICT_OBJC_MSGSEND = YES; 430 | GCC_C_LANGUAGE_STANDARD = gnu11; 431 | GCC_NO_COMMON_BLOCKS = YES; 432 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 433 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 434 | GCC_WARN_UNDECLARED_SELECTOR = YES; 435 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 436 | GCC_WARN_UNUSED_FUNCTION = YES; 437 | GCC_WARN_UNUSED_VARIABLE = YES; 438 | IPHONEOS_DEPLOYMENT_TARGET = 11.0; 439 | MTL_ENABLE_DEBUG_INFO = NO; 440 | SDKROOT = iphoneos; 441 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 442 | VALIDATE_PRODUCT = YES; 443 | }; 444 | name = Release; 445 | }; 446 | 8B33BD3E1FAEFE81002046F2 /* Debug */ = { 447 | isa = XCBuildConfiguration; 448 | buildSettings = { 449 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 450 | CODE_SIGN_STYLE = Manual; 451 | DEVELOPMENT_TEAM = ""; 452 | INFOPLIST_FILE = VPCollectionViewLayoutExample/Info.plist; 453 | IPHONEOS_DEPLOYMENT_TARGET = 9.0; 454 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 455 | PRODUCT_BUNDLE_IDENTIFIER = VPM.VPCollectionViewLayoutExample; 456 | PRODUCT_NAME = "$(TARGET_NAME)"; 457 | PROVISIONING_PROFILE_SPECIFIER = ""; 458 | SWIFT_VERSION = 4.0; 459 | TARGETED_DEVICE_FAMILY = "1,2"; 460 | }; 461 | name = Debug; 462 | }; 463 | 8B33BD3F1FAEFE81002046F2 /* Release */ = { 464 | isa = XCBuildConfiguration; 465 | buildSettings = { 466 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 467 | CODE_SIGN_STYLE = Manual; 468 | DEVELOPMENT_TEAM = ""; 469 | INFOPLIST_FILE = VPCollectionViewLayoutExample/Info.plist; 470 | IPHONEOS_DEPLOYMENT_TARGET = 9.0; 471 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 472 | PRODUCT_BUNDLE_IDENTIFIER = VPM.VPCollectionViewLayoutExample; 473 | PRODUCT_NAME = "$(TARGET_NAME)"; 474 | PROVISIONING_PROFILE_SPECIFIER = ""; 475 | SWIFT_VERSION = 4.0; 476 | TARGETED_DEVICE_FAMILY = "1,2"; 477 | }; 478 | name = Release; 479 | }; 480 | 8B33BD411FAEFE81002046F2 /* Debug */ = { 481 | isa = XCBuildConfiguration; 482 | buildSettings = { 483 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 484 | BUNDLE_LOADER = "$(TEST_HOST)"; 485 | CODE_SIGN_STYLE = Automatic; 486 | INFOPLIST_FILE = VPCollectionViewLayoutExampleTests/Info.plist; 487 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 488 | PRODUCT_BUNDLE_IDENTIFIER = VPM.VPCollectionViewLayoutExampleTests; 489 | PRODUCT_NAME = "$(TARGET_NAME)"; 490 | SWIFT_VERSION = 4.0; 491 | TARGETED_DEVICE_FAMILY = "1,2"; 492 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/VPCollectionViewLayoutExample.app/VPCollectionViewLayoutExample"; 493 | }; 494 | name = Debug; 495 | }; 496 | 8B33BD421FAEFE81002046F2 /* Release */ = { 497 | isa = XCBuildConfiguration; 498 | buildSettings = { 499 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 500 | BUNDLE_LOADER = "$(TEST_HOST)"; 501 | CODE_SIGN_STYLE = Automatic; 502 | INFOPLIST_FILE = VPCollectionViewLayoutExampleTests/Info.plist; 503 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 504 | PRODUCT_BUNDLE_IDENTIFIER = VPM.VPCollectionViewLayoutExampleTests; 505 | PRODUCT_NAME = "$(TARGET_NAME)"; 506 | SWIFT_VERSION = 4.0; 507 | TARGETED_DEVICE_FAMILY = "1,2"; 508 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/VPCollectionViewLayoutExample.app/VPCollectionViewLayoutExample"; 509 | }; 510 | name = Release; 511 | }; 512 | 8B33BD441FAEFE81002046F2 /* Debug */ = { 513 | isa = XCBuildConfiguration; 514 | buildSettings = { 515 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 516 | CODE_SIGN_STYLE = Automatic; 517 | INFOPLIST_FILE = VPCollectionViewLayoutExampleUITests/Info.plist; 518 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 519 | PRODUCT_BUNDLE_IDENTIFIER = VPM.VPCollectionViewLayoutExampleUITests; 520 | PRODUCT_NAME = "$(TARGET_NAME)"; 521 | SWIFT_VERSION = 4.0; 522 | TARGETED_DEVICE_FAMILY = "1,2"; 523 | TEST_TARGET_NAME = VPCollectionViewLayoutExample; 524 | }; 525 | name = Debug; 526 | }; 527 | 8B33BD451FAEFE81002046F2 /* Release */ = { 528 | isa = XCBuildConfiguration; 529 | buildSettings = { 530 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 531 | CODE_SIGN_STYLE = Automatic; 532 | INFOPLIST_FILE = VPCollectionViewLayoutExampleUITests/Info.plist; 533 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 534 | PRODUCT_BUNDLE_IDENTIFIER = VPM.VPCollectionViewLayoutExampleUITests; 535 | PRODUCT_NAME = "$(TARGET_NAME)"; 536 | SWIFT_VERSION = 4.0; 537 | TARGETED_DEVICE_FAMILY = "1,2"; 538 | TEST_TARGET_NAME = VPCollectionViewLayoutExample; 539 | }; 540 | name = Release; 541 | }; 542 | /* End XCBuildConfiguration section */ 543 | 544 | /* Begin XCConfigurationList section */ 545 | 8B33BD101FAEFE81002046F2 /* Build configuration list for PBXProject "VPCollectionViewLayoutExample" */ = { 546 | isa = XCConfigurationList; 547 | buildConfigurations = ( 548 | 8B33BD3B1FAEFE81002046F2 /* Debug */, 549 | 8B33BD3C1FAEFE81002046F2 /* Release */, 550 | ); 551 | defaultConfigurationIsVisible = 0; 552 | defaultConfigurationName = Release; 553 | }; 554 | 8B33BD3D1FAEFE81002046F2 /* Build configuration list for PBXNativeTarget "VPCollectionViewLayoutExample" */ = { 555 | isa = XCConfigurationList; 556 | buildConfigurations = ( 557 | 8B33BD3E1FAEFE81002046F2 /* Debug */, 558 | 8B33BD3F1FAEFE81002046F2 /* Release */, 559 | ); 560 | defaultConfigurationIsVisible = 0; 561 | defaultConfigurationName = Release; 562 | }; 563 | 8B33BD401FAEFE81002046F2 /* Build configuration list for PBXNativeTarget "VPCollectionViewLayoutExampleTests" */ = { 564 | isa = XCConfigurationList; 565 | buildConfigurations = ( 566 | 8B33BD411FAEFE81002046F2 /* Debug */, 567 | 8B33BD421FAEFE81002046F2 /* Release */, 568 | ); 569 | defaultConfigurationIsVisible = 0; 570 | defaultConfigurationName = Release; 571 | }; 572 | 8B33BD431FAEFE81002046F2 /* Build configuration list for PBXNativeTarget "VPCollectionViewLayoutExampleUITests" */ = { 573 | isa = XCConfigurationList; 574 | buildConfigurations = ( 575 | 8B33BD441FAEFE81002046F2 /* Debug */, 576 | 8B33BD451FAEFE81002046F2 /* Release */, 577 | ); 578 | defaultConfigurationIsVisible = 0; 579 | defaultConfigurationName = Release; 580 | }; 581 | /* End XCConfigurationList section */ 582 | }; 583 | rootObject = 8B33BD0D1FAEFE81002046F2 /* Project object */; 584 | } 585 | --------------------------------------------------------------------------------