├── .gitignore
├── .travis.yml
├── Examples
├── .gitignore
├── AppleTVDemo
│ ├── AppDelegate.swift
│ ├── Assets.xcassets
│ │ ├── App Icon & Top Shelf Image.brandassets
│ │ │ ├── App Icon - Large.imagestack
│ │ │ │ ├── Back.imagestacklayer
│ │ │ │ │ ├── Content.imageset
│ │ │ │ │ │ └── Contents.json
│ │ │ │ │ └── Contents.json
│ │ │ │ ├── Contents.json
│ │ │ │ ├── Front.imagestacklayer
│ │ │ │ │ ├── Content.imageset
│ │ │ │ │ │ └── Contents.json
│ │ │ │ │ └── Contents.json
│ │ │ │ └── Middle.imagestacklayer
│ │ │ │ │ ├── Content.imageset
│ │ │ │ │ └── Contents.json
│ │ │ │ │ └── Contents.json
│ │ │ ├── App Icon - Small.imagestack
│ │ │ │ ├── Back.imagestacklayer
│ │ │ │ │ ├── Content.imageset
│ │ │ │ │ │ └── Contents.json
│ │ │ │ │ └── Contents.json
│ │ │ │ ├── Contents.json
│ │ │ │ ├── Front.imagestacklayer
│ │ │ │ │ ├── Content.imageset
│ │ │ │ │ │ └── Contents.json
│ │ │ │ │ └── Contents.json
│ │ │ │ └── Middle.imagestacklayer
│ │ │ │ │ ├── Content.imageset
│ │ │ │ │ └── Contents.json
│ │ │ │ │ └── Contents.json
│ │ │ ├── Contents.json
│ │ │ └── Top Shelf Image.imageset
│ │ │ │ └── Contents.json
│ │ ├── Contents.json
│ │ └── LaunchImage.launchimage
│ │ │ └── Contents.json
│ ├── Base.lproj
│ │ └── Main.storyboard
│ ├── Info.plist
│ └── ViewController.swift
├── RDHCollectionViewGridLayout.xcconfig
├── RDHCollectionViewGridLayout
│ └── Info.plist
├── RDHCollectionViewGridLayoutDemo.xcodeproj
│ ├── project.pbxproj
│ ├── project.xcworkspace
│ │ └── contents.xcworkspacedata
│ └── xcshareddata
│ │ └── xcschemes
│ │ ├── AppleTVDemo.xcscheme
│ │ ├── RDHCollectionViewGridLayout.xcscheme
│ │ └── RDHCollectionViewGridLayoutDemo.xcscheme
├── RDHCollectionViewGridLayoutDemo
│ ├── Images.xcassets
│ │ ├── AppIcon.appiconset
│ │ │ └── Contents.json
│ │ └── LaunchImage.launchimage
│ │ │ ├── Contents.json
│ │ │ ├── Default-568h@2x-1.png
│ │ │ ├── Default-568h@2x.png
│ │ │ ├── Default.png
│ │ │ ├── Default@2x-1.png
│ │ │ └── Default@2x.png
│ ├── RDHAppDelegate.h
│ ├── RDHAppDelegate.m
│ ├── RDHCollectionViewGridLayoutDemo-Info.plist
│ ├── RDHCollectionViewGridLayoutDemo-Prefix.pch
│ ├── RDHDemoCell.h
│ ├── RDHDemoCell.m
│ ├── RDHDemoViewController.h
│ ├── RDHDemoViewController.m
│ ├── en.lproj
│ │ └── InfoPlist.strings
│ └── main.m
├── Tests
│ ├── Info.plist
│ ├── RDHEmptyGridLayoutTests.swift
│ ├── RDHGridLayoutTests.m
│ ├── RDHStoryboardTests.swift
│ ├── TestDataSources.swift
│ ├── TestStoryboard.storyboard
│ ├── Tests-Bridging-Header.h
│ ├── Tests-Prefix.pch
│ ├── XCTestCase+Extensions.h
│ └── XCTestCase+Extensions.m
├── cartfile
└── images
│ ├── 5-line, 10-item.png
│ ├── horizontal.png
│ └── vertical.png
├── LICENSE
├── RDHCollectionViewGridLayout.podspec
├── RDHCollectionViewGridLayout
├── RDHCollectionViewGridLayout.h
└── RDHCollectionViewGridLayout.m
└── README.md
/.gitignore:
--------------------------------------------------------------------------------
1 | # Xcode
2 | #
3 | build/
4 | *.pbxuser
5 | !default.pbxuser
6 | *.mode1v3
7 | !default.mode1v3
8 | *.mode2v3
9 | !default.mode2v3
10 | *.perspectivev3
11 | !default.perspectivev3
12 | xcuserdata
13 | *.xccheckout
14 | *.moved-aside
15 | DerivedData
16 | *.hmap
17 | *.ipa
18 | *.xcuserstate
19 |
20 | # CocoaPods
21 | #
22 | # We recommend against adding the Pods directory to your .gitignore. However
23 | # you should judge for yourself, the pros and cons are mentioned at:
24 | # http://guides.cocoapods.org/using/using-cocoapods.html#should-i-ignore-the-pods-directory-in-source-control
25 | #
26 | # Pods/
27 |
28 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: objective-c
2 | osx_image: xcode8
3 | script:
4 | # Build framework
5 | - xcodebuild -project Examples/RDHCollectionViewGridLayoutDemo.xcodeproj -scheme RDHCollectionViewGridLayout CODE_SIGNING_REQUIRED=NO CODE_SIGN_IDENTITY= clean build
6 | - xcodebuild -project Examples/RDHCollectionViewGridLayoutDemo.xcodeproj -scheme RDHCollectionViewGridLayoutDemo -destination "platform=iOS Simulator,name=iPhone 6" clean build test
7 | #- xcodebuild -project Examples/RDHCollectionViewGridLayoutDemo.xcodeproj -scheme AppleTVDemo -destination "platform=tvOS Simulator,name=Apple TV 1080p" clean build test
8 |
--------------------------------------------------------------------------------
/Examples/.gitignore:
--------------------------------------------------------------------------------
1 | Carthage
2 | Cartfile.resolved
3 |
--------------------------------------------------------------------------------
/Examples/AppleTVDemo/AppDelegate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.swift
3 | // AppleTVDemo
4 | //
5 | // Created by Richard Hodgkins on 24/02/2016.
6 | // Copyright © 2016 Rich H. 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: [NSObject: AnyObject]?) -> 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 throttle down OpenGL ES frame rates. 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 inactive 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 |
--------------------------------------------------------------------------------
/Examples/AppleTVDemo/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Large.imagestack/Back.imagestacklayer/Content.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "tv",
5 | "scale" : "1x"
6 | }
7 | ],
8 | "info" : {
9 | "version" : 1,
10 | "author" : "xcode"
11 | }
12 | }
--------------------------------------------------------------------------------
/Examples/AppleTVDemo/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Large.imagestack/Back.imagestacklayer/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "version" : 1,
4 | "author" : "xcode"
5 | }
6 | }
--------------------------------------------------------------------------------
/Examples/AppleTVDemo/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Large.imagestack/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "layers" : [
3 | {
4 | "filename" : "Front.imagestacklayer"
5 | },
6 | {
7 | "filename" : "Middle.imagestacklayer"
8 | },
9 | {
10 | "filename" : "Back.imagestacklayer"
11 | }
12 | ],
13 | "info" : {
14 | "version" : 1,
15 | "author" : "xcode"
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/Examples/AppleTVDemo/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Large.imagestack/Front.imagestacklayer/Content.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "tv",
5 | "scale" : "1x"
6 | }
7 | ],
8 | "info" : {
9 | "version" : 1,
10 | "author" : "xcode"
11 | }
12 | }
--------------------------------------------------------------------------------
/Examples/AppleTVDemo/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Large.imagestack/Front.imagestacklayer/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "version" : 1,
4 | "author" : "xcode"
5 | }
6 | }
--------------------------------------------------------------------------------
/Examples/AppleTVDemo/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Large.imagestack/Middle.imagestacklayer/Content.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "tv",
5 | "scale" : "1x"
6 | }
7 | ],
8 | "info" : {
9 | "version" : 1,
10 | "author" : "xcode"
11 | }
12 | }
--------------------------------------------------------------------------------
/Examples/AppleTVDemo/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Large.imagestack/Middle.imagestacklayer/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "version" : 1,
4 | "author" : "xcode"
5 | }
6 | }
--------------------------------------------------------------------------------
/Examples/AppleTVDemo/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Small.imagestack/Back.imagestacklayer/Content.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "tv",
5 | "scale" : "1x"
6 | }
7 | ],
8 | "info" : {
9 | "version" : 1,
10 | "author" : "xcode"
11 | }
12 | }
--------------------------------------------------------------------------------
/Examples/AppleTVDemo/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Small.imagestack/Back.imagestacklayer/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "version" : 1,
4 | "author" : "xcode"
5 | }
6 | }
--------------------------------------------------------------------------------
/Examples/AppleTVDemo/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Small.imagestack/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "layers" : [
3 | {
4 | "filename" : "Front.imagestacklayer"
5 | },
6 | {
7 | "filename" : "Middle.imagestacklayer"
8 | },
9 | {
10 | "filename" : "Back.imagestacklayer"
11 | }
12 | ],
13 | "info" : {
14 | "version" : 1,
15 | "author" : "xcode"
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/Examples/AppleTVDemo/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Small.imagestack/Front.imagestacklayer/Content.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "tv",
5 | "scale" : "1x"
6 | }
7 | ],
8 | "info" : {
9 | "version" : 1,
10 | "author" : "xcode"
11 | }
12 | }
--------------------------------------------------------------------------------
/Examples/AppleTVDemo/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Small.imagestack/Front.imagestacklayer/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "version" : 1,
4 | "author" : "xcode"
5 | }
6 | }
--------------------------------------------------------------------------------
/Examples/AppleTVDemo/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Small.imagestack/Middle.imagestacklayer/Content.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "tv",
5 | "scale" : "1x"
6 | }
7 | ],
8 | "info" : {
9 | "version" : 1,
10 | "author" : "xcode"
11 | }
12 | }
--------------------------------------------------------------------------------
/Examples/AppleTVDemo/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - Small.imagestack/Middle.imagestacklayer/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "version" : 1,
4 | "author" : "xcode"
5 | }
6 | }
--------------------------------------------------------------------------------
/Examples/AppleTVDemo/Assets.xcassets/App Icon & Top Shelf Image.brandassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "assets" : [
3 | {
4 | "size" : "1280x768",
5 | "idiom" : "tv",
6 | "filename" : "App Icon - Large.imagestack",
7 | "role" : "primary-app-icon"
8 | },
9 | {
10 | "size" : "400x240",
11 | "idiom" : "tv",
12 | "filename" : "App Icon - Small.imagestack",
13 | "role" : "primary-app-icon"
14 | },
15 | {
16 | "size" : "1920x720",
17 | "idiom" : "tv",
18 | "filename" : "Top Shelf Image.imageset",
19 | "role" : "top-shelf-image"
20 | }
21 | ],
22 | "info" : {
23 | "version" : 1,
24 | "author" : "xcode"
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/Examples/AppleTVDemo/Assets.xcassets/App Icon & Top Shelf Image.brandassets/Top Shelf Image.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "tv",
5 | "scale" : "1x"
6 | }
7 | ],
8 | "info" : {
9 | "version" : 1,
10 | "author" : "xcode"
11 | }
12 | }
--------------------------------------------------------------------------------
/Examples/AppleTVDemo/Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "version" : 1,
4 | "author" : "xcode"
5 | }
6 | }
--------------------------------------------------------------------------------
/Examples/AppleTVDemo/Assets.xcassets/LaunchImage.launchimage/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "orientation" : "landscape",
5 | "idiom" : "tv",
6 | "extent" : "full-screen",
7 | "minimum-system-version" : "9.0",
8 | "scale" : "1x"
9 | }
10 | ],
11 | "info" : {
12 | "version" : 1,
13 | "author" : "xcode"
14 | }
15 | }
--------------------------------------------------------------------------------
/Examples/AppleTVDemo/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 |
--------------------------------------------------------------------------------
/Examples/AppleTVDemo/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 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | 1
23 | LSRequiresIPhoneOS
24 |
25 | UIMainStoryboardFile
26 | Main
27 | UIRequiredDeviceCapabilities
28 |
29 | arm64
30 |
31 |
32 |
33 |
--------------------------------------------------------------------------------
/Examples/AppleTVDemo/ViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ViewController.swift
3 | // AppleTVDemo
4 | //
5 | // Created by Richard Hodgkins on 24/02/2016.
6 | // Copyright © 2016 Rich H. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | import RDHCollectionViewGridLayout
12 |
13 | final class ViewController: UIViewController {
14 |
15 | override func viewDidLoad() {
16 | super.viewDidLoad()
17 | // Do any additional setup after loading the view, typically from a nib.
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/Examples/RDHCollectionViewGridLayout.xcconfig:
--------------------------------------------------------------------------------
1 | //
2 | // RDHCollectionViewGridLayout.xcconfig
3 | // RDHCollectionViewGridLayout
4 | //
5 | // Created by Richard Hodgkins on 24/02/2016.
6 | // Copyright © 2016 Rich H. All rights reserved.
7 | //
8 |
9 | // Custom
10 |
11 | PRODUCT_NAME = RDHCollectionViewGridLayout
12 | PRODUCT_BUNDLE_IDENTIFIER = uk.co.rhodgkins.$(PRODUCT_NAME:rfc1034identifier)
13 | CVGL_VERSION = 1.2.5
14 |
15 | // Universal Framework
16 | APPLICATION_EXTENSION_API_ONLY = YES
17 | COMBINE_HIDPI_IMAGES = YES
18 |
19 | SUPPORTED_PLATFORMS = iphoneos iphonesimulator appletvos appletvsimulator
20 |
21 | VALID_ARCHS[sdk=iphone*] = arm64 armv7 armv7s
22 | VALID_ARCHS[sdk=appletv*] = arm64
23 |
24 | LD_RUNPATH_SEARCH_PATHS[sdk=iphone*] = $(inherited) @executable_path/Frameworks @loader_path/Frameworks
25 | LD_RUNPATH_SEARCH_PATHS[sdk=appletv*] = $(inherited) @executable_path/Frameworks @loader_path/Frameworks
26 |
27 | INFOPLIST_FILE = $(PRODUCT_NAME)/Info.plist
28 |
29 | // Platform-specific
30 |
31 | // iOS
32 | CODE_SIGN_IDENTITY[sdk=iphoneos*] = iPhone Developer
33 | IPHONEOS_DEPLOYMENT_TARGET[sdk=iphone*] = 8.0
34 | TARGETED_DEVICE_FAMILY[sdk=iphone*] = 1,2
35 |
36 | // tvOS
37 | CODE_SIGN_IDENTITY[sdk=appletvos*] = iPhone Developer
38 | TVOS_DEPLOYMENT_TARGET[sdk=appletv*] = 9.0
39 | TARGETED_DEVICE_FAMILY[sdk=appletv*] = 3
40 |
--------------------------------------------------------------------------------
/Examples/RDHCollectionViewGridLayout/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 | $(CVGL_VERSION)
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | $(CURRENT_PROJECT_VERSION)
23 | NSPrincipalClass
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/Examples/RDHCollectionViewGridLayoutDemo.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 46;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | 442B4B261C7E694400C5B5FC /* RDHCollectionViewGridLayout.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 442B4B1A1C7E685800C5B5FC /* RDHCollectionViewGridLayout.framework */; };
11 | 442B4B271C7E694400C5B5FC /* RDHCollectionViewGridLayout.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 442B4B1A1C7E685800C5B5FC /* RDHCollectionViewGridLayout.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
12 | 442B4B2B1C7E694700C5B5FC /* RDHCollectionViewGridLayout.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 442B4B1A1C7E685800C5B5FC /* RDHCollectionViewGridLayout.framework */; };
13 | 442B4B2C1C7E694700C5B5FC /* RDHCollectionViewGridLayout.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 442B4B1A1C7E685800C5B5FC /* RDHCollectionViewGridLayout.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
14 | 442B4B301C7E699900C5B5FC /* RDHCollectionViewGridLayout.h in Headers */ = {isa = PBXBuildFile; fileRef = 4437B7D31954CB5A005CCBBC /* RDHCollectionViewGridLayout.h */; settings = {ATTRIBUTES = (Public, ); }; };
15 | 442B4B311C7E699E00C5B5FC /* RDHCollectionViewGridLayout.m in Sources */ = {isa = PBXBuildFile; fileRef = 4437B7D41954CB5A005CCBBC /* RDHCollectionViewGridLayout.m */; };
16 | 442BF3FF17896E1D003FAB93 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 442BF3FD17896E1D003FAB93 /* InfoPlist.strings */; };
17 | 442BF40117896E1D003FAB93 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 442BF40017896E1D003FAB93 /* main.m */; };
18 | 442BF40517896E1D003FAB93 /* RDHAppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 442BF40417896E1D003FAB93 /* RDHAppDelegate.m */; };
19 | 442BF41317896E5D003FAB93 /* RDHDemoCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 442BF41217896E5D003FAB93 /* RDHDemoCell.m */; };
20 | 442BF41617896E84003FAB93 /* RDHDemoViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 442BF41517896E84003FAB93 /* RDHDemoViewController.m */; };
21 | 4437B7DC1954CC83005CCBBC /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 4437B7DB1954CC83005CCBBC /* Images.xcassets */; };
22 | 4437B80F1956DF41005CCBBC /* RDHGridLayoutTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 4437B80E1956DF41005CCBBC /* RDHGridLayoutTests.m */; };
23 | 444926661AB6C17A002DBB57 /* RDHStoryboardTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 444926651AB6C17A002DBB57 /* RDHStoryboardTests.swift */; };
24 | 4449266A1AB6C24D002DBB57 /* TestDataSources.swift in Sources */ = {isa = PBXBuildFile; fileRef = 444926691AB6C24D002DBB57 /* TestDataSources.swift */; };
25 | 4449266C1AB6C94C002DBB57 /* TestStoryboard.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 4449266B1AB6C94C002DBB57 /* TestStoryboard.storyboard */; };
26 | 445AEA2E1D8EE6A7009FFBFF /* RDHEmptyGridLayoutTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 445AEA2D1D8EE6A7009FFBFF /* RDHEmptyGridLayoutTests.swift */; };
27 | 44BA60941D8EFC3600444CD8 /* XCTestCase+Extensions.m in Sources */ = {isa = PBXBuildFile; fileRef = 44BA60931D8EFC3600444CD8 /* XCTestCase+Extensions.m */; };
28 | 44F92B381C7E65E900A0AD0A /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 44F92B371C7E65E900A0AD0A /* AppDelegate.swift */; };
29 | 44F92B3A1C7E65E900A0AD0A /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 44F92B391C7E65E900A0AD0A /* ViewController.swift */; };
30 | 44F92B3D1C7E65E900A0AD0A /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 44F92B3B1C7E65E900A0AD0A /* Main.storyboard */; };
31 | 44F92B3F1C7E65EA00A0AD0A /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 44F92B3E1C7E65EA00A0AD0A /* Assets.xcassets */; };
32 | /* End PBXBuildFile section */
33 |
34 | /* Begin PBXContainerItemProxy section */
35 | 442B4B281C7E694400C5B5FC /* PBXContainerItemProxy */ = {
36 | isa = PBXContainerItemProxy;
37 | containerPortal = 442BF3E917896E1D003FAB93 /* Project object */;
38 | proxyType = 1;
39 | remoteGlobalIDString = 442B4B191C7E685800C5B5FC;
40 | remoteInfo = RDHCollectionViewGridLayout;
41 | };
42 | 442B4B2D1C7E694700C5B5FC /* PBXContainerItemProxy */ = {
43 | isa = PBXContainerItemProxy;
44 | containerPortal = 442BF3E917896E1D003FAB93 /* Project object */;
45 | proxyType = 1;
46 | remoteGlobalIDString = 442B4B191C7E685800C5B5FC;
47 | remoteInfo = RDHCollectionViewGridLayout;
48 | };
49 | 4437B8091956DF0B005CCBBC /* PBXContainerItemProxy */ = {
50 | isa = PBXContainerItemProxy;
51 | containerPortal = 442BF3E917896E1D003FAB93 /* Project object */;
52 | proxyType = 1;
53 | remoteGlobalIDString = 442BF3F017896E1D003FAB93;
54 | remoteInfo = RDHCollectionViewGridLayoutDemo;
55 | };
56 | /* End PBXContainerItemProxy section */
57 |
58 | /* Begin PBXCopyFilesBuildPhase section */
59 | 442B4B2A1C7E694400C5B5FC /* Embed Frameworks */ = {
60 | isa = PBXCopyFilesBuildPhase;
61 | buildActionMask = 2147483647;
62 | dstPath = "";
63 | dstSubfolderSpec = 10;
64 | files = (
65 | 442B4B271C7E694400C5B5FC /* RDHCollectionViewGridLayout.framework in Embed Frameworks */,
66 | );
67 | name = "Embed Frameworks";
68 | runOnlyForDeploymentPostprocessing = 0;
69 | };
70 | 44F92B481C7E662900A0AD0A /* Embed Frameworks */ = {
71 | isa = PBXCopyFilesBuildPhase;
72 | buildActionMask = 2147483647;
73 | dstPath = "";
74 | dstSubfolderSpec = 10;
75 | files = (
76 | 442B4B2C1C7E694700C5B5FC /* RDHCollectionViewGridLayout.framework in Embed Frameworks */,
77 | );
78 | name = "Embed Frameworks";
79 | runOnlyForDeploymentPostprocessing = 0;
80 | };
81 | /* End PBXCopyFilesBuildPhase section */
82 |
83 | /* Begin PBXFileReference section */
84 | 442B4B1A1C7E685800C5B5FC /* RDHCollectionViewGridLayout.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = RDHCollectionViewGridLayout.framework; sourceTree = BUILT_PRODUCTS_DIR; };
85 | 442BF3F117896E1D003FAB93 /* RDHCollectionViewGridLayoutDemo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = RDHCollectionViewGridLayoutDemo.app; sourceTree = BUILT_PRODUCTS_DIR; };
86 | 442BF3FC17896E1D003FAB93 /* RDHCollectionViewGridLayoutDemo-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "RDHCollectionViewGridLayoutDemo-Info.plist"; sourceTree = ""; };
87 | 442BF3FE17896E1D003FAB93 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; };
88 | 442BF40017896E1D003FAB93 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; };
89 | 442BF40217896E1D003FAB93 /* RDHCollectionViewGridLayoutDemo-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "RDHCollectionViewGridLayoutDemo-Prefix.pch"; sourceTree = ""; };
90 | 442BF40317896E1D003FAB93 /* RDHAppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RDHAppDelegate.h; sourceTree = ""; };
91 | 442BF40417896E1D003FAB93 /* RDHAppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RDHAppDelegate.m; sourceTree = ""; };
92 | 442BF41117896E5D003FAB93 /* RDHDemoCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RDHDemoCell.h; sourceTree = ""; };
93 | 442BF41217896E5D003FAB93 /* RDHDemoCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RDHDemoCell.m; sourceTree = ""; };
94 | 442BF41417896E84003FAB93 /* RDHDemoViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RDHDemoViewController.h; sourceTree = ""; };
95 | 442BF41517896E84003FAB93 /* RDHDemoViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RDHDemoViewController.m; sourceTree = ""; };
96 | 4437B7CF1954CB54005CCBBC /* LICENSE */ = {isa = PBXFileReference; lastKnownFileType = text; name = LICENSE; path = ../LICENSE; sourceTree = ""; };
97 | 4437B7D01954CB54005CCBBC /* RDHCollectionViewGridLayout.podspec */ = {isa = PBXFileReference; explicitFileType = text.script.ruby; name = RDHCollectionViewGridLayout.podspec; path = ../RDHCollectionViewGridLayout.podspec; sourceTree = ""; };
98 | 4437B7D11954CB54005CCBBC /* README.md */ = {isa = PBXFileReference; lastKnownFileType = text; name = README.md; path = ../README.md; sourceTree = ""; };
99 | 4437B7D31954CB5A005CCBBC /* RDHCollectionViewGridLayout.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RDHCollectionViewGridLayout.h; path = ../../RDHCollectionViewGridLayout/RDHCollectionViewGridLayout.h; sourceTree = ""; };
100 | 4437B7D41954CB5A005CCBBC /* RDHCollectionViewGridLayout.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = RDHCollectionViewGridLayout.m; path = ../../RDHCollectionViewGridLayout/RDHCollectionViewGridLayout.m; sourceTree = ""; };
101 | 4437B7D71954CB6F005CCBBC /* 5-line, 10-item.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "5-line, 10-item.png"; sourceTree = ""; };
102 | 4437B7D81954CB6F005CCBBC /* horizontal.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = horizontal.png; sourceTree = ""; };
103 | 4437B7D91954CB6F005CCBBC /* vertical.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = vertical.png; sourceTree = ""; };
104 | 4437B7DA1954CB94005CCBBC /* .travis.yml */ = {isa = PBXFileReference; lastKnownFileType = text; name = .travis.yml; path = ../.travis.yml; sourceTree = ""; };
105 | 4437B7DB1954CC83005CCBBC /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; };
106 | 4437B7FB1956DF0B005CCBBC /* Tests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = Tests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
107 | 4437B80E1956DF41005CCBBC /* RDHGridLayoutTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RDHGridLayoutTests.m; sourceTree = ""; };
108 | 4437B8101956DF56005CCBBC /* Tests-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Tests-Prefix.pch"; sourceTree = ""; };
109 | 444926641AB6C179002DBB57 /* Tests-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Tests-Bridging-Header.h"; sourceTree = ""; };
110 | 444926651AB6C17A002DBB57 /* RDHStoryboardTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RDHStoryboardTests.swift; sourceTree = ""; };
111 | 444926691AB6C24D002DBB57 /* TestDataSources.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TestDataSources.swift; sourceTree = ""; };
112 | 4449266B1AB6C94C002DBB57 /* TestStoryboard.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = TestStoryboard.storyboard; sourceTree = ""; };
113 | 445AEA2D1D8EE6A7009FFBFF /* RDHEmptyGridLayoutTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RDHEmptyGridLayoutTests.swift; sourceTree = ""; };
114 | 446C8D781BA76D7C00FFB0E3 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
115 | 449FA2371B54490100EAD562 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
116 | 44BA60921D8EFC3600444CD8 /* XCTestCase+Extensions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "XCTestCase+Extensions.h"; sourceTree = ""; };
117 | 44BA60931D8EFC3600444CD8 /* XCTestCase+Extensions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "XCTestCase+Extensions.m"; sourceTree = ""; };
118 | 44F92B301C7E4A6000A0AD0A /* RDHCollectionViewGridLayout.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = RDHCollectionViewGridLayout.xcconfig; sourceTree = ""; };
119 | 44F92B351C7E65E900A0AD0A /* AppleTVDemo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = AppleTVDemo.app; sourceTree = BUILT_PRODUCTS_DIR; };
120 | 44F92B371C7E65E900A0AD0A /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; };
121 | 44F92B391C7E65E900A0AD0A /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; };
122 | 44F92B3C1C7E65E900A0AD0A /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; };
123 | 44F92B3E1C7E65EA00A0AD0A /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
124 | 44F92B401C7E65EA00A0AD0A /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
125 | /* End PBXFileReference section */
126 |
127 | /* Begin PBXFrameworksBuildPhase section */
128 | 442B4B161C7E685800C5B5FC /* Frameworks */ = {
129 | isa = PBXFrameworksBuildPhase;
130 | buildActionMask = 2147483647;
131 | files = (
132 | );
133 | runOnlyForDeploymentPostprocessing = 0;
134 | };
135 | 442BF3EE17896E1D003FAB93 /* Frameworks */ = {
136 | isa = PBXFrameworksBuildPhase;
137 | buildActionMask = 2147483647;
138 | files = (
139 | 442B4B261C7E694400C5B5FC /* RDHCollectionViewGridLayout.framework in Frameworks */,
140 | );
141 | runOnlyForDeploymentPostprocessing = 0;
142 | };
143 | 4437B7F81956DF0B005CCBBC /* Frameworks */ = {
144 | isa = PBXFrameworksBuildPhase;
145 | buildActionMask = 2147483647;
146 | files = (
147 | );
148 | runOnlyForDeploymentPostprocessing = 0;
149 | };
150 | 44F92B321C7E65E900A0AD0A /* Frameworks */ = {
151 | isa = PBXFrameworksBuildPhase;
152 | buildActionMask = 2147483647;
153 | files = (
154 | 442B4B2B1C7E694700C5B5FC /* RDHCollectionViewGridLayout.framework in Frameworks */,
155 | );
156 | runOnlyForDeploymentPostprocessing = 0;
157 | };
158 | /* End PBXFrameworksBuildPhase section */
159 |
160 | /* Begin PBXGroup section */
161 | 442BF3E817896E1D003FAB93 = {
162 | isa = PBXGroup;
163 | children = (
164 | 4437B7DA1954CB94005CCBBC /* .travis.yml */,
165 | 4437B7CF1954CB54005CCBBC /* LICENSE */,
166 | 4437B7D01954CB54005CCBBC /* RDHCollectionViewGridLayout.podspec */,
167 | 44F92B301C7E4A6000A0AD0A /* RDHCollectionViewGridLayout.xcconfig */,
168 | 4437B7D11954CB54005CCBBC /* README.md */,
169 | 4437B7D61954CB6F005CCBBC /* images */,
170 | 449FA2351B54490100EAD562 /* RDHCollectionViewGridLayout */,
171 | 442BF3FA17896E1D003FAB93 /* RDHCollectionViewGridLayoutDemo */,
172 | 4437B8001956DF0B005CCBBC /* Tests */,
173 | 44F92B361C7E65E900A0AD0A /* AppleTVDemo */,
174 | 442BF3F217896E1D003FAB93 /* Products */,
175 | );
176 | sourceTree = "";
177 | };
178 | 442BF3F217896E1D003FAB93 /* Products */ = {
179 | isa = PBXGroup;
180 | children = (
181 | 442BF3F117896E1D003FAB93 /* RDHCollectionViewGridLayoutDemo.app */,
182 | 4437B7FB1956DF0B005CCBBC /* Tests.xctest */,
183 | 44F92B351C7E65E900A0AD0A /* AppleTVDemo.app */,
184 | 442B4B1A1C7E685800C5B5FC /* RDHCollectionViewGridLayout.framework */,
185 | );
186 | name = Products;
187 | sourceTree = "";
188 | };
189 | 442BF3FA17896E1D003FAB93 /* RDHCollectionViewGridLayoutDemo */ = {
190 | isa = PBXGroup;
191 | children = (
192 | 442BF40317896E1D003FAB93 /* RDHAppDelegate.h */,
193 | 442BF40417896E1D003FAB93 /* RDHAppDelegate.m */,
194 | 442BF41417896E84003FAB93 /* RDHDemoViewController.h */,
195 | 442BF41517896E84003FAB93 /* RDHDemoViewController.m */,
196 | 442BF41117896E5D003FAB93 /* RDHDemoCell.h */,
197 | 442BF41217896E5D003FAB93 /* RDHDemoCell.m */,
198 | 442BF3FB17896E1D003FAB93 /* Supporting Files */,
199 | );
200 | path = RDHCollectionViewGridLayoutDemo;
201 | sourceTree = "";
202 | };
203 | 442BF3FB17896E1D003FAB93 /* Supporting Files */ = {
204 | isa = PBXGroup;
205 | children = (
206 | 442BF3FC17896E1D003FAB93 /* RDHCollectionViewGridLayoutDemo-Info.plist */,
207 | 442BF3FD17896E1D003FAB93 /* InfoPlist.strings */,
208 | 442BF40017896E1D003FAB93 /* main.m */,
209 | 442BF40217896E1D003FAB93 /* RDHCollectionViewGridLayoutDemo-Prefix.pch */,
210 | 4437B7DB1954CC83005CCBBC /* Images.xcassets */,
211 | );
212 | name = "Supporting Files";
213 | sourceTree = "";
214 | };
215 | 4437B7D61954CB6F005CCBBC /* images */ = {
216 | isa = PBXGroup;
217 | children = (
218 | 4437B7D71954CB6F005CCBBC /* 5-line, 10-item.png */,
219 | 4437B7D81954CB6F005CCBBC /* horizontal.png */,
220 | 4437B7D91954CB6F005CCBBC /* vertical.png */,
221 | );
222 | path = images;
223 | sourceTree = "";
224 | };
225 | 4437B8001956DF0B005CCBBC /* Tests */ = {
226 | isa = PBXGroup;
227 | children = (
228 | 4449266B1AB6C94C002DBB57 /* TestStoryboard.storyboard */,
229 | 444926691AB6C24D002DBB57 /* TestDataSources.swift */,
230 | 4437B8011956DF0B005CCBBC /* Supporting Files */,
231 | 4437B80E1956DF41005CCBBC /* RDHGridLayoutTests.m */,
232 | 445AEA2D1D8EE6A7009FFBFF /* RDHEmptyGridLayoutTests.swift */,
233 | 444926651AB6C17A002DBB57 /* RDHStoryboardTests.swift */,
234 | );
235 | path = Tests;
236 | sourceTree = "";
237 | };
238 | 4437B8011956DF0B005CCBBC /* Supporting Files */ = {
239 | isa = PBXGroup;
240 | children = (
241 | 446C8D781BA76D7C00FFB0E3 /* Info.plist */,
242 | 4437B8101956DF56005CCBBC /* Tests-Prefix.pch */,
243 | 444926641AB6C179002DBB57 /* Tests-Bridging-Header.h */,
244 | 44BA60921D8EFC3600444CD8 /* XCTestCase+Extensions.h */,
245 | 44BA60931D8EFC3600444CD8 /* XCTestCase+Extensions.m */,
246 | );
247 | name = "Supporting Files";
248 | sourceTree = "";
249 | };
250 | 449FA2351B54490100EAD562 /* RDHCollectionViewGridLayout */ = {
251 | isa = PBXGroup;
252 | children = (
253 | 4437B7D31954CB5A005CCBBC /* RDHCollectionViewGridLayout.h */,
254 | 4437B7D41954CB5A005CCBBC /* RDHCollectionViewGridLayout.m */,
255 | 449FA2361B54490100EAD562 /* Supporting Files */,
256 | );
257 | path = RDHCollectionViewGridLayout;
258 | sourceTree = "";
259 | };
260 | 449FA2361B54490100EAD562 /* Supporting Files */ = {
261 | isa = PBXGroup;
262 | children = (
263 | 449FA2371B54490100EAD562 /* Info.plist */,
264 | );
265 | name = "Supporting Files";
266 | sourceTree = "";
267 | };
268 | 44F92B361C7E65E900A0AD0A /* AppleTVDemo */ = {
269 | isa = PBXGroup;
270 | children = (
271 | 44F92B371C7E65E900A0AD0A /* AppDelegate.swift */,
272 | 44F92B391C7E65E900A0AD0A /* ViewController.swift */,
273 | 44F92B3B1C7E65E900A0AD0A /* Main.storyboard */,
274 | 44F92B3E1C7E65EA00A0AD0A /* Assets.xcassets */,
275 | 44F92B401C7E65EA00A0AD0A /* Info.plist */,
276 | );
277 | path = AppleTVDemo;
278 | sourceTree = "";
279 | };
280 | /* End PBXGroup section */
281 |
282 | /* Begin PBXHeadersBuildPhase section */
283 | 442B4B171C7E685800C5B5FC /* Headers */ = {
284 | isa = PBXHeadersBuildPhase;
285 | buildActionMask = 2147483647;
286 | files = (
287 | 442B4B301C7E699900C5B5FC /* RDHCollectionViewGridLayout.h in Headers */,
288 | );
289 | runOnlyForDeploymentPostprocessing = 0;
290 | };
291 | /* End PBXHeadersBuildPhase section */
292 |
293 | /* Begin PBXNativeTarget section */
294 | 442B4B191C7E685800C5B5FC /* RDHCollectionViewGridLayout */ = {
295 | isa = PBXNativeTarget;
296 | buildConfigurationList = 442B4B231C7E685800C5B5FC /* Build configuration list for PBXNativeTarget "RDHCollectionViewGridLayout" */;
297 | buildPhases = (
298 | 442B4B151C7E685800C5B5FC /* Sources */,
299 | 442B4B161C7E685800C5B5FC /* Frameworks */,
300 | 442B4B171C7E685800C5B5FC /* Headers */,
301 | 442B4B181C7E685800C5B5FC /* Resources */,
302 | );
303 | buildRules = (
304 | );
305 | dependencies = (
306 | );
307 | name = RDHCollectionViewGridLayout;
308 | productName = RDHCollectionViewGridLayout;
309 | productReference = 442B4B1A1C7E685800C5B5FC /* RDHCollectionViewGridLayout.framework */;
310 | productType = "com.apple.product-type.framework";
311 | };
312 | 442BF3F017896E1D003FAB93 /* RDHCollectionViewGridLayoutDemo */ = {
313 | isa = PBXNativeTarget;
314 | buildConfigurationList = 442BF40E17896E1D003FAB93 /* Build configuration list for PBXNativeTarget "RDHCollectionViewGridLayoutDemo" */;
315 | buildPhases = (
316 | 442BF3ED17896E1D003FAB93 /* Sources */,
317 | 442BF3EE17896E1D003FAB93 /* Frameworks */,
318 | 442BF3EF17896E1D003FAB93 /* Resources */,
319 | 442B4B2A1C7E694400C5B5FC /* Embed Frameworks */,
320 | );
321 | buildRules = (
322 | );
323 | dependencies = (
324 | 442B4B291C7E694400C5B5FC /* PBXTargetDependency */,
325 | );
326 | name = RDHCollectionViewGridLayoutDemo;
327 | productName = RDHCollectionViewGridLayoutDemo;
328 | productReference = 442BF3F117896E1D003FAB93 /* RDHCollectionViewGridLayoutDemo.app */;
329 | productType = "com.apple.product-type.application";
330 | };
331 | 4437B7FA1956DF0B005CCBBC /* Tests */ = {
332 | isa = PBXNativeTarget;
333 | buildConfigurationList = 4437B80D1956DF0B005CCBBC /* Build configuration list for PBXNativeTarget "Tests" */;
334 | buildPhases = (
335 | 4437B7F71956DF0B005CCBBC /* Sources */,
336 | 4437B7F81956DF0B005CCBBC /* Frameworks */,
337 | 4437B7F91956DF0B005CCBBC /* Resources */,
338 | );
339 | buildRules = (
340 | );
341 | dependencies = (
342 | 4437B80A1956DF0B005CCBBC /* PBXTargetDependency */,
343 | );
344 | name = Tests;
345 | productName = Tests;
346 | productReference = 4437B7FB1956DF0B005CCBBC /* Tests.xctest */;
347 | productType = "com.apple.product-type.bundle.unit-test";
348 | };
349 | 44F92B341C7E65E900A0AD0A /* AppleTVDemo */ = {
350 | isa = PBXNativeTarget;
351 | buildConfigurationList = 44F92B411C7E65EA00A0AD0A /* Build configuration list for PBXNativeTarget "AppleTVDemo" */;
352 | buildPhases = (
353 | 44F92B311C7E65E900A0AD0A /* Sources */,
354 | 44F92B321C7E65E900A0AD0A /* Frameworks */,
355 | 44F92B331C7E65E900A0AD0A /* Resources */,
356 | 44F92B481C7E662900A0AD0A /* Embed Frameworks */,
357 | );
358 | buildRules = (
359 | );
360 | dependencies = (
361 | 442B4B2E1C7E694700C5B5FC /* PBXTargetDependency */,
362 | );
363 | name = AppleTVDemo;
364 | productName = AppleTVDemo;
365 | productReference = 44F92B351C7E65E900A0AD0A /* AppleTVDemo.app */;
366 | productType = "com.apple.product-type.application";
367 | };
368 | /* End PBXNativeTarget section */
369 |
370 | /* Begin PBXProject section */
371 | 442BF3E917896E1D003FAB93 /* Project object */ = {
372 | isa = PBXProject;
373 | attributes = {
374 | CLASSPREFIX = RDH;
375 | LastSwiftUpdateCheck = 0720;
376 | LastUpgradeCheck = 0800;
377 | ORGANIZATIONNAME = "Rich H";
378 | TargetAttributes = {
379 | 442B4B191C7E685800C5B5FC = {
380 | CreatedOnToolsVersion = 7.2.1;
381 | };
382 | 442BF3F017896E1D003FAB93 = {
383 | ProvisioningStyle = Manual;
384 | };
385 | 4437B7FA1956DF0B005CCBBC = {
386 | LastSwiftMigration = 0800;
387 | TestTargetID = 442BF3F017896E1D003FAB93;
388 | };
389 | 44F92B341C7E65E900A0AD0A = {
390 | CreatedOnToolsVersion = 7.2.1;
391 | ProvisioningStyle = Manual;
392 | };
393 | };
394 | };
395 | buildConfigurationList = 442BF3EC17896E1D003FAB93 /* Build configuration list for PBXProject "RDHCollectionViewGridLayoutDemo" */;
396 | compatibilityVersion = "Xcode 3.2";
397 | developmentRegion = English;
398 | hasScannedForEncodings = 0;
399 | knownRegions = (
400 | en,
401 | Base,
402 | );
403 | mainGroup = 442BF3E817896E1D003FAB93;
404 | productRefGroup = 442BF3F217896E1D003FAB93 /* Products */;
405 | projectDirPath = "";
406 | projectRoot = "";
407 | targets = (
408 | 442B4B191C7E685800C5B5FC /* RDHCollectionViewGridLayout */,
409 | 442BF3F017896E1D003FAB93 /* RDHCollectionViewGridLayoutDemo */,
410 | 44F92B341C7E65E900A0AD0A /* AppleTVDemo */,
411 | 4437B7FA1956DF0B005CCBBC /* Tests */,
412 | );
413 | };
414 | /* End PBXProject section */
415 |
416 | /* Begin PBXResourcesBuildPhase section */
417 | 442B4B181C7E685800C5B5FC /* Resources */ = {
418 | isa = PBXResourcesBuildPhase;
419 | buildActionMask = 2147483647;
420 | files = (
421 | );
422 | runOnlyForDeploymentPostprocessing = 0;
423 | };
424 | 442BF3EF17896E1D003FAB93 /* Resources */ = {
425 | isa = PBXResourcesBuildPhase;
426 | buildActionMask = 2147483647;
427 | files = (
428 | 442BF3FF17896E1D003FAB93 /* InfoPlist.strings in Resources */,
429 | 4437B7DC1954CC83005CCBBC /* Images.xcassets in Resources */,
430 | );
431 | runOnlyForDeploymentPostprocessing = 0;
432 | };
433 | 4437B7F91956DF0B005CCBBC /* Resources */ = {
434 | isa = PBXResourcesBuildPhase;
435 | buildActionMask = 2147483647;
436 | files = (
437 | 4449266C1AB6C94C002DBB57 /* TestStoryboard.storyboard in Resources */,
438 | );
439 | runOnlyForDeploymentPostprocessing = 0;
440 | };
441 | 44F92B331C7E65E900A0AD0A /* Resources */ = {
442 | isa = PBXResourcesBuildPhase;
443 | buildActionMask = 2147483647;
444 | files = (
445 | 44F92B3F1C7E65EA00A0AD0A /* Assets.xcassets in Resources */,
446 | 44F92B3D1C7E65E900A0AD0A /* Main.storyboard in Resources */,
447 | );
448 | runOnlyForDeploymentPostprocessing = 0;
449 | };
450 | /* End PBXResourcesBuildPhase section */
451 |
452 | /* Begin PBXSourcesBuildPhase section */
453 | 442B4B151C7E685800C5B5FC /* Sources */ = {
454 | isa = PBXSourcesBuildPhase;
455 | buildActionMask = 2147483647;
456 | files = (
457 | 442B4B311C7E699E00C5B5FC /* RDHCollectionViewGridLayout.m in Sources */,
458 | );
459 | runOnlyForDeploymentPostprocessing = 0;
460 | };
461 | 442BF3ED17896E1D003FAB93 /* Sources */ = {
462 | isa = PBXSourcesBuildPhase;
463 | buildActionMask = 2147483647;
464 | files = (
465 | 442BF40117896E1D003FAB93 /* main.m in Sources */,
466 | 442BF40517896E1D003FAB93 /* RDHAppDelegate.m in Sources */,
467 | 442BF41617896E84003FAB93 /* RDHDemoViewController.m in Sources */,
468 | 442BF41317896E5D003FAB93 /* RDHDemoCell.m in Sources */,
469 | );
470 | runOnlyForDeploymentPostprocessing = 0;
471 | };
472 | 4437B7F71956DF0B005CCBBC /* Sources */ = {
473 | isa = PBXSourcesBuildPhase;
474 | buildActionMask = 2147483647;
475 | files = (
476 | 445AEA2E1D8EE6A7009FFBFF /* RDHEmptyGridLayoutTests.swift in Sources */,
477 | 4437B80F1956DF41005CCBBC /* RDHGridLayoutTests.m in Sources */,
478 | 444926661AB6C17A002DBB57 /* RDHStoryboardTests.swift in Sources */,
479 | 44BA60941D8EFC3600444CD8 /* XCTestCase+Extensions.m in Sources */,
480 | 4449266A1AB6C24D002DBB57 /* TestDataSources.swift in Sources */,
481 | );
482 | runOnlyForDeploymentPostprocessing = 0;
483 | };
484 | 44F92B311C7E65E900A0AD0A /* Sources */ = {
485 | isa = PBXSourcesBuildPhase;
486 | buildActionMask = 2147483647;
487 | files = (
488 | 44F92B3A1C7E65E900A0AD0A /* ViewController.swift in Sources */,
489 | 44F92B381C7E65E900A0AD0A /* AppDelegate.swift in Sources */,
490 | );
491 | runOnlyForDeploymentPostprocessing = 0;
492 | };
493 | /* End PBXSourcesBuildPhase section */
494 |
495 | /* Begin PBXTargetDependency section */
496 | 442B4B291C7E694400C5B5FC /* PBXTargetDependency */ = {
497 | isa = PBXTargetDependency;
498 | target = 442B4B191C7E685800C5B5FC /* RDHCollectionViewGridLayout */;
499 | targetProxy = 442B4B281C7E694400C5B5FC /* PBXContainerItemProxy */;
500 | };
501 | 442B4B2E1C7E694700C5B5FC /* PBXTargetDependency */ = {
502 | isa = PBXTargetDependency;
503 | target = 442B4B191C7E685800C5B5FC /* RDHCollectionViewGridLayout */;
504 | targetProxy = 442B4B2D1C7E694700C5B5FC /* PBXContainerItemProxy */;
505 | };
506 | 4437B80A1956DF0B005CCBBC /* PBXTargetDependency */ = {
507 | isa = PBXTargetDependency;
508 | target = 442BF3F017896E1D003FAB93 /* RDHCollectionViewGridLayoutDemo */;
509 | targetProxy = 4437B8091956DF0B005CCBBC /* PBXContainerItemProxy */;
510 | };
511 | /* End PBXTargetDependency section */
512 |
513 | /* Begin PBXVariantGroup section */
514 | 442BF3FD17896E1D003FAB93 /* InfoPlist.strings */ = {
515 | isa = PBXVariantGroup;
516 | children = (
517 | 442BF3FE17896E1D003FAB93 /* en */,
518 | );
519 | name = InfoPlist.strings;
520 | sourceTree = "";
521 | };
522 | 44F92B3B1C7E65E900A0AD0A /* Main.storyboard */ = {
523 | isa = PBXVariantGroup;
524 | children = (
525 | 44F92B3C1C7E65E900A0AD0A /* Base */,
526 | );
527 | name = Main.storyboard;
528 | sourceTree = "";
529 | };
530 | /* End PBXVariantGroup section */
531 |
532 | /* Begin XCBuildConfiguration section */
533 | 442B4B241C7E685800C5B5FC /* Debug */ = {
534 | isa = XCBuildConfiguration;
535 | baseConfigurationReference = 44F92B301C7E4A6000A0AD0A /* RDHCollectionViewGridLayout.xcconfig */;
536 | buildSettings = {
537 | CLANG_ENABLE_MODULES = YES;
538 | CLANG_WARN_BOOL_CONVERSION = YES;
539 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
540 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
541 | CLANG_WARN_UNREACHABLE_CODE = YES;
542 | CODE_SIGN_IDENTITY = "";
543 | CURRENT_PROJECT_VERSION = 1;
544 | DEFINES_MODULE = YES;
545 | DYLIB_COMPATIBILITY_VERSION = 1;
546 | DYLIB_CURRENT_VERSION = 1;
547 | DYLIB_INSTALL_NAME_BASE = "@rpath";
548 | ENABLE_STRICT_OBJC_MSGSEND = YES;
549 | GCC_NO_COMMON_BLOCKS = YES;
550 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
551 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
552 | GCC_WARN_UNDECLARED_SELECTOR = YES;
553 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
554 | GCC_WARN_UNUSED_FUNCTION = YES;
555 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
556 | MTL_ENABLE_DEBUG_INFO = YES;
557 | SDKROOT = "";
558 | SKIP_INSTALL = YES;
559 | VERSIONING_SYSTEM = "apple-generic";
560 | VERSION_INFO_PREFIX = "";
561 | };
562 | name = Debug;
563 | };
564 | 442B4B251C7E685800C5B5FC /* Release */ = {
565 | isa = XCBuildConfiguration;
566 | baseConfigurationReference = 44F92B301C7E4A6000A0AD0A /* RDHCollectionViewGridLayout.xcconfig */;
567 | buildSettings = {
568 | CLANG_ENABLE_MODULES = YES;
569 | CLANG_WARN_BOOL_CONVERSION = YES;
570 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
571 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
572 | CLANG_WARN_UNREACHABLE_CODE = YES;
573 | CODE_SIGN_IDENTITY = "";
574 | COPY_PHASE_STRIP = NO;
575 | CURRENT_PROJECT_VERSION = 1;
576 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
577 | DEFINES_MODULE = YES;
578 | DYLIB_COMPATIBILITY_VERSION = 1;
579 | DYLIB_CURRENT_VERSION = 1;
580 | DYLIB_INSTALL_NAME_BASE = "@rpath";
581 | ENABLE_NS_ASSERTIONS = NO;
582 | ENABLE_STRICT_OBJC_MSGSEND = YES;
583 | GCC_NO_COMMON_BLOCKS = YES;
584 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
585 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
586 | GCC_WARN_UNDECLARED_SELECTOR = YES;
587 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
588 | GCC_WARN_UNUSED_FUNCTION = YES;
589 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
590 | MTL_ENABLE_DEBUG_INFO = NO;
591 | SDKROOT = "";
592 | SKIP_INSTALL = YES;
593 | VERSIONING_SYSTEM = "apple-generic";
594 | VERSION_INFO_PREFIX = "";
595 | };
596 | name = Release;
597 | };
598 | 442BF40C17896E1D003FAB93 /* Debug */ = {
599 | isa = XCBuildConfiguration;
600 | buildSettings = {
601 | ALWAYS_SEARCH_USER_PATHS = NO;
602 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
603 | CLANG_CXX_LIBRARY = "libc++";
604 | CLANG_ENABLE_OBJC_ARC = YES;
605 | CLANG_WARN_BOOL_CONVERSION = YES;
606 | CLANG_WARN_CONSTANT_CONVERSION = YES;
607 | CLANG_WARN_EMPTY_BODY = YES;
608 | CLANG_WARN_ENUM_CONVERSION = YES;
609 | CLANG_WARN_INFINITE_RECURSION = YES;
610 | CLANG_WARN_INT_CONVERSION = YES;
611 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
612 | CLANG_WARN_UNREACHABLE_CODE = YES;
613 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
614 | COPY_PHASE_STRIP = NO;
615 | DEBUG_INFORMATION_FORMAT = dwarf;
616 | DEFINES_MODULE = YES;
617 | ENABLE_STRICT_OBJC_MSGSEND = YES;
618 | ENABLE_TESTABILITY = YES;
619 | GCC_C_LANGUAGE_STANDARD = gnu99;
620 | GCC_DYNAMIC_NO_PIC = NO;
621 | GCC_NO_COMMON_BLOCKS = YES;
622 | GCC_OPTIMIZATION_LEVEL = 0;
623 | GCC_PREPROCESSOR_DEFINITIONS = (
624 | "DEBUG=1",
625 | "$(inherited)",
626 | );
627 | GCC_SYMBOLS_PRIVATE_EXTERN = NO;
628 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
629 | GCC_WARN_ABOUT_RETURN_TYPE = YES;
630 | GCC_WARN_UNDECLARED_SELECTOR = YES;
631 | GCC_WARN_UNINITIALIZED_AUTOS = YES;
632 | GCC_WARN_UNUSED_FUNCTION = YES;
633 | GCC_WARN_UNUSED_VARIABLE = YES;
634 | IPHONEOS_DEPLOYMENT_TARGET = 8.0;
635 | ONLY_ACTIVE_ARCH = YES;
636 | SDKROOT = iphoneos;
637 | SWIFT_VERSION = 3.0;
638 | TARGETED_DEVICE_FAMILY = "1,2";
639 | };
640 | name = Debug;
641 | };
642 | 442BF40D17896E1D003FAB93 /* Release */ = {
643 | isa = XCBuildConfiguration;
644 | buildSettings = {
645 | ALWAYS_SEARCH_USER_PATHS = NO;
646 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
647 | CLANG_CXX_LIBRARY = "libc++";
648 | CLANG_ENABLE_OBJC_ARC = YES;
649 | CLANG_WARN_BOOL_CONVERSION = YES;
650 | CLANG_WARN_CONSTANT_CONVERSION = YES;
651 | CLANG_WARN_EMPTY_BODY = YES;
652 | CLANG_WARN_ENUM_CONVERSION = YES;
653 | CLANG_WARN_INFINITE_RECURSION = YES;
654 | CLANG_WARN_INT_CONVERSION = YES;
655 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
656 | CLANG_WARN_UNREACHABLE_CODE = YES;
657 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
658 | COPY_PHASE_STRIP = YES;
659 | DEFINES_MODULE = YES;
660 | ENABLE_STRICT_OBJC_MSGSEND = YES;
661 | GCC_C_LANGUAGE_STANDARD = gnu99;
662 | GCC_NO_COMMON_BLOCKS = YES;
663 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
664 | GCC_WARN_ABOUT_RETURN_TYPE = YES;
665 | GCC_WARN_UNDECLARED_SELECTOR = YES;
666 | GCC_WARN_UNINITIALIZED_AUTOS = YES;
667 | GCC_WARN_UNUSED_FUNCTION = YES;
668 | GCC_WARN_UNUSED_VARIABLE = YES;
669 | IPHONEOS_DEPLOYMENT_TARGET = 8.0;
670 | OTHER_CFLAGS = "-DNS_BLOCK_ASSERTIONS=1";
671 | PROVISIONING_PROFILE = "";
672 | SDKROOT = iphoneos;
673 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
674 | SWIFT_VERSION = 3.0;
675 | TARGETED_DEVICE_FAMILY = "1,2";
676 | VALIDATE_PRODUCT = YES;
677 | };
678 | name = Release;
679 | };
680 | 442BF40F17896E1D003FAB93 /* Debug */ = {
681 | isa = XCBuildConfiguration;
682 | buildSettings = {
683 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
684 | ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage;
685 | CLANG_ENABLE_MODULES = YES;
686 | CODE_SIGN_IDENTITY = "iPhone Developer";
687 | DEVELOPMENT_TEAM = "";
688 | GCC_PRECOMPILE_PREFIX_HEADER = YES;
689 | GCC_PREFIX_HEADER = "RDHCollectionViewGridLayoutDemo/RDHCollectionViewGridLayoutDemo-Prefix.pch";
690 | INFOPLIST_FILE = "RDHCollectionViewGridLayoutDemo/RDHCollectionViewGridLayoutDemo-Info.plist";
691 | IPHONEOS_DEPLOYMENT_TARGET = 8.0;
692 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
693 | PRODUCT_BUNDLE_IDENTIFIER = "uk.co.rhodgkins.${PRODUCT_NAME:rfc1034identifier}";
694 | PRODUCT_NAME = "$(TARGET_NAME)";
695 | WRAPPER_EXTENSION = app;
696 | };
697 | name = Debug;
698 | };
699 | 442BF41017896E1D003FAB93 /* Release */ = {
700 | isa = XCBuildConfiguration;
701 | buildSettings = {
702 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
703 | ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage;
704 | CLANG_ENABLE_MODULES = YES;
705 | CODE_SIGN_IDENTITY = "iPhone Distribution";
706 | DEVELOPMENT_TEAM = "";
707 | GCC_PRECOMPILE_PREFIX_HEADER = YES;
708 | GCC_PREFIX_HEADER = "RDHCollectionViewGridLayoutDemo/RDHCollectionViewGridLayoutDemo-Prefix.pch";
709 | INFOPLIST_FILE = "RDHCollectionViewGridLayoutDemo/RDHCollectionViewGridLayoutDemo-Info.plist";
710 | IPHONEOS_DEPLOYMENT_TARGET = 8.0;
711 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
712 | PRODUCT_BUNDLE_IDENTIFIER = "uk.co.rhodgkins.${PRODUCT_NAME:rfc1034identifier}";
713 | PRODUCT_NAME = "$(TARGET_NAME)";
714 | WRAPPER_EXTENSION = app;
715 | };
716 | name = Release;
717 | };
718 | 4437B80B1956DF0B005CCBBC /* Debug */ = {
719 | isa = XCBuildConfiguration;
720 | buildSettings = {
721 | BUNDLE_LOADER = "$(BUILT_PRODUCTS_DIR)/RDHCollectionViewGridLayoutDemo.app/RDHCollectionViewGridLayoutDemo";
722 | CLANG_ENABLE_MODULES = YES;
723 | CLANG_WARN_BOOL_CONVERSION = YES;
724 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
725 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
726 | GCC_PRECOMPILE_PREFIX_HEADER = YES;
727 | GCC_PREFIX_HEADER = "Tests/Tests-Prefix.pch";
728 | GCC_PREPROCESSOR_DEFINITIONS = (
729 | "DEBUG=1",
730 | "$(inherited)",
731 | );
732 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
733 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
734 | GCC_WARN_UNDECLARED_SELECTOR = YES;
735 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
736 | GCC_WARN_UNUSED_FUNCTION = YES;
737 | INFOPLIST_FILE = Tests/Info.plist;
738 | IPHONEOS_DEPLOYMENT_TARGET = 8.0;
739 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
740 | PRODUCT_NAME = "$(TARGET_NAME)";
741 | SWIFT_OBJC_BRIDGING_HEADER = "Tests/Tests-Bridging-Header.h";
742 | SWIFT_OPTIMIZATION_LEVEL = "-Onone";
743 | TEST_HOST = "$(BUNDLE_LOADER)";
744 | WRAPPER_EXTENSION = xctest;
745 | };
746 | name = Debug;
747 | };
748 | 4437B80C1956DF0B005CCBBC /* Release */ = {
749 | isa = XCBuildConfiguration;
750 | buildSettings = {
751 | BUNDLE_LOADER = "$(BUILT_PRODUCTS_DIR)/RDHCollectionViewGridLayoutDemo.app/RDHCollectionViewGridLayoutDemo";
752 | CLANG_ENABLE_MODULES = YES;
753 | CLANG_WARN_BOOL_CONVERSION = YES;
754 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
755 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
756 | ENABLE_NS_ASSERTIONS = NO;
757 | GCC_PRECOMPILE_PREFIX_HEADER = YES;
758 | GCC_PREFIX_HEADER = "Tests/Tests-Prefix.pch";
759 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
760 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
761 | GCC_WARN_UNDECLARED_SELECTOR = YES;
762 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
763 | GCC_WARN_UNUSED_FUNCTION = YES;
764 | INFOPLIST_FILE = Tests/Info.plist;
765 | IPHONEOS_DEPLOYMENT_TARGET = 8.0;
766 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
767 | PRODUCT_NAME = "$(TARGET_NAME)";
768 | SWIFT_OBJC_BRIDGING_HEADER = "Tests/Tests-Bridging-Header.h";
769 | TEST_HOST = "$(BUNDLE_LOADER)";
770 | WRAPPER_EXTENSION = xctest;
771 | };
772 | name = Release;
773 | };
774 | 44F92B421C7E65EA00A0AD0A /* Debug */ = {
775 | isa = XCBuildConfiguration;
776 | buildSettings = {
777 | ASSETCATALOG_COMPILER_APPICON_NAME = "App Icon & Top Shelf Image";
778 | ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage;
779 | CLANG_ENABLE_MODULES = YES;
780 | CLANG_WARN_BOOL_CONVERSION = YES;
781 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
782 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
783 | CLANG_WARN_UNREACHABLE_CODE = YES;
784 | CODE_SIGN_IDENTITY = "iPhone Developer";
785 | DEVELOPMENT_TEAM = "";
786 | ENABLE_STRICT_OBJC_MSGSEND = YES;
787 | GCC_NO_COMMON_BLOCKS = YES;
788 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
789 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
790 | GCC_WARN_UNDECLARED_SELECTOR = YES;
791 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
792 | GCC_WARN_UNUSED_FUNCTION = YES;
793 | INFOPLIST_FILE = AppleTVDemo/Info.plist;
794 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
795 | MTL_ENABLE_DEBUG_INFO = YES;
796 | PRODUCT_BUNDLE_IDENTIFIER = uk.co.rhodgkins.RDHCollectionViewGridLayoutDemo.AppleTVDemo;
797 | PRODUCT_NAME = "$(TARGET_NAME)";
798 | SDKROOT = appletvos;
799 | SWIFT_OPTIMIZATION_LEVEL = "-Onone";
800 | TARGETED_DEVICE_FAMILY = 3;
801 | TVOS_DEPLOYMENT_TARGET = 9.0;
802 | };
803 | name = Debug;
804 | };
805 | 44F92B431C7E65EA00A0AD0A /* Release */ = {
806 | isa = XCBuildConfiguration;
807 | buildSettings = {
808 | ASSETCATALOG_COMPILER_APPICON_NAME = "App Icon & Top Shelf Image";
809 | ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage;
810 | CLANG_ENABLE_MODULES = YES;
811 | CLANG_WARN_BOOL_CONVERSION = YES;
812 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
813 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
814 | CLANG_WARN_UNREACHABLE_CODE = YES;
815 | CODE_SIGN_IDENTITY = "iPhone Distribution";
816 | COPY_PHASE_STRIP = NO;
817 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
818 | DEVELOPMENT_TEAM = "";
819 | ENABLE_NS_ASSERTIONS = NO;
820 | ENABLE_STRICT_OBJC_MSGSEND = YES;
821 | GCC_NO_COMMON_BLOCKS = YES;
822 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
823 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
824 | GCC_WARN_UNDECLARED_SELECTOR = YES;
825 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
826 | GCC_WARN_UNUSED_FUNCTION = YES;
827 | INFOPLIST_FILE = AppleTVDemo/Info.plist;
828 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
829 | MTL_ENABLE_DEBUG_INFO = NO;
830 | PRODUCT_BUNDLE_IDENTIFIER = uk.co.rhodgkins.RDHCollectionViewGridLayoutDemo.AppleTVDemo;
831 | PRODUCT_NAME = "$(TARGET_NAME)";
832 | SDKROOT = appletvos;
833 | TARGETED_DEVICE_FAMILY = 3;
834 | TVOS_DEPLOYMENT_TARGET = 9.0;
835 | };
836 | name = Release;
837 | };
838 | /* End XCBuildConfiguration section */
839 |
840 | /* Begin XCConfigurationList section */
841 | 442B4B231C7E685800C5B5FC /* Build configuration list for PBXNativeTarget "RDHCollectionViewGridLayout" */ = {
842 | isa = XCConfigurationList;
843 | buildConfigurations = (
844 | 442B4B241C7E685800C5B5FC /* Debug */,
845 | 442B4B251C7E685800C5B5FC /* Release */,
846 | );
847 | defaultConfigurationIsVisible = 0;
848 | defaultConfigurationName = Release;
849 | };
850 | 442BF3EC17896E1D003FAB93 /* Build configuration list for PBXProject "RDHCollectionViewGridLayoutDemo" */ = {
851 | isa = XCConfigurationList;
852 | buildConfigurations = (
853 | 442BF40C17896E1D003FAB93 /* Debug */,
854 | 442BF40D17896E1D003FAB93 /* Release */,
855 | );
856 | defaultConfigurationIsVisible = 0;
857 | defaultConfigurationName = Release;
858 | };
859 | 442BF40E17896E1D003FAB93 /* Build configuration list for PBXNativeTarget "RDHCollectionViewGridLayoutDemo" */ = {
860 | isa = XCConfigurationList;
861 | buildConfigurations = (
862 | 442BF40F17896E1D003FAB93 /* Debug */,
863 | 442BF41017896E1D003FAB93 /* Release */,
864 | );
865 | defaultConfigurationIsVisible = 0;
866 | defaultConfigurationName = Release;
867 | };
868 | 4437B80D1956DF0B005CCBBC /* Build configuration list for PBXNativeTarget "Tests" */ = {
869 | isa = XCConfigurationList;
870 | buildConfigurations = (
871 | 4437B80B1956DF0B005CCBBC /* Debug */,
872 | 4437B80C1956DF0B005CCBBC /* Release */,
873 | );
874 | defaultConfigurationIsVisible = 0;
875 | defaultConfigurationName = Release;
876 | };
877 | 44F92B411C7E65EA00A0AD0A /* Build configuration list for PBXNativeTarget "AppleTVDemo" */ = {
878 | isa = XCConfigurationList;
879 | buildConfigurations = (
880 | 44F92B421C7E65EA00A0AD0A /* Debug */,
881 | 44F92B431C7E65EA00A0AD0A /* Release */,
882 | );
883 | defaultConfigurationIsVisible = 0;
884 | defaultConfigurationName = Release;
885 | };
886 | /* End XCConfigurationList section */
887 | };
888 | rootObject = 442BF3E917896E1D003FAB93 /* Project object */;
889 | }
890 |
--------------------------------------------------------------------------------
/Examples/RDHCollectionViewGridLayoutDemo.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/Examples/RDHCollectionViewGridLayoutDemo.xcodeproj/xcshareddata/xcschemes/AppleTVDemo.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
32 |
33 |
39 |
40 |
41 |
42 |
43 |
44 |
54 |
56 |
62 |
63 |
64 |
65 |
66 |
67 |
73 |
75 |
81 |
82 |
83 |
84 |
86 |
87 |
90 |
91 |
92 |
--------------------------------------------------------------------------------
/Examples/RDHCollectionViewGridLayoutDemo.xcodeproj/xcshareddata/xcschemes/RDHCollectionViewGridLayout.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
33 |
39 |
40 |
41 |
42 |
43 |
49 |
50 |
51 |
52 |
53 |
54 |
64 |
65 |
71 |
72 |
73 |
74 |
75 |
76 |
82 |
83 |
89 |
90 |
91 |
92 |
94 |
95 |
98 |
99 |
100 |
--------------------------------------------------------------------------------
/Examples/RDHCollectionViewGridLayoutDemo.xcodeproj/xcshareddata/xcschemes/RDHCollectionViewGridLayoutDemo.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
29 |
35 |
36 |
37 |
38 |
39 |
45 |
46 |
48 |
54 |
55 |
56 |
57 |
58 |
64 |
65 |
66 |
67 |
68 |
69 |
79 |
81 |
87 |
88 |
89 |
90 |
91 |
92 |
98 |
100 |
106 |
107 |
108 |
109 |
111 |
112 |
115 |
116 |
117 |
--------------------------------------------------------------------------------
/Examples/RDHCollectionViewGridLayoutDemo/Images.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "iphone",
5 | "size" : "29x29",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "iphone",
10 | "size" : "29x29",
11 | "scale" : "2x"
12 | },
13 | {
14 | "idiom" : "iphone",
15 | "size" : "40x40",
16 | "scale" : "2x"
17 | },
18 | {
19 | "idiom" : "iphone",
20 | "size" : "57x57",
21 | "scale" : "1x"
22 | },
23 | {
24 | "idiom" : "iphone",
25 | "size" : "57x57",
26 | "scale" : "2x"
27 | },
28 | {
29 | "idiom" : "iphone",
30 | "size" : "60x60",
31 | "scale" : "2x"
32 | }
33 | ],
34 | "info" : {
35 | "version" : 1,
36 | "author" : "xcode"
37 | },
38 | "properties" : {
39 | "pre-rendered" : true
40 | }
41 | }
--------------------------------------------------------------------------------
/Examples/RDHCollectionViewGridLayoutDemo/Images.xcassets/LaunchImage.launchimage/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "orientation" : "portrait",
5 | "idiom" : "iphone",
6 | "extent" : "full-screen",
7 | "minimum-system-version" : "7.0",
8 | "filename" : "Default@2x-1.png",
9 | "scale" : "2x"
10 | },
11 | {
12 | "extent" : "full-screen",
13 | "idiom" : "iphone",
14 | "subtype" : "retina4",
15 | "filename" : "Default-568h@2x-1.png",
16 | "minimum-system-version" : "7.0",
17 | "orientation" : "portrait",
18 | "scale" : "2x"
19 | },
20 | {
21 | "orientation" : "portrait",
22 | "idiom" : "iphone",
23 | "extent" : "full-screen",
24 | "filename" : "Default.png",
25 | "scale" : "1x"
26 | },
27 | {
28 | "orientation" : "portrait",
29 | "idiom" : "iphone",
30 | "extent" : "full-screen",
31 | "filename" : "Default@2x.png",
32 | "scale" : "2x"
33 | },
34 | {
35 | "orientation" : "portrait",
36 | "idiom" : "iphone",
37 | "extent" : "full-screen",
38 | "filename" : "Default-568h@2x.png",
39 | "subtype" : "retina4",
40 | "scale" : "2x"
41 | }
42 | ],
43 | "info" : {
44 | "version" : 1,
45 | "author" : "xcode"
46 | }
47 | }
--------------------------------------------------------------------------------
/Examples/RDHCollectionViewGridLayoutDemo/Images.xcassets/LaunchImage.launchimage/Default-568h@2x-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rhodgkins/RDHCollectionViewGridLayout/cb12530775efc6f4f9827be831a166b192b30d2a/Examples/RDHCollectionViewGridLayoutDemo/Images.xcassets/LaunchImage.launchimage/Default-568h@2x-1.png
--------------------------------------------------------------------------------
/Examples/RDHCollectionViewGridLayoutDemo/Images.xcassets/LaunchImage.launchimage/Default-568h@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rhodgkins/RDHCollectionViewGridLayout/cb12530775efc6f4f9827be831a166b192b30d2a/Examples/RDHCollectionViewGridLayoutDemo/Images.xcassets/LaunchImage.launchimage/Default-568h@2x.png
--------------------------------------------------------------------------------
/Examples/RDHCollectionViewGridLayoutDemo/Images.xcassets/LaunchImage.launchimage/Default.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rhodgkins/RDHCollectionViewGridLayout/cb12530775efc6f4f9827be831a166b192b30d2a/Examples/RDHCollectionViewGridLayoutDemo/Images.xcassets/LaunchImage.launchimage/Default.png
--------------------------------------------------------------------------------
/Examples/RDHCollectionViewGridLayoutDemo/Images.xcassets/LaunchImage.launchimage/Default@2x-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rhodgkins/RDHCollectionViewGridLayout/cb12530775efc6f4f9827be831a166b192b30d2a/Examples/RDHCollectionViewGridLayoutDemo/Images.xcassets/LaunchImage.launchimage/Default@2x-1.png
--------------------------------------------------------------------------------
/Examples/RDHCollectionViewGridLayoutDemo/Images.xcassets/LaunchImage.launchimage/Default@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rhodgkins/RDHCollectionViewGridLayout/cb12530775efc6f4f9827be831a166b192b30d2a/Examples/RDHCollectionViewGridLayoutDemo/Images.xcassets/LaunchImage.launchimage/Default@2x.png
--------------------------------------------------------------------------------
/Examples/RDHCollectionViewGridLayoutDemo/RDHAppDelegate.h:
--------------------------------------------------------------------------------
1 | //
2 | // RDHAppDelegate.h
3 | // RDHCollectionViewGridLayoutDemo
4 | //
5 | // Created by Richard Hodgkins on 07/07/2013.
6 | // Copyright (c) 2013 Rich H. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @class RDHDemoViewController;
12 |
13 | @interface RDHAppDelegate : UIResponder
14 |
15 | @property (strong, nonatomic) UIWindow *window;
16 |
17 | @property (nonatomic, weak) RDHDemoViewController *demoViewController;
18 |
19 | @end
20 |
--------------------------------------------------------------------------------
/Examples/RDHCollectionViewGridLayoutDemo/RDHAppDelegate.m:
--------------------------------------------------------------------------------
1 | //
2 | // RDHAppDelegate.m
3 | // RDHCollectionViewGridLayoutDemo
4 | //
5 | // Created by Richard Hodgkins on 07/07/2013.
6 | // Copyright (c) 2013 Rich H. All rights reserved.
7 | //
8 |
9 | #import "RDHAppDelegate.h"
10 |
11 | #import "RDHDemoViewController.h"
12 |
13 | @implementation RDHAppDelegate
14 |
15 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
16 | {
17 | RDHDemoViewController *vc = [RDHDemoViewController new];
18 | self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
19 | // Override point for customization after application launch.
20 | self.window.rootViewController = [[UINavigationController alloc] initWithRootViewController:vc];
21 | self.demoViewController = vc;
22 | self.window.backgroundColor = [UIColor whiteColor];
23 | [self.window makeKeyAndVisible];
24 | return YES;
25 | }
26 |
27 | @end
28 |
--------------------------------------------------------------------------------
/Examples/RDHCollectionViewGridLayoutDemo/RDHCollectionViewGridLayoutDemo-Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleDisplayName
8 | ${PRODUCT_NAME}
9 | CFBundleExecutable
10 | ${EXECUTABLE_NAME}
11 | CFBundleIdentifier
12 | $(PRODUCT_BUNDLE_IDENTIFIER)
13 | CFBundleInfoDictionaryVersion
14 | 6.0
15 | CFBundleName
16 | ${PRODUCT_NAME}
17 | CFBundlePackageType
18 | APPL
19 | CFBundleShortVersionString
20 | 1.0
21 | CFBundleSignature
22 | ????
23 | CFBundleVersion
24 | 1
25 | LSRequiresIPhoneOS
26 |
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 |
--------------------------------------------------------------------------------
/Examples/RDHCollectionViewGridLayoutDemo/RDHCollectionViewGridLayoutDemo-Prefix.pch:
--------------------------------------------------------------------------------
1 | //
2 | // Prefix header for all source files of the 'RDHCollectionViewGridLayoutDemo' target in the 'RDHCollectionViewGridLayoutDemo' project
3 | //
4 |
5 | #import
6 |
7 | #ifndef __IPHONE_6_0
8 | # warning "This project uses features only available in iOS SDK 6.0 and later."
9 | #endif
10 |
11 | #ifdef __OBJC__
12 | #import
13 | #endif
14 |
--------------------------------------------------------------------------------
/Examples/RDHCollectionViewGridLayoutDemo/RDHDemoCell.h:
--------------------------------------------------------------------------------
1 | //
2 | // RDHDemoCell.h
3 | // RDHCollectionViewGridLayoutDemo
4 | //
5 | // Created by Richard Hodgkins on 06/07/2013.
6 | // Copyright (c) 2013 Rich H. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface RDHDemoCell : UICollectionViewCell
12 |
13 | +(NSString *)reuseIdentifier;
14 |
15 | -(void)setText:(NSString *)text;
16 |
17 | @end
18 |
--------------------------------------------------------------------------------
/Examples/RDHCollectionViewGridLayoutDemo/RDHDemoCell.m:
--------------------------------------------------------------------------------
1 | //
2 | // RDHDemoCell.m
3 | // RDHCollectionViewGridLayoutDemo
4 | //
5 | // Created by Richard Hodgkins on 06/07/2013.
6 | // Copyright (c) 2013 Rich H. All rights reserved.
7 | //
8 |
9 | #import "RDHDemoCell.h"
10 |
11 | static CGFloat const RDH_CELL_PADDING = 0;
12 |
13 | @interface RDHDemoCell ()
14 |
15 | @property (nonatomic, weak, readonly) UILabel *label;
16 |
17 | @end
18 |
19 | @implementation RDHDemoCell
20 |
21 | +(NSString *)reuseIdentifier
22 | {
23 | return NSStringFromClass(self);
24 | }
25 |
26 | - (instancetype)initWithFrame:(CGRect)frame
27 | {
28 | self = [super initWithFrame:frame];
29 | if (self) {
30 | // Initialization code
31 |
32 | UILabel *label = [UILabel new];
33 | label.translatesAutoresizingMaskIntoConstraints = NO;
34 | label.backgroundColor = [UIColor clearColor];
35 | label.font = [UIFont boldSystemFontOfSize:14];
36 | label.textAlignment = NSTextAlignmentCenter;
37 | label.textColor = [UIColor whiteColor];
38 | label.numberOfLines = 3;
39 | [self.contentView addSubview:label];
40 | _label = label;
41 |
42 | // Pin to superview edge with RDH_CELL_PADDING inset
43 | [self pinView:label toContentViewAttribute:NSLayoutAttributeLeft withPadding:RDH_CELL_PADDING];
44 | [self pinView:label toContentViewAttribute:NSLayoutAttributeTop withPadding:RDH_CELL_PADDING];
45 | [self pinView:label toContentViewAttribute:NSLayoutAttributeRight withPadding:RDH_CELL_PADDING];
46 | [self pinView:label toContentViewAttribute:NSLayoutAttributeBottom withPadding:RDH_CELL_PADDING];
47 |
48 | self.backgroundView = [UIView new];
49 | self.backgroundView.backgroundColor = [[UIColor blueColor] colorWithAlphaComponent:0.2];
50 | }
51 | return self;
52 | }
53 |
54 | -(void)pinView:(UIView *)view toContentViewAttribute:(NSLayoutAttribute)attribute withPadding:(CGFloat)padding
55 | {
56 | [self.contentView addConstraint:[NSLayoutConstraint constraintWithItem:self.contentView attribute:attribute relatedBy:NSLayoutRelationEqual toItem:view attribute:attribute multiplier:1 constant:padding]];
57 | }
58 |
59 | -(void)setText:(NSString *)text
60 | {
61 | self.label.text = text;
62 | }
63 |
64 | @end
65 |
--------------------------------------------------------------------------------
/Examples/RDHCollectionViewGridLayoutDemo/RDHDemoViewController.h:
--------------------------------------------------------------------------------
1 | //
2 | // RDHDemoViewController.h
3 | // RDHCollectionViewGridLayoutDemo
4 | //
5 | // Created by Richard Hodgkins on 06/07/2013.
6 | // Copyright (c) 2013 Rich H. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface RDHDemoViewController : UICollectionViewController
12 |
13 | @end
14 |
--------------------------------------------------------------------------------
/Examples/RDHCollectionViewGridLayoutDemo/RDHDemoViewController.m:
--------------------------------------------------------------------------------
1 | //
2 | // RDHDemoViewController.m
3 | // RDHCollectionViewGridLayoutDemo
4 | //
5 | // Created by Richard Hodgkins on 06/07/2013.
6 | // Copyright (c) 2013 Rich H. All rights reserved.
7 | //
8 |
9 | #import "RDHDemoViewController.h"
10 |
11 | @import RDHCollectionViewGridLayout;
12 |
13 | #import "RDHDemoCell.h"
14 |
15 | #define RDH_RANDOM_DATA 1
16 |
17 | #define FIXED_LAYOUT 0
18 |
19 | @interface RDHDemoViewController ()
20 |
21 | @property (nonatomic, readonly) RDHCollectionViewGridLayout *currentCollectionViewLayout;
22 |
23 | @property (nonatomic, copy, readonly) NSDictionary *testData;
24 |
25 | @end
26 |
27 | @implementation RDHDemoViewController
28 |
29 | @dynamic collectionViewLayout;
30 |
31 | +(RDHCollectionViewGridLayout *)newGridLayout
32 | {
33 | RDHCollectionViewGridLayout *layout = [RDHCollectionViewGridLayout new];
34 | layout.lineItemCount = RDH_RANDOM_DATA ? ((arc4random() % 5) + 1) : 4;
35 | layout.lineSpacing = RDH_RANDOM_DATA ? (arc4random() % 16) : 5;
36 | layout.itemSpacing = RDH_RANDOM_DATA ? (arc4random() % 16) : 10;
37 | if (RDH_RANDOM_DATA) {
38 | switch (arc4random() % 3) {
39 | case 0: // Line size
40 | layout.lineSize = 160;
41 | break;
42 | case 1: // Line multiplier
43 | layout.lineMultiplier = 2;
44 | break;
45 | case 2: // Line extension
46 | layout.lineExtension = 100;
47 | break;
48 | }
49 | } else {
50 | layout.lineSize = 0;
51 | }
52 | layout.scrollDirection = UICollectionViewScrollDirectionVertical;
53 | layout.sectionsStartOnNewLine = YES;
54 |
55 | #if FIXED_LAYOUT
56 |
57 | layout.itemSpacing = 0;
58 | layout.lineSpacing = 0;
59 | layout.lineSize = 0;
60 | layout.lineItemCount = 3;
61 | layout.scrollDirection = UICollectionViewScrollDirectionVertical;
62 | layout.sectionsStartOnNewLine = NO;
63 |
64 | #endif
65 |
66 | return layout;
67 | }
68 |
69 | - (instancetype)init
70 | {
71 | self = [self initWithCollectionViewLayout:[[self class] newGridLayout]];
72 | if (self) {
73 | // Custom initialization
74 |
75 | NSUInteger sectionCount = RDH_RANDOM_DATA ? (arc4random() % 20) + 10 : 10;
76 | NSMutableDictionary *testData = [NSMutableDictionary dictionaryWithCapacity:sectionCount];
77 | for (NSUInteger i=0; i
10 |
11 | #import "RDHAppDelegate.h"
12 |
13 | int main(int argc, char *argv[])
14 | {
15 | @autoreleasepool {
16 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([RDHAppDelegate class]));
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/Examples/Tests/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 | BNDL
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | 1
23 |
24 |
25 |
--------------------------------------------------------------------------------
/Examples/Tests/RDHEmptyGridLayoutTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // RDHEmptyGridLayoutTests.swift
3 | // RDHCollectionViewGridLayoutDemo
4 | //
5 | // Created by Richard Hodgkins on 22/06/2014.
6 | // Copyright (c) 2014 Rich H. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import XCTest
11 |
12 | final class RDHEmptyGridLayoutTests : XCTestCase {
13 |
14 | private var collectionView: UICollectionView!
15 | private var layout: RDHCollectionViewGridLayout!
16 | private let dataSource = EmptyTestDataSource()
17 | private var window: UIWindow!
18 |
19 | override func setUp() {
20 | super.setUp()
21 |
22 | window = UIWindow(frame: CGRect(x: 0, y: 0, width: 640, height: 640))
23 | window.makeKeyAndVisible()
24 | layout = RDHCollectionViewGridLayout()
25 | let controller = UICollectionViewController(collectionViewLayout: layout)
26 | controller.edgesForExtendedLayout = []
27 | controller.automaticallyAdjustsScrollViewInsets = false
28 | collectionView = controller.collectionView
29 | collectionView.register(UICollectionViewCell.self, forCellWithReuseIdentifier: TestDataSource.CellID)
30 | collectionView.dataSource = dataSource
31 | collectionView.delegate = nil
32 | window.rootViewController = controller
33 | }
34 |
35 | func testParameters() {
36 | layout.scrollDirection = .horizontal
37 | XCTAssertEqual(layout.scrollDirection, .horizontal, "Scroll direction should be horiztonal")
38 |
39 | layout.scrollDirection = .horizontal
40 | XCTAssertFalse(layout.value(forKeyPath: "verticalScrolling") as! Bool)
41 | layout.scrollDirection = .vertical
42 | XCTAssertTrue(layout.value(forKeyPath: "verticalScrolling") as! Bool)
43 |
44 | layout.setValue(true, forKey: "verticalScrolling")
45 | XCTAssertEqual(layout.scrollDirection, .vertical, "Scroll direction should be vertical")
46 | layout.setValue(false, forKey: "verticalScrolling")
47 | XCTAssertEqual(layout.scrollDirection, .horizontal, "Scroll direction should be horiztonal")
48 |
49 | layout.lineSize = 123
50 | XCTAssertEqual(layout.lineSize, 123, "Line size is incorrect")
51 | XCTAssertEqual(layout.lineMultiplier, 1, "Line multiplier should be default")
52 | XCTAssertEqual(layout.lineExtension, 0, "Line extension should be default")
53 |
54 | layout.lineMultiplier = 2
55 | XCTAssertEqual(layout.lineSize, 0, "Line size should be default")
56 | XCTAssertEqual(layout.lineMultiplier, 2, "Line multiplier is incorrect")
57 | XCTAssertEqual(layout.lineExtension, 0, "Line extension should be default")
58 |
59 | layout.lineExtension = 200
60 | XCTAssertEqual(layout.lineSize, 0, "Line size should be default")
61 | XCTAssertEqual(layout.lineMultiplier, 1, "Line multiplier should be default")
62 | XCTAssertEqual(layout.lineExtension, 200, "Line extension is incorrect")
63 |
64 | layout.lineExtension = 0
65 | XCTAssertEqual(layout.lineSize, 0, "Line size should be default")
66 | XCTAssertEqual(layout.lineMultiplier, 1, "Line multiplier should be default")
67 | XCTAssertEqual(layout.lineExtension, 0, "Line extension should be default")
68 |
69 | layout.lineDimension = 123
70 | XCTAssertEqual(layout.lineSize, 123, "Line size is incorrect")
71 | XCTAssertEqual(layout.lineMultiplier, 1, "Line multiplier should be default")
72 | XCTAssertEqual(layout.lineExtension, 0, "Line extension should be default")
73 |
74 | layout.lineItemCount = 7
75 | XCTAssertEqual(layout.lineItemCount, 7, "Line item count should be 7")
76 |
77 | layout.itemSpacing = 23
78 | XCTAssertEqual(layout.itemSpacing, 23, "Line spacing should be 23")
79 |
80 | layout.sectionsStartOnNewLine = true
81 | XCTAssertTrue(layout.sectionsStartOnNewLine, "Sections should start on new lines")
82 | }
83 |
84 | func updateWindowFrame() {
85 |
86 | var frame = CGRect.zero
87 | if (layout.scrollDirection == .vertical) {
88 | frame.size.width = 640
89 | frame.size.height = 2000
90 | } else {
91 | frame.size.width = 2000
92 | frame.size.height = 640
93 | }
94 | window.frame = frame
95 | window.layoutIfNeeded()
96 | collectionView.frame = frame
97 | collectionView.layoutIfNeeded()
98 | }
99 |
100 | func testSizeLocationsWithVerticalScrollViewSectionsStartOnNewLineFalse() {
101 |
102 | layout.itemSpacing = 0
103 | layout.lineSpacing = 0
104 | layout.lineSize = 120
105 | layout.lineItemCount = 2
106 | layout.scrollDirection = .vertical
107 | layout.sectionsStartOnNewLine = false
108 | updateWindowFrame()
109 |
110 | checkFrames()
111 | }
112 |
113 | func testSizeLocationsWithHorizontalScrollViewSectionsStartOnNewLineFalse() {
114 |
115 | layout.itemSpacing = 0
116 | layout.lineSpacing = 0
117 | layout.lineSize = 120
118 | layout.lineItemCount = 2
119 | layout.scrollDirection = .horizontal
120 | layout.sectionsStartOnNewLine = false
121 | updateWindowFrame()
122 |
123 | checkFrames()
124 | }
125 |
126 | func testSizeLocationsWithVerticalScrollViewSectionsStartOnNewLineTrue() {
127 |
128 | layout.itemSpacing = 0
129 | layout.lineSpacing = 0
130 | layout.lineSize = 120
131 | layout.lineItemCount = 2
132 | layout.scrollDirection = .vertical
133 | layout.sectionsStartOnNewLine = true
134 | updateWindowFrame()
135 |
136 | checkFrames()
137 | }
138 |
139 | func testSizeLocationsWithHorizontalScrollViewSectionsStartOnNewLineTrue() {
140 |
141 | layout.itemSpacing = 0
142 | layout.lineSpacing = 0
143 | layout.lineSize = 120
144 | layout.lineItemCount = 2
145 | layout.scrollDirection = .horizontal
146 | layout.sectionsStartOnNewLine = true
147 | updateWindowFrame()
148 |
149 | checkFrames()
150 | }
151 |
152 | func testMultiplierDimensionLocationsWithVerticalScrollViewSectionsStartOnNewLineFalse() {
153 |
154 | layout.itemSpacing = 0
155 | layout.lineSpacing = 0
156 | layout.lineMultiplier = 0.5
157 | layout.lineItemCount = 2
158 | layout.scrollDirection = .vertical
159 | layout.sectionsStartOnNewLine = false
160 | updateWindowFrame()
161 |
162 | checkFrames()
163 | }
164 |
165 | func testMultiplierDimensionLocationsWithHorizontalScrollViewSectionsStartOnNewLineFalse() {
166 |
167 | layout.itemSpacing = 0
168 | layout.lineSpacing = 0
169 | layout.lineMultiplier = 0.5
170 | layout.lineItemCount = 2
171 | layout.scrollDirection = .horizontal
172 | layout.sectionsStartOnNewLine = false
173 | updateWindowFrame()
174 |
175 | checkFrames()
176 | }
177 |
178 | func testMultiplierDimensionLocationsWithVerticalScrollViewSectionsStartOnNewLineTrue() {
179 |
180 | layout.itemSpacing = 0
181 | layout.lineSpacing = 0
182 | layout.lineMultiplier = 0.5
183 | layout.lineItemCount = 2
184 | layout.scrollDirection = .vertical
185 | layout.sectionsStartOnNewLine = true
186 | updateWindowFrame()
187 |
188 | checkFrames()
189 | }
190 |
191 | func testMultiplierDimensionLocationsWithHorizontalScrollViewSectionsStartOnNewLineTrue() {
192 |
193 | layout.itemSpacing = 0
194 | layout.lineSpacing = 0
195 | layout.lineMultiplier = 0.5
196 | layout.lineItemCount = 2
197 | layout.scrollDirection = .horizontal
198 | layout.sectionsStartOnNewLine = true
199 | updateWindowFrame()
200 |
201 | checkFrames()
202 | }
203 |
204 | func testExtensionDimensionLocationsWithVerticalScrollViewSectionsStartOnNewLineFalse() {
205 |
206 | layout.itemSpacing = 0
207 | layout.lineSpacing = 0
208 | layout.lineExtension = 50
209 | layout.lineItemCount = 2
210 | layout.scrollDirection = .vertical
211 | layout.sectionsStartOnNewLine = false
212 | updateWindowFrame()
213 |
214 | checkFrames()
215 | }
216 |
217 | func testExtensionDimensionLocationsWithHorizontalScrollViewSectionsStartOnNewLineFalse() {
218 |
219 | layout.itemSpacing = 0
220 | layout.lineSpacing = 0
221 | layout.lineExtension = 50
222 | layout.lineItemCount = 2
223 | layout.scrollDirection = .horizontal
224 | layout.sectionsStartOnNewLine = false
225 | updateWindowFrame()
226 |
227 | checkFrames()
228 | }
229 |
230 | func testExtensionDimensionLocationsWithVerticalScrollViewSectionsStartOnNewLineTrue() {
231 |
232 | layout.itemSpacing = 0
233 | layout.lineSpacing = 0
234 | layout.lineExtension = 50
235 | layout.lineItemCount = 2
236 | layout.scrollDirection = .vertical
237 | layout.sectionsStartOnNewLine = true
238 | updateWindowFrame()
239 |
240 | checkFrames()
241 | }
242 |
243 | func testExtensionWithHorizontalScrollViewSectionsStartOnNewLineTrue() {
244 |
245 | layout.itemSpacing = 0
246 | layout.lineSpacing = 0
247 | layout.lineExtension = 50
248 | layout.lineItemCount = 2
249 | layout.scrollDirection = .horizontal
250 | layout.sectionsStartOnNewLine = true
251 | updateWindowFrame()
252 |
253 | checkFrames()
254 | }
255 |
256 | func testAutomaticDimensionLocationsWithVerticalScrollViewSectionsStartOnNewLineFalse() {
257 |
258 | layout.itemSpacing = 0
259 | layout.lineSpacing = 0
260 | layout.lineSize = 0
261 | layout.lineItemCount = 2
262 | layout.scrollDirection = .vertical
263 | layout.sectionsStartOnNewLine = false
264 | updateWindowFrame()
265 |
266 | checkFrames()
267 | }
268 |
269 | func testAutomaticDimensionLocationsWithHorizontalScrollViewSectionsStartOnNewLineFalse() {
270 |
271 | layout.itemSpacing = 0
272 | layout.lineSpacing = 0
273 | layout.lineSize = 0
274 | layout.lineItemCount = 2
275 | layout.scrollDirection = .horizontal
276 | layout.sectionsStartOnNewLine = false
277 | updateWindowFrame()
278 |
279 | checkFrames()
280 | }
281 |
282 | func testAutomaticDimensionLocationsWithVerticalScrollViewSectionsStartOnNewLineTrue() {
283 |
284 | layout.itemSpacing = 0
285 | layout.lineSpacing = 0
286 | layout.lineSize = 0
287 | layout.lineItemCount = 2
288 | layout.scrollDirection = .vertical
289 | layout.sectionsStartOnNewLine = true
290 | updateWindowFrame()
291 |
292 | checkFrames()
293 | }
294 |
295 | func testAutomaticWithHorizontalScrollViewSectionsStartOnNewLineTrue() {
296 |
297 | layout.itemSpacing = 0
298 | layout.lineSpacing = 0
299 | layout.lineSize = 0
300 | layout.lineItemCount = 2
301 | layout.scrollDirection = .horizontal
302 | layout.sectionsStartOnNewLine = true
303 | updateWindowFrame()
304 |
305 | checkFrames()
306 | }
307 |
308 | func testDirtyPixelCalculationsVerticalScrolling() {
309 |
310 | layout.itemSpacing = 0
311 | layout.lineSpacing = 0
312 | layout.lineSize = 0
313 | layout.lineItemCount = 3
314 | layout.scrollDirection = .vertical
315 | layout.sectionsStartOnNewLine = false
316 | updateWindowFrame()
317 |
318 | checkFrames()
319 | }
320 |
321 | func testDirtyPixelCalculationsHorizontalScrolling() {
322 |
323 | layout.itemSpacing = 0
324 | layout.lineSpacing = 0
325 | layout.lineSize = 0
326 | layout.lineItemCount = 3
327 | layout.scrollDirection = .horizontal
328 | layout.sectionsStartOnNewLine = false
329 | updateWindowFrame()
330 |
331 | checkFrames()
332 | }
333 |
334 | func testItemSpacingWithVerticalScrolling() {
335 |
336 | layout.itemSpacing = 2
337 | layout.lineSpacing = 0
338 | layout.lineSize = 0
339 | layout.lineItemCount = 3
340 | layout.scrollDirection = .vertical
341 | layout.sectionsStartOnNewLine = false
342 | updateWindowFrame()
343 |
344 | checkFrames()
345 | }
346 |
347 | func testItemSpacingWithHorizontalScrolling() {
348 |
349 | layout.itemSpacing = 2
350 | layout.lineSpacing = 0
351 | layout.lineSize = 0
352 | layout.lineItemCount = 3
353 | layout.scrollDirection = .horizontal
354 | layout.sectionsStartOnNewLine = false
355 | updateWindowFrame()
356 |
357 | checkFrames()
358 | }
359 |
360 | func testLineSpacingVerticalScrolling() {
361 |
362 | layout.itemSpacing = 0
363 | layout.lineSpacing = 10
364 | layout.lineSize = 0
365 | layout.lineItemCount = 2
366 | layout.scrollDirection = .vertical
367 | layout.sectionsStartOnNewLine = false
368 | updateWindowFrame()
369 |
370 | checkFrames()
371 | }
372 |
373 | func testLineSpacingHorizontalScrolling() {
374 |
375 | layout.itemSpacing = 0
376 | layout.lineSpacing = 10
377 | layout.lineSize = 0
378 | layout.lineItemCount = 2
379 | layout.scrollDirection = .horizontal
380 | layout.sectionsStartOnNewLine = false
381 | updateWindowFrame()
382 |
383 | checkFrames()
384 | }
385 |
386 | func testItemAndLineSpacingVerticalScrolling() {
387 |
388 | layout.itemSpacing = 2
389 | layout.lineSpacing = 10
390 | layout.lineSize = 0
391 | layout.lineItemCount = 3
392 | layout.scrollDirection = .vertical
393 | layout.sectionsStartOnNewLine = false
394 | updateWindowFrame()
395 |
396 | checkFrames()
397 | }
398 |
399 | func testItemAndLineSpacingHorizontalScrolling() {
400 |
401 | layout.itemSpacing = 2
402 | layout.lineSpacing = 10
403 | layout.lineSize = 0
404 | layout.lineItemCount = 3
405 | layout.scrollDirection = .horizontal
406 | layout.sectionsStartOnNewLine = false
407 | updateWindowFrame()
408 |
409 | checkFrames()
410 | }
411 |
412 | func checkFrames() {
413 |
414 | let exp = expectation(description: "Frame checking")
415 |
416 | DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 0.25) {
417 | var frame = CGRect.zero
418 | var size = CGSize.zero
419 | if (self.layout.scrollDirection == .vertical) {
420 | frame.size.width = 640
421 | frame.size.height = 2000
422 | size.width = frame.size.width
423 | } else {
424 | frame.size.width = 2000
425 | frame.size.height = 640
426 | size.height = frame.size.height
427 | }
428 | XCTAssertEqual(self.window.frame, frame, "Incorrect window frame (\(self.testCaseName))")
429 | XCTAssertEqual(self.collectionView.contentSize, size, "Incorrect content size (\(self.testCaseName))")
430 |
431 | exp.fulfill()
432 | }
433 |
434 | waitForExpectations(timeout: 5, handler: nil)
435 | }
436 | }
437 |
--------------------------------------------------------------------------------
/Examples/Tests/RDHGridLayoutTests.m:
--------------------------------------------------------------------------------
1 | //
2 | // RDHGridLayoutTests.m
3 | // RDHCollectionViewGridLayoutDemo
4 | //
5 | // Created by Richard Hodgkins on 22/06/2014.
6 | // Copyright (c) 2014 Rich H. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @import RDHCollectionViewGridLayout;
12 |
13 | #import "Tests-Swift.h"
14 |
15 | @interface RDHGridLayoutTests : XCTestCase
16 |
17 | @property (nonatomic, strong) UICollectionView *collectionView;
18 | @property (nonatomic, strong) RDHCollectionViewGridLayout *layout;
19 | @property (nonatomic, strong) TestDataSource *dataSource;
20 | @property (nonatomic, strong) UIWindow *window;
21 |
22 | @end
23 |
24 | @implementation RDHGridLayoutTests
25 |
26 | -(void)setUp
27 | {
28 | [super setUp];
29 |
30 | self.dataSource = [TestDataSource new];
31 |
32 | self.window = [[UIWindow alloc] initWithFrame:CGRectMake(0, 0, 640, 640)];
33 | [self.window makeKeyAndVisible];
34 | self.layout = [RDHCollectionViewGridLayout new];
35 | UICollectionViewController *controller = [[UICollectionViewController alloc] initWithCollectionViewLayout:self.layout];
36 | controller.edgesForExtendedLayout = UIRectEdgeNone;
37 | controller.automaticallyAdjustsScrollViewInsets = NO;
38 | self.collectionView = controller.collectionView;
39 | [self.collectionView registerClass:[UICollectionViewCell class] forCellWithReuseIdentifier:[TestDataSource CellID]];
40 | self.collectionView.dataSource = self.dataSource;
41 | self.collectionView.delegate = nil;
42 | self.window.rootViewController = controller;
43 | }
44 |
45 | -(void)testParameters
46 | {
47 | self.layout.scrollDirection = UICollectionViewScrollDirectionHorizontal;
48 | XCTAssertEqual(self.layout.scrollDirection, UICollectionViewScrollDirectionHorizontal, @"Scroll direction should be horiztonal");
49 |
50 | self.layout.scrollDirection = UICollectionViewScrollDirectionHorizontal;
51 | XCTAssertFalse([[self.layout valueForKeyPath:@"verticalScrolling"] boolValue]);
52 | self.layout.scrollDirection = UICollectionViewScrollDirectionVertical;
53 | XCTAssertTrue([[self.layout valueForKeyPath:@"verticalScrolling"] boolValue]);
54 |
55 | [self.layout setValue:@YES forKey:@"verticalScrolling"];
56 | XCTAssertEqual(self.layout.scrollDirection, UICollectionViewScrollDirectionVertical, @"Scroll direction should be vertical");
57 | [self.layout setValue:@NO forKey:@"verticalScrolling"];
58 | XCTAssertEqual(self.layout.scrollDirection, UICollectionViewScrollDirectionHorizontal, @"Scroll direction should be horiztonal");
59 |
60 | self.layout.lineSize = 123;
61 | XCTAssertEqual(self.layout.lineSize, 123, @"Line size is incorrect");
62 | XCTAssertEqual(self.layout.lineMultiplier, 1, @"Line multiplier should be default");
63 | XCTAssertEqual(self.layout.lineExtension, 0, @"Line extension should be default");
64 |
65 | self.layout.lineMultiplier = 2;
66 | XCTAssertEqual(self.layout.lineSize, 0, @"Line size should be default");
67 | XCTAssertEqual(self.layout.lineMultiplier, 2, @"Line multiplier is incorrect");
68 | XCTAssertEqual(self.layout.lineExtension, 0, @"Line extension should be default");
69 |
70 | self.layout.lineExtension = 200;
71 | XCTAssertEqual(self.layout.lineSize, 0, @"Line size should be default");
72 | XCTAssertEqual(self.layout.lineMultiplier, 1, @"Line multiplier should be default");
73 | XCTAssertEqual(self.layout.lineExtension, 200, @"Line extension is incorrect");
74 |
75 | self.layout.lineExtension = 0;
76 | XCTAssertEqual(self.layout.lineSize, 0, @"Line size should be default");
77 | XCTAssertEqual(self.layout.lineMultiplier, 1, @"Line multiplier should be default");
78 | XCTAssertEqual(self.layout.lineExtension, 0, @"Line extension should be default");
79 |
80 | #pragma clang diagnostic push
81 | #pragma clang diagnostic ignored "-Wdeprecated-declarations"
82 | self.layout.lineDimension = 123;
83 | #pragma clang diagnostic pop
84 | XCTAssertEqual(self.layout.lineSize, 123, @"Line size is incorrect");
85 | XCTAssertEqual(self.layout.lineMultiplier, 1, @"Line multiplier should be default");
86 | XCTAssertEqual(self.layout.lineExtension, 0, @"Line extension should be default");
87 |
88 | self.layout.lineItemCount = 7;
89 | XCTAssertEqual(self.layout.lineItemCount, 7, @"Line item count should be 7");
90 |
91 | self.layout.itemSpacing = 23;
92 | XCTAssertEqual(self.layout.itemSpacing, 23, @"Line spacing should be 23");
93 |
94 | self.layout.sectionsStartOnNewLine = YES;
95 | XCTAssertTrue(self.layout.sectionsStartOnNewLine, @"Sections should start on new lines");
96 | }
97 |
98 | -(void)testInvalidParameters
99 | {
100 | XCTAssertThrowsSpecificNamed(self.layout.scrollDirection = 5, NSException, NSInternalInconsistencyException, @"Setting an unknown scroll direction should throw an exception");
101 |
102 | XCTAssertThrowsSpecificNamed(self.layout.lineSize = -10, NSException, NSInternalInconsistencyException, @"Setting a negative line size should throw an exception");
103 | XCTAssertThrowsSpecificNamed(self.layout.lineMultiplier = -10, NSException, NSInternalInconsistencyException, @"Setting a negative line multiplier should throw an exception");
104 | XCTAssertThrowsSpecificNamed(self.layout.lineMultiplier = 0, NSException, NSInternalInconsistencyException, @"Setting a zero line multiplier should throw an exception");
105 | XCTAssertThrowsSpecificNamed(self.layout.lineExtension = -10, NSException, NSInternalInconsistencyException, @"Setting a negative line extension should throw an exception");
106 | #pragma clang diagnostic push
107 | #pragma clang diagnostic ignored "-Wdeprecated-declarations"
108 | XCTAssertThrowsSpecificNamed(self.layout.lineDimension = -10, NSException, NSInternalInconsistencyException, @"Setting a negative line dimension should throw an exception");
109 | #pragma clang diagnostic pop
110 |
111 | XCTAssertThrowsSpecificNamed(self.layout.lineItemCount = 0, NSException, NSInternalInconsistencyException, @"Setting a zero line item count should throw an exception");
112 | }
113 |
114 | -(void)updateWindowFrame
115 | {
116 | CGRect frame = CGRectZero;
117 | if (self.layout.scrollDirection == UICollectionViewScrollDirectionVertical) {
118 | frame.size.width = 640;
119 | frame.size.height = 2000;
120 | } else {
121 | frame.size.width = 2000;
122 | frame.size.height = 640;
123 | }
124 | self.window.frame = frame;
125 | [self.window layoutIfNeeded];
126 | self.collectionView.frame = frame;
127 | [self.collectionView layoutIfNeeded];
128 | }
129 |
130 | -(void)testSizeLocationsWithVerticalScrollViewSectionsStartOnNewLineNO
131 | {
132 | NSDictionary *frames = @{
133 | IPF(0, 0, 0, 000, 320, 120), IPF(0, 1, 320, 000, 320, 120),
134 | IPF(0, 2, 0, 120, 320, 120), IPF(1, 0, 320, 120, 320, 120),
135 | IPF(1, 1, 0, 240, 320, 120), IPF(1, 2, 320, 240, 320, 120),
136 | IPF(2, 0, 0, 360, 320, 120), IPF(2, 1, 320, 360, 320, 120),
137 | IPF(2, 2, 0, 480, 320, 120)
138 | };
139 |
140 | self.layout.itemSpacing = 0;
141 | self.layout.lineSpacing = 0;
142 | self.layout.lineSize = 120;
143 | self.layout.lineItemCount = 2;
144 | self.layout.scrollDirection = UICollectionViewScrollDirectionVertical;
145 | self.layout.sectionsStartOnNewLine = NO;
146 | [self updateWindowFrame];
147 |
148 | [self checkFrames:frames withContentSize:CGSizeMake(640, 600)];
149 | }
150 |
151 | -(void)testSizeLocationsWithHorizontalScrollViewSectionsStartOnNewLineNO
152 | {
153 | NSDictionary *frames = @{
154 |
155 | IPF(0, 0, 000, 000, 120, 320), IPF(0, 2, 120, 000, 120, 320), IPF(1, 1, 240, 000, 120, 320), IPF(2, 0, 360, 000, 120, 320), IPF(2, 2, 480, 000, 120, 320),
156 |
157 | IPF(0, 1, 000, 320, 120, 320), IPF(1, 0, 120, 320, 120, 320), IPF(1, 2, 240, 320, 120, 320), IPF(2, 1, 360, 320, 120, 320)
158 | };
159 |
160 | self.layout.itemSpacing = 0;
161 | self.layout.lineSpacing = 0;
162 | self.layout.lineSize = 120;
163 | self.layout.lineItemCount = 2;
164 | self.layout.scrollDirection = UICollectionViewScrollDirectionHorizontal;
165 | self.layout.sectionsStartOnNewLine = NO;
166 | [self updateWindowFrame];
167 |
168 | [self checkFrames:frames withContentSize:CGSizeMake(600, 640)];
169 | }
170 |
171 | -(void)testSizeLocationsWithVerticalScrollViewSectionsStartOnNewLineYES
172 | {
173 | NSDictionary *frames = @{
174 | IPF(0, 0, 0, 000, 320, 120), IPF(0, 1, 320, 000, 320, 120),
175 | IPF(0, 2, 0, 120, 320, 120),
176 | IPF(1, 0, 0, 240, 320, 120), IPF(1, 1, 320, 240, 320, 120),
177 | IPF(1, 2, 0, 360, 320, 120),
178 | IPF(2, 0, 0, 480, 320, 120), IPF(2, 1, 320, 480, 320, 120),
179 | IPF(2, 2, 0, 600, 320, 120)
180 | };
181 |
182 | self.layout.itemSpacing = 0;
183 | self.layout.lineSpacing = 0;
184 | self.layout.lineSize = 120;
185 | self.layout.lineItemCount = 2;
186 | self.layout.scrollDirection = UICollectionViewScrollDirectionVertical;
187 | self.layout.sectionsStartOnNewLine = YES;
188 | [self updateWindowFrame];
189 |
190 | [self checkFrames:frames withContentSize:CGSizeMake(640, 720)];
191 | }
192 |
193 | -(void)testSizeLocationsWithHorizontalScrollViewSectionsStartOnNewLineYES
194 | {
195 | NSDictionary *frames = @{
196 |
197 | IPF(0, 0, 000, 000, 120, 320), IPF(0, 2, 120, 000, 120, 320), IPF(1, 0, 240, 000, 120, 320), IPF(1, 2, 360, 000, 120, 320), IPF(2, 0, 480, 000, 120, 320), IPF(2, 2, 600, 000, 120, 320),
198 |
199 | IPF(0, 1, 000, 320, 120, 320), IPF(1, 1, 240, 320, 120, 320), IPF(2, 1, 480, 320, 120, 320)
200 | };
201 |
202 | self.layout.itemSpacing = 0;
203 | self.layout.lineSpacing = 0;
204 | self.layout.lineSize = 120;
205 | self.layout.lineItemCount = 2;
206 | self.layout.scrollDirection = UICollectionViewScrollDirectionHorizontal;
207 | self.layout.sectionsStartOnNewLine = YES;
208 | [self updateWindowFrame];
209 |
210 | [self checkFrames:frames withContentSize:CGSizeMake(720, 640)];
211 | }
212 |
213 | -(void)testMultiplierDimensionLocationsWithVerticalScrollViewSectionsStartOnNewLineNO
214 | {
215 | NSDictionary *frames = @{
216 | IPF(0, 0, 0, 000, 320, 160), IPF(0, 1, 320, 000, 320, 160),
217 | IPF(0, 2, 0, 160, 320, 160), IPF(1, 0, 320, 160, 320, 160),
218 | IPF(1, 1, 0, 320, 320, 160), IPF(1, 2, 320, 320, 320, 160),
219 | IPF(2, 0, 0, 480, 320, 160), IPF(2, 1, 320, 480, 320, 160),
220 | IPF(2, 2, 0, 640, 320, 160)
221 | };
222 |
223 | self.layout.itemSpacing = 0;
224 | self.layout.lineSpacing = 0;
225 | self.layout.lineMultiplier = 0.5;
226 | self.layout.lineItemCount = 2;
227 | self.layout.scrollDirection = UICollectionViewScrollDirectionVertical;
228 | self.layout.sectionsStartOnNewLine = NO;
229 | [self updateWindowFrame];
230 |
231 | [self checkFrames:frames withContentSize:CGSizeMake(640, 800)];
232 | }
233 |
234 | -(void)testMultiplierDimensionLocationsWithHorizontalScrollViewSectionsStartOnNewLineNO
235 | {
236 | NSDictionary *frames = @{
237 |
238 | IPF(0, 0, 000, 000, 160, 320), IPF(0, 2, 160, 000, 160, 320), IPF(1, 1, 320, 000, 160, 320), IPF(2, 0, 480, 000, 160, 320), IPF(2, 2, 640, 000, 160, 320),
239 |
240 | IPF(0, 1, 000, 320, 160, 320), IPF(1, 0, 160, 320, 160, 320), IPF(1, 2, 320, 320, 160, 320), IPF(2, 1, 480, 320, 160, 320)
241 | };
242 |
243 | self.layout.itemSpacing = 0;
244 | self.layout.lineSpacing = 0;
245 | self.layout.lineMultiplier = 0.5;
246 | self.layout.lineItemCount = 2;
247 | self.layout.scrollDirection = UICollectionViewScrollDirectionHorizontal;
248 | self.layout.sectionsStartOnNewLine = NO;
249 | [self updateWindowFrame];
250 |
251 | [self checkFrames:frames withContentSize:CGSizeMake(800, 640)];
252 | }
253 |
254 | -(void)testMultiplierDimensionLocationsWithVerticalScrollViewSectionsStartOnNewLineYES
255 | {
256 | NSDictionary *frames = @{
257 | IPF(0, 0, 0, 000, 320, 160), IPF(0, 1, 320, 000, 320, 160),
258 | IPF(0, 2, 0, 160, 320, 160),
259 | IPF(1, 0, 0, 320, 320, 160), IPF(1, 1, 320, 320, 320, 160),
260 | IPF(1, 2, 0, 480, 320, 160),
261 | IPF(2, 0, 0, 640, 320, 160), IPF(2, 1, 320, 640, 320, 160),
262 | IPF(2, 2, 0, 800, 320, 160)
263 | };
264 |
265 | self.layout.itemSpacing = 0;
266 | self.layout.lineSpacing = 0;
267 | self.layout.lineMultiplier = 0.5;
268 | self.layout.lineItemCount = 2;
269 | self.layout.scrollDirection = UICollectionViewScrollDirectionVertical;
270 | self.layout.sectionsStartOnNewLine = YES;
271 | [self updateWindowFrame];
272 |
273 | [self checkFrames:frames withContentSize:CGSizeMake(640, 960)];
274 | }
275 |
276 | -(void)testMultiplierDimensionLocationsWithHorizontalScrollViewSectionsStartOnNewLineYES
277 | {
278 | NSDictionary *frames = @{
279 |
280 | IPF(0, 0, 000, 000, 160, 320), IPF(0, 2, 160, 000, 160, 320), IPF(1, 0, 320, 000, 160, 320), IPF(1, 2, 480, 000, 160, 320), IPF(2, 0, 640, 000, 160, 320), IPF(2, 2, 800, 000, 160, 320),
281 |
282 | IPF(0, 1, 000, 320, 160, 320), IPF(1, 1, 320, 320, 160, 320), IPF(2, 1, 640, 320, 160, 320)
283 | };
284 |
285 | self.layout.itemSpacing = 0;
286 | self.layout.lineSpacing = 0;
287 | self.layout.lineMultiplier = 0.5;
288 | self.layout.lineItemCount = 2;
289 | self.layout.scrollDirection = UICollectionViewScrollDirectionHorizontal;
290 | self.layout.sectionsStartOnNewLine = YES;
291 | [self updateWindowFrame];
292 |
293 | [self checkFrames:frames withContentSize:CGSizeMake(960, 640)];
294 | }
295 |
296 | -(void)testExtensionDimensionLocationsWithVerticalScrollViewSectionsStartOnNewLineNO
297 | {
298 | NSDictionary *frames = @{
299 | IPF(0, 0, 0, 000, 320, 370), IPF(0, 1, 320, 000, 320, 370),
300 | IPF(0, 2, 0, 370, 320, 370), IPF(1, 0, 320, 370, 320, 370),
301 | IPF(1, 1, 0, 740, 320, 370), IPF(1, 2, 320, 740, 320, 370),
302 | IPF(2, 0, 0, 1110, 320, 370), IPF(2, 1, 320, 1110, 320, 370),
303 | IPF(2, 2, 0, 1480, 320, 370)
304 | };
305 |
306 | self.layout.itemSpacing = 0;
307 | self.layout.lineSpacing = 0;
308 | self.layout.lineExtension = 50;
309 | self.layout.lineItemCount = 2;
310 | self.layout.scrollDirection = UICollectionViewScrollDirectionVertical;
311 | self.layout.sectionsStartOnNewLine = NO;
312 | [self updateWindowFrame];
313 |
314 | [self checkFrames:frames withContentSize:CGSizeMake(640, 1850)];
315 | }
316 |
317 | -(void)testExtensionDimensionLocationsWithHorizontalScrollViewSectionsStartOnNewLineNO
318 | {
319 | NSDictionary *frames = @{
320 |
321 | IPF(0, 0, 0000, 000, 370, 320), IPF(0, 2, 370, 000, 370, 320), IPF(1, 1, 740, 000, 370, 320), IPF(2, 0, 1110, 000, 370, 320), IPF(2, 2, 1480, 000, 370, 320),
322 |
323 | IPF(0, 1, 0000, 320, 370, 320), IPF(1, 0, 370, 320, 370, 320), IPF(1, 2, 740, 320, 370, 320), IPF(2, 1, 1110, 320, 370, 320)
324 | };
325 |
326 | self.layout.itemSpacing = 0;
327 | self.layout.lineSpacing = 0;
328 | self.layout.lineExtension = 50;
329 | self.layout.lineItemCount = 2;
330 | self.layout.scrollDirection = UICollectionViewScrollDirectionHorizontal;
331 | self.layout.sectionsStartOnNewLine = NO;
332 | [self updateWindowFrame];
333 |
334 | [self checkFrames:frames withContentSize:CGSizeMake(1850, 640)];
335 | }
336 |
337 | -(void)testExtensionDimensionLocationsWithVerticalScrollViewSectionsStartOnNewLineYES
338 | {
339 | NSDictionary *frames = @{
340 | IPF(0, 0, 0, 000, 320, 370), IPF(0, 1, 320, 000, 320, 370),
341 | IPF(0, 2, 0, 370, 320, 370),
342 | IPF(1, 0, 0, 740, 320, 370), IPF(1, 1, 320, 740, 320, 370),
343 | IPF(1, 2, 0, 1110, 320, 370),
344 | IPF(2, 0, 0, 1480, 320, 370), IPF(2, 1, 320, 1480, 320, 370),
345 | IPF(2, 2, 0, 1850, 320, 370)
346 | };
347 |
348 | self.layout.itemSpacing = 0;
349 | self.layout.lineSpacing = 0;
350 | self.layout.lineExtension = 50;
351 | self.layout.lineItemCount = 2;
352 | self.layout.scrollDirection = UICollectionViewScrollDirectionVertical;
353 | self.layout.sectionsStartOnNewLine = YES;
354 | [self updateWindowFrame];
355 |
356 | [self checkFrames:frames withContentSize:CGSizeMake(640, 2220)];
357 | }
358 |
359 | -(void)testExtensionWithHorizontalScrollViewSectionsStartOnNewLineYES
360 | {
361 | NSDictionary *frames = @{
362 |
363 | IPF(0, 0, 000, 000, 370, 320), IPF(0, 2, 370, 000, 370, 320), IPF(1, 0, 740, 000, 370, 320), IPF(1, 2, 1110, 000, 370, 320), IPF(2, 0, 1480, 000, 370, 320), IPF(2, 2, 1850, 000, 370, 320),
364 |
365 | IPF(0, 1, 000, 320, 370, 320), IPF(1, 1, 740, 320, 370, 320), IPF(2, 1, 1480, 320, 370, 320)
366 | };
367 |
368 | self.layout.itemSpacing = 0;
369 | self.layout.lineSpacing = 0;
370 | self.layout.lineExtension = 50;
371 | self.layout.lineItemCount = 2;
372 | self.layout.scrollDirection = UICollectionViewScrollDirectionHorizontal;
373 | self.layout.sectionsStartOnNewLine = YES;
374 | [self updateWindowFrame];
375 |
376 | [self checkFrames:frames withContentSize:CGSizeMake(2220, 640)];
377 | }
378 |
379 | -(void)testAutomaticDimensionLocationsWithVerticalScrollViewSectionsStartOnNewLineNO
380 | {
381 | NSDictionary *frames = @{
382 | IPF(0, 0, 0, 000, 320, 320), IPF(0, 1, 320, 000, 320, 320),
383 | IPF(0, 2, 0, 320, 320, 320), IPF(1, 0, 320, 320, 320, 320),
384 | IPF(1, 1, 0, 640, 320, 320), IPF(1, 2, 320, 640, 320, 320),
385 | IPF(2, 0, 0, 960, 320, 320), IPF(2, 1, 320, 960, 320, 320),
386 | IPF(2, 2, 0, 1280, 320, 320)
387 | };
388 |
389 | self.layout.itemSpacing = 0;
390 | self.layout.lineSpacing = 0;
391 | self.layout.lineSize = 0;
392 | self.layout.lineItemCount = 2;
393 | self.layout.scrollDirection = UICollectionViewScrollDirectionVertical;
394 | self.layout.sectionsStartOnNewLine = NO;
395 | [self updateWindowFrame];
396 |
397 | [self checkFrames:frames withContentSize:CGSizeMake(640, 1600)];
398 | }
399 |
400 | -(void)testAutomaticDimensionLocationsWithHorizontalScrollViewSectionsStartOnNewLineNO
401 | {
402 | NSDictionary *frames = @{
403 |
404 | IPF(0, 0, 000, 000, 320, 320), IPF(0, 2, 320, 000, 320, 320), IPF(1, 1, 640, 000, 320, 320), IPF(2, 0, 960, 000, 320, 320), IPF(2, 2, 1280, 000, 320, 320),
405 |
406 | IPF(0, 1, 000, 320, 320, 320), IPF(1, 0, 320, 320, 320, 320), IPF(1, 2, 640, 320, 320, 320), IPF(2, 1, 960, 320, 320, 320)
407 | };
408 |
409 | self.layout.itemSpacing = 0;
410 | self.layout.lineSpacing = 0;
411 | self.layout.lineSize = 0;
412 | self.layout.lineItemCount = 2;
413 | self.layout.scrollDirection = UICollectionViewScrollDirectionHorizontal;
414 | self.layout.sectionsStartOnNewLine = NO;
415 | [self updateWindowFrame];
416 |
417 | [self checkFrames:frames withContentSize:CGSizeMake(1600, 640)];
418 | }
419 |
420 | -(void)testAutomaticDimensionLocationsWithVerticalScrollViewSectionsStartOnNewLineYES
421 | {
422 | NSDictionary *frames = @{
423 | IPF(0, 0, 0, 000, 320, 320), IPF(0, 1, 320, 000, 320, 320),
424 | IPF(0, 2, 0, 320, 320, 320),
425 | IPF(1, 0, 0, 640, 320, 320), IPF(1, 1, 320, 640, 320, 320),
426 | IPF(1, 2, 0, 960, 320, 320),
427 | IPF(2, 0, 0, 1280, 320, 320), IPF(2, 1, 320, 1280, 320, 320),
428 | IPF(2, 2, 0, 1600, 320, 320)
429 | };
430 |
431 | self.layout.itemSpacing = 0;
432 | self.layout.lineSpacing = 0;
433 | self.layout.lineSize = 0;
434 | self.layout.lineItemCount = 2;
435 | self.layout.scrollDirection = UICollectionViewScrollDirectionVertical;
436 | self.layout.sectionsStartOnNewLine = YES;
437 | [self updateWindowFrame];
438 |
439 | [self checkFrames:frames withContentSize:CGSizeMake(640, 1920)];
440 | }
441 |
442 | -(void)testAutomaticWithHorizontalScrollViewSectionsStartOnNewLineYES
443 | {
444 | NSDictionary *frames = @{
445 |
446 | IPF(0, 0, 000, 000, 320, 320), IPF(0, 2, 320, 000, 320, 320), IPF(1, 0, 640, 000, 320, 320), IPF(1, 2, 960, 000, 320, 320), IPF(2, 0, 1280, 000, 320, 320), IPF(2, 2, 1600, 000, 320, 320),
447 |
448 | IPF(0, 1, 000, 320, 320, 320), IPF(1, 1, 640, 320, 320, 320), IPF(2, 1, 1280, 320, 320, 320)
449 | };
450 |
451 | self.layout.itemSpacing = 0;
452 | self.layout.lineSpacing = 0;
453 | self.layout.lineSize = 0;
454 | self.layout.lineItemCount = 2;
455 | self.layout.scrollDirection = UICollectionViewScrollDirectionHorizontal;
456 | self.layout.sectionsStartOnNewLine = YES;
457 | [self updateWindowFrame];
458 |
459 | [self checkFrames:frames withContentSize:CGSizeMake(1920, 640)];
460 | }
461 |
462 | -(void)testDirtyPixelCalculations
463 | {
464 | NSDictionary *frames = @{
465 | IPF(0, 0, 0, 000, 214, 213), IPF(0, 1, 214, 000, 213, 213), IPF(0, 2, 427, 000, 213, 213),
466 | IPF(1, 0, 0, 213, 214, 213), IPF(1, 1, 214, 213, 213, 213), IPF(1, 2, 427, 213, 213, 213),
467 | IPF(2, 0, 0, 426, 214, 213), IPF(2, 1, 214, 426, 213, 213), IPF(2, 2, 427, 426, 213, 213)
468 | };
469 |
470 | self.layout.itemSpacing = 0;
471 | self.layout.lineSpacing = 0;
472 | self.layout.lineSize = 0;
473 | self.layout.lineItemCount = 3;
474 | self.layout.scrollDirection = UICollectionViewScrollDirectionVertical;
475 | self.layout.sectionsStartOnNewLine = NO;
476 | [self updateWindowFrame];
477 |
478 | [self checkFrames:frames withContentSize:CGSizeMake(640, 639)];
479 | }
480 |
481 | -(void)testItemSpacing
482 | {
483 | NSDictionary *frames = @{
484 | IPF(0, 0, 0, 000, 212, 212), IPF(0, 1, 214, 000, 212, 212), IPF(0, 2, 428, 000, 212, 212),
485 | IPF(1, 0, 0, 212, 212, 212), IPF(1, 1, 214, 212, 212, 212), IPF(1, 2, 428, 212, 212, 212),
486 | IPF(2, 0, 0, 424, 212, 212), IPF(2, 1, 214, 424, 212, 212), IPF(2, 2, 428, 424, 212, 212)
487 | };
488 |
489 | self.layout.itemSpacing = 2;
490 | self.layout.lineSpacing = 0;
491 | self.layout.lineSize = 0;
492 | self.layout.lineItemCount = 3;
493 | self.layout.scrollDirection = UICollectionViewScrollDirectionVertical;
494 | self.layout.sectionsStartOnNewLine = NO;
495 | [self updateWindowFrame];
496 |
497 | [self checkFrames:frames withContentSize:CGSizeMake(640, 636)];
498 | }
499 |
500 | -(void)testLineSpacing
501 | {
502 | NSDictionary *frames = @{
503 | IPF(0, 0, 0, 000, 320, 320), IPF(0, 1, 320, 000, 320, 320),
504 | IPF(0, 2, 0, 330, 320, 320), IPF(1, 0, 320, 330, 320, 320),
505 | IPF(1, 1, 0, 660, 320, 320), IPF(1, 2, 320, 660, 320, 320),
506 | IPF(2, 0, 0, 990, 320, 320), IPF(2, 1, 320, 990, 320, 320),
507 | IPF(2, 2, 0, 1320, 320, 320)
508 | };
509 |
510 | self.layout.itemSpacing = 0;
511 | self.layout.lineSpacing = 10;
512 | self.layout.lineSize = 0;
513 | self.layout.lineItemCount = 2;
514 | self.layout.scrollDirection = UICollectionViewScrollDirectionVertical;
515 | self.layout.sectionsStartOnNewLine = NO;
516 | [self updateWindowFrame];
517 |
518 | [self checkFrames:frames withContentSize:CGSizeMake(640, 1640)];
519 | }
520 |
521 | -(void)testItemAndLineSpacingVerticalScrolling
522 | {
523 | NSDictionary *frames = @{
524 | IPF(0, 0, 0, 000, 212, 212), IPF(0, 1, 214, 000, 212, 212), IPF(0, 2, 428, 000, 212, 212),
525 | IPF(1, 0, 0, 222, 212, 212), IPF(1, 1, 214, 222, 212, 212), IPF(1, 2, 428, 222, 212, 212),
526 | IPF(2, 0, 0, 444, 212, 212), IPF(2, 1, 214, 444, 212, 212), IPF(2, 2, 428, 444, 212, 212)
527 | };
528 |
529 | self.layout.itemSpacing = 2;
530 | self.layout.lineSpacing = 10;
531 | self.layout.lineSize = 0;
532 | self.layout.lineItemCount = 3;
533 | self.layout.scrollDirection = UICollectionViewScrollDirectionVertical;
534 | self.layout.sectionsStartOnNewLine = NO;
535 | [self updateWindowFrame];
536 |
537 | [self checkFrames:frames withContentSize:CGSizeMake(640, 656)];
538 | }
539 |
540 | -(void)testItemAndLineSpacingHorizontalScrolling
541 | {
542 | NSDictionary *frames = @{
543 | IPF(0, 0, 0, 000, 212, 212), IPF(1, 0, 222, 000, 212, 212), IPF(2, 0, 444, 000, 212, 212),
544 | IPF(0, 1, 0, 214, 212, 212), IPF(1, 1, 222, 214, 212, 212), IPF(2, 1, 444, 214, 212, 212),
545 | IPF(0, 2, 0, 428, 212, 212), IPF(1, 2, 222, 428, 212, 212), IPF(2, 2, 444, 428, 212, 212)
546 | };
547 |
548 | self.layout.itemSpacing = 2;
549 | self.layout.lineSpacing = 10;
550 | self.layout.lineSize = 0;
551 | self.layout.lineItemCount = 3;
552 | self.layout.scrollDirection = UICollectionViewScrollDirectionHorizontal;
553 | self.layout.sectionsStartOnNewLine = NO;
554 | [self updateWindowFrame];
555 |
556 | [self checkFrames:frames withContentSize:CGSizeMake(656, 640)];
557 | }
558 |
559 | -(void)checkFrames:(NSDictionary *)frames withContentSize:(CGSize)contentSize
560 | {
561 | NSAssert(frames.count == 9, @"Incorrect number of frames (%@)", self.testCaseName);
562 |
563 | XCTestExpectation *expectation = [self expectationWithDescription:@"Frame checking"];
564 |
565 | dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.25 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
566 |
567 | CGRect frame = CGRectZero;
568 | if (self.layout.scrollDirection == UICollectionViewScrollDirectionVertical) {
569 | frame.size.width = 640;
570 | frame.size.height = 2000;
571 | } else {
572 | frame.size.width = 2000;
573 | frame.size.height = 640;
574 | }
575 | XCTAssertEqualRect(self.window.frame, frame, @"Incorrect window frame (%@", self.testCaseName);
576 | XCTAssertEqualSize(self.collectionView.contentSize, contentSize, @"Incorrect content size (%@)", self.testCaseName);
577 |
578 | [frames enumerateKeysAndObjectsUsingBlock:^(NSIndexPath *indexPath, NSValue *vRect, BOOL *stop) {
579 |
580 | NSString *indexPathDesc = [NSString stringWithFormat:@"NSIndexPath(%ld,%ld)", (long) indexPath.section, (long) indexPath.row];
581 |
582 | CGRect expectedFrame = [vRect CGRectValue];
583 | CGRect actualFrame = [self.collectionView cellForItemAtIndexPath:indexPath].frame;
584 |
585 | XCTAssertEqualRect(actualFrame, expectedFrame, @"Frame of %@ is incorrect (%@)", indexPathDesc, self.testCaseName);
586 | }];
587 |
588 | [expectation fulfill];
589 |
590 | });
591 |
592 | [self waitForExpectationsWithTimeout:5 handler:nil];
593 | }
594 |
595 | @end
596 |
597 |
--------------------------------------------------------------------------------
/Examples/Tests/RDHStoryboardTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // RDHStoryboardTests.swift
3 | // RDHCollectionViewGridLayoutDemo
4 | //
5 | // Created by Richard Hodgkins on 16/03/2015.
6 | // Copyright (c) 2015 Rich H. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import XCTest
11 |
12 |
13 | class RDHStoryboardTests: XCTestCase {
14 |
15 | var collectionView: UICollectionView!
16 | var dataSource: TestDataSource!
17 | var layout: RDHCollectionViewGridLayout!
18 | var window: UIWindow!
19 |
20 | override func setUp() {
21 | super.setUp()
22 |
23 | dataSource = TestDataSource()
24 |
25 | let collectionVC = UIStoryboard(name: "TestStoryboard", bundle: Bundle(for: type(of: self))).instantiateInitialViewController() as! UICollectionViewController
26 |
27 | window = UIWindow(frame:CGRect(x: 0, y: 0, width: 640, height: 640))
28 | collectionView = collectionVC.collectionView
29 | layout = collectionView.collectionViewLayout as! RDHCollectionViewGridLayout
30 | collectionView.register(UICollectionViewCell.self, forCellWithReuseIdentifier: TestDataSource.CellID)
31 | collectionView.dataSource = dataSource
32 | collectionView.delegate = nil
33 |
34 | window.rootViewController = collectionVC
35 | window.makeKeyAndVisible()
36 | }
37 |
38 | fileprivate func updateWindowFrame() {
39 | var frame = CGRect.zero
40 | if layout.scrollDirection == .vertical {
41 | frame.size.width = 640
42 | frame.size.height = 2000
43 | } else {
44 | frame.size.width = 2000
45 | frame.size.height = 640
46 | }
47 | window.frame = frame
48 | window.layoutIfNeeded()
49 | collectionView.frame = frame
50 | collectionView.layoutIfNeeded()
51 | }
52 |
53 | func testDefaults() {
54 |
55 | updateWindowFrame()
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/Examples/Tests/TestDataSources.swift:
--------------------------------------------------------------------------------
1 | //
2 | // TestDataSources.swift
3 | // RDHCollectionViewGridLayoutDemo
4 | //
5 | // Created by Richard Hodgkins on 16/03/2015.
6 | // Copyright (c) 2015 Rich H. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import UIKit.UICollectionView
11 |
12 | @objc final class TestDataSource: NSObject, UICollectionViewDataSource {
13 |
14 | class var CellID: String { return "CellID" }
15 |
16 | func numberOfSections(in collectionView: UICollectionView) -> Int {
17 | return 3
18 | }
19 |
20 | func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
21 | return 3
22 | }
23 |
24 | func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
25 | let cell = collectionView.dequeueReusableCell(withReuseIdentifier: type(of: self).CellID, for:indexPath)
26 | cell.backgroundColor = UIColor.red
27 | return cell
28 | }
29 | }
30 |
31 | @objc final class EmptyTestDataSource: NSObject, UICollectionViewDataSource {
32 |
33 | class var CellID: String { return "CellID" }
34 |
35 | func numberOfSections(in collectionView: UICollectionView) -> Int {
36 | return 0
37 | }
38 |
39 | func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
40 | return 0
41 | }
42 |
43 | func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
44 | let cell = collectionView.dequeueReusableCell(withReuseIdentifier: type(of: self).CellID, for:indexPath)
45 | cell.backgroundColor = UIColor.red
46 | return cell
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/Examples/Tests/TestStoryboard.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 |
--------------------------------------------------------------------------------
/Examples/Tests/Tests-Bridging-Header.h:
--------------------------------------------------------------------------------
1 | //
2 | // Use this file to import your target's public headers that you would like to expose to Swift.
3 | //
4 |
5 | @import RDHCollectionViewGridLayout;
6 |
7 | #import "XCTestCase+Extensions.h"
8 |
--------------------------------------------------------------------------------
/Examples/Tests/Tests-Prefix.pch:
--------------------------------------------------------------------------------
1 | //
2 | // Prefix header
3 | //
4 | // The contents of this file are implicitly included at the beginning of every source file.
5 | //
6 |
7 | @import UIKit;
8 | @import Foundation;
9 | @import CoreGraphics;
10 |
11 | #import "XCTestCase+Extensions.h"
12 |
13 | #define CGRectObject(x, y, width, height) [NSValue valueWithCGRect:CGRectMake(x, y, width, height)]
14 |
15 | #define IPF(section, item, x, y, width, height) [NSIndexPath indexPathForItem:item inSection:section] : CGRectObject(x, y, width, height)
16 |
17 | #define XCTAssertEqualRect(expression1, expression2, ...) \
18 | _XCTAssertEqualRect(self, expression1, @#expression1, expression2, @#expression2, __VA_ARGS__)
19 |
20 | #define _XCTAssertEqualRect(test, expression1, expressionStr1, expression2, expressionStr2, ...) \
21 | ({ \
22 | @try { \
23 | if (!CGRectEqualToRect(expression1, expression2)) { \
24 | NSValue *expressionBox1 = [NSValue valueWithCGRect:expression1]; \
25 | NSValue *expressionBox2 = [NSValue valueWithCGRect:expression2]; \
26 | _XCTRegisterFailure(test, _XCTFailureDescription(_XCTAssertion_Equal, 0, expressionStr1, expressionStr2, _XCTDescriptionForValue(expressionBox1), _XCTDescriptionForValue(expressionBox2)), __VA_ARGS__); \
27 | } \
28 | } \
29 | @catch (_XCTestCaseInterruptionException *interruption) { [interruption raise]; } \
30 | @catch (NSException *exception) { \
31 | _XCTRegisterFailure(test, _XCTFailureDescription(_XCTAssertion_Equal, 1, expressionStr1, expressionStr2, [exception reason]), __VA_ARGS__); \
32 | } \
33 | @catch (...) { \
34 | _XCTRegisterFailure(test, _XCTFailureDescription(_XCTAssertion_Equal, 2, expressionStr1, expressionStr2), __VA_ARGS__); \
35 | } \
36 | })
37 |
38 | #define XCTAssertEqualSize(expression1, expression2, ...) \
39 | _XCTAssertEqualSize(self, expression1, @#expression1, expression2, @#expression2, __VA_ARGS__)
40 |
41 | #define _XCTAssertEqualSize(test, expression1, expressionStr1, expression2, expressionStr2, ...) \
42 | ({ \
43 | @try { \
44 | if (!CGSizeEqualToSize(expression1, expression2)) { \
45 | NSValue *expressionBox1 = [NSValue valueWithCGSize:expression1]; \
46 | NSValue *expressionBox2 = [NSValue valueWithCGSize:expression2]; \
47 | _XCTRegisterFailure(test, _XCTFailureDescription(_XCTAssertion_Equal, 0, expressionStr1, expressionStr2, _XCTDescriptionForValue(expressionBox1), _XCTDescriptionForValue(expressionBox2)), __VA_ARGS__); \
48 | } \
49 | } \
50 | @catch (_XCTestCaseInterruptionException *interruption) { [interruption raise]; } \
51 | @catch (NSException *exception) { \
52 | _XCTRegisterFailure(test, _XCTFailureDescription(_XCTAssertion_Equal, 1, expressionStr1, expressionStr2, [exception reason]), __VA_ARGS__); \
53 | } \
54 | @catch (...) { \
55 | _XCTRegisterFailure(test, _XCTFailureDescription(_XCTAssertion_Equal, 2, expressionStr1, expressionStr2), __VA_ARGS__); \
56 | } \
57 | })
58 |
--------------------------------------------------------------------------------
/Examples/Tests/XCTestCase+Extensions.h:
--------------------------------------------------------------------------------
1 | //
2 | // XCTestCase+Extensions.h
3 | // RDHCollectionViewGridLayoutDemo
4 | //
5 | // Created by Richard Hodgkins on 18/09/2016.
6 | // Copyright © 2016 Rich H. All rights reserved.
7 | //
8 |
9 | @import XCTest;
10 |
11 | @interface XCTestCase (TestCaseName)
12 |
13 | @property (nonatomic, nonnull, readonly) NSString *testCaseName;
14 |
15 | @end
16 |
--------------------------------------------------------------------------------
/Examples/Tests/XCTestCase+Extensions.m:
--------------------------------------------------------------------------------
1 | //
2 | // XCTestCase+Extensions.m
3 | // RDHCollectionViewGridLayoutDemo
4 | //
5 | // Created by Richard Hodgkins on 18/09/2016.
6 | // Copyright © 2016 Rich H. All rights reserved.
7 | //
8 |
9 | #import "XCTestCase+Extensions.h"
10 |
11 | @implementation XCTestCase (TestCaseName)
12 |
13 | -(NSString *_Nonnull)testCaseName
14 | {
15 | NSString *className = [NSStringFromClass([self class]) stringByReplacingOccurrencesOfString:@"Tests." withString:@""];
16 | return [[self.name stringByReplacingOccurrencesOfString:[NSString stringWithFormat:@"-[%@ ", className] withString:@""] stringByReplacingOccurrencesOfString:@"]" withString:@""];
17 | }
18 |
19 | @end
20 |
--------------------------------------------------------------------------------
/Examples/cartfile:
--------------------------------------------------------------------------------
1 | github "rhodgkins/RDHCollectionViewGridLayout" "master"
2 |
--------------------------------------------------------------------------------
/Examples/images/5-line, 10-item.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rhodgkins/RDHCollectionViewGridLayout/cb12530775efc6f4f9827be831a166b192b30d2a/Examples/images/5-line, 10-item.png
--------------------------------------------------------------------------------
/Examples/images/horizontal.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rhodgkins/RDHCollectionViewGridLayout/cb12530775efc6f4f9827be831a166b192b30d2a/Examples/images/horizontal.png
--------------------------------------------------------------------------------
/Examples/images/vertical.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rhodgkins/RDHCollectionViewGridLayout/cb12530775efc6f4f9827be831a166b192b30d2a/Examples/images/vertical.png
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | This code is distributed under the terms and conditions of the MIT license.
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
4 |
5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
6 |
7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
8 |
--------------------------------------------------------------------------------
/RDHCollectionViewGridLayout.podspec:
--------------------------------------------------------------------------------
1 | Pod::Spec.new do |s|
2 | s.name = 'RDHCollectionViewGridLayout'
3 | s.version = '1.2.5'
4 | s.license = 'MIT'
5 |
6 | s.summary = 'Grid layout for UICollectionView.'
7 | s.homepage = 'https://github.com/rhodgkins/RDHCollectionViewGridLayout'
8 | s.author = 'Rich Hodgkins'
9 | s.source = { :git => 'https://github.com/rhodgkins/RDHCollectionViewGridLayout.git', :tag => s.version.to_s }
10 | s.social_media_url = 'https://twitter.com/rhodgkins'
11 |
12 | s.frameworks = 'Foundation', 'UIKit', 'CoreGraphics'
13 | s.requires_arc = true
14 |
15 | s.ios.deployment_target = '6.0'
16 | s.tvos.deployment_target = '9.0'
17 | s.source_files = 'RDHCollectionViewGridLayout/'
18 | end
19 |
--------------------------------------------------------------------------------
/RDHCollectionViewGridLayout/RDHCollectionViewGridLayout.h:
--------------------------------------------------------------------------------
1 | //
2 | // RDHCollectionViewGridLayout.h
3 | // RDHCollectionViewGridLayout
4 | //
5 | // Created by Richard Hodgkins on 06/07/2013.
6 | // Copyright (c) 2013 Rich H. All rights reserved.
7 | //
8 |
9 | @import UIKit;
10 | @import CoreGraphics;
11 |
12 | /// Project version number for RDHCollectionViewGridLayout.
13 | FOUNDATION_EXPORT double RDHCollectionViewGridLayoutVersionNumber;
14 |
15 | /// Project version string for RDHCollectionViewGridLayout.
16 | FOUNDATION_EXPORT const unsigned char RDHCollectionViewGridLayoutVersionString[];
17 |
18 | @interface RDHCollectionViewGridLayout : UICollectionViewLayout
19 |
20 | /**
21 | * A vertical direction will constrain the layout by rows (lineItemCount per row), a horizontal direction by columns (lineItemCount per column).
22 | *
23 | * The default value of this property is UICollectionViewScrollDirectionVertical.
24 | *
25 | * @warning A non-enum value will throw an exception.
26 | */
27 | @property (nonatomic, assign) IBInspectable UICollectionViewScrollDirection scrollDirection;
28 |
29 | /**
30 | * Defines the size of the unconstrained dimension of the items. Simply, for vertical layouts this is the height of the items, and for horizontal layouts this is the width of the items.
31 | * Setting this value to 0 will make layout set the dimension to the average of the other dimenion on the same line. This is due to the layout taking account of dirty pixels, adding these extra pixels in the first X items on the line.
32 | * For example, if using a vertical scrollDirection and a lineItemCount of 5 when the collectionView has a width of 104, the first 4 items on every line will have a width of 21, and the last 20 (21 + 21 + 21 + 21 + 20 = 104), so the height of the lines would be 21 (20.8 rounded).
33 | *
34 | * A value of 0 is the same as setting the `lineExtension` property to 0 `lineMultiplier` property to 1. Setting this value will ignore the `lineExtension` and `lineMultiplier` properties and set them to their default values. When used and set in Interface Builder, leave the other properties to Default and only set this property.
35 | *
36 | * The default value of this property is 0.
37 | *
38 | * @see lineMultiplier
39 | * @see lineExtension
40 | *
41 | * @warning A negative value will throw an exception.
42 | */
43 | @property (nonatomic, assign) IBInspectable CGFloat lineSize;
44 |
45 | /**
46 | * Defines a multipler of the unconstrained dimension of the items in relation to the strained dimension. Simply, for vertical layouts this value is multiplied by the width and used as the height of the items, and for horizontal layouts this value is multiplied by the height and used as the width of the items. The final dimension is rounded to a whole integer.
47 | *
48 | * A value of 1 is the same as setting the `lineSize` or `lineExtension` properties to 0.Setting this value will ignore the `lineSize` and `lineExtension` properties and set them to their default values. When used and set in Interface Builder, leave the other properties to Default and only set this property.
49 | *
50 | * The default value of this property is 1.
51 | *
52 | * @see lineSize
53 | * @see lineExtension
54 | *
55 | * @warning A none positive value will throw an exception.
56 | */
57 | @property (nonatomic, assign) IBInspectable CGFloat lineMultiplier;
58 |
59 | /**
60 | * Defines a multipler of the unconstrained dimension of the items in relation to the strained dimension. Simply, for vertical layouts this value is added to the width and used as the height of the items, and for horizontal layouts this value is added to the height and used as the width of the items.
61 | *
62 | * A value of 0 is the same as setting the `lineSize` property to 0 `lineMultiplier` property to 1. Setting this value will ignore the `lineSize` and `lineMultiplier` properties and set them to their default values. When used and set in Interface Builder, leave the other properties to Default and only set this property.
63 | *
64 | * The default value of this property is 0.
65 | *
66 | * @see lineSize
67 | * @see lineMultiplier
68 | *
69 | * @warning A negative value will throw an exception.
70 | */
71 | @property (nonatomic, assign) IBInspectable CGFloat lineExtension;
72 |
73 | /**
74 | * Defines the maximum number of items allowed per line. Simply, for vertical layouts this is the number of columns, and for horizontal layouts this is the number of rows. The layout accounts for adding the extra pixels to the first X items on the line. Best case is that the useable width is exactly divisible by lineItemCount, worse case is that `(useable width) mod lineItemCount = (lineItemCount - 1)` and that the first `(lineItemCount - 1)` items are 1 pixel bigger.
75 | *
76 | * The default value of this property is 4.
77 | *
78 | * @warning A 0 value will throw an exception.
79 | */
80 | @property (nonatomic, assign) IBInspectable NSUInteger lineItemCount;
81 |
82 | /**
83 | * Defines the spacing of items on the same line of the layout. Simply, for vertical layouts this is the column spacing, and for horizontal layouts this is the row spacing.
84 | *
85 | * The default value of this property is 0.
86 | */
87 | @property (nonatomic, assign) IBInspectable CGFloat itemSpacing;
88 |
89 | /**
90 | * Defines the line spacing of the layout. Simply, for vertical layouts this is the row spacing, and for horizontal layouts this is the column spacing.
91 | *
92 | * The default value of this property is 0.
93 | */
94 | @property (nonatomic, assign) IBInspectable CGFloat lineSpacing;
95 |
96 | /**
97 | * To force sections to start on a new line set this property to YES. Otherwise the section will follow on on from the previous one.
98 | *
99 | * The default value of this property is NO.
100 | */
101 | @property (nonatomic, assign) IBInspectable BOOL sectionsStartOnNewLine;
102 |
103 | @end
104 |
105 | @interface RDHCollectionViewGridLayout (RDHCollectionViewGridLayout_Deprecated)
106 |
107 | /**
108 | * **Deprecated and replaced with `lineSize` as other line dimensions have been added. (`lineSize` has exactly the same functionality as this property) **
109 | *
110 | * Defines the size of the unconstrained dimension of the items. Simply, for vertical layouts this is the height of the items, and for horizontal layouts this is the width of the items.
111 | * Setting this value to 0 will make layout set the dimension to the average of the other dimenion on the same line. This is due to the layout taking account of dirty pixels, adding these extra pixels in the first X items on the line.
112 | * For example, if using a vertical scrollDirection and a lineItemCount of 5 when the collectionView has a width of 104, the first 4 items on every line will have a width of 21, and the last 20 (21 + 21 + 21 + 21 + 20 = 104), so the height of the lines would be 21 (20.8 rounded).
113 | *
114 | * A value of 0 is the same as setting the `lineExtension` property to 0 `lineMultiplier` property to 1. Setting this value will ignore the `lineExtension` and `lineMultiplier` properties and set them to their default values. When used and set in Interface Builder, leave the other properties to Default and only set this property.
115 | *
116 | * The default value of this property is 0.
117 | *
118 | * @see lineSize
119 | * @see lineMultiplier
120 | * @see lineExtension
121 | *
122 | * @warning A negative value will throw an exception.
123 | */
124 | @property (nonatomic, assign) IBInspectable CGFloat lineDimension DEPRECATED_MSG_ATTRIBUTE("use lineSize instead.");
125 |
126 | @end
--------------------------------------------------------------------------------
/RDHCollectionViewGridLayout/RDHCollectionViewGridLayout.m:
--------------------------------------------------------------------------------
1 | //
2 | // RDHCollectionViewGridLayout.m
3 | // RDHCollectionViewGridLayout
4 | //
5 | // Created by Richard Hodgkins on 06/07/2013.
6 | // Copyright (c) 2013 Rich H. All rights reserved.
7 | //
8 |
9 | #import "RDHCollectionViewGridLayout.h"
10 |
11 | typedef NS_ENUM(NSUInteger, RDHLineDimensionType) {
12 | RDHLineDimensionTypeSize,
13 | RDHLineDimensionTypeMultiplier,
14 | RDHLineDimensionTypeExtension
15 | };
16 |
17 | static RDHLineDimensionType const RDHLineDimensionTypeDefault = RDHLineDimensionTypeSize;
18 |
19 | static CGFloat const RDHLineSizeDefault = 0;
20 | static CGFloat const RDHLineMutliplierDefault = 1;
21 | static CGFloat const RDHLineExtensionDefault = 0;
22 |
23 | @interface RDHCollectionViewGridLayout ()
24 |
25 | @property (nonatomic, copy, nullable) NSArray * firstLineFrames;
26 | @property (nonatomic, copy, nonnull, readonly) NSMutableDictionary * itemAttributes;
27 |
28 | /// This property is used to store the lineDimension when it is set to 0 (depends on the average item size) and the base item size.
29 | @property (nonatomic, assign) CGSize calculatedItemSize;
30 |
31 | /// This property is re-calculated when invalidating the layout
32 | @property (nonatomic, assign) NSUInteger numberOfLines;
33 |
34 | @property (nonatomic, assign) RDHLineDimensionType lineDimensionType;
35 |
36 | @end
37 |
38 | @implementation RDHCollectionViewGridLayout
39 |
40 | -(instancetype)init
41 | {
42 | self = [super init];
43 | if (self) {
44 | [self commonInit];
45 | }
46 | return self;
47 | }
48 |
49 | -(instancetype)initWithCoder:(nonnull NSCoder *)aDecoder
50 | {
51 | self = [super initWithCoder:aDecoder];
52 | if (self) {
53 | [self commonInit];
54 | }
55 | return self;
56 | }
57 |
58 | -(void)commonInit
59 | {
60 | [self setInitialDefaults];
61 |
62 | _firstLineFrames = nil;
63 | _itemAttributes = [NSMutableDictionary dictionary];
64 | _numberOfLines = 0;
65 | }
66 |
67 | -(void)setInitialDefaults
68 | {
69 | // Default properties
70 | _scrollDirection = UICollectionViewScrollDirectionVertical;
71 | _lineDimensionType = RDHLineDimensionTypeDefault;
72 | _lineSize = RDHLineSizeDefault;
73 | _lineMultiplier = RDHLineMutliplierDefault;
74 | _lineExtension = RDHLineExtensionDefault;
75 | _lineItemCount = 4;
76 | _itemSpacing = 0;
77 | _lineSpacing = 0;
78 | _sectionsStartOnNewLine = NO;
79 | }
80 |
81 | -(void)invalidateLayout
82 | {
83 | [super invalidateLayout];
84 |
85 | self.firstLineFrames = nil;
86 | [self.itemAttributes removeAllObjects];
87 | self.numberOfLines = 0;
88 | self.calculatedItemSize = CGSizeZero;
89 | }
90 |
91 | -(void)prepareLayout
92 | {
93 | [super prepareLayout];
94 |
95 | self.numberOfLines = [self calculateNumberOfLines];
96 |
97 | self.calculatedItemSize = [self calculateItemSize];
98 |
99 | NSInteger const sectionCount = [self.collectionView numberOfSections];
100 | for (NSInteger section=0; section 0) {
122 | size.width += (self.numberOfLines - 1) * self.lineSpacing;
123 | }
124 | size.height = [self constrainedCollectionViewDimension];
125 | break;
126 |
127 | case UICollectionViewScrollDirectionVertical:
128 | size.width = [self constrainedCollectionViewDimension];
129 | size.height = self.numberOfLines * self.calculatedItemSize.height;
130 | // Add spacings
131 | if (self.numberOfLines > 0) {
132 | size.height += (self.numberOfLines - 1) * self.lineSpacing;
133 | }
134 | break;
135 | }
136 |
137 | return size;
138 | }
139 |
140 | -(nullable UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(nonnull NSIndexPath *)indexPath
141 | {
142 | UICollectionViewLayoutAttributes *layoutAttrs = self.itemAttributes[indexPath];
143 |
144 | if (!layoutAttrs) {
145 | layoutAttrs = [self calculateLayoutAttributesForItemAtIndexPath:indexPath];
146 | self.itemAttributes[indexPath] = layoutAttrs;
147 | }
148 |
149 | return layoutAttrs;
150 | }
151 |
152 | -(NSArray<__kindof UICollectionViewLayoutAttributes *> *)layoutAttributesForElementsInRect:(CGRect)rect
153 | {
154 | NSMutableArray<__kindof UICollectionViewLayoutAttributes *> *layoutAttributes = [NSMutableArray arrayWithCapacity:[self.itemAttributes count]];
155 |
156 | [self.itemAttributes enumerateKeysAndObjectsUsingBlock:^(NSIndexPath *const indexPath, UICollectionViewLayoutAttributes *attr, BOOL *stop) {
157 |
158 | if (CGRectIntersectsRect(rect, attr.frame)) {
159 | [layoutAttributes addObject:attr];
160 | }
161 | }];
162 |
163 | return layoutAttributes;
164 | }
165 |
166 | -(BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds
167 | {
168 | return !CGSizeEqualToSize(self.collectionView.bounds.size, newBounds.size);
169 | }
170 |
171 | #pragma mark - Lazily loaded properties
172 |
173 | /// Precalculate the frames for the first line as they can be reused for every line
174 | -(NSArray *)firstLineFrames
175 | {
176 | if (!_firstLineFrames) {
177 |
178 | CGFloat collectionConstrainedDimension = [self constrainedCollectionViewDimension];
179 | // Subtract the spacing between items on a line
180 | collectionConstrainedDimension -= (self.itemSpacing * (self.lineItemCount - 1));
181 |
182 | CGFloat constrainedItemDimension;
183 | switch (self.scrollDirection) {
184 | case UICollectionViewScrollDirectionVertical:
185 | constrainedItemDimension = self.calculatedItemSize.width;
186 | break;
187 |
188 | case UICollectionViewScrollDirectionHorizontal:
189 | constrainedItemDimension = self.calculatedItemSize.height;
190 | break;
191 | }
192 |
193 | // This value will always be less than the lineItemCount - this is the number of dirty pixels
194 | CGFloat remainingDimension = collectionConstrainedDimension - (constrainedItemDimension * self.lineItemCount);
195 |
196 | CGRect frame = CGRectZero;
197 | frame.size = self.calculatedItemSize;
198 |
199 | NSMutableArray *frames = [NSMutableArray arrayWithCapacity:self.lineItemCount];
200 |
201 | for (NSUInteger i=0; i 0) {
207 | switch (self.scrollDirection) {
208 | case UICollectionViewScrollDirectionVertical:
209 | itemFrame.size.width++;
210 | break;
211 |
212 | case UICollectionViewScrollDirectionHorizontal:
213 | itemFrame.size.height++;
214 | break;
215 | }
216 | }
217 |
218 | [frames addObject:[NSValue valueWithCGRect:itemFrame]];
219 |
220 | // Move to the next item
221 | switch (self.scrollDirection) {
222 | case UICollectionViewScrollDirectionVertical:
223 | frame.origin.x = itemFrame.origin.x + itemFrame.size.width + self.itemSpacing;
224 | break;
225 |
226 | case UICollectionViewScrollDirectionHorizontal:
227 | frame.origin.y = itemFrame.origin.y + itemFrame.size.height + self.itemSpacing;
228 | break;
229 | }
230 | }
231 |
232 | _firstLineFrames = [frames copy];
233 | }
234 |
235 | return _firstLineFrames;
236 | }
237 |
238 | #pragma mark - Calculation methods
239 |
240 | -(NSUInteger)calculateNumberOfLines
241 | {
242 | NSInteger numberOfLines;
243 | if (self.sectionsStartOnNewLine) {
244 |
245 | numberOfLines = 0;
246 |
247 | NSInteger const sectionCount = [self.collectionView numberOfSections];
248 | for (NSInteger section=0; section= 0, @"Negative lineSize is meaningless");
437 |
438 | // Reset other line dimensions
439 | _lineMultiplier = RDHLineMutliplierDefault;
440 | _lineExtension = RDHLineExtensionDefault;
441 | self.lineDimensionType = RDHLineDimensionTypeSize;
442 |
443 | if (_lineSize != lineSize) {
444 | _lineSize = lineSize;
445 |
446 | [self invalidateLayout];
447 | }
448 | }
449 |
450 | -(void)setLineMultiplier:(CGFloat)lineMultiplier
451 | {
452 | NSAssert(lineMultiplier > 0, @"None positive lineMultiplier is meaningless");
453 |
454 | // Reset other line dimensions
455 | _lineSize = RDHLineSizeDefault;
456 | _lineExtension = RDHLineExtensionDefault;
457 | self.lineDimensionType = RDHLineDimensionTypeMultiplier;
458 |
459 | if (_lineMultiplier != lineMultiplier) {
460 | _lineMultiplier = lineMultiplier;
461 |
462 | [self invalidateLayout];
463 | }
464 | }
465 |
466 | -(void)setLineExtension:(CGFloat)lineExtension
467 | {
468 | NSAssert(lineExtension >= 0, @"Negative lineExtension is meaningless");
469 |
470 | // Reset other line dimensions
471 | _lineSize = RDHLineSizeDefault;
472 | _lineMultiplier = RDHLineMutliplierDefault;
473 | self.lineDimensionType = RDHLineDimensionTypeExtension;
474 |
475 | if (_lineExtension != lineExtension) {
476 | _lineExtension = lineExtension;
477 |
478 | [self invalidateLayout];
479 | }
480 | }
481 |
482 | -(void)setLineItemCount:(NSUInteger)lineItemCount
483 | {
484 | NSAssert(lineItemCount > 0, @"Zero line item count is meaningless");
485 | if (_lineItemCount != lineItemCount) {
486 | _lineItemCount = lineItemCount;
487 |
488 | [self invalidateLayout];
489 | }
490 | }
491 |
492 | -(void)setItemSpacing:(CGFloat)itemSpacing
493 | {
494 | if (_itemSpacing != itemSpacing) {
495 | _itemSpacing = itemSpacing;
496 |
497 | [self invalidateLayout];
498 | }
499 | }
500 |
501 | -(void)setLineSpacing:(CGFloat)lineSpacing
502 | {
503 | if (_lineSpacing != lineSpacing) {
504 | _lineSpacing = lineSpacing;
505 |
506 | [self invalidateLayout];
507 | }
508 | }
509 |
510 | -(void)setSectionsStartOnNewLine:(BOOL)sectionsStartOnNewLine
511 | {
512 | if (_sectionsStartOnNewLine != sectionsStartOnNewLine) {
513 | _sectionsStartOnNewLine = sectionsStartOnNewLine;
514 |
515 | [self invalidateLayout];
516 | }
517 | }
518 |
519 | -(NSString *)description
520 | {
521 | NSString *lineDimension;
522 |
523 | if ((self.lineSize == RDHLineSizeDefault)
524 | && (self.lineMultiplier == RDHLineMutliplierDefault)
525 | && (self.lineExtension == RDHLineExtensionDefault)) {
526 |
527 | lineDimension = @"(Auto)";
528 |
529 | } else {
530 |
531 | switch (self.lineDimensionType) {
532 | case RDHLineDimensionTypeSize:
533 | lineDimension = [NSString stringWithFormat:@"(Size, %.3lf)", self.lineSize];
534 | break;
535 | case RDHLineDimensionTypeMultiplier:
536 | lineDimension = [NSString stringWithFormat:@"(Multiplier, %.3lf)", self.lineMultiplier];
537 | break;
538 | case RDHLineDimensionTypeExtension:
539 | lineDimension = [NSString stringWithFormat:@"(Extension, %.3lf)", self.lineExtension];
540 | break;
541 | }
542 | }
543 |
544 | return [NSString stringWithFormat:@"<%@: %p; scrollDirection = %@; lineDimension = %@; lineItemCount = %llu; itemSpacing = %.3lf; lineSpacing = %.3lf; sectionsStartOnNewLine = %@>", NSStringFromClass([self class]), self, (self.scrollDirection == UICollectionViewScrollDirectionVertical ? @"Vertical" : @"Horizontal"), lineDimension, (unsigned long long) self.lineItemCount, self.itemSpacing, self.lineSpacing, (self.sectionsStartOnNewLine ? @"YES" : @"NO")];
545 | }
546 |
547 | @end
548 |
549 | @interface RDHCollectionViewGridLayout (InspectableScrolling)
550 |
551 | @property (nonatomic, assign) IBInspectable BOOL verticalScrolling;
552 |
553 | @end
554 |
555 | @implementation RDHCollectionViewGridLayout (InspectableScrolling)
556 |
557 | -(BOOL)verticalScrolling
558 | {
559 | return self.scrollDirection == UICollectionViewScrollDirectionVertical;
560 | }
561 |
562 | -(void)setVerticalScrolling:(BOOL)verticalScrolling
563 | {
564 | self.scrollDirection = verticalScrolling ? UICollectionViewScrollDirectionVertical : UICollectionViewScrollDirectionHorizontal;
565 | }
566 |
567 | @end
568 |
569 | @implementation RDHCollectionViewGridLayout (RDHCollectionViewGridLayout_Deprecated)
570 |
571 | -(CGFloat)lineDimension
572 | {
573 | return self.lineSize;
574 | }
575 |
576 | -(void)setLineDimension:(CGFloat)lineDimension
577 | {
578 | self.lineSize = lineDimension;
579 | }
580 |
581 | @end
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | RDHCollectionViewGridLayout
2 | ===========================
3 | [](https://travis-ci.org/rhodgkins/RDHCollectionViewGridLayout)
4 | [](https://github.com/Carthage/Carthage)
5 | [](http://cocoadocs.org/docsets/RDHCollectionViewGridLayout/)
6 | [](http://cocoadocs.org/docsets/RDHCollectionViewGridLayout/)
7 | [](http://opensource.org/licenses/MIT)
8 |
9 | Works and tested on iOS 6.0 to 10.0 (under Xcode 8.0), but if you find any issues please [report](https://github.com/rhodgkins/RDHCollectionViewGridLayout/issues) them!
10 |
11 | Carthage
12 | ---------
13 |
14 | As of 1.2.2 Carthage support has been added.
15 |
16 | ```ogdl
17 | github "rhodgkins/RDHCollectionViewGridLayout" ~> 1.2
18 | ```
19 |
20 | CocoaPods
21 | ---------
22 |
23 | Grid layout for UICollectionView.
24 | ```ruby
25 | pod 'RDHCollectionViewGridLayout', '~> 1.2'
26 | ```
27 |
28 | Summary
29 | -------
30 |
31 | This layout provides simple options for customisation of a collection view as a grid layout.
32 | Supporting vertical and horizontal scrolling directions, the layout also takes care of left over pixels at the end of rows.
33 | For example, a `UICollectionView` for a size of 320x300 scrolling vertically with 6 columns would normally mean each cell would be 53.3333 points wide.
34 | This would mean the cells would be blurry due to it's frame sitting over non-integer pixel values.
35 | `RDHCollectionViewGridLayout` takes care of this, and distributes these dirty extra pixels over the columns. This means the first 2 columns would have a cell width of 54 points, while the rest have a size of 53 points.
36 |
37 | PSTCollectionView
38 | -----------------
39 |
40 | From version 1.1.0 `PSTCollectionView` support is removed.
41 |
42 | If `PSTCollectionView` is being used, use the `RDHCollectionViewGridLayout/PST` pod and a version less than 1.1.0:
43 | ``` ruby
44 | pod 'RDHCollectionViewGridLayout/PST', '< 1.1'
45 | ```
46 |
47 |
48 | Documentation
49 | -------------
50 |
51 | [Online docs](http://cocoadocs.org/docsets/RDHCollectionViewGridLayout)
52 |
57 | Properties
58 | ----------
59 |
60 | ``` objective-c
61 | /**
62 | * A vertical direction will constrain the layout by rows (lineItemCount per row), a horizontal direction by columns
63 | (lineItemCount per column).
64 | *
65 | * The default value of this property is UICollectionViewScrollDirectionVertical.
66 | */
67 | @property (nonatomic, assign) UICollectionViewScrollDirection scrollDirection;
68 |
69 | /**
70 | * Defines the size of the unconstrained dimension of the items. Simply, for vertical layouts this is the height of the items, and for horizontal layouts this is the width of the items.
71 | * Setting this value to 0 will make layout set the dimension to the average of the other dimenion on the same line. This is due to the layout taking account of dirty pixels, adding these extra pixels in the first X items on the line.
72 | * For example, if using a vertical scrollDirection and a lineItemCount of 5 when the collectionView has a width of 104, the first 4 items on every line will have a width of 21, and the last 20 (21 + 21 + 21 + 21 + 20 = 104), so the height of the lines would be 21 (20.8 rounded).
73 | *
74 | * A value of 0 is the same as setting the `lineExtension` property to 0 `lineMultiplier` property to 1. Setting this value will ignore the `lineExtension` and `lineMultiplier` properties and set them to their default values. When used and set in Interface Builder, leave the other properties to Default and only set this property.
75 | *
76 | * The default value of this property is 0.
77 | *
78 | * @see lineMultiplier
79 | * @see lineExtension
80 | *
81 | * @warning A negative value will throw an exception.
82 | */
83 | @property (nonatomic, assign) IBInspectable CGFloat lineSize;
84 |
85 | /**
86 | * Defines a multipler of the unconstrained dimension of the items in relation to the strained dimension. Simply, for vertical layouts this value is multiplied by the width and used as the height of the items, and for horizontal layouts this value is multiplied by the height and used as the width of the items. The final dimension is rounded to a whole integer.
87 | *
88 | * A value of 1 is the same as setting the `lineSize` or `lineExtension` properties to 0.Setting this value will ignore the `lineSize` and `lineExtension` properties and set them to their default values. When used and set in Interface Builder, leave the other properties to Default and only set this property.
89 | *
90 | * The default value of this property is 1.
91 | *
92 | * @see lineSize
93 | * @see lineExtension
94 | *
95 | * @warning A negative value will throw an exception.
96 | */
97 | @property (nonatomic, assign) IBInspectable CGFloat lineMultiplier;
98 |
99 | /**
100 | * Defines a multipler of the unconstrained dimension of the items in relation to the strained dimension. Simply, for vertical layouts this value is added to the width and used as the height of the items, and for horizontal layouts this value is added to the height and used as the width of the items.
101 | *
102 | * A value of 0 is the same as setting the `lineSize` property to 0 `lineMultiplier` property to 1. Setting this value will ignore the `lineSize` and `lineMultiplier` properties and set them to their default values. When used and set in Interface Builder, leave the other properties to Default and only set this property.
103 | *
104 | * The default value of this property is 0.
105 | *
106 | * @see lineSize
107 | * @see lineMultiplier
108 | *
109 | * @warning A negative value will throw an exception.
110 | */
111 | @property (nonatomic, assign) IBInspectable CGFloat lineExtension;
112 |
113 | /**
114 | * Defines the maximum number of items allowed per line. Simply, for vertical layouts this is the number of columns,
115 | and for horizontal layouts this is the number of rows. The layout accounts for adding the extra pixels to the first
116 | X items on the line. Best case is that the useable width is exactly divisible by lineItemCount, worse case is that
117 | (useable width) mod lineItemCount = (lineItemCount - 1) and that the first (lineItemCount - 1) items are 1 pixel
118 | bigger.
119 | *
120 | * The default value of this property is 4.
121 | */
122 | @property (nonatomic, assign) NSUInteger lineItemCount;
123 |
124 | /**
125 | * Defines the spacing of items on the same line of the layout. Simply, for vertical layouts this is the column
126 | spacing, and for horizontal layouts this is the row spacing.
127 | *
128 | * The default value of this property is 0.
129 | */
130 | @property (nonatomic, assign) CGFloat itemSpacing;
131 |
132 | /**
133 | * Defines the line spacing of the layout. Simply, for vertical layouts this is the row spacing, and for horizontal
134 | layouts this is the column spacing.
135 | *
136 | * The default value of this property is 0.
137 | */
138 | @property (nonatomic, assign) CGFloat lineSpacing;
139 |
140 | /**
141 | * To force sections to start on a new line set this property to YES. Otherwise the section will follow on on from
142 | the previous one.
143 | *
144 | * The default value of this property is NO.
145 | */
146 | @property (nonatomic, assign) BOOL sectionsStartOnNewLine;
147 |
148 | ```
149 |
150 | Examples
151 | --------
152 |
153 |
154 | The following example is with a `lineSpacing` of 5 and `itemSpacing` of 10. Each section starts on a new line (`sectionsStartOnNewLine` is `YES`).
155 |
156 | 
157 |
158 |
159 | This is the same as the above but the `sectionsStartOnNewLine` is `NO`.
160 |
161 | 
162 |
163 |
164 | Horizontal scrolling
165 |
166 | 
167 |
168 |
169 |
--------------------------------------------------------------------------------