├── .github ├── FUNDING.yml └── workflows │ └── CI.yml ├── .gitignore ├── CHTCollectionViewWaterfallLayout.podspec ├── Demo ├── Objective-C │ ├── Demo.xcodeproj │ │ ├── project.pbxproj │ │ └── xcshareddata │ │ │ └── xcschemes │ │ │ ├── Demo.xcscheme │ │ │ └── SPMDemo.xcscheme │ └── Demo │ │ ├── AppDelegate.h │ │ ├── AppDelegate.m │ │ ├── CHTCollectionViewWaterfallCell.h │ │ ├── CHTCollectionViewWaterfallCell.m │ │ ├── CHTCollectionViewWaterfallFooter.h │ │ ├── CHTCollectionViewWaterfallFooter.m │ │ ├── CHTCollectionViewWaterfallHeader.h │ │ ├── CHTCollectionViewWaterfallHeader.m │ │ ├── Default-568h@2x.png │ │ ├── Default.png │ │ ├── Default@2x.png │ │ ├── Demo-Info.plist │ │ ├── Demo-Prefix.pch │ │ ├── Launch Screen.storyboard │ │ ├── SPMDemo-Info.plist │ │ ├── ViewController.h │ │ ├── ViewController.m │ │ ├── en.lproj │ │ ├── InfoPlist.strings │ │ └── MainStoryboard_iPhone.storyboard │ │ ├── images │ │ ├── cat1.jpg │ │ ├── cat2.jpg │ │ ├── cat3.jpg │ │ └── cat4.jpg │ │ └── main.m └── Swift │ ├── CHTWaterfallSwiftDemo.xcodeproj │ ├── project.pbxproj │ └── xcshareddata │ │ └── xcschemes │ │ ├── CHTWaterfallSwiftDemo.xcscheme │ │ └── CHTWaterfallSwiftSPMDemo.xcscheme │ └── CHTWaterfallSwiftDemo │ ├── AppDelegate.swift │ ├── Base.lproj │ └── LaunchScreen.xib │ ├── CHTWaterfallSwiftDemo-Info.plist │ ├── CHTWaterfallSwiftSPMDemo-Info.plist │ ├── ImageUICollectionViewCell.swift │ ├── ImageUICollectionViewCell.xib │ ├── Images.xcassets │ ├── AppIcon.appiconset │ │ └── Contents.json │ ├── image1.imageset │ │ ├── Contents.json │ │ └── public-domain-images-free-stock-photos-black-white-vintage-suitcase-girl-railroadtracks-walking-1.jpg │ ├── image2.imageset │ │ ├── Contents.json │ │ └── public-domain-images-free-stock-photos-brick-wall-rustic-old-metal-doors-private-parking-1000x664.jpg │ ├── image3.imageset │ │ ├── Contents.json │ │ └── public-domain-images-free-stock-photos-down-town-chicago-blue-sky-6.jpg │ ├── image4.imageset │ │ ├── Contents.json │ │ └── public-domain-images-free-stock-photos-down-town-chicago-tribune-building-1-1000x666.jpg │ ├── image5.imageset │ │ ├── Contents.json │ │ └── public-domain-images-free-stock-photos-high-quality-resolution-downloads-public-domain-archive-5.jpg │ ├── image6.imageset │ │ ├── Contents.json │ │ └── public-domain-images-free-stock-photos-high-quality-resolution-downloads-public-domain-archive-10.jpg │ └── image7.imageset │ │ ├── Contents.json │ │ └── public-domain-images-free-stock-photos-sky-scrapers-bricks-chicago-161-1000x666.jpg │ ├── Main.storyboard │ ├── Model.swift │ └── ViewController.swift ├── Info.plist ├── LICENSE ├── Package.swift ├── README.md ├── Screenshots └── 2-columns.png ├── Source ├── CHTCollectionViewWaterfallLayout.h ├── CHTCollectionViewWaterfallLayout.m └── CHTCollectionViewWaterfallLayout.swift └── uncrustify.cfg /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | buy_me_a_coffee: chiahsien 4 | -------------------------------------------------------------------------------- /.github/workflows/CI.yml: -------------------------------------------------------------------------------- 1 | name: "CHTCollectionViewWaterfallLayout CI" 2 | 3 | on: 4 | push: 5 | branches: 6 | - develop 7 | pull_request: 8 | branches: 9 | - '*' 10 | 11 | jobs: 12 | Demo: 13 | name: Demo Project (Latest Stable Xcode) 14 | runs-on: macOS-11 15 | steps: 16 | - name: Checkout 17 | uses: actions/checkout@v2 18 | 19 | - name: Setup Xcode version 20 | uses: maxim-lobanov/setup-xcode@v1.4.0 21 | with: 22 | xcode-version: latest-stable 23 | 24 | - name: Build Swift Project 25 | uses: sersoft-gmbh/xcodebuild-action@v1.8.0 26 | with: 27 | project: Demo/Swift/CHTWaterfallSwiftDemo.xcodeproj 28 | scheme: CHTWaterfallSwiftDemo 29 | destination: name=iPhone 13 Pro 30 | action: build 31 | 32 | - name: Build Swift SPM Project 33 | uses: sersoft-gmbh/xcodebuild-action@v1.8.0 34 | with: 35 | project: Demo/Swift/CHTWaterfallSwiftDemo.xcodeproj 36 | scheme: CHTWaterfallSwiftSPMDemo 37 | destination: name=iPhone 13 Pro 38 | action: build 39 | 40 | - name: Build Objective-C Project 41 | uses: sersoft-gmbh/xcodebuild-action@v1.8.0 42 | with: 43 | project: Demo/Objective-C/Demo.xcodeproj 44 | scheme: Demo 45 | destination: name=iPhone 13 Pro 46 | action: build 47 | 48 | - name: Build Objective-C SPM Project 49 | uses: sersoft-gmbh/xcodebuild-action@v1.8.0 50 | with: 51 | project: Demo/Objective-C/Demo.xcodeproj 52 | scheme: SPMDemo 53 | destination: name=iPhone 13 Pro 54 | action: build 55 | 56 | Pods: 57 | name: Cocoapods Lint (Latest Stable Xcode) 58 | runs-on: macOS-11 59 | steps: 60 | - name: Checkout 61 | uses: actions/checkout@v2 62 | 63 | - name: Setup Xcode version 64 | uses: maxim-lobanov/setup-xcode@v1.4.0 65 | with: 66 | xcode-version: latest-stable 67 | 68 | - name: Run pod lib lint dynamic-framework 69 | run: pod lib lint --fail-fast 70 | 71 | - name: Run pod lib lint static-framework 72 | run: pod lib lint --fail-fast --use-libraries --use-modular-headers 73 | 74 | SwiftPM: 75 | name: SwiftPM (Latest Stable Xcode) 76 | runs-on: macOS-11 77 | steps: 78 | - name: Checkout 79 | uses: actions/checkout@v2 80 | 81 | - name: Setup Xcode version 82 | uses: maxim-lobanov/setup-xcode@v1.4.0 83 | with: 84 | xcode-version: latest-stable 85 | 86 | - name: Build Swift Version 87 | run: xcodebuild -scheme CHTCollectionViewWaterfallLayout -destination generic/platform=iOS 88 | 89 | - name: Build ObjC Version 90 | run: xcodebuild -scheme CHTCollectionViewWaterfallLayoutObjC -destination generic/platform=iOS 91 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | .DS_Store 3 | build/ 4 | *.pbxuser 5 | !default.pbxuser 6 | *.mode1v3 7 | !default.mode1v3 8 | *.mode2v3 9 | !default.mode2v3 10 | *.perspectivev3 11 | !default.perspectivev3 12 | *.xcworkspace 13 | !default.xcworkspace 14 | xcuserdata 15 | profile 16 | *.moved-aside 17 | DerivedData 18 | .idea/ 19 | 20 | # Carthage 21 | Carthage/ 22 | 23 | # Swift Package Manager 24 | .build/ 25 | .swiftpm/ 26 | Package.resolved 27 | -------------------------------------------------------------------------------- /CHTCollectionViewWaterfallLayout.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |s| 2 | s.name = "CHTCollectionViewWaterfallLayout" 3 | s.version = "0.9.10" 4 | s.summary = "The waterfall (i.e., Pinterest-like) layout for UICollectionView." 5 | s.homepage = "https://github.com/chiahsien/CHTCollectionViewWaterfallLayout" 6 | s.screenshots = "https://raw.github.com/chiahsien/UICollectionViewWaterfallLayout/master/Screenshots/2-columns.png" 7 | s.license = 'MIT' 8 | s.author = { "Nelson" => "chiahsien@gmail.com" } 9 | s.source = { :git => "https://github.com/chiahsien/CHTCollectionViewWaterfallLayout.git", :tag => "#{s.version}" } 10 | s.requires_arc = true 11 | 12 | s.ios.deployment_target = '9.0' 13 | s.tvos.deployment_target = '9.0' 14 | 15 | s.default_subspec = 'Swift' 16 | 17 | s.subspec 'ObjC' do |ss| 18 | ss.ios.deployment_target = '9.0' 19 | ss.source_files = 'Source/*.{h,m}' 20 | end 21 | 22 | s.swift_version = '4.2' 23 | s.subspec 'Swift' do |ss| 24 | ss.ios.deployment_target = '9.0' 25 | ss.source_files = 'Source/*.swift' 26 | end 27 | end 28 | -------------------------------------------------------------------------------- /Demo/Objective-C/Demo.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 52; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 28ACC0CB181E580300454F4A /* CHTCollectionViewWaterfallFooter.m in Sources */ = {isa = PBXBuildFile; fileRef = 28ACC0CA181E580300454F4A /* CHTCollectionViewWaterfallFooter.m */; }; 11 | 28FB2A16181580B1002B84F4 /* CHTCollectionViewWaterfallHeader.m in Sources */ = {isa = PBXBuildFile; fileRef = 28FB2A15181580B1002B84F4 /* CHTCollectionViewWaterfallHeader.m */; }; 12 | 526109C516646F4D005E4B6E /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 526109C416646F4D005E4B6E /* UIKit.framework */; }; 13 | 526109C716646F4D005E4B6E /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 526109C616646F4D005E4B6E /* Foundation.framework */; }; 14 | 526109C916646F4D005E4B6E /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 526109C816646F4D005E4B6E /* CoreGraphics.framework */; }; 15 | 526109CF16646F4D005E4B6E /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 526109CD16646F4D005E4B6E /* InfoPlist.strings */; }; 16 | 526109D116646F4D005E4B6E /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 526109D016646F4D005E4B6E /* main.m */; }; 17 | 526109D516646F4D005E4B6E /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 526109D416646F4D005E4B6E /* AppDelegate.m */; }; 18 | 526109D716646F4D005E4B6E /* Default.png in Resources */ = {isa = PBXBuildFile; fileRef = 526109D616646F4D005E4B6E /* Default.png */; }; 19 | 526109D916646F4D005E4B6E /* Default@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 526109D816646F4D005E4B6E /* Default@2x.png */; }; 20 | 526109DB16646F4D005E4B6E /* Default-568h@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 526109DA16646F4D005E4B6E /* Default-568h@2x.png */; }; 21 | 526109DE16646F4D005E4B6E /* MainStoryboard_iPhone.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 526109DC16646F4D005E4B6E /* MainStoryboard_iPhone.storyboard */; }; 22 | 526109E416646F4D005E4B6E /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 526109E316646F4D005E4B6E /* ViewController.m */; }; 23 | 526109F31664740B005E4B6E /* CHTCollectionViewWaterfallCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 526109F21664740B005E4B6E /* CHTCollectionViewWaterfallCell.m */; }; 24 | 529599F62757C93E00621B8A /* Launch Screen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 529599F52757C93E00621B8A /* Launch Screen.storyboard */; }; 25 | 52FADF371CB64F5C0097FB12 /* CHTCollectionViewWaterfallLayout.m in Sources */ = {isa = PBXBuildFile; fileRef = 52FADF361CB64F5C0097FB12 /* CHTCollectionViewWaterfallLayout.m */; }; 26 | A56112A127595F0600D4F206 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 526109D016646F4D005E4B6E /* main.m */; }; 27 | A56112A227595F0600D4F206 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 526109D416646F4D005E4B6E /* AppDelegate.m */; }; 28 | A56112A327595F0600D4F206 /* CHTCollectionViewWaterfallFooter.m in Sources */ = {isa = PBXBuildFile; fileRef = 28ACC0CA181E580300454F4A /* CHTCollectionViewWaterfallFooter.m */; }; 29 | A56112A527595F0600D4F206 /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 526109E316646F4D005E4B6E /* ViewController.m */; }; 30 | A56112A627595F0600D4F206 /* CHTCollectionViewWaterfallHeader.m in Sources */ = {isa = PBXBuildFile; fileRef = 28FB2A15181580B1002B84F4 /* CHTCollectionViewWaterfallHeader.m */; }; 31 | A56112A727595F0600D4F206 /* CHTCollectionViewWaterfallCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 526109F21664740B005E4B6E /* CHTCollectionViewWaterfallCell.m */; }; 32 | A56112A927595F0600D4F206 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 526109C416646F4D005E4B6E /* UIKit.framework */; }; 33 | A56112AA27595F0600D4F206 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 526109C616646F4D005E4B6E /* Foundation.framework */; }; 34 | A56112AB27595F0600D4F206 /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 526109C816646F4D005E4B6E /* CoreGraphics.framework */; }; 35 | A56112AD27595F0600D4F206 /* cat2.jpg in Resources */ = {isa = PBXBuildFile; fileRef = E5E70738195E341500773FD3 /* cat2.jpg */; }; 36 | A56112AE27595F0600D4F206 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 526109CD16646F4D005E4B6E /* InfoPlist.strings */; }; 37 | A56112AF27595F0600D4F206 /* cat3.jpg in Resources */ = {isa = PBXBuildFile; fileRef = E5E70739195E341500773FD3 /* cat3.jpg */; }; 38 | A56112B027595F0600D4F206 /* Default.png in Resources */ = {isa = PBXBuildFile; fileRef = 526109D616646F4D005E4B6E /* Default.png */; }; 39 | A56112B127595F0600D4F206 /* cat4.jpg in Resources */ = {isa = PBXBuildFile; fileRef = E5E7073A195E341500773FD3 /* cat4.jpg */; }; 40 | A56112B227595F0600D4F206 /* Default@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 526109D816646F4D005E4B6E /* Default@2x.png */; }; 41 | A56112B327595F0600D4F206 /* Default-568h@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 526109DA16646F4D005E4B6E /* Default-568h@2x.png */; }; 42 | A56112B427595F0600D4F206 /* cat1.jpg in Resources */ = {isa = PBXBuildFile; fileRef = E5E70737195E341500773FD3 /* cat1.jpg */; }; 43 | A56112B527595F0600D4F206 /* Launch Screen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 529599F52757C93E00621B8A /* Launch Screen.storyboard */; }; 44 | A56112B627595F0600D4F206 /* MainStoryboard_iPhone.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 526109DC16646F4D005E4B6E /* MainStoryboard_iPhone.storyboard */; }; 45 | A56112C12759620E00D4F206 /* CHTCollectionViewWaterfallLayoutObjC in Frameworks */ = {isa = PBXBuildFile; productRef = A56112C02759620E00D4F206 /* CHTCollectionViewWaterfallLayoutObjC */; }; 46 | E5E7073B195E341500773FD3 /* cat1.jpg in Resources */ = {isa = PBXBuildFile; fileRef = E5E70737195E341500773FD3 /* cat1.jpg */; }; 47 | E5E7073C195E341500773FD3 /* cat2.jpg in Resources */ = {isa = PBXBuildFile; fileRef = E5E70738195E341500773FD3 /* cat2.jpg */; }; 48 | E5E7073D195E341500773FD3 /* cat3.jpg in Resources */ = {isa = PBXBuildFile; fileRef = E5E70739195E341500773FD3 /* cat3.jpg */; }; 49 | E5E7073E195E341500773FD3 /* cat4.jpg in Resources */ = {isa = PBXBuildFile; fileRef = E5E7073A195E341500773FD3 /* cat4.jpg */; }; 50 | /* End PBXBuildFile section */ 51 | 52 | /* Begin PBXFileReference section */ 53 | 28ACC0C9181E580300454F4A /* CHTCollectionViewWaterfallFooter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CHTCollectionViewWaterfallFooter.h; sourceTree = ""; }; 54 | 28ACC0CA181E580300454F4A /* CHTCollectionViewWaterfallFooter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CHTCollectionViewWaterfallFooter.m; sourceTree = ""; }; 55 | 28FB2A14181580B0002B84F4 /* CHTCollectionViewWaterfallHeader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CHTCollectionViewWaterfallHeader.h; sourceTree = ""; }; 56 | 28FB2A15181580B1002B84F4 /* CHTCollectionViewWaterfallHeader.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CHTCollectionViewWaterfallHeader.m; sourceTree = ""; }; 57 | 526109C016646F4D005E4B6E /* Demo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Demo.app; sourceTree = BUILT_PRODUCTS_DIR; }; 58 | 526109C416646F4D005E4B6E /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; }; 59 | 526109C616646F4D005E4B6E /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; 60 | 526109C816646F4D005E4B6E /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; }; 61 | 526109CC16646F4D005E4B6E /* Demo-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "Demo-Info.plist"; sourceTree = ""; }; 62 | 526109CE16646F4D005E4B6E /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; 63 | 526109D016646F4D005E4B6E /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 64 | 526109D216646F4D005E4B6E /* Demo-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Demo-Prefix.pch"; sourceTree = ""; }; 65 | 526109D316646F4D005E4B6E /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 66 | 526109D416646F4D005E4B6E /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; 67 | 526109D616646F4D005E4B6E /* Default.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = Default.png; sourceTree = ""; }; 68 | 526109D816646F4D005E4B6E /* Default@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Default@2x.png"; sourceTree = ""; }; 69 | 526109DA16646F4D005E4B6E /* Default-568h@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Default-568h@2x.png"; sourceTree = ""; }; 70 | 526109DD16646F4D005E4B6E /* en */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = en; path = en.lproj/MainStoryboard_iPhone.storyboard; sourceTree = ""; }; 71 | 526109E216646F4D005E4B6E /* ViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = ""; }; 72 | 526109E316646F4D005E4B6E /* ViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = ""; }; 73 | 526109F11664740B005E4B6E /* CHTCollectionViewWaterfallCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CHTCollectionViewWaterfallCell.h; sourceTree = ""; }; 74 | 526109F21664740B005E4B6E /* CHTCollectionViewWaterfallCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CHTCollectionViewWaterfallCell.m; sourceTree = ""; }; 75 | 529599F52757C93E00621B8A /* Launch Screen.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = "Launch Screen.storyboard"; sourceTree = ""; }; 76 | 52FADF351CB64F5C0097FB12 /* CHTCollectionViewWaterfallLayout.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CHTCollectionViewWaterfallLayout.h; path = ../../Source/CHTCollectionViewWaterfallLayout.h; sourceTree = SOURCE_ROOT; }; 77 | 52FADF361CB64F5C0097FB12 /* CHTCollectionViewWaterfallLayout.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = CHTCollectionViewWaterfallLayout.m; path = ../../Source/CHTCollectionViewWaterfallLayout.m; sourceTree = SOURCE_ROOT; }; 78 | A561129E27595EF100D4F206 /* CHTCollectionViewWaterfallLayout */ = {isa = PBXFileReference; lastKnownFileType = folder; name = CHTCollectionViewWaterfallLayout; path = ../..; sourceTree = ""; }; 79 | A56112BA27595F0600D4F206 /* SPMDemo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = SPMDemo.app; sourceTree = BUILT_PRODUCTS_DIR; }; 80 | A56112BB27595F0600D4F206 /* SPMDemo-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "SPMDemo-Info.plist"; sourceTree = ""; }; 81 | E5E70737195E341500773FD3 /* cat1.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = cat1.jpg; sourceTree = ""; }; 82 | E5E70738195E341500773FD3 /* cat2.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = cat2.jpg; sourceTree = ""; }; 83 | E5E70739195E341500773FD3 /* cat3.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = cat3.jpg; sourceTree = ""; }; 84 | E5E7073A195E341500773FD3 /* cat4.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = cat4.jpg; sourceTree = ""; }; 85 | /* End PBXFileReference section */ 86 | 87 | /* Begin PBXFrameworksBuildPhase section */ 88 | 526109BD16646F4D005E4B6E /* Frameworks */ = { 89 | isa = PBXFrameworksBuildPhase; 90 | buildActionMask = 2147483647; 91 | files = ( 92 | 526109C516646F4D005E4B6E /* UIKit.framework in Frameworks */, 93 | 526109C716646F4D005E4B6E /* Foundation.framework in Frameworks */, 94 | 526109C916646F4D005E4B6E /* CoreGraphics.framework in Frameworks */, 95 | ); 96 | runOnlyForDeploymentPostprocessing = 0; 97 | }; 98 | A56112A827595F0600D4F206 /* Frameworks */ = { 99 | isa = PBXFrameworksBuildPhase; 100 | buildActionMask = 2147483647; 101 | files = ( 102 | A56112A927595F0600D4F206 /* UIKit.framework in Frameworks */, 103 | A56112AA27595F0600D4F206 /* Foundation.framework in Frameworks */, 104 | A56112C12759620E00D4F206 /* CHTCollectionViewWaterfallLayoutObjC in Frameworks */, 105 | A56112AB27595F0600D4F206 /* CoreGraphics.framework in Frameworks */, 106 | ); 107 | runOnlyForDeploymentPostprocessing = 0; 108 | }; 109 | /* End PBXFrameworksBuildPhase section */ 110 | 111 | /* Begin PBXGroup section */ 112 | 526109B516646F4D005E4B6E = { 113 | isa = PBXGroup; 114 | children = ( 115 | A561129E27595EF100D4F206 /* CHTCollectionViewWaterfallLayout */, 116 | 526109CA16646F4D005E4B6E /* Demo */, 117 | 526109C316646F4D005E4B6E /* Frameworks */, 118 | 526109C116646F4D005E4B6E /* Products */, 119 | ); 120 | sourceTree = ""; 121 | }; 122 | 526109C116646F4D005E4B6E /* Products */ = { 123 | isa = PBXGroup; 124 | children = ( 125 | 526109C016646F4D005E4B6E /* Demo.app */, 126 | A56112BA27595F0600D4F206 /* SPMDemo.app */, 127 | ); 128 | name = Products; 129 | sourceTree = ""; 130 | }; 131 | 526109C316646F4D005E4B6E /* Frameworks */ = { 132 | isa = PBXGroup; 133 | children = ( 134 | 526109C416646F4D005E4B6E /* UIKit.framework */, 135 | 526109C616646F4D005E4B6E /* Foundation.framework */, 136 | 526109C816646F4D005E4B6E /* CoreGraphics.framework */, 137 | ); 138 | name = Frameworks; 139 | sourceTree = ""; 140 | }; 141 | 526109CA16646F4D005E4B6E /* Demo */ = { 142 | isa = PBXGroup; 143 | children = ( 144 | 526109D316646F4D005E4B6E /* AppDelegate.h */, 145 | 526109D416646F4D005E4B6E /* AppDelegate.m */, 146 | 526109F11664740B005E4B6E /* CHTCollectionViewWaterfallCell.h */, 147 | 526109F21664740B005E4B6E /* CHTCollectionViewWaterfallCell.m */, 148 | 28FB2A14181580B0002B84F4 /* CHTCollectionViewWaterfallHeader.h */, 149 | 28FB2A15181580B1002B84F4 /* CHTCollectionViewWaterfallHeader.m */, 150 | 28ACC0C9181E580300454F4A /* CHTCollectionViewWaterfallFooter.h */, 151 | 28ACC0CA181E580300454F4A /* CHTCollectionViewWaterfallFooter.m */, 152 | 526109DC16646F4D005E4B6E /* MainStoryboard_iPhone.storyboard */, 153 | 526109E216646F4D005E4B6E /* ViewController.h */, 154 | 526109E316646F4D005E4B6E /* ViewController.m */, 155 | 52E121B91867F53600B9DC53 /* Vender */, 156 | 526109CB16646F4D005E4B6E /* Supporting Files */, 157 | ); 158 | path = Demo; 159 | sourceTree = ""; 160 | }; 161 | 526109CB16646F4D005E4B6E /* Supporting Files */ = { 162 | isa = PBXGroup; 163 | children = ( 164 | E5E70736195E341500773FD3 /* images */, 165 | 526109CC16646F4D005E4B6E /* Demo-Info.plist */, 166 | A56112BB27595F0600D4F206 /* SPMDemo-Info.plist */, 167 | 526109CD16646F4D005E4B6E /* InfoPlist.strings */, 168 | 526109D016646F4D005E4B6E /* main.m */, 169 | 526109D216646F4D005E4B6E /* Demo-Prefix.pch */, 170 | 526109D616646F4D005E4B6E /* Default.png */, 171 | 526109D816646F4D005E4B6E /* Default@2x.png */, 172 | 526109DA16646F4D005E4B6E /* Default-568h@2x.png */, 173 | 529599F52757C93E00621B8A /* Launch Screen.storyboard */, 174 | ); 175 | name = "Supporting Files"; 176 | sourceTree = ""; 177 | }; 178 | 52E121B91867F53600B9DC53 /* Vender */ = { 179 | isa = PBXGroup; 180 | children = ( 181 | 52FADF351CB64F5C0097FB12 /* CHTCollectionViewWaterfallLayout.h */, 182 | 52FADF361CB64F5C0097FB12 /* CHTCollectionViewWaterfallLayout.m */, 183 | ); 184 | name = Vender; 185 | sourceTree = ""; 186 | }; 187 | E5E70736195E341500773FD3 /* images */ = { 188 | isa = PBXGroup; 189 | children = ( 190 | E5E70737195E341500773FD3 /* cat1.jpg */, 191 | E5E70738195E341500773FD3 /* cat2.jpg */, 192 | E5E70739195E341500773FD3 /* cat3.jpg */, 193 | E5E7073A195E341500773FD3 /* cat4.jpg */, 194 | ); 195 | path = images; 196 | sourceTree = ""; 197 | }; 198 | /* End PBXGroup section */ 199 | 200 | /* Begin PBXNativeTarget section */ 201 | 526109BF16646F4D005E4B6E /* Demo */ = { 202 | isa = PBXNativeTarget; 203 | buildConfigurationList = 526109E716646F4D005E4B6E /* Build configuration list for PBXNativeTarget "Demo" */; 204 | buildPhases = ( 205 | 526109BC16646F4D005E4B6E /* Sources */, 206 | 526109BD16646F4D005E4B6E /* Frameworks */, 207 | 526109BE16646F4D005E4B6E /* Resources */, 208 | ); 209 | buildRules = ( 210 | ); 211 | dependencies = ( 212 | ); 213 | name = Demo; 214 | productName = Demo; 215 | productReference = 526109C016646F4D005E4B6E /* Demo.app */; 216 | productType = "com.apple.product-type.application"; 217 | }; 218 | A561129F27595F0600D4F206 /* SPMDemo */ = { 219 | isa = PBXNativeTarget; 220 | buildConfigurationList = A56112B727595F0600D4F206 /* Build configuration list for PBXNativeTarget "SPMDemo" */; 221 | buildPhases = ( 222 | A56112A027595F0600D4F206 /* Sources */, 223 | A56112A827595F0600D4F206 /* Frameworks */, 224 | A56112AC27595F0600D4F206 /* Resources */, 225 | ); 226 | buildRules = ( 227 | ); 228 | dependencies = ( 229 | ); 230 | name = SPMDemo; 231 | packageProductDependencies = ( 232 | A56112C02759620E00D4F206 /* CHTCollectionViewWaterfallLayoutObjC */, 233 | ); 234 | productName = Demo; 235 | productReference = A56112BA27595F0600D4F206 /* SPMDemo.app */; 236 | productType = "com.apple.product-type.application"; 237 | }; 238 | /* End PBXNativeTarget section */ 239 | 240 | /* Begin PBXProject section */ 241 | 526109B716646F4D005E4B6E /* Project object */ = { 242 | isa = PBXProject; 243 | attributes = { 244 | LastUpgradeCheck = 1250; 245 | ORGANIZATIONNAME = Nelson; 246 | }; 247 | buildConfigurationList = 526109BA16646F4D005E4B6E /* Build configuration list for PBXProject "Demo" */; 248 | compatibilityVersion = "Xcode 3.2"; 249 | developmentRegion = English; 250 | hasScannedForEncodings = 0; 251 | knownRegions = ( 252 | English, 253 | en, 254 | ); 255 | mainGroup = 526109B516646F4D005E4B6E; 256 | productRefGroup = 526109C116646F4D005E4B6E /* Products */; 257 | projectDirPath = ""; 258 | projectRoot = ""; 259 | targets = ( 260 | 526109BF16646F4D005E4B6E /* Demo */, 261 | A561129F27595F0600D4F206 /* SPMDemo */, 262 | ); 263 | }; 264 | /* End PBXProject section */ 265 | 266 | /* Begin PBXResourcesBuildPhase section */ 267 | 526109BE16646F4D005E4B6E /* Resources */ = { 268 | isa = PBXResourcesBuildPhase; 269 | buildActionMask = 2147483647; 270 | files = ( 271 | E5E7073C195E341500773FD3 /* cat2.jpg in Resources */, 272 | 526109CF16646F4D005E4B6E /* InfoPlist.strings in Resources */, 273 | E5E7073D195E341500773FD3 /* cat3.jpg in Resources */, 274 | 526109D716646F4D005E4B6E /* Default.png in Resources */, 275 | E5E7073E195E341500773FD3 /* cat4.jpg in Resources */, 276 | 526109D916646F4D005E4B6E /* Default@2x.png in Resources */, 277 | 526109DB16646F4D005E4B6E /* Default-568h@2x.png in Resources */, 278 | E5E7073B195E341500773FD3 /* cat1.jpg in Resources */, 279 | 529599F62757C93E00621B8A /* Launch Screen.storyboard in Resources */, 280 | 526109DE16646F4D005E4B6E /* MainStoryboard_iPhone.storyboard in Resources */, 281 | ); 282 | runOnlyForDeploymentPostprocessing = 0; 283 | }; 284 | A56112AC27595F0600D4F206 /* Resources */ = { 285 | isa = PBXResourcesBuildPhase; 286 | buildActionMask = 2147483647; 287 | files = ( 288 | A56112AD27595F0600D4F206 /* cat2.jpg in Resources */, 289 | A56112AE27595F0600D4F206 /* InfoPlist.strings in Resources */, 290 | A56112AF27595F0600D4F206 /* cat3.jpg in Resources */, 291 | A56112B027595F0600D4F206 /* Default.png in Resources */, 292 | A56112B127595F0600D4F206 /* cat4.jpg in Resources */, 293 | A56112B227595F0600D4F206 /* Default@2x.png in Resources */, 294 | A56112B327595F0600D4F206 /* Default-568h@2x.png in Resources */, 295 | A56112B427595F0600D4F206 /* cat1.jpg in Resources */, 296 | A56112B527595F0600D4F206 /* Launch Screen.storyboard in Resources */, 297 | A56112B627595F0600D4F206 /* MainStoryboard_iPhone.storyboard in Resources */, 298 | ); 299 | runOnlyForDeploymentPostprocessing = 0; 300 | }; 301 | /* End PBXResourcesBuildPhase section */ 302 | 303 | /* Begin PBXSourcesBuildPhase section */ 304 | 526109BC16646F4D005E4B6E /* Sources */ = { 305 | isa = PBXSourcesBuildPhase; 306 | buildActionMask = 2147483647; 307 | files = ( 308 | 526109D116646F4D005E4B6E /* main.m in Sources */, 309 | 526109D516646F4D005E4B6E /* AppDelegate.m in Sources */, 310 | 28ACC0CB181E580300454F4A /* CHTCollectionViewWaterfallFooter.m in Sources */, 311 | 52FADF371CB64F5C0097FB12 /* CHTCollectionViewWaterfallLayout.m in Sources */, 312 | 526109E416646F4D005E4B6E /* ViewController.m in Sources */, 313 | 28FB2A16181580B1002B84F4 /* CHTCollectionViewWaterfallHeader.m in Sources */, 314 | 526109F31664740B005E4B6E /* CHTCollectionViewWaterfallCell.m in Sources */, 315 | ); 316 | runOnlyForDeploymentPostprocessing = 0; 317 | }; 318 | A56112A027595F0600D4F206 /* Sources */ = { 319 | isa = PBXSourcesBuildPhase; 320 | buildActionMask = 2147483647; 321 | files = ( 322 | A56112A127595F0600D4F206 /* main.m in Sources */, 323 | A56112A227595F0600D4F206 /* AppDelegate.m in Sources */, 324 | A56112A327595F0600D4F206 /* CHTCollectionViewWaterfallFooter.m in Sources */, 325 | A56112A527595F0600D4F206 /* ViewController.m in Sources */, 326 | A56112A627595F0600D4F206 /* CHTCollectionViewWaterfallHeader.m in Sources */, 327 | A56112A727595F0600D4F206 /* CHTCollectionViewWaterfallCell.m in Sources */, 328 | ); 329 | runOnlyForDeploymentPostprocessing = 0; 330 | }; 331 | /* End PBXSourcesBuildPhase section */ 332 | 333 | /* Begin PBXVariantGroup section */ 334 | 526109CD16646F4D005E4B6E /* InfoPlist.strings */ = { 335 | isa = PBXVariantGroup; 336 | children = ( 337 | 526109CE16646F4D005E4B6E /* en */, 338 | ); 339 | name = InfoPlist.strings; 340 | sourceTree = ""; 341 | }; 342 | 526109DC16646F4D005E4B6E /* MainStoryboard_iPhone.storyboard */ = { 343 | isa = PBXVariantGroup; 344 | children = ( 345 | 526109DD16646F4D005E4B6E /* en */, 346 | ); 347 | name = MainStoryboard_iPhone.storyboard; 348 | sourceTree = ""; 349 | }; 350 | /* End PBXVariantGroup section */ 351 | 352 | /* Begin XCBuildConfiguration section */ 353 | 526109E516646F4D005E4B6E /* Debug */ = { 354 | isa = XCBuildConfiguration; 355 | buildSettings = { 356 | ALWAYS_SEARCH_USER_PATHS = NO; 357 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 358 | CLANG_CXX_LIBRARY = "libc++"; 359 | CLANG_ENABLE_OBJC_ARC = YES; 360 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 361 | CLANG_WARN_BOOL_CONVERSION = YES; 362 | CLANG_WARN_COMMA = YES; 363 | CLANG_WARN_CONSTANT_CONVERSION = YES; 364 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 365 | CLANG_WARN_EMPTY_BODY = YES; 366 | CLANG_WARN_ENUM_CONVERSION = YES; 367 | CLANG_WARN_INFINITE_RECURSION = YES; 368 | CLANG_WARN_INT_CONVERSION = YES; 369 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 370 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 371 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 372 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 373 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 374 | CLANG_WARN_STRICT_PROTOTYPES = YES; 375 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 376 | CLANG_WARN_UNREACHABLE_CODE = YES; 377 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 378 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 379 | COPY_PHASE_STRIP = NO; 380 | ENABLE_STRICT_OBJC_MSGSEND = YES; 381 | ENABLE_TESTABILITY = YES; 382 | GCC_C_LANGUAGE_STANDARD = gnu99; 383 | GCC_DYNAMIC_NO_PIC = NO; 384 | GCC_NO_COMMON_BLOCKS = YES; 385 | GCC_OPTIMIZATION_LEVEL = 0; 386 | GCC_PREPROCESSOR_DEFINITIONS = ( 387 | "DEBUG=1", 388 | "$(inherited)", 389 | ); 390 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 391 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 392 | GCC_WARN_ABOUT_RETURN_TYPE = YES; 393 | GCC_WARN_UNDECLARED_SELECTOR = YES; 394 | GCC_WARN_UNINITIALIZED_AUTOS = YES; 395 | GCC_WARN_UNUSED_FUNCTION = YES; 396 | GCC_WARN_UNUSED_VARIABLE = YES; 397 | IPHONEOS_DEPLOYMENT_TARGET = 9.0; 398 | ONLY_ACTIVE_ARCH = YES; 399 | SDKROOT = iphoneos; 400 | TARGETED_DEVICE_FAMILY = "1,2"; 401 | }; 402 | name = Debug; 403 | }; 404 | 526109E616646F4D005E4B6E /* Release */ = { 405 | isa = XCBuildConfiguration; 406 | buildSettings = { 407 | ALWAYS_SEARCH_USER_PATHS = NO; 408 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 409 | CLANG_CXX_LIBRARY = "libc++"; 410 | CLANG_ENABLE_OBJC_ARC = YES; 411 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 412 | CLANG_WARN_BOOL_CONVERSION = YES; 413 | CLANG_WARN_COMMA = YES; 414 | CLANG_WARN_CONSTANT_CONVERSION = YES; 415 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 416 | CLANG_WARN_EMPTY_BODY = YES; 417 | CLANG_WARN_ENUM_CONVERSION = YES; 418 | CLANG_WARN_INFINITE_RECURSION = YES; 419 | CLANG_WARN_INT_CONVERSION = YES; 420 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 421 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 422 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 423 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 424 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 425 | CLANG_WARN_STRICT_PROTOTYPES = YES; 426 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 427 | CLANG_WARN_UNREACHABLE_CODE = YES; 428 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 429 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 430 | COPY_PHASE_STRIP = YES; 431 | ENABLE_STRICT_OBJC_MSGSEND = YES; 432 | GCC_C_LANGUAGE_STANDARD = gnu99; 433 | GCC_NO_COMMON_BLOCKS = YES; 434 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 435 | GCC_WARN_ABOUT_RETURN_TYPE = YES; 436 | GCC_WARN_UNDECLARED_SELECTOR = YES; 437 | GCC_WARN_UNINITIALIZED_AUTOS = YES; 438 | GCC_WARN_UNUSED_FUNCTION = YES; 439 | GCC_WARN_UNUSED_VARIABLE = YES; 440 | IPHONEOS_DEPLOYMENT_TARGET = 9.0; 441 | OTHER_CFLAGS = "-DNS_BLOCK_ASSERTIONS=1"; 442 | SDKROOT = iphoneos; 443 | TARGETED_DEVICE_FAMILY = "1,2"; 444 | VALIDATE_PRODUCT = YES; 445 | }; 446 | name = Release; 447 | }; 448 | 526109E816646F4D005E4B6E /* Debug */ = { 449 | isa = XCBuildConfiguration; 450 | buildSettings = { 451 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 452 | GCC_PREFIX_HEADER = "Demo/Demo-Prefix.pch"; 453 | INFOPLIST_FILE = "Demo/Demo-Info.plist"; 454 | PRODUCT_BUNDLE_IDENTIFIER = "nelson.${PRODUCT_NAME:rfc1034identifier}"; 455 | PRODUCT_NAME = "$(TARGET_NAME)"; 456 | WRAPPER_EXTENSION = app; 457 | }; 458 | name = Debug; 459 | }; 460 | 526109E916646F4D005E4B6E /* Release */ = { 461 | isa = XCBuildConfiguration; 462 | buildSettings = { 463 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 464 | GCC_PREFIX_HEADER = "Demo/Demo-Prefix.pch"; 465 | INFOPLIST_FILE = "Demo/Demo-Info.plist"; 466 | PRODUCT_BUNDLE_IDENTIFIER = "nelson.${PRODUCT_NAME:rfc1034identifier}"; 467 | PRODUCT_NAME = "$(TARGET_NAME)"; 468 | WRAPPER_EXTENSION = app; 469 | }; 470 | name = Release; 471 | }; 472 | A56112B827595F0600D4F206 /* Debug */ = { 473 | isa = XCBuildConfiguration; 474 | buildSettings = { 475 | CLANG_ENABLE_MODULES = YES; 476 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 477 | GCC_PREFIX_HEADER = "Demo/Demo-Prefix.pch"; 478 | INFOPLIST_FILE = "Demo/SPMDemo-Info.plist"; 479 | PRODUCT_BUNDLE_IDENTIFIER = "nelson.${PRODUCT_NAME:rfc1034identifier}"; 480 | PRODUCT_NAME = "$(TARGET_NAME)"; 481 | WRAPPER_EXTENSION = app; 482 | }; 483 | name = Debug; 484 | }; 485 | A56112B927595F0600D4F206 /* Release */ = { 486 | isa = XCBuildConfiguration; 487 | buildSettings = { 488 | CLANG_ENABLE_MODULES = YES; 489 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 490 | GCC_PREFIX_HEADER = "Demo/Demo-Prefix.pch"; 491 | INFOPLIST_FILE = "Demo/SPMDemo-Info.plist"; 492 | PRODUCT_BUNDLE_IDENTIFIER = "nelson.${PRODUCT_NAME:rfc1034identifier}"; 493 | PRODUCT_NAME = "$(TARGET_NAME)"; 494 | WRAPPER_EXTENSION = app; 495 | }; 496 | name = Release; 497 | }; 498 | /* End XCBuildConfiguration section */ 499 | 500 | /* Begin XCConfigurationList section */ 501 | 526109BA16646F4D005E4B6E /* Build configuration list for PBXProject "Demo" */ = { 502 | isa = XCConfigurationList; 503 | buildConfigurations = ( 504 | 526109E516646F4D005E4B6E /* Debug */, 505 | 526109E616646F4D005E4B6E /* Release */, 506 | ); 507 | defaultConfigurationIsVisible = 0; 508 | defaultConfigurationName = Release; 509 | }; 510 | 526109E716646F4D005E4B6E /* Build configuration list for PBXNativeTarget "Demo" */ = { 511 | isa = XCConfigurationList; 512 | buildConfigurations = ( 513 | 526109E816646F4D005E4B6E /* Debug */, 514 | 526109E916646F4D005E4B6E /* Release */, 515 | ); 516 | defaultConfigurationIsVisible = 0; 517 | defaultConfigurationName = Release; 518 | }; 519 | A56112B727595F0600D4F206 /* Build configuration list for PBXNativeTarget "SPMDemo" */ = { 520 | isa = XCConfigurationList; 521 | buildConfigurations = ( 522 | A56112B827595F0600D4F206 /* Debug */, 523 | A56112B927595F0600D4F206 /* Release */, 524 | ); 525 | defaultConfigurationIsVisible = 0; 526 | defaultConfigurationName = Release; 527 | }; 528 | /* End XCConfigurationList section */ 529 | 530 | /* Begin XCSwiftPackageProductDependency section */ 531 | A56112C02759620E00D4F206 /* CHTCollectionViewWaterfallLayoutObjC */ = { 532 | isa = XCSwiftPackageProductDependency; 533 | productName = CHTCollectionViewWaterfallLayoutObjC; 534 | }; 535 | /* End XCSwiftPackageProductDependency section */ 536 | }; 537 | rootObject = 526109B716646F4D005E4B6E /* Project object */; 538 | } 539 | -------------------------------------------------------------------------------- /Demo/Objective-C/Demo.xcodeproj/xcshareddata/xcschemes/Demo.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 37 | 38 | 39 | 40 | 41 | 42 | 52 | 54 | 60 | 61 | 62 | 63 | 69 | 71 | 77 | 78 | 79 | 80 | 82 | 83 | 86 | 87 | 88 | -------------------------------------------------------------------------------- /Demo/Objective-C/Demo.xcodeproj/xcshareddata/xcschemes/SPMDemo.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 43 | 45 | 51 | 52 | 53 | 54 | 60 | 62 | 68 | 69 | 70 | 71 | 73 | 74 | 77 | 78 | 79 | -------------------------------------------------------------------------------- /Demo/Objective-C/Demo/AppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.h 3 | // Demo 4 | // 5 | // Created by Nelson on 12/11/27. 6 | // Copyright (c) 2012年 Nelson. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface AppDelegate : UIResponder 12 | 13 | @property (strong, nonatomic) UIWindow *window; 14 | 15 | @end 16 | -------------------------------------------------------------------------------- /Demo/Objective-C/Demo/AppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.m 3 | // Demo 4 | // 5 | // Created by Nelson on 12/11/27. 6 | // Copyright (c) 2012年 Nelson. All rights reserved. 7 | // 8 | 9 | #import "AppDelegate.h" 10 | 11 | @implementation AppDelegate 12 | 13 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions 14 | { 15 | // Override point for customization after application launch. 16 | return YES; 17 | } 18 | 19 | - (void)applicationWillResignActive:(UIApplication *)application 20 | { 21 | // 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. 22 | // 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. 23 | } 24 | 25 | - (void)applicationDidEnterBackground:(UIApplication *)application 26 | { 27 | // 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. 28 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 29 | } 30 | 31 | - (void)applicationWillEnterForeground:(UIApplication *)application 32 | { 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 | - (void)applicationDidBecomeActive:(UIApplication *)application 37 | { 38 | // 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. 39 | } 40 | 41 | - (void)applicationWillTerminate:(UIApplication *)application 42 | { 43 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 44 | } 45 | 46 | @end 47 | -------------------------------------------------------------------------------- /Demo/Objective-C/Demo/CHTCollectionViewWaterfallCell.h: -------------------------------------------------------------------------------- 1 | // 2 | // UICollectionViewWaterfallCell.h 3 | // Demo 4 | // 5 | // Created by Nelson on 12/11/27. 6 | // Copyright (c) 2012年 Nelson. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface CHTCollectionViewWaterfallCell : UICollectionViewCell 12 | @property (nonatomic, strong) UIImageView *imageView; 13 | @end 14 | -------------------------------------------------------------------------------- /Demo/Objective-C/Demo/CHTCollectionViewWaterfallCell.m: -------------------------------------------------------------------------------- 1 | // 2 | // UICollectionViewWaterfallCell.m 3 | // Demo 4 | // 5 | // Created by Nelson on 12/11/27. 6 | // Copyright (c) 2012年 Nelson. All rights reserved. 7 | // 8 | 9 | #import "CHTCollectionViewWaterfallCell.h" 10 | 11 | @implementation CHTCollectionViewWaterfallCell 12 | 13 | #pragma mark - Accessors 14 | - (UIImageView *)imageView { 15 | if (!_imageView) { 16 | _imageView = [[UIImageView alloc] initWithFrame:self.contentView.bounds]; 17 | _imageView.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth; 18 | _imageView.contentMode = UIViewContentModeScaleAspectFill; 19 | [_imageView.layer setMasksToBounds:YES]; 20 | } 21 | return _imageView; 22 | } 23 | 24 | - (id)initWithFrame:(CGRect)frame { 25 | if (self = [super initWithFrame:frame]) { 26 | [self.contentView addSubview:self.imageView]; 27 | } 28 | return self; 29 | } 30 | 31 | @end 32 | -------------------------------------------------------------------------------- /Demo/Objective-C/Demo/CHTCollectionViewWaterfallFooter.h: -------------------------------------------------------------------------------- 1 | // 2 | // CHTCollectionViewWaterfallFooter.h 3 | // Demo 4 | // 5 | // Created by Neil Kimmett on 28/10/2013. 6 | // Copyright (c) 2013 Nelson. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface CHTCollectionViewWaterfallFooter : UICollectionReusableView 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /Demo/Objective-C/Demo/CHTCollectionViewWaterfallFooter.m: -------------------------------------------------------------------------------- 1 | // 2 | // CHTCollectionViewWaterfallFooter.m 3 | // Demo 4 | // 5 | // Created by Neil Kimmett on 28/10/2013. 6 | // Copyright (c) 2013 Nelson. All rights reserved. 7 | // 8 | 9 | #import "CHTCollectionViewWaterfallFooter.h" 10 | 11 | @implementation CHTCollectionViewWaterfallFooter 12 | 13 | #pragma mark - Accessors 14 | - (id)initWithFrame:(CGRect)frame { 15 | if (self = [super initWithFrame:frame]) { 16 | self.backgroundColor = [UIColor blueColor]; 17 | } 18 | return self; 19 | } 20 | 21 | @end 22 | -------------------------------------------------------------------------------- /Demo/Objective-C/Demo/CHTCollectionViewWaterfallHeader.h: -------------------------------------------------------------------------------- 1 | // 2 | // CHTCollectionViewWaterfallHeader.h 3 | // Demo 4 | // 5 | // Created by Neil Kimmett on 21/10/2013. 6 | // Copyright (c) 2013 Nelson. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface CHTCollectionViewWaterfallHeader : UICollectionReusableView 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /Demo/Objective-C/Demo/CHTCollectionViewWaterfallHeader.m: -------------------------------------------------------------------------------- 1 | // 2 | // CHTCollectionViewWaterfallHeader.m 3 | // Demo 4 | // 5 | // Created by Neil Kimmett on 21/10/2013. 6 | // Copyright (c) 2013 Nelson. All rights reserved. 7 | // 8 | 9 | #import "CHTCollectionViewWaterfallHeader.h" 10 | 11 | @implementation CHTCollectionViewWaterfallHeader 12 | 13 | #pragma mark - Accessors 14 | - (id)initWithFrame:(CGRect)frame { 15 | if (self = [super initWithFrame:frame]) { 16 | self.backgroundColor = [UIColor redColor]; 17 | } 18 | return self; 19 | } 20 | 21 | @end 22 | -------------------------------------------------------------------------------- /Demo/Objective-C/Demo/Default-568h@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chiahsien/CHTCollectionViewWaterfallLayout/918ea98b054ed3e70edee4736edbad5c479b6c63/Demo/Objective-C/Demo/Default-568h@2x.png -------------------------------------------------------------------------------- /Demo/Objective-C/Demo/Default.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chiahsien/CHTCollectionViewWaterfallLayout/918ea98b054ed3e70edee4736edbad5c479b6c63/Demo/Objective-C/Demo/Default.png -------------------------------------------------------------------------------- /Demo/Objective-C/Demo/Default@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chiahsien/CHTCollectionViewWaterfallLayout/918ea98b054ed3e70edee4736edbad5c479b6c63/Demo/Objective-C/Demo/Default@2x.png -------------------------------------------------------------------------------- /Demo/Objective-C/Demo/Demo-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.0 25 | LSRequiresIPhoneOS 26 | 27 | UILaunchStoryboardName 28 | Launch Screen 29 | UIMainStoryboardFile 30 | MainStoryboard_iPhone 31 | UIMainStoryboardFile~ipad 32 | MainStoryboard_iPhone 33 | UIRequiredDeviceCapabilities 34 | 35 | armv7 36 | 37 | UISupportedInterfaceOrientations 38 | 39 | UIInterfaceOrientationPortrait 40 | UIInterfaceOrientationLandscapeLeft 41 | UIInterfaceOrientationLandscapeRight 42 | 43 | UISupportedInterfaceOrientations~ipad 44 | 45 | UIInterfaceOrientationPortrait 46 | UIInterfaceOrientationPortraitUpsideDown 47 | UIInterfaceOrientationLandscapeLeft 48 | UIInterfaceOrientationLandscapeRight 49 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /Demo/Objective-C/Demo/Demo-Prefix.pch: -------------------------------------------------------------------------------- 1 | // 2 | // Prefix header for all source files of the 'Demo' target in the 'Demo' project 3 | // 4 | 5 | #import 6 | 7 | #ifndef __IPHONE_5_0 8 | #warning "This project uses features only available in iOS SDK 5.0 and later." 9 | #endif 10 | 11 | #ifdef __OBJC__ 12 | #import 13 | #import 14 | #endif 15 | -------------------------------------------------------------------------------- /Demo/Objective-C/Demo/Launch Screen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /Demo/Objective-C/Demo/SPMDemo-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.0 25 | LSRequiresIPhoneOS 26 | 27 | UILaunchStoryboardName 28 | Launch Screen 29 | UIMainStoryboardFile 30 | MainStoryboard_iPhone 31 | UIMainStoryboardFile~ipad 32 | MainStoryboard_iPhone 33 | UIRequiredDeviceCapabilities 34 | 35 | armv7 36 | 37 | UISupportedInterfaceOrientations 38 | 39 | UIInterfaceOrientationPortrait 40 | UIInterfaceOrientationLandscapeLeft 41 | UIInterfaceOrientationLandscapeRight 42 | 43 | UISupportedInterfaceOrientations~ipad 44 | 45 | UIInterfaceOrientationPortrait 46 | UIInterfaceOrientationPortraitUpsideDown 47 | UIInterfaceOrientationLandscapeLeft 48 | UIInterfaceOrientationLandscapeRight 49 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /Demo/Objective-C/Demo/ViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.h 3 | // Demo 4 | // 5 | // Created by Nelson on 12/11/27. 6 | // Copyright (c) 2012年 Nelson. All rights reserved. 7 | // 8 | 9 | #import 10 | #if __has_feature(modules) 11 | @import CHTCollectionViewWaterfallLayoutObjC; 12 | #else 13 | #import "CHTCollectionViewWaterfallLayout.h" 14 | #endif 15 | 16 | @interface ViewController : UIViewController 17 | @property (nonatomic, strong) IBOutlet UICollectionView *collectionView; 18 | @end 19 | -------------------------------------------------------------------------------- /Demo/Objective-C/Demo/ViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.m 3 | // Demo 4 | // 5 | // Created by Nelson on 12/11/27. 6 | // Copyright (c) 2012年 Nelson. All rights reserved. 7 | // 8 | 9 | #import "ViewController.h" 10 | #import "CHTCollectionViewWaterfallCell.h" 11 | #import "CHTCollectionViewWaterfallHeader.h" 12 | #import "CHTCollectionViewWaterfallFooter.h" 13 | 14 | #define CELL_COUNT 30 15 | #define CELL_IDENTIFIER @"WaterfallCell" 16 | #define HEADER_IDENTIFIER @"WaterfallHeader" 17 | #define FOOTER_IDENTIFIER @"WaterfallFooter" 18 | 19 | @interface ViewController () 20 | @property (nonatomic, strong) NSArray *cellSizes; 21 | @property (nonatomic, strong) NSArray *cats; 22 | @end 23 | 24 | @implementation ViewController 25 | 26 | #pragma mark - Accessors 27 | 28 | - (UICollectionView *)collectionView { 29 | if (!_collectionView) { 30 | CHTCollectionViewWaterfallLayout *layout = [[CHTCollectionViewWaterfallLayout alloc] init]; 31 | 32 | layout.sectionInset = UIEdgeInsetsMake(10, 10, 10, 10); 33 | layout.headerHeight = 15; 34 | layout.footerHeight = 10; 35 | layout.minimumColumnSpacing = 20; 36 | layout.minimumInteritemSpacing = 30; 37 | 38 | _collectionView = [[UICollectionView alloc] initWithFrame:self.view.bounds collectionViewLayout:layout]; 39 | _collectionView.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth; 40 | _collectionView.dataSource = self; 41 | _collectionView.delegate = self; 42 | _collectionView.backgroundColor = [UIColor whiteColor]; 43 | [_collectionView registerClass:[CHTCollectionViewWaterfallCell class] 44 | forCellWithReuseIdentifier:CELL_IDENTIFIER]; 45 | [_collectionView registerClass:[CHTCollectionViewWaterfallHeader class] 46 | forSupplementaryViewOfKind:CHTCollectionElementKindSectionHeader 47 | withReuseIdentifier:HEADER_IDENTIFIER]; 48 | [_collectionView registerClass:[CHTCollectionViewWaterfallFooter class] 49 | forSupplementaryViewOfKind:CHTCollectionElementKindSectionFooter 50 | withReuseIdentifier:FOOTER_IDENTIFIER]; 51 | } 52 | return _collectionView; 53 | } 54 | 55 | - (NSArray *)cellSizes { 56 | if (!_cellSizes) { 57 | _cellSizes = @[ 58 | [NSValue valueWithCGSize:CGSizeMake(550, 550)], 59 | [NSValue valueWithCGSize:CGSizeMake(1000, 665)], 60 | [NSValue valueWithCGSize:CGSizeMake(1024, 689)], 61 | [NSValue valueWithCGSize:CGSizeMake(640, 427)] 62 | ]; 63 | } 64 | return _cellSizes; 65 | } 66 | 67 | - (NSArray *)cats { 68 | if (!_cats) { 69 | _cats = @[@"cat1.jpg", @"cat2.jpg", @"cat3.jpg", @"cat4.jpg"]; 70 | } 71 | return _cats; 72 | } 73 | 74 | #pragma mark - Life Cycle 75 | 76 | - (void)dealloc { 77 | _collectionView.delegate = nil; 78 | _collectionView.dataSource = nil; 79 | } 80 | 81 | - (void)viewDidLoad { 82 | [super viewDidLoad]; 83 | [self.view addSubview:self.collectionView]; 84 | } 85 | 86 | - (void)viewDidAppear:(BOOL)animated { 87 | [super viewDidAppear:animated]; 88 | [self updateLayoutForOrientation:[UIApplication sharedApplication].statusBarOrientation]; 89 | } 90 | 91 | - (void)willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration { 92 | [super willAnimateRotationToInterfaceOrientation:toInterfaceOrientation duration:duration]; 93 | [self updateLayoutForOrientation:toInterfaceOrientation]; 94 | } 95 | 96 | - (void)updateLayoutForOrientation:(UIInterfaceOrientation)orientation { 97 | CHTCollectionViewWaterfallLayout *layout = 98 | (CHTCollectionViewWaterfallLayout *)self.collectionView.collectionViewLayout; 99 | layout.columnCount = UIInterfaceOrientationIsPortrait(orientation) ? 2 : 3; 100 | } 101 | 102 | #pragma mark - UICollectionViewDataSource 103 | 104 | - (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section { 105 | return CELL_COUNT; 106 | } 107 | 108 | - (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView { 109 | return 2; 110 | } 111 | 112 | - (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath { 113 | CHTCollectionViewWaterfallCell *cell = 114 | (CHTCollectionViewWaterfallCell *)[collectionView dequeueReusableCellWithReuseIdentifier:CELL_IDENTIFIER 115 | forIndexPath:indexPath]; 116 | cell.imageView.image = [UIImage imageNamed:self.cats[indexPath.item % 4]]; 117 | return cell; 118 | } 119 | 120 | - (UICollectionReusableView *)collectionView:(UICollectionView *)collectionView viewForSupplementaryElementOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath { 121 | UICollectionReusableView *reusableView = nil; 122 | 123 | if ([kind isEqualToString:CHTCollectionElementKindSectionHeader]) { 124 | reusableView = [collectionView dequeueReusableSupplementaryViewOfKind:kind 125 | withReuseIdentifier:HEADER_IDENTIFIER 126 | forIndexPath:indexPath]; 127 | } else if ([kind isEqualToString:CHTCollectionElementKindSectionFooter]) { 128 | reusableView = [collectionView dequeueReusableSupplementaryViewOfKind:kind 129 | withReuseIdentifier:FOOTER_IDENTIFIER 130 | forIndexPath:indexPath]; 131 | } 132 | 133 | return reusableView; 134 | } 135 | 136 | #pragma mark - CHTCollectionViewDelegateWaterfallLayout 137 | - (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath { 138 | return [self.cellSizes[indexPath.item % 4] CGSizeValue]; 139 | } 140 | 141 | @end 142 | -------------------------------------------------------------------------------- /Demo/Objective-C/Demo/en.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* Localized versions of Info.plist keys */ 2 | 3 | -------------------------------------------------------------------------------- /Demo/Objective-C/Demo/en.lproj/MainStoryboard_iPhone.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 | -------------------------------------------------------------------------------- /Demo/Objective-C/Demo/images/cat1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chiahsien/CHTCollectionViewWaterfallLayout/918ea98b054ed3e70edee4736edbad5c479b6c63/Demo/Objective-C/Demo/images/cat1.jpg -------------------------------------------------------------------------------- /Demo/Objective-C/Demo/images/cat2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chiahsien/CHTCollectionViewWaterfallLayout/918ea98b054ed3e70edee4736edbad5c479b6c63/Demo/Objective-C/Demo/images/cat2.jpg -------------------------------------------------------------------------------- /Demo/Objective-C/Demo/images/cat3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chiahsien/CHTCollectionViewWaterfallLayout/918ea98b054ed3e70edee4736edbad5c479b6c63/Demo/Objective-C/Demo/images/cat3.jpg -------------------------------------------------------------------------------- /Demo/Objective-C/Demo/images/cat4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chiahsien/CHTCollectionViewWaterfallLayout/918ea98b054ed3e70edee4736edbad5c479b6c63/Demo/Objective-C/Demo/images/cat4.jpg -------------------------------------------------------------------------------- /Demo/Objective-C/Demo/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // Demo 4 | // 5 | // Created by Nelson on 12/11/27. 6 | // Copyright (c) 2012年 Nelson. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | #import "AppDelegate.h" 12 | 13 | int main(int argc, char *argv[]) 14 | { 15 | @autoreleasepool { 16 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Demo/Swift/CHTWaterfallSwiftDemo.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 52; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 52FADF3D1CB650DC0097FB12 /* CHTCollectionViewWaterfallLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = 52FADF3C1CB650DC0097FB12 /* CHTCollectionViewWaterfallLayout.swift */; }; 11 | 62C26FDF1ABE01840027F8D4 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62C26FDE1ABE01840027F8D4 /* AppDelegate.swift */; }; 12 | 62C26FE61ABE01840027F8D4 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 62C26FE51ABE01840027F8D4 /* Images.xcassets */; }; 13 | 62C26FE91ABE01840027F8D4 /* LaunchScreen.xib in Resources */ = {isa = PBXBuildFile; fileRef = 62C26FE71ABE01840027F8D4 /* LaunchScreen.xib */; }; 14 | 62C26FFF1ABE01B20027F8D4 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 62C26FFE1ABE01B20027F8D4 /* Main.storyboard */; }; 15 | 62C270011ABE01BA0027F8D4 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62C270001ABE01BA0027F8D4 /* ViewController.swift */; }; 16 | 62C270031ABE01D70027F8D4 /* Model.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62C270021ABE01D70027F8D4 /* Model.swift */; }; 17 | 62C270061ABE01E70027F8D4 /* ImageUICollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62C270041ABE01E70027F8D4 /* ImageUICollectionViewCell.swift */; }; 18 | 62C270071ABE01E70027F8D4 /* ImageUICollectionViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 62C270051ABE01E70027F8D4 /* ImageUICollectionViewCell.xib */; }; 19 | A561128927595AE500D4F206 /* Model.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62C270021ABE01D70027F8D4 /* Model.swift */; }; 20 | A561128A27595AE500D4F206 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62C270001ABE01BA0027F8D4 /* ViewController.swift */; }; 21 | A561128B27595AE500D4F206 /* ImageUICollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62C270041ABE01E70027F8D4 /* ImageUICollectionViewCell.swift */; }; 22 | A561128C27595AE500D4F206 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62C26FDE1ABE01840027F8D4 /* AppDelegate.swift */; }; 23 | A561129027595AE500D4F206 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 62C26FFE1ABE01B20027F8D4 /* Main.storyboard */; }; 24 | A561129127595AE500D4F206 /* LaunchScreen.xib in Resources */ = {isa = PBXBuildFile; fileRef = 62C26FE71ABE01840027F8D4 /* LaunchScreen.xib */; }; 25 | A561129227595AE500D4F206 /* ImageUICollectionViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 62C270051ABE01E70027F8D4 /* ImageUICollectionViewCell.xib */; }; 26 | A561129327595AE500D4F206 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 62C26FE51ABE01840027F8D4 /* Images.xcassets */; }; 27 | A561129B27595BAC00D4F206 /* CHTCollectionViewWaterfallLayout in Frameworks */ = {isa = PBXBuildFile; productRef = A561129A27595BAC00D4F206 /* CHTCollectionViewWaterfallLayout */; }; 28 | /* End PBXBuildFile section */ 29 | 30 | /* Begin PBXFileReference section */ 31 | 52FADF3C1CB650DC0097FB12 /* CHTCollectionViewWaterfallLayout.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = CHTCollectionViewWaterfallLayout.swift; path = ../../Source/CHTCollectionViewWaterfallLayout.swift; sourceTree = SOURCE_ROOT; }; 32 | 62C26FD91ABE01840027F8D4 /* CHTWaterfallSwiftDemo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = CHTWaterfallSwiftDemo.app; sourceTree = BUILT_PRODUCTS_DIR; }; 33 | 62C26FDD1ABE01840027F8D4 /* CHTWaterfallSwiftDemo-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "CHTWaterfallSwiftDemo-Info.plist"; sourceTree = ""; }; 34 | 62C26FDE1ABE01840027F8D4 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 35 | 62C26FE51ABE01840027F8D4 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; 36 | 62C26FE81ABE01840027F8D4 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/LaunchScreen.xib; sourceTree = ""; }; 37 | 62C26FFE1ABE01B20027F8D4 /* Main.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = Main.storyboard; sourceTree = ""; }; 38 | 62C270001ABE01BA0027F8D4 /* ViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; 39 | 62C270021ABE01D70027F8D4 /* Model.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Model.swift; sourceTree = ""; }; 40 | 62C270041ABE01E70027F8D4 /* ImageUICollectionViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ImageUICollectionViewCell.swift; sourceTree = ""; }; 41 | 62C270051ABE01E70027F8D4 /* ImageUICollectionViewCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = ImageUICollectionViewCell.xib; sourceTree = ""; }; 42 | A561128627595AB200D4F206 /* CHTCollectionViewWaterfallLayout */ = {isa = PBXFileReference; lastKnownFileType = folder; name = CHTCollectionViewWaterfallLayout; path = ../..; sourceTree = ""; }; 43 | A561129727595AE500D4F206 /* CHTWaterfallSwiftSPMDemo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = CHTWaterfallSwiftSPMDemo.app; sourceTree = BUILT_PRODUCTS_DIR; }; 44 | A561129827595AE600D4F206 /* CHTWaterfallSwiftSPMDemo-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "CHTWaterfallSwiftSPMDemo-Info.plist"; sourceTree = ""; }; 45 | /* End PBXFileReference section */ 46 | 47 | /* Begin PBXFrameworksBuildPhase section */ 48 | 62C26FD61ABE01840027F8D4 /* Frameworks */ = { 49 | isa = PBXFrameworksBuildPhase; 50 | buildActionMask = 2147483647; 51 | files = ( 52 | ); 53 | runOnlyForDeploymentPostprocessing = 0; 54 | }; 55 | A561128E27595AE500D4F206 /* Frameworks */ = { 56 | isa = PBXFrameworksBuildPhase; 57 | buildActionMask = 2147483647; 58 | files = ( 59 | A561129B27595BAC00D4F206 /* CHTCollectionViewWaterfallLayout in Frameworks */, 60 | ); 61 | runOnlyForDeploymentPostprocessing = 0; 62 | }; 63 | /* End PBXFrameworksBuildPhase section */ 64 | 65 | /* Begin PBXGroup section */ 66 | 52FADF381CB64FA20097FB12 /* Vender */ = { 67 | isa = PBXGroup; 68 | children = ( 69 | 52FADF3C1CB650DC0097FB12 /* CHTCollectionViewWaterfallLayout.swift */, 70 | ); 71 | name = Vender; 72 | sourceTree = ""; 73 | }; 74 | 62C26FD01ABE01840027F8D4 = { 75 | isa = PBXGroup; 76 | children = ( 77 | A561128627595AB200D4F206 /* CHTCollectionViewWaterfallLayout */, 78 | 62C26FDB1ABE01840027F8D4 /* CHTWaterfallSwiftDemo */, 79 | 62C26FDA1ABE01840027F8D4 /* Products */, 80 | A561129927595BAC00D4F206 /* Frameworks */, 81 | ); 82 | sourceTree = ""; 83 | }; 84 | 62C26FDA1ABE01840027F8D4 /* Products */ = { 85 | isa = PBXGroup; 86 | children = ( 87 | 62C26FD91ABE01840027F8D4 /* CHTWaterfallSwiftDemo.app */, 88 | A561129727595AE500D4F206 /* CHTWaterfallSwiftSPMDemo.app */, 89 | ); 90 | name = Products; 91 | sourceTree = ""; 92 | }; 93 | 62C26FDB1ABE01840027F8D4 /* CHTWaterfallSwiftDemo */ = { 94 | isa = PBXGroup; 95 | children = ( 96 | 52FADF381CB64FA20097FB12 /* Vender */, 97 | 62C26FFE1ABE01B20027F8D4 /* Main.storyboard */, 98 | 62C26FDE1ABE01840027F8D4 /* AppDelegate.swift */, 99 | 62C270001ABE01BA0027F8D4 /* ViewController.swift */, 100 | 62C270041ABE01E70027F8D4 /* ImageUICollectionViewCell.swift */, 101 | 62C270051ABE01E70027F8D4 /* ImageUICollectionViewCell.xib */, 102 | 62C270021ABE01D70027F8D4 /* Model.swift */, 103 | 62C26FE71ABE01840027F8D4 /* LaunchScreen.xib */, 104 | 62C26FE51ABE01840027F8D4 /* Images.xcassets */, 105 | 62C26FDC1ABE01840027F8D4 /* Supporting Files */, 106 | ); 107 | path = CHTWaterfallSwiftDemo; 108 | sourceTree = ""; 109 | }; 110 | 62C26FDC1ABE01840027F8D4 /* Supporting Files */ = { 111 | isa = PBXGroup; 112 | children = ( 113 | 62C26FDD1ABE01840027F8D4 /* CHTWaterfallSwiftDemo-Info.plist */, 114 | A561129827595AE600D4F206 /* CHTWaterfallSwiftSPMDemo-Info.plist */, 115 | ); 116 | name = "Supporting Files"; 117 | sourceTree = ""; 118 | }; 119 | A561129927595BAC00D4F206 /* Frameworks */ = { 120 | isa = PBXGroup; 121 | children = ( 122 | ); 123 | name = Frameworks; 124 | sourceTree = ""; 125 | }; 126 | /* End PBXGroup section */ 127 | 128 | /* Begin PBXNativeTarget section */ 129 | 62C26FD81ABE01840027F8D4 /* CHTWaterfallSwiftDemo */ = { 130 | isa = PBXNativeTarget; 131 | buildConfigurationList = 62C26FF81ABE01850027F8D4 /* Build configuration list for PBXNativeTarget "CHTWaterfallSwiftDemo" */; 132 | buildPhases = ( 133 | 62C26FD51ABE01840027F8D4 /* Sources */, 134 | 62C26FD61ABE01840027F8D4 /* Frameworks */, 135 | 62C26FD71ABE01840027F8D4 /* Resources */, 136 | ); 137 | buildRules = ( 138 | ); 139 | dependencies = ( 140 | ); 141 | name = CHTWaterfallSwiftDemo; 142 | productName = CHTWaterfallSwiftDemo; 143 | productReference = 62C26FD91ABE01840027F8D4 /* CHTWaterfallSwiftDemo.app */; 144 | productType = "com.apple.product-type.application"; 145 | }; 146 | A561128727595AE500D4F206 /* CHTWaterfallSwiftSPMDemo */ = { 147 | isa = PBXNativeTarget; 148 | buildConfigurationList = A561129427595AE500D4F206 /* Build configuration list for PBXNativeTarget "CHTWaterfallSwiftSPMDemo" */; 149 | buildPhases = ( 150 | A561128827595AE500D4F206 /* Sources */, 151 | A561128E27595AE500D4F206 /* Frameworks */, 152 | A561128F27595AE500D4F206 /* Resources */, 153 | ); 154 | buildRules = ( 155 | ); 156 | dependencies = ( 157 | ); 158 | name = CHTWaterfallSwiftSPMDemo; 159 | packageProductDependencies = ( 160 | A561129A27595BAC00D4F206 /* CHTCollectionViewWaterfallLayout */, 161 | ); 162 | productName = CHTWaterfallSwiftDemo; 163 | productReference = A561129727595AE500D4F206 /* CHTWaterfallSwiftSPMDemo.app */; 164 | productType = "com.apple.product-type.application"; 165 | }; 166 | /* End PBXNativeTarget section */ 167 | 168 | /* Begin PBXProject section */ 169 | 62C26FD11ABE01840027F8D4 /* Project object */ = { 170 | isa = PBXProject; 171 | attributes = { 172 | LastSwiftMigration = 0730; 173 | LastSwiftUpdateCheck = 0730; 174 | LastUpgradeCheck = 1250; 175 | ORGANIZATIONNAME = "Sophie Fader"; 176 | TargetAttributes = { 177 | 62C26FD81ABE01840027F8D4 = { 178 | CreatedOnToolsVersion = 6.1.1; 179 | DevelopmentTeam = KZ7D8X3PRK; 180 | }; 181 | }; 182 | }; 183 | buildConfigurationList = 62C26FD41ABE01840027F8D4 /* Build configuration list for PBXProject "CHTWaterfallSwiftDemo" */; 184 | compatibilityVersion = "Xcode 3.2"; 185 | developmentRegion = en; 186 | hasScannedForEncodings = 0; 187 | knownRegions = ( 188 | en, 189 | Base, 190 | ); 191 | mainGroup = 62C26FD01ABE01840027F8D4; 192 | productRefGroup = 62C26FDA1ABE01840027F8D4 /* Products */; 193 | projectDirPath = ""; 194 | projectRoot = ""; 195 | targets = ( 196 | 62C26FD81ABE01840027F8D4 /* CHTWaterfallSwiftDemo */, 197 | A561128727595AE500D4F206 /* CHTWaterfallSwiftSPMDemo */, 198 | ); 199 | }; 200 | /* End PBXProject section */ 201 | 202 | /* Begin PBXResourcesBuildPhase section */ 203 | 62C26FD71ABE01840027F8D4 /* Resources */ = { 204 | isa = PBXResourcesBuildPhase; 205 | buildActionMask = 2147483647; 206 | files = ( 207 | 62C26FFF1ABE01B20027F8D4 /* Main.storyboard in Resources */, 208 | 62C26FE91ABE01840027F8D4 /* LaunchScreen.xib in Resources */, 209 | 62C270071ABE01E70027F8D4 /* ImageUICollectionViewCell.xib in Resources */, 210 | 62C26FE61ABE01840027F8D4 /* Images.xcassets in Resources */, 211 | ); 212 | runOnlyForDeploymentPostprocessing = 0; 213 | }; 214 | A561128F27595AE500D4F206 /* Resources */ = { 215 | isa = PBXResourcesBuildPhase; 216 | buildActionMask = 2147483647; 217 | files = ( 218 | A561129027595AE500D4F206 /* Main.storyboard in Resources */, 219 | A561129127595AE500D4F206 /* LaunchScreen.xib in Resources */, 220 | A561129227595AE500D4F206 /* ImageUICollectionViewCell.xib in Resources */, 221 | A561129327595AE500D4F206 /* Images.xcassets in Resources */, 222 | ); 223 | runOnlyForDeploymentPostprocessing = 0; 224 | }; 225 | /* End PBXResourcesBuildPhase section */ 226 | 227 | /* Begin PBXSourcesBuildPhase section */ 228 | 62C26FD51ABE01840027F8D4 /* Sources */ = { 229 | isa = PBXSourcesBuildPhase; 230 | buildActionMask = 2147483647; 231 | files = ( 232 | 62C270031ABE01D70027F8D4 /* Model.swift in Sources */, 233 | 62C270011ABE01BA0027F8D4 /* ViewController.swift in Sources */, 234 | 62C270061ABE01E70027F8D4 /* ImageUICollectionViewCell.swift in Sources */, 235 | 62C26FDF1ABE01840027F8D4 /* AppDelegate.swift in Sources */, 236 | 52FADF3D1CB650DC0097FB12 /* CHTCollectionViewWaterfallLayout.swift in Sources */, 237 | ); 238 | runOnlyForDeploymentPostprocessing = 0; 239 | }; 240 | A561128827595AE500D4F206 /* Sources */ = { 241 | isa = PBXSourcesBuildPhase; 242 | buildActionMask = 2147483647; 243 | files = ( 244 | A561128927595AE500D4F206 /* Model.swift in Sources */, 245 | A561128A27595AE500D4F206 /* ViewController.swift in Sources */, 246 | A561128B27595AE500D4F206 /* ImageUICollectionViewCell.swift in Sources */, 247 | A561128C27595AE500D4F206 /* AppDelegate.swift in Sources */, 248 | ); 249 | runOnlyForDeploymentPostprocessing = 0; 250 | }; 251 | /* End PBXSourcesBuildPhase section */ 252 | 253 | /* Begin PBXVariantGroup section */ 254 | 62C26FE71ABE01840027F8D4 /* LaunchScreen.xib */ = { 255 | isa = PBXVariantGroup; 256 | children = ( 257 | 62C26FE81ABE01840027F8D4 /* Base */, 258 | ); 259 | name = LaunchScreen.xib; 260 | sourceTree = ""; 261 | }; 262 | /* End PBXVariantGroup section */ 263 | 264 | /* Begin XCBuildConfiguration section */ 265 | 62C26FF61ABE01850027F8D4 /* Debug */ = { 266 | isa = XCBuildConfiguration; 267 | buildSettings = { 268 | ALWAYS_SEARCH_USER_PATHS = NO; 269 | CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; 270 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 271 | CLANG_CXX_LIBRARY = "libc++"; 272 | CLANG_ENABLE_MODULES = YES; 273 | CLANG_ENABLE_OBJC_ARC = YES; 274 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 275 | CLANG_WARN_BOOL_CONVERSION = YES; 276 | CLANG_WARN_COMMA = YES; 277 | CLANG_WARN_CONSTANT_CONVERSION = YES; 278 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 279 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 280 | CLANG_WARN_EMPTY_BODY = YES; 281 | CLANG_WARN_ENUM_CONVERSION = YES; 282 | CLANG_WARN_INFINITE_RECURSION = YES; 283 | CLANG_WARN_INT_CONVERSION = YES; 284 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 285 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 286 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 287 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 288 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 289 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 290 | CLANG_WARN_STRICT_PROTOTYPES = YES; 291 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 292 | CLANG_WARN_UNREACHABLE_CODE = YES; 293 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 294 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 295 | COPY_PHASE_STRIP = NO; 296 | ENABLE_STRICT_OBJC_MSGSEND = YES; 297 | ENABLE_TESTABILITY = YES; 298 | GCC_C_LANGUAGE_STANDARD = gnu99; 299 | GCC_DYNAMIC_NO_PIC = NO; 300 | GCC_NO_COMMON_BLOCKS = YES; 301 | GCC_OPTIMIZATION_LEVEL = 0; 302 | GCC_PREPROCESSOR_DEFINITIONS = ( 303 | "DEBUG=1", 304 | "$(inherited)", 305 | ); 306 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 307 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 308 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 309 | GCC_WARN_UNDECLARED_SELECTOR = YES; 310 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 311 | GCC_WARN_UNUSED_FUNCTION = YES; 312 | GCC_WARN_UNUSED_VARIABLE = YES; 313 | IPHONEOS_DEPLOYMENT_TARGET = 9.0; 314 | MTL_ENABLE_DEBUG_INFO = YES; 315 | ONLY_ACTIVE_ARCH = YES; 316 | SDKROOT = iphoneos; 317 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 318 | TARGETED_DEVICE_FAMILY = "1,2"; 319 | }; 320 | name = Debug; 321 | }; 322 | 62C26FF71ABE01850027F8D4 /* Release */ = { 323 | isa = XCBuildConfiguration; 324 | buildSettings = { 325 | ALWAYS_SEARCH_USER_PATHS = NO; 326 | CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; 327 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 328 | CLANG_CXX_LIBRARY = "libc++"; 329 | CLANG_ENABLE_MODULES = YES; 330 | CLANG_ENABLE_OBJC_ARC = YES; 331 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 332 | CLANG_WARN_BOOL_CONVERSION = YES; 333 | CLANG_WARN_COMMA = YES; 334 | CLANG_WARN_CONSTANT_CONVERSION = YES; 335 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 336 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 337 | CLANG_WARN_EMPTY_BODY = YES; 338 | CLANG_WARN_ENUM_CONVERSION = YES; 339 | CLANG_WARN_INFINITE_RECURSION = YES; 340 | CLANG_WARN_INT_CONVERSION = YES; 341 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 342 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 343 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 344 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 345 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 346 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 347 | CLANG_WARN_STRICT_PROTOTYPES = YES; 348 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 349 | CLANG_WARN_UNREACHABLE_CODE = YES; 350 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 351 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 352 | COPY_PHASE_STRIP = YES; 353 | ENABLE_NS_ASSERTIONS = NO; 354 | ENABLE_STRICT_OBJC_MSGSEND = YES; 355 | GCC_C_LANGUAGE_STANDARD = gnu99; 356 | GCC_NO_COMMON_BLOCKS = YES; 357 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 358 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 359 | GCC_WARN_UNDECLARED_SELECTOR = YES; 360 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 361 | GCC_WARN_UNUSED_FUNCTION = YES; 362 | GCC_WARN_UNUSED_VARIABLE = YES; 363 | IPHONEOS_DEPLOYMENT_TARGET = 9.0; 364 | MTL_ENABLE_DEBUG_INFO = NO; 365 | SDKROOT = iphoneos; 366 | SWIFT_COMPILATION_MODE = wholemodule; 367 | TARGETED_DEVICE_FAMILY = "1,2"; 368 | VALIDATE_PRODUCT = YES; 369 | }; 370 | name = Release; 371 | }; 372 | 62C26FF91ABE01850027F8D4 /* Debug */ = { 373 | isa = XCBuildConfiguration; 374 | buildSettings = { 375 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 376 | CLANG_ENABLE_MODULES = YES; 377 | INFOPLIST_FILE = "CHTWaterfallSwiftDemo/CHTWaterfallSwiftDemo-Info.plist"; 378 | LD_RUNPATH_SEARCH_PATHS = ( 379 | "$(inherited)", 380 | "@executable_path/Frameworks", 381 | ); 382 | PRODUCT_BUNDLE_IDENTIFIER = "com.sophiefader.$(PRODUCT_NAME:rfc1034identifier)"; 383 | PRODUCT_NAME = "$(TARGET_NAME)"; 384 | SWIFT_VERSION = 5.0; 385 | }; 386 | name = Debug; 387 | }; 388 | 62C26FFA1ABE01850027F8D4 /* Release */ = { 389 | isa = XCBuildConfiguration; 390 | buildSettings = { 391 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 392 | CLANG_ENABLE_MODULES = YES; 393 | INFOPLIST_FILE = "CHTWaterfallSwiftDemo/CHTWaterfallSwiftDemo-Info.plist"; 394 | LD_RUNPATH_SEARCH_PATHS = ( 395 | "$(inherited)", 396 | "@executable_path/Frameworks", 397 | ); 398 | PRODUCT_BUNDLE_IDENTIFIER = "com.sophiefader.$(PRODUCT_NAME:rfc1034identifier)"; 399 | PRODUCT_NAME = "$(TARGET_NAME)"; 400 | SWIFT_VERSION = 5.0; 401 | }; 402 | name = Release; 403 | }; 404 | A561129527595AE500D4F206 /* Debug */ = { 405 | isa = XCBuildConfiguration; 406 | buildSettings = { 407 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 408 | CLANG_ENABLE_MODULES = YES; 409 | INFOPLIST_FILE = "CHTWaterfallSwiftDemo/CHTWaterfallSwiftSPMDemo-Info.plist"; 410 | LD_RUNPATH_SEARCH_PATHS = ( 411 | "$(inherited)", 412 | "@executable_path/Frameworks", 413 | ); 414 | PRODUCT_BUNDLE_IDENTIFIER = "com.sophiefader.$(PRODUCT_NAME:rfc1034identifier)"; 415 | PRODUCT_NAME = "$(TARGET_NAME)"; 416 | SWIFT_VERSION = 5.0; 417 | }; 418 | name = Debug; 419 | }; 420 | A561129627595AE500D4F206 /* Release */ = { 421 | isa = XCBuildConfiguration; 422 | buildSettings = { 423 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 424 | CLANG_ENABLE_MODULES = YES; 425 | INFOPLIST_FILE = "CHTWaterfallSwiftDemo/CHTWaterfallSwiftSPMDemo-Info.plist"; 426 | LD_RUNPATH_SEARCH_PATHS = ( 427 | "$(inherited)", 428 | "@executable_path/Frameworks", 429 | ); 430 | PRODUCT_BUNDLE_IDENTIFIER = com.sophiefader.CHTWaterfallSwiftSPMDemo; 431 | PRODUCT_NAME = "$(TARGET_NAME)"; 432 | SWIFT_VERSION = 5.0; 433 | }; 434 | name = Release; 435 | }; 436 | /* End XCBuildConfiguration section */ 437 | 438 | /* Begin XCConfigurationList section */ 439 | 62C26FD41ABE01840027F8D4 /* Build configuration list for PBXProject "CHTWaterfallSwiftDemo" */ = { 440 | isa = XCConfigurationList; 441 | buildConfigurations = ( 442 | 62C26FF61ABE01850027F8D4 /* Debug */, 443 | 62C26FF71ABE01850027F8D4 /* Release */, 444 | ); 445 | defaultConfigurationIsVisible = 0; 446 | defaultConfigurationName = Release; 447 | }; 448 | 62C26FF81ABE01850027F8D4 /* Build configuration list for PBXNativeTarget "CHTWaterfallSwiftDemo" */ = { 449 | isa = XCConfigurationList; 450 | buildConfigurations = ( 451 | 62C26FF91ABE01850027F8D4 /* Debug */, 452 | 62C26FFA1ABE01850027F8D4 /* Release */, 453 | ); 454 | defaultConfigurationIsVisible = 0; 455 | defaultConfigurationName = Release; 456 | }; 457 | A561129427595AE500D4F206 /* Build configuration list for PBXNativeTarget "CHTWaterfallSwiftSPMDemo" */ = { 458 | isa = XCConfigurationList; 459 | buildConfigurations = ( 460 | A561129527595AE500D4F206 /* Debug */, 461 | A561129627595AE500D4F206 /* Release */, 462 | ); 463 | defaultConfigurationIsVisible = 0; 464 | defaultConfigurationName = Release; 465 | }; 466 | /* End XCConfigurationList section */ 467 | 468 | /* Begin XCSwiftPackageProductDependency section */ 469 | A561129A27595BAC00D4F206 /* CHTCollectionViewWaterfallLayout */ = { 470 | isa = XCSwiftPackageProductDependency; 471 | productName = CHTCollectionViewWaterfallLayout; 472 | }; 473 | /* End XCSwiftPackageProductDependency section */ 474 | }; 475 | rootObject = 62C26FD11ABE01840027F8D4 /* Project object */; 476 | } 477 | -------------------------------------------------------------------------------- /Demo/Swift/CHTWaterfallSwiftDemo.xcodeproj/xcshareddata/xcschemes/CHTWaterfallSwiftDemo.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 43 | 45 | 51 | 52 | 53 | 54 | 60 | 62 | 68 | 69 | 70 | 71 | 73 | 74 | 77 | 78 | 79 | -------------------------------------------------------------------------------- /Demo/Swift/CHTWaterfallSwiftDemo.xcodeproj/xcshareddata/xcschemes/CHTWaterfallSwiftSPMDemo.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 43 | 45 | 51 | 52 | 53 | 54 | 60 | 62 | 68 | 69 | 70 | 71 | 73 | 74 | 77 | 78 | 79 | -------------------------------------------------------------------------------- /Demo/Swift/CHTWaterfallSwiftDemo/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // CHTWaterfallSwiftDemo 4 | // 5 | // Created by Sophie Fader on 3/21/15. 6 | // Copyright (c) 2015 Sophie Fader. 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: [UIApplication.LaunchOptionsKey : Any]? = nil) -> 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 | -------------------------------------------------------------------------------- /Demo/Swift/CHTWaterfallSwiftDemo/Base.lproj/LaunchScreen.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /Demo/Swift/CHTWaterfallSwiftDemo/CHTWaterfallSwiftDemo-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 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIMainStoryboardFile 28 | Main 29 | UIRequiredDeviceCapabilities 30 | 31 | armv7 32 | 33 | UISupportedInterfaceOrientations 34 | 35 | UIInterfaceOrientationPortrait 36 | UIInterfaceOrientationLandscapeLeft 37 | UIInterfaceOrientationLandscapeRight 38 | 39 | UISupportedInterfaceOrientations~ipad 40 | 41 | UIInterfaceOrientationPortrait 42 | UIInterfaceOrientationPortraitUpsideDown 43 | UIInterfaceOrientationLandscapeLeft 44 | UIInterfaceOrientationLandscapeRight 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /Demo/Swift/CHTWaterfallSwiftDemo/CHTWaterfallSwiftSPMDemo-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 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIMainStoryboardFile 28 | Main 29 | UIRequiredDeviceCapabilities 30 | 31 | armv7 32 | 33 | UISupportedInterfaceOrientations 34 | 35 | UIInterfaceOrientationPortrait 36 | UIInterfaceOrientationLandscapeLeft 37 | UIInterfaceOrientationLandscapeRight 38 | 39 | UISupportedInterfaceOrientations~ipad 40 | 41 | UIInterfaceOrientationPortrait 42 | UIInterfaceOrientationPortraitUpsideDown 43 | UIInterfaceOrientationLandscapeLeft 44 | UIInterfaceOrientationLandscapeRight 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /Demo/Swift/CHTWaterfallSwiftDemo/ImageUICollectionViewCell.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ImageUICollectionViewCell.swift 3 | // CHTWaterfallSwift 4 | // 5 | // Created by Sophie Fader on 3/21/15. 6 | // Copyright (c) 2015 Sophie Fader. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class ImageUICollectionViewCell: UICollectionViewCell { 12 | 13 | @IBOutlet weak var image: UIImageView! 14 | 15 | override func awakeFromNib() { 16 | super.awakeFromNib() 17 | // Initialization code 18 | } 19 | 20 | } 21 | -------------------------------------------------------------------------------- /Demo/Swift/CHTWaterfallSwiftDemo/ImageUICollectionViewCell.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /Demo/Swift/CHTWaterfallSwiftDemo/Images.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "scale" : "2x", 6 | "size" : "20x20" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "scale" : "3x", 11 | "size" : "20x20" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "scale" : "2x", 16 | "size" : "29x29" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "scale" : "3x", 21 | "size" : "29x29" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "scale" : "2x", 26 | "size" : "40x40" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "scale" : "3x", 31 | "size" : "40x40" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "scale" : "2x", 36 | "size" : "60x60" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "scale" : "3x", 41 | "size" : "60x60" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "scale" : "1x", 46 | "size" : "20x20" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "scale" : "2x", 51 | "size" : "20x20" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "scale" : "1x", 56 | "size" : "29x29" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "scale" : "2x", 61 | "size" : "29x29" 62 | }, 63 | { 64 | "idiom" : "ipad", 65 | "scale" : "1x", 66 | "size" : "40x40" 67 | }, 68 | { 69 | "idiom" : "ipad", 70 | "scale" : "2x", 71 | "size" : "40x40" 72 | }, 73 | { 74 | "idiom" : "ipad", 75 | "scale" : "1x", 76 | "size" : "76x76" 77 | }, 78 | { 79 | "idiom" : "ipad", 80 | "scale" : "2x", 81 | "size" : "76x76" 82 | }, 83 | { 84 | "idiom" : "ipad", 85 | "scale" : "2x", 86 | "size" : "83.5x83.5" 87 | }, 88 | { 89 | "idiom" : "ios-marketing", 90 | "scale" : "1x", 91 | "size" : "1024x1024" 92 | } 93 | ], 94 | "info" : { 95 | "author" : "xcode", 96 | "version" : 1 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /Demo/Swift/CHTWaterfallSwiftDemo/Images.xcassets/image1.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x", 6 | "filename" : "public-domain-images-free-stock-photos-black-white-vintage-suitcase-girl-railroadtracks-walking-1.jpg" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Demo/Swift/CHTWaterfallSwiftDemo/Images.xcassets/image1.imageset/public-domain-images-free-stock-photos-black-white-vintage-suitcase-girl-railroadtracks-walking-1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chiahsien/CHTCollectionViewWaterfallLayout/918ea98b054ed3e70edee4736edbad5c479b6c63/Demo/Swift/CHTWaterfallSwiftDemo/Images.xcassets/image1.imageset/public-domain-images-free-stock-photos-black-white-vintage-suitcase-girl-railroadtracks-walking-1.jpg -------------------------------------------------------------------------------- /Demo/Swift/CHTWaterfallSwiftDemo/Images.xcassets/image2.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x", 6 | "filename" : "public-domain-images-free-stock-photos-brick-wall-rustic-old-metal-doors-private-parking-1000x664.jpg" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Demo/Swift/CHTWaterfallSwiftDemo/Images.xcassets/image2.imageset/public-domain-images-free-stock-photos-brick-wall-rustic-old-metal-doors-private-parking-1000x664.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chiahsien/CHTCollectionViewWaterfallLayout/918ea98b054ed3e70edee4736edbad5c479b6c63/Demo/Swift/CHTWaterfallSwiftDemo/Images.xcassets/image2.imageset/public-domain-images-free-stock-photos-brick-wall-rustic-old-metal-doors-private-parking-1000x664.jpg -------------------------------------------------------------------------------- /Demo/Swift/CHTWaterfallSwiftDemo/Images.xcassets/image3.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x", 6 | "filename" : "public-domain-images-free-stock-photos-down-town-chicago-blue-sky-6.jpg" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Demo/Swift/CHTWaterfallSwiftDemo/Images.xcassets/image3.imageset/public-domain-images-free-stock-photos-down-town-chicago-blue-sky-6.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chiahsien/CHTCollectionViewWaterfallLayout/918ea98b054ed3e70edee4736edbad5c479b6c63/Demo/Swift/CHTWaterfallSwiftDemo/Images.xcassets/image3.imageset/public-domain-images-free-stock-photos-down-town-chicago-blue-sky-6.jpg -------------------------------------------------------------------------------- /Demo/Swift/CHTWaterfallSwiftDemo/Images.xcassets/image4.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x", 6 | "filename" : "public-domain-images-free-stock-photos-down-town-chicago-tribune-building-1-1000x666.jpg" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Demo/Swift/CHTWaterfallSwiftDemo/Images.xcassets/image4.imageset/public-domain-images-free-stock-photos-down-town-chicago-tribune-building-1-1000x666.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chiahsien/CHTCollectionViewWaterfallLayout/918ea98b054ed3e70edee4736edbad5c479b6c63/Demo/Swift/CHTWaterfallSwiftDemo/Images.xcassets/image4.imageset/public-domain-images-free-stock-photos-down-town-chicago-tribune-building-1-1000x666.jpg -------------------------------------------------------------------------------- /Demo/Swift/CHTWaterfallSwiftDemo/Images.xcassets/image5.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x", 6 | "filename" : "public-domain-images-free-stock-photos-high-quality-resolution-downloads-public-domain-archive-5.jpg" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Demo/Swift/CHTWaterfallSwiftDemo/Images.xcassets/image5.imageset/public-domain-images-free-stock-photos-high-quality-resolution-downloads-public-domain-archive-5.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chiahsien/CHTCollectionViewWaterfallLayout/918ea98b054ed3e70edee4736edbad5c479b6c63/Demo/Swift/CHTWaterfallSwiftDemo/Images.xcassets/image5.imageset/public-domain-images-free-stock-photos-high-quality-resolution-downloads-public-domain-archive-5.jpg -------------------------------------------------------------------------------- /Demo/Swift/CHTWaterfallSwiftDemo/Images.xcassets/image6.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x", 6 | "filename" : "public-domain-images-free-stock-photos-high-quality-resolution-downloads-public-domain-archive-10.jpg" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Demo/Swift/CHTWaterfallSwiftDemo/Images.xcassets/image6.imageset/public-domain-images-free-stock-photos-high-quality-resolution-downloads-public-domain-archive-10.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chiahsien/CHTCollectionViewWaterfallLayout/918ea98b054ed3e70edee4736edbad5c479b6c63/Demo/Swift/CHTWaterfallSwiftDemo/Images.xcassets/image6.imageset/public-domain-images-free-stock-photos-high-quality-resolution-downloads-public-domain-archive-10.jpg -------------------------------------------------------------------------------- /Demo/Swift/CHTWaterfallSwiftDemo/Images.xcassets/image7.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x", 6 | "filename" : "public-domain-images-free-stock-photos-sky-scrapers-bricks-chicago-161-1000x666.jpg" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Demo/Swift/CHTWaterfallSwiftDemo/Images.xcassets/image7.imageset/public-domain-images-free-stock-photos-sky-scrapers-bricks-chicago-161-1000x666.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chiahsien/CHTCollectionViewWaterfallLayout/918ea98b054ed3e70edee4736edbad5c479b6c63/Demo/Swift/CHTWaterfallSwiftDemo/Images.xcassets/image7.imageset/public-domain-images-free-stock-photos-sky-scrapers-bricks-chicago-161-1000x666.jpg -------------------------------------------------------------------------------- /Demo/Swift/CHTWaterfallSwiftDemo/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | -------------------------------------------------------------------------------- /Demo/Swift/CHTWaterfallSwiftDemo/Model.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Model.swift 3 | // CHTWaterfallSwift 4 | // 5 | // Created by Sophie Fader on 3/21/15. 6 | // Copyright (c) 2015 Sophie Fader. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class Model: NSObject { 12 | 13 | var images : [UIImage] = [] 14 | 15 | // Assemble an array of images to use for sample content for the collectionView 16 | func buildDataSource(){ 17 | images = (1...7).map { UIImage(named: "image\($0)")! } 18 | } 19 | 20 | } 21 | -------------------------------------------------------------------------------- /Demo/Swift/CHTWaterfallSwiftDemo/ViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.swift 3 | // CHTWaterfallSwift 4 | // 5 | // Created by Sophie Fader on 3/21/15. 6 | // Copyright (c) 2015 Sophie Fader. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | #if canImport(CHTCollectionViewWaterfallLayout) 11 | import CHTCollectionViewWaterfallLayout 12 | #endif 13 | 14 | class ViewController: UIViewController, UICollectionViewDelegate, UICollectionViewDataSource, CHTCollectionViewDelegateWaterfallLayout { 15 | 16 | @IBOutlet weak var collectionView: UICollectionView! 17 | let model = Model() 18 | 19 | //MARK: - View Controller Lifecycle 20 | override func viewDidLoad() { 21 | 22 | super.viewDidLoad() 23 | model.buildDataSource() 24 | 25 | // Attach datasource and delegate 26 | collectionView.dataSource = self 27 | collectionView.delegate = self 28 | 29 | //Layout setup 30 | setupCollectionView() 31 | 32 | //Register nibs 33 | registerNibs() 34 | } 35 | 36 | //MARK: - CollectionView UI Setup 37 | func setupCollectionView(){ 38 | 39 | // Create a waterfall layout 40 | let layout = CHTCollectionViewWaterfallLayout() 41 | 42 | // Change individual layout attributes for the spacing between cells 43 | layout.minimumColumnSpacing = 5.0 44 | layout.minimumInteritemSpacing = 5.0 45 | 46 | // Collection view attributes 47 | collectionView.alwaysBounceVertical = true 48 | 49 | // Add the waterfall layout to your collection view 50 | collectionView.collectionViewLayout = layout 51 | } 52 | 53 | // Register CollectionView Nibs 54 | func registerNibs(){ 55 | 56 | // attach the UI nib file for the ImageUICollectionViewCell to the collectionview 57 | let viewNib = UINib(nibName: "ImageUICollectionViewCell", bundle: nil) 58 | collectionView.register(viewNib, forCellWithReuseIdentifier: "cell") 59 | } 60 | 61 | //MARK: - CollectionView Delegate Methods 62 | 63 | //** Number of Cells in the CollectionView */ 64 | func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { 65 | return model.images.count 66 | } 67 | 68 | 69 | //** Create a basic CollectionView Cell */ 70 | func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { 71 | 72 | // Create the cell and return the cell 73 | let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! ImageUICollectionViewCell 74 | 75 | // Add image to cell 76 | cell.image.image = model.images[indexPath.item] 77 | return cell 78 | } 79 | 80 | 81 | //MARK: - CollectionView Waterfall Layout Delegate Methods (Required) 82 | 83 | //** Size for the cells in the Waterfall Layout */ 84 | func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize { 85 | // create a cell size from the image size, and return the size 86 | let imageSize = model.images[indexPath.item].size 87 | 88 | return imageSize 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | $(CURRENT_PROJECT_VERSION) 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2012 Nelson Tai 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is furnished 8 | to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version:5.0 2 | import PackageDescription 3 | 4 | let package = Package( 5 | name: "CHTCollectionViewWaterfallLayout", 6 | platforms: [ 7 | .iOS(.v9), 8 | .tvOS(.v9) 9 | ], 10 | products: [ 11 | .library(name: "CHTCollectionViewWaterfallLayout", targets: ["CHTCollectionViewWaterfallLayout"]), 12 | .library(name: "CHTCollectionViewWaterfallLayoutObjC", targets: ["CHTCollectionViewWaterfallLayoutObjC"]) 13 | ], 14 | targets: [ 15 | .target( 16 | name: "CHTCollectionViewWaterfallLayout", 17 | path: "Source", 18 | sources: [ 19 | "CHTCollectionViewWaterfallLayout.swift" 20 | ] 21 | ), 22 | .target( 23 | name: "CHTCollectionViewWaterfallLayoutObjC", 24 | path: "Source", 25 | sources: [ 26 | "CHTCollectionViewWaterfallLayout.h", 27 | "CHTCollectionViewWaterfallLayout.m" 28 | ], 29 | publicHeadersPath: "." 30 | ) 31 | ], 32 | swiftLanguageVersions: [.v5] 33 | ) 34 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | CHTCollectionViewWaterfallLayout 2 | ================================ 3 | 4 | [![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage) 5 | [![Swift Package Manager compatible](https://img.shields.io/badge/Swift%20Package%20Manager-compatible-brightgreen.svg)](https://github.com/apple/swift-package-manager) 6 | [![Version](https://cocoapod-badges.herokuapp.com/v/CHTCollectionViewWaterfallLayout/badge.png)](http://cocoadocs.org/docsets/CHTCollectionViewWaterfallLayout) 7 | [![Platform](https://cocoapod-badges.herokuapp.com/p/CHTCollectionViewWaterfallLayout/badge.png)](http://cocoadocs.org/docsets/CHTCollectionViewWaterfallLayout) 8 | [![Build Status](https://github.com/chiahsien/CHTCollectionViewWaterfallLayout/workflows/CHTCollectionViewWaterfallLayout%20CI/badge.svg?branch=develop)](https://github.com/chiahsien/CHTCollectionViewWaterfallLayout/actions) 9 | 10 | **CHTCollectionViewWaterfallLayout** is a subclass of [UICollectionViewLayout], and it trys to imitate [UICollectionViewFlowLayout]'s usage as much as possible. 11 | 12 | This layout is inspired by [Pinterest]. 13 | 14 | Screen Shots 15 | ------------ 16 | ![2 columns](https://cloud.githubusercontent.com/assets/474/3419095/25b4de9e-fe56-11e3-9b98-690319d736ce.png) 17 | 18 | Features 19 | -------- 20 | * Easy to use, it tries to imitate [UICollectionViewFlowLayout]'s usage as much as possible. 21 | * Highly customizable. 22 | * Outstanding performance, try 10,000+ items and see the smoothness for yourself. 23 | * Support header and footer views. 24 | * Different column counts in different sections. 25 | 26 | 27 | 28 | Requirements 29 | ------------ 30 | * iOS 9+ / tvOS 9+ 31 | * Objective-C or Swift 4.2 32 | 33 | How to install 34 | -------------- 35 | * [CocoaPods] 36 | - Add `pod 'CHTCollectionViewWaterfallLayout'` to your Podfile. 37 | - If you prefer Objective-C, `pod 'CHTCollectionViewWaterfallLayout/ObjC'` is ready for you. 38 | 39 | * [Carthage] 40 | - Add `github chiahsien/CHTCollectionViewWaterfallLayout` to your Cartfile. 41 | 42 | * [Swift Package Manager] 43 | - Add it to the `dependencies` value of your `Package.swift`. 44 | ``` 45 | dependencies: [ 46 | .package(url: "https://github.com/chiahsien/CHTCollectionViewWaterfallLayout.git", from: "0.9.9") 47 | ] 48 | ``` 49 | 50 | * Manual 51 | - Copy `CHTCollectionViewWaterfallLayout.h/m` or `CHTCollectionViewWaterfallLayout.swift` to your project. 52 | 53 | How to Use 54 | ---------- 55 | Read the demo codes and `CHTCollectionViewWaterfallLayout.h` header file for more information. 56 | 57 | #### Step 1 58 | Below lists the properties for you to customize the layout. Although they have default values, I strongly recommend you to set up at least the `columnCount` property to suit your needs. 59 | The `itemRenderDirection` property is an enum which decides the order in which your items will be rendered in subsequent rows. For eg. Left-Right | Right-Left | Shortest column filling up first. 60 | 61 | ``` objc 62 | @property (nonatomic, assign) NSInteger columnCount; 63 | @property (nonatomic, assign) CGFloat minimumColumnSpacing; 64 | @property (nonatomic, assign) CGFloat minimumInteritemSpacing; 65 | @property (nonatomic, assign) CGFloat headerHeight; 66 | @property (nonatomic, assign) CGFloat footerHeight; 67 | @property (nonatomic, assign) UIEdgeInsets sectionInset; 68 | @property (nonatomic, assign) ItemRenderDirection itemRenderDirection; 69 | ``` 70 | 71 | #### Step 2 72 | Your collection view's delegate (which often is your view controller) must conforms to `CHTCollectionViewDelegateWaterfallLayout` protocol and implements the required method, all you need to do is return the original size of the item: 73 | 74 | ``` objc 75 | - (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath; 76 | ``` 77 | 78 | Limitation 79 | ---------- 80 | * Only vertical scrolling is supported. 81 | * No decoration view. 82 | 83 | Who is using it 84 | --------------- 85 | Please let me know if your app is using this library. I'm glad to put your app on the list :-) 86 | 87 | * [F3PiX](https://itunes.apple.com/us/app/samenwerken-f3pix/id897714553?mt=8) 88 | F3PiX is a series of apps which gives you a concise, curated collection of pictures by professional (Dutch) photographers according to a specific theme. You can use the pictures freely for your own work. 89 | * [GroupMe for iOS](https://itunes.apple.com/us/app/groupme/id392796698?mt=8) 90 | GroupMe - A Home for All the Groups in Your Life. 91 | * [Flickr](https://itunes.apple.com/us/app/id328407587) 92 | Access and organize your photos from anywhere. 93 | * [Tumblr](https://www.tumblr.com/policy/en/ios-credits) 94 | Post whatever you want to your Tumblr. Follow other people who are doing the same. You’ll probably never be bored again. 95 | * [Funliday](https://itunes.apple.com/us/app/funlidays-lu-you-gui-hua/id905768387) 96 | The best trip planning app in the world! 97 | * [Imgur](https://itunes.apple.com/us/app/imgur-funny-gifs-memes-images/id639881495?mt=8) 98 | Funny GIFs, Memes, and Images! 99 | * [DealPad](https://itunes.apple.com/us/app/dealpad-bargains-freebies/id949294107?mt=8) 100 | DealPad gives you access to the UK’s hottest Deals, Voucher Codes and Freebies in the palm of your hand. 101 | * [Teespring Shopping](https://itunes.apple.com/app/apple-store/id1144693237?pt=117854047&ct=CHTCollectionViewWaterfallLayout%20README&mt=8) 102 | Browse and purchase shirts, mugs, totes and more! 103 | 104 | License 105 | ------- 106 | CHTCollectionViewWaterfallLayout is available under the MIT license. See the LICENSE file for more info. 107 | 108 | Changelog 109 | --------- 110 | Refer to the [Releases page](https://github.com/chiahsien/CHTCollectionViewWaterfallLayout/releases). 111 | 112 | [UICollectionViewLayout]: http://developer.apple.com/library/ios/#documentation/uikit/reference/UICollectionViewLayout_class/Reference/Reference.html 113 | [UICollectionViewFlowLayout]: https://developer.apple.com/library/ios/documentation/uikit/reference/UICollectionViewFlowLayout_class/Reference/Reference.html 114 | [Pinterest]: http://pinterest.com/ 115 | [CocoaPods]: http://cocoapods.org/ 116 | [Carthage]: https://github.com/Carthage/Carthage 117 | [Swift Package Manager]: https://swift.org/package-manager/ 118 | -------------------------------------------------------------------------------- /Screenshots/2-columns.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chiahsien/CHTCollectionViewWaterfallLayout/918ea98b054ed3e70edee4736edbad5c479b6c63/Screenshots/2-columns.png -------------------------------------------------------------------------------- /Source/CHTCollectionViewWaterfallLayout.h: -------------------------------------------------------------------------------- 1 | // 2 | // UICollectionViewWaterfallLayout.h 3 | // 4 | // Created by Nelson on 12/11/19. 5 | // Copyright (c) 2012 Nelson Tai. All rights reserved. 6 | // 7 | 8 | #import 9 | 10 | /** 11 | * Enumerated structure to define direction in which items can be rendered. 12 | */ 13 | typedef NS_ENUM (NSUInteger, CHTCollectionViewWaterfallLayoutItemRenderDirection) { 14 | CHTCollectionViewWaterfallLayoutItemRenderDirectionShortestFirst, 15 | CHTCollectionViewWaterfallLayoutItemRenderDirectionLeftToRight, 16 | CHTCollectionViewWaterfallLayoutItemRenderDirectionRightToLeft 17 | }; 18 | 19 | /** 20 | * Constants that specify the types of supplementary views that can be presented using a waterfall layout. 21 | */ 22 | 23 | /// A supplementary view that identifies the header for a given section. 24 | extern NSString *const CHTCollectionElementKindSectionHeader; 25 | /// A supplementary view that identifies the footer for a given section. 26 | extern NSString *const CHTCollectionElementKindSectionFooter; 27 | 28 | #pragma mark - CHTCollectionViewDelegateWaterfallLayout 29 | 30 | @class CHTCollectionViewWaterfallLayout; 31 | 32 | /** 33 | * The CHTCollectionViewDelegateWaterfallLayout protocol defines methods that let you coordinate with a 34 | * CHTCollectionViewWaterfallLayout object to implement a waterfall-based layout. 35 | * The methods of this protocol define the size of items. 36 | * 37 | * The waterfall layout object expects the collection view’s delegate object to adopt this protocol. 38 | * Therefore, implement this protocol on object assigned to your collection view’s delegate property. 39 | */ 40 | @protocol CHTCollectionViewDelegateWaterfallLayout 41 | @required 42 | /** 43 | * Asks the delegate for the size of the specified item’s cell. 44 | * 45 | * @param collectionView 46 | * The collection view object displaying the waterfall layout. 47 | * @param collectionViewLayout 48 | * The layout object requesting the information. 49 | * @param indexPath 50 | * The index path of the item. 51 | * 52 | * @return 53 | * The original size of the specified item. Both width and height must be greater than 0. 54 | */ 55 | - (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath; 56 | 57 | @optional 58 | /** 59 | * Asks the delegate for the column count in a section 60 | * 61 | * @param collectionView 62 | * The collection view object displaying the waterfall layout. 63 | * @param collectionViewLayout 64 | * The layout object requesting the information. 65 | * @param section 66 | * The section. 67 | * 68 | * @return 69 | * The original column count for that section. Must be greater than 0. 70 | */ 71 | - (NSInteger)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout columnCountForSection:(NSInteger)section; 72 | 73 | /** 74 | * Asks the delegate for the height of the header view in the specified section. 75 | * 76 | * @param collectionView 77 | * The collection view object displaying the waterfall layout. 78 | * @param collectionViewLayout 79 | * The layout object requesting the information. 80 | * @param section 81 | * The index of the section whose header size is being requested. 82 | * 83 | * @return 84 | * The height of the header. If you return 0, no header is added. 85 | * 86 | * @discussion 87 | * If you do not implement this method, the waterfall layout uses the value in its headerHeight property to set the size of the header. 88 | * 89 | * @see 90 | * headerHeight 91 | */ 92 | - (CGFloat)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout heightForHeaderInSection:(NSInteger)section; 93 | 94 | /** 95 | * Asks the delegate for the height of the footer view in the specified section. 96 | * 97 | * @param collectionView 98 | * The collection view object displaying the waterfall layout. 99 | * @param collectionViewLayout 100 | * The layout object requesting the information. 101 | * @param section 102 | * The index of the section whose header size is being requested. 103 | * 104 | * @return 105 | * The height of the footer. If you return 0, no footer is added. 106 | * 107 | * @discussion 108 | * If you do not implement this method, the waterfall layout uses the value in its footerHeight property to set the size of the footer. 109 | * 110 | * @see 111 | * footerHeight 112 | */ 113 | - (CGFloat)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout heightForFooterInSection:(NSInteger)section; 114 | 115 | /** 116 | * Asks the delegate for the insets in the specified section. 117 | * 118 | * @param collectionView 119 | * The collection view object displaying the waterfall layout. 120 | * @param collectionViewLayout 121 | * The layout object requesting the information. 122 | * @param section 123 | * The index of the section whose insets are being requested. 124 | * 125 | * @discussion 126 | * If you do not implement this method, the waterfall layout uses the value in its sectionInset property. 127 | * 128 | * @return 129 | * The insets for the section. 130 | */ 131 | - (UIEdgeInsets)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout insetForSectionAtIndex:(NSInteger)section; 132 | 133 | /** 134 | * Asks the delegate for the header insets in the specified section. 135 | * 136 | * @param collectionView 137 | * The collection view object displaying the waterfall layout. 138 | * @param collectionViewLayout 139 | * The layout object requesting the information. 140 | * @param section 141 | * The index of the section whose header insets are being requested. 142 | * 143 | * @discussion 144 | * If you do not implement this method, the waterfall layout uses the value in its headerInset property. 145 | * 146 | * @return 147 | * The headerInsets for the section. 148 | */ 149 | - (UIEdgeInsets)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout insetForHeaderInSection:(NSInteger)section; 150 | 151 | /** 152 | * Asks the delegate for the footer insets in the specified section. 153 | * 154 | * @param collectionView 155 | * The collection view object displaying the waterfall layout. 156 | * @param collectionViewLayout 157 | * The layout object requesting the information. 158 | * @param section 159 | * The index of the section whose footer insets are being requested. 160 | * 161 | * @discussion 162 | * If you do not implement this method, the waterfall layout uses the value in its footerInset property. 163 | * 164 | * @return 165 | * The footerInsets for the section. 166 | */ 167 | - (UIEdgeInsets)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout insetForFooterInSection:(NSInteger)section; 168 | 169 | /** 170 | * Asks the delegate for the minimum spacing between two items in the same column 171 | * in the specified section. If this method is not implemented, the 172 | * minimumInteritemSpacing property is used for all sections. 173 | * 174 | * @param collectionView 175 | * The collection view object displaying the waterfall layout. 176 | * @param collectionViewLayout 177 | * The layout object requesting the information. 178 | * @param section 179 | * The index of the section whose minimum interitem spacing is being requested. 180 | * 181 | * @discussion 182 | * If you do not implement this method, the waterfall layout uses the value in its minimumInteritemSpacing property to determine the amount of space between items in the same column. 183 | * 184 | * @return 185 | * The minimum interitem spacing. 186 | */ 187 | - (CGFloat)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout minimumInteritemSpacingForSectionAtIndex:(NSInteger)section; 188 | 189 | /** 190 | * Asks the delegate for the minimum spacing between colums in a secified section. If this method is not implemented, the 191 | * minimumColumnSpacing property is used for all sections. 192 | * 193 | * @param collectionView 194 | * The collection view object displaying the waterfall layout. 195 | * @param collectionViewLayout 196 | * The layout object requesting the information. 197 | * @param section 198 | * The index of the section whose minimum interitem spacing is being requested. 199 | * 200 | * @discussion 201 | * If you do not implement this method, the waterfall layout uses the value in its minimumColumnSpacing property to determine the amount of space between columns in each section. 202 | * 203 | * @return 204 | * The minimum spacing between each column. 205 | */ 206 | - (CGFloat)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout minimumColumnSpacingForSectionAtIndex:(NSInteger)section; 207 | 208 | @end 209 | 210 | #pragma mark - CHTCollectionViewWaterfallLayout 211 | 212 | /** 213 | * The CHTCollectionViewWaterfallLayout class is a concrete layout object that organizes items into waterfall-based grids 214 | * with optional header and footer views for each section. 215 | * 216 | * A waterfall layout works with the collection view’s delegate object to determine the size of items, headers, and footers 217 | * in each section. That delegate object must conform to the `CHTCollectionViewDelegateWaterfallLayout` protocol. 218 | * 219 | * Each section in a waterfall layout can have its own custom header and footer. To configure the header or footer for a view, 220 | * you must configure the height of the header or footer to be non zero. You can do this by implementing the appropriate delegate 221 | * methods or by assigning appropriate values to the `headerHeight` and `footerHeight` properties. 222 | * If the header or footer height is 0, the corresponding view is not added to the collection view. 223 | * 224 | * @note CHTCollectionViewWaterfallLayout doesn't support decoration view, and it supports vertical scrolling direction only. 225 | */ 226 | @interface CHTCollectionViewWaterfallLayout : UICollectionViewLayout 227 | 228 | /** 229 | * @brief How many columns for this layout. 230 | * @discussion Default: 2 231 | */ 232 | @property (nonatomic, assign) NSInteger columnCount; 233 | 234 | /** 235 | * @brief The minimum spacing to use between successive columns. 236 | * @discussion Default: 10.0 237 | */ 238 | @property (nonatomic, assign) CGFloat minimumColumnSpacing; 239 | 240 | /** 241 | * @brief The minimum spacing to use between items in the same column. 242 | * @discussion Default: 10.0 243 | * @note This spacing is not applied to the space between header and columns or between columns and footer. 244 | */ 245 | @property (nonatomic, assign) CGFloat minimumInteritemSpacing; 246 | 247 | /** 248 | * @brief Height for section header 249 | * @discussion 250 | * If your collectionView's delegate doesn't implement `collectionView:layout:heightForHeaderInSection:`, 251 | * then this value will be used. 252 | * 253 | * Default: 0 254 | */ 255 | @property (nonatomic, assign) CGFloat headerHeight; 256 | 257 | /** 258 | * @brief Height for section footer 259 | * @discussion 260 | * If your collectionView's delegate doesn't implement `collectionView:layout:heightForFooterInSection:`, 261 | * then this value will be used. 262 | * 263 | * Default: 0 264 | */ 265 | @property (nonatomic, assign) CGFloat footerHeight; 266 | 267 | /** 268 | * @brief The margins that are used to lay out the header for each section. 269 | * @discussion 270 | * These insets are applied to the headers in each section. 271 | * They represent the distance between the top of the collection view and the top of the content items 272 | * They also indicate the spacing on either side of the header. They do not affect the size of the headers or footers themselves. 273 | * 274 | * Default: UIEdgeInsetsZero 275 | */ 276 | @property (nonatomic, assign) UIEdgeInsets headerInset; 277 | 278 | /** 279 | * @brief The margins that are used to lay out the footer for each section. 280 | * @discussion 281 | * These insets are applied to the footers in each section. 282 | * They represent the distance between the top of the collection view and the top of the content items 283 | * They also indicate the spacing on either side of the footer. They do not affect the size of the headers or footers themselves. 284 | * 285 | * Default: UIEdgeInsetsZero 286 | */ 287 | @property (nonatomic, assign) UIEdgeInsets footerInset; 288 | 289 | /** 290 | * @brief The margins that are used to lay out content in each section. 291 | * @discussion 292 | * Section insets are margins applied only to the items in the section. 293 | * They represent the distance between the header view and the columns and between the columns and the footer view. 294 | * They also indicate the spacing on either side of columns. They do not affect the size of the headers or footers themselves. 295 | * 296 | * Default: UIEdgeInsetsZero 297 | */ 298 | @property (nonatomic, assign) UIEdgeInsets sectionInset; 299 | 300 | /** 301 | * @brief The direction in which items will be rendered in subsequent rows. 302 | * @discussion 303 | * The direction in which each item is rendered. This could be left to right (CHTCollectionViewWaterfallLayoutItemRenderDirectionLeftToRight), right to left (CHTCollectionViewWaterfallLayoutItemRenderDirectionRightToLeft), or shortest column fills first (CHTCollectionViewWaterfallLayoutItemRenderDirectionShortestFirst). 304 | * 305 | * Default: CHTCollectionViewWaterfallLayoutItemRenderDirectionShortestFirst 306 | */ 307 | @property (nonatomic, assign) CHTCollectionViewWaterfallLayoutItemRenderDirection itemRenderDirection; 308 | 309 | /** 310 | * @brief The minimum height of the collection view's content. 311 | * @discussion 312 | * The minimum height of the collection view's content. This could be used to allow hidden headers with no content. 313 | * 314 | * Default: 0.f 315 | */ 316 | @property (nonatomic, assign) CGFloat minimumContentHeight; 317 | 318 | /** 319 | * @brief The calculated width of an item in the specified section. 320 | * @discussion 321 | * The width of an item is calculated based on number of columns, the collection view width, and the horizontal insets for that section. 322 | */ 323 | - (CGFloat)itemWidthInSectionAtIndex:(NSInteger)section; 324 | 325 | @end 326 | -------------------------------------------------------------------------------- /Source/CHTCollectionViewWaterfallLayout.m: -------------------------------------------------------------------------------- 1 | // 2 | // UICollectionViewWaterfallLayout.m 3 | // 4 | // Created by Nelson on 12/11/19. 5 | // Copyright (c) 2012 Nelson Tai. All rights reserved. 6 | // 7 | 8 | #import "CHTCollectionViewWaterfallLayout.h" 9 | #import "tgmath.h" 10 | 11 | NSString *const CHTCollectionElementKindSectionHeader = @"CHTCollectionElementKindSectionHeader"; 12 | NSString *const CHTCollectionElementKindSectionFooter = @"CHTCollectionElementKindSectionFooter"; 13 | 14 | @interface CHTCollectionViewWaterfallLayout () 15 | /// The delegate will point to collection view's delegate automatically. 16 | @property (nonatomic, weak) id delegate; 17 | /// Array to store height for each column 18 | @property (nonatomic, strong) NSMutableArray *columnHeights; 19 | /// Array of arrays. Each array stores item attributes for each section 20 | @property (nonatomic, strong) NSMutableArray *sectionItemAttributes; 21 | /// Array to store attributes for all items includes headers, cells, and footers 22 | @property (nonatomic, strong) NSMutableArray *allItemAttributes; 23 | /// Dictionary to store section headers' attribute 24 | @property (nonatomic, strong) NSMutableDictionary *headersAttribute; 25 | /// Dictionary to store section footers' attribute 26 | @property (nonatomic, strong) NSMutableDictionary *footersAttribute; 27 | /// Array to store union rectangles 28 | @property (nonatomic, strong) NSMutableArray *unionRects; 29 | @end 30 | 31 | @implementation CHTCollectionViewWaterfallLayout 32 | 33 | /// How many items to be union into a single rectangle 34 | static const NSInteger unionSize = 20; 35 | 36 | static CGFloat CHTFloorCGFloat(CGFloat value) { 37 | CGFloat scale = [UIScreen mainScreen].scale; 38 | return floor(value * scale) / scale; 39 | } 40 | 41 | #pragma mark - Public Accessors 42 | - (void)setColumnCount:(NSInteger)columnCount { 43 | if (_columnCount != columnCount) { 44 | _columnCount = columnCount; 45 | [self invalidateLayout]; 46 | } 47 | } 48 | 49 | - (void)setMinimumColumnSpacing:(CGFloat)minimumColumnSpacing { 50 | if (_minimumColumnSpacing != minimumColumnSpacing) { 51 | _minimumColumnSpacing = minimumColumnSpacing; 52 | [self invalidateLayout]; 53 | } 54 | } 55 | 56 | - (void)setMinimumInteritemSpacing:(CGFloat)minimumInteritemSpacing { 57 | if (_minimumInteritemSpacing != minimumInteritemSpacing) { 58 | _minimumInteritemSpacing = minimumInteritemSpacing; 59 | [self invalidateLayout]; 60 | } 61 | } 62 | 63 | - (void)setHeaderHeight:(CGFloat)headerHeight { 64 | if (_headerHeight != headerHeight) { 65 | _headerHeight = headerHeight; 66 | [self invalidateLayout]; 67 | } 68 | } 69 | 70 | - (void)setFooterHeight:(CGFloat)footerHeight { 71 | if (_footerHeight != footerHeight) { 72 | _footerHeight = footerHeight; 73 | [self invalidateLayout]; 74 | } 75 | } 76 | 77 | - (void)setHeaderInset:(UIEdgeInsets)headerInset { 78 | if (!UIEdgeInsetsEqualToEdgeInsets(_headerInset, headerInset)) { 79 | _headerInset = headerInset; 80 | [self invalidateLayout]; 81 | } 82 | } 83 | 84 | - (void)setFooterInset:(UIEdgeInsets)footerInset { 85 | if (!UIEdgeInsetsEqualToEdgeInsets(_footerInset, footerInset)) { 86 | _footerInset = footerInset; 87 | [self invalidateLayout]; 88 | } 89 | } 90 | 91 | - (void)setSectionInset:(UIEdgeInsets)sectionInset { 92 | if (!UIEdgeInsetsEqualToEdgeInsets(_sectionInset, sectionInset)) { 93 | _sectionInset = sectionInset; 94 | [self invalidateLayout]; 95 | } 96 | } 97 | 98 | - (void)setItemRenderDirection:(CHTCollectionViewWaterfallLayoutItemRenderDirection)itemRenderDirection { 99 | if (_itemRenderDirection != itemRenderDirection) { 100 | _itemRenderDirection = itemRenderDirection; 101 | [self invalidateLayout]; 102 | } 103 | } 104 | 105 | - (NSInteger)columnCountForSection:(NSInteger)section { 106 | if ([self.delegate respondsToSelector:@selector(collectionView:layout:columnCountForSection:)]) { 107 | return [self.delegate collectionView:self.collectionView layout:self columnCountForSection:section]; 108 | } else { 109 | return self.columnCount; 110 | } 111 | } 112 | 113 | - (CGFloat)itemWidthInSectionAtIndex:(NSInteger)section { 114 | UIEdgeInsets sectionInset; 115 | if ([self.delegate respondsToSelector:@selector(collectionView:layout:insetForSectionAtIndex:)]) { 116 | sectionInset = [self.delegate collectionView:self.collectionView layout:self insetForSectionAtIndex:section]; 117 | } else { 118 | sectionInset = self.sectionInset; 119 | } 120 | CGFloat width = self.collectionView.bounds.size.width - sectionInset.left - sectionInset.right; 121 | NSInteger columnCount = [self columnCountForSection:section]; 122 | 123 | CGFloat columnSpacing = self.minimumColumnSpacing; 124 | if ([self.delegate respondsToSelector:@selector(collectionView:layout:minimumColumnSpacingForSectionAtIndex:)]) { 125 | columnSpacing = [self.delegate collectionView:self.collectionView layout:self minimumColumnSpacingForSectionAtIndex:section]; 126 | } 127 | 128 | return CHTFloorCGFloat((width - (columnCount - 1) * columnSpacing) / columnCount); 129 | } 130 | 131 | #pragma mark - Private Accessors 132 | - (NSMutableDictionary *)headersAttribute { 133 | if (!_headersAttribute) { 134 | _headersAttribute = [NSMutableDictionary dictionary]; 135 | } 136 | return _headersAttribute; 137 | } 138 | 139 | - (NSMutableDictionary *)footersAttribute { 140 | if (!_footersAttribute) { 141 | _footersAttribute = [NSMutableDictionary dictionary]; 142 | } 143 | return _footersAttribute; 144 | } 145 | 146 | - (NSMutableArray *)unionRects { 147 | if (!_unionRects) { 148 | _unionRects = [NSMutableArray array]; 149 | } 150 | return _unionRects; 151 | } 152 | 153 | - (NSMutableArray *)columnHeights { 154 | if (!_columnHeights) { 155 | _columnHeights = [NSMutableArray array]; 156 | } 157 | return _columnHeights; 158 | } 159 | 160 | - (NSMutableArray *)allItemAttributes { 161 | if (!_allItemAttributes) { 162 | _allItemAttributes = [NSMutableArray array]; 163 | } 164 | return _allItemAttributes; 165 | } 166 | 167 | - (NSMutableArray *)sectionItemAttributes { 168 | if (!_sectionItemAttributes) { 169 | _sectionItemAttributes = [NSMutableArray array]; 170 | } 171 | return _sectionItemAttributes; 172 | } 173 | 174 | - (id )delegate { 175 | return (id )self.collectionView.delegate; 176 | } 177 | 178 | #pragma mark - Init 179 | - (void)commonInit { 180 | _columnCount = 2; 181 | _minimumColumnSpacing = 10; 182 | _minimumInteritemSpacing = 10; 183 | _headerHeight = 0; 184 | _footerHeight = 0; 185 | _sectionInset = UIEdgeInsetsZero; 186 | _headerInset = UIEdgeInsetsZero; 187 | _footerInset = UIEdgeInsetsZero; 188 | _itemRenderDirection = CHTCollectionViewWaterfallLayoutItemRenderDirectionShortestFirst; 189 | } 190 | 191 | - (id)init { 192 | if (self = [super init]) { 193 | [self commonInit]; 194 | } 195 | return self; 196 | } 197 | 198 | - (id)initWithCoder:(NSCoder *)aDecoder { 199 | if (self = [super initWithCoder:aDecoder]) { 200 | [self commonInit]; 201 | } 202 | return self; 203 | } 204 | 205 | #pragma mark - Methods to Override 206 | - (void)prepareLayout { 207 | [super prepareLayout]; 208 | 209 | [self.headersAttribute removeAllObjects]; 210 | [self.footersAttribute removeAllObjects]; 211 | [self.unionRects removeAllObjects]; 212 | [self.columnHeights removeAllObjects]; 213 | [self.allItemAttributes removeAllObjects]; 214 | [self.sectionItemAttributes removeAllObjects]; 215 | 216 | NSInteger numberOfSections = [self.collectionView numberOfSections]; 217 | if (numberOfSections == 0) { 218 | return; 219 | } 220 | 221 | NSAssert([self.delegate conformsToProtocol:@protocol(CHTCollectionViewDelegateWaterfallLayout)], @"UICollectionView's delegate should conform to CHTCollectionViewDelegateWaterfallLayout protocol"); 222 | NSAssert(self.columnCount > 0 || [self.delegate respondsToSelector:@selector(collectionView:layout:columnCountForSection:)], @"UICollectionViewWaterfallLayout's columnCount should be greater than 0, or delegate must implement columnCountForSection:"); 223 | 224 | // Initialize variables 225 | NSInteger idx = 0; 226 | 227 | for (NSInteger section = 0; section < numberOfSections; section++) { 228 | NSInteger columnCount = [self columnCountForSection:section]; 229 | NSMutableArray *sectionColumnHeights = [NSMutableArray arrayWithCapacity:columnCount]; 230 | for (idx = 0; idx < columnCount; idx++) { 231 | [sectionColumnHeights addObject:@(0)]; 232 | } 233 | [self.columnHeights addObject:sectionColumnHeights]; 234 | } 235 | // Create attributes 236 | CGFloat top = 0; 237 | UICollectionViewLayoutAttributes *attributes; 238 | 239 | for (NSInteger section = 0; section < numberOfSections; ++section) { 240 | /* 241 | * 1. Get section-specific metrics (minimumInteritemSpacing, sectionInset) 242 | */ 243 | CGFloat minimumInteritemSpacing; 244 | if ([self.delegate respondsToSelector:@selector(collectionView:layout:minimumInteritemSpacingForSectionAtIndex:)]) { 245 | minimumInteritemSpacing = [self.delegate collectionView:self.collectionView layout:self minimumInteritemSpacingForSectionAtIndex:section]; 246 | } else { 247 | minimumInteritemSpacing = self.minimumInteritemSpacing; 248 | } 249 | 250 | CGFloat columnSpacing = self.minimumColumnSpacing; 251 | if ([self.delegate respondsToSelector:@selector(collectionView:layout:minimumColumnSpacingForSectionAtIndex:)]) { 252 | columnSpacing = [self.delegate collectionView:self.collectionView layout:self minimumColumnSpacingForSectionAtIndex:section]; 253 | } 254 | 255 | UIEdgeInsets sectionInset; 256 | if ([self.delegate respondsToSelector:@selector(collectionView:layout:insetForSectionAtIndex:)]) { 257 | sectionInset = [self.delegate collectionView:self.collectionView layout:self insetForSectionAtIndex:section]; 258 | } else { 259 | sectionInset = self.sectionInset; 260 | } 261 | 262 | CGFloat width = self.collectionView.bounds.size.width - sectionInset.left - sectionInset.right; 263 | NSInteger columnCount = [self columnCountForSection:section]; 264 | CGFloat itemWidth = CHTFloorCGFloat((width - (columnCount - 1) * columnSpacing) / columnCount); 265 | 266 | /* 267 | * 2. Section header 268 | */ 269 | CGFloat headerHeight; 270 | if ([self.delegate respondsToSelector:@selector(collectionView:layout:heightForHeaderInSection:)]) { 271 | headerHeight = [self.delegate collectionView:self.collectionView layout:self heightForHeaderInSection:section]; 272 | } else { 273 | headerHeight = self.headerHeight; 274 | } 275 | 276 | UIEdgeInsets headerInset; 277 | if ([self.delegate respondsToSelector:@selector(collectionView:layout:insetForHeaderInSection:)]) { 278 | headerInset = [self.delegate collectionView:self.collectionView layout:self insetForHeaderInSection:section]; 279 | } else { 280 | headerInset = self.headerInset; 281 | } 282 | 283 | top += headerInset.top; 284 | 285 | if (headerHeight > 0) { 286 | attributes = [UICollectionViewLayoutAttributes layoutAttributesForSupplementaryViewOfKind:CHTCollectionElementKindSectionHeader withIndexPath:[NSIndexPath indexPathForItem:0 inSection:section]]; 287 | attributes.frame = CGRectMake(headerInset.left, 288 | top, 289 | self.collectionView.bounds.size.width - (headerInset.left + headerInset.right), 290 | headerHeight); 291 | 292 | self.headersAttribute[@(section)] = attributes; 293 | [self.allItemAttributes addObject:attributes]; 294 | 295 | top = CGRectGetMaxY(attributes.frame) + headerInset.bottom; 296 | } 297 | 298 | top += sectionInset.top; 299 | for (idx = 0; idx < columnCount; idx++) { 300 | self.columnHeights[section][idx] = @(top); 301 | } 302 | 303 | /* 304 | * 3. Section items 305 | */ 306 | NSInteger itemCount = [self.collectionView numberOfItemsInSection:section]; 307 | NSMutableArray *itemAttributes = [NSMutableArray arrayWithCapacity:itemCount]; 308 | 309 | // Item will be put into shortest column. 310 | for (idx = 0; idx < itemCount; idx++) { 311 | NSIndexPath *indexPath = [NSIndexPath indexPathForItem:idx inSection:section]; 312 | NSUInteger columnIndex = [self nextColumnIndexForItem:idx inSection:section]; 313 | CGFloat xOffset = sectionInset.left + (itemWidth + columnSpacing) * columnIndex; 314 | CGFloat yOffset = [self.columnHeights[section][columnIndex] floatValue]; 315 | CGSize itemSize = [self.delegate collectionView:self.collectionView layout:self sizeForItemAtIndexPath:indexPath]; 316 | CGFloat itemHeight = 0; 317 | if (itemSize.height > 0 && itemSize.width > 0) { 318 | itemHeight = CHTFloorCGFloat(itemSize.height * itemWidth / itemSize.width); 319 | } 320 | 321 | attributes = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath]; 322 | attributes.frame = CGRectMake(xOffset, yOffset, itemWidth, itemHeight); 323 | [itemAttributes addObject:attributes]; 324 | [self.allItemAttributes addObject:attributes]; 325 | self.columnHeights[section][columnIndex] = @(CGRectGetMaxY(attributes.frame) + minimumInteritemSpacing); 326 | } 327 | 328 | [self.sectionItemAttributes addObject:itemAttributes]; 329 | 330 | /* 331 | * 4. Section footer 332 | */ 333 | CGFloat footerHeight; 334 | NSUInteger columnIndex = [self longestColumnIndexInSection:section]; 335 | if (((NSArray *)self.columnHeights[section]).count > 0) { 336 | top = [self.columnHeights[section][columnIndex] floatValue] - minimumInteritemSpacing + sectionInset.bottom; 337 | } else { 338 | top = 0; 339 | } 340 | 341 | if ([self.delegate respondsToSelector:@selector(collectionView:layout:heightForFooterInSection:)]) { 342 | footerHeight = [self.delegate collectionView:self.collectionView layout:self heightForFooterInSection:section]; 343 | } else { 344 | footerHeight = self.footerHeight; 345 | } 346 | 347 | UIEdgeInsets footerInset; 348 | if ([self.delegate respondsToSelector:@selector(collectionView:layout:insetForFooterInSection:)]) { 349 | footerInset = [self.delegate collectionView:self.collectionView layout:self insetForFooterInSection:section]; 350 | } else { 351 | footerInset = self.footerInset; 352 | } 353 | 354 | top += footerInset.top; 355 | 356 | if (footerHeight > 0) { 357 | attributes = [UICollectionViewLayoutAttributes layoutAttributesForSupplementaryViewOfKind:CHTCollectionElementKindSectionFooter withIndexPath:[NSIndexPath indexPathForItem:0 inSection:section]]; 358 | attributes.frame = CGRectMake(footerInset.left, 359 | top, 360 | self.collectionView.bounds.size.width - (footerInset.left + footerInset.right), 361 | footerHeight); 362 | 363 | self.footersAttribute[@(section)] = attributes; 364 | [self.allItemAttributes addObject:attributes]; 365 | 366 | top = CGRectGetMaxY(attributes.frame) + footerInset.bottom; 367 | } 368 | 369 | for (idx = 0; idx < columnCount; idx++) { 370 | self.columnHeights[section][idx] = @(top); 371 | } 372 | } // end of for (NSInteger section = 0; section < numberOfSections; ++section) 373 | 374 | // Build union rects 375 | idx = 0; 376 | NSInteger itemCounts = [self.allItemAttributes count]; 377 | while (idx < itemCounts) { 378 | CGRect unionRect = ((UICollectionViewLayoutAttributes *)self.allItemAttributes[idx]).frame; 379 | NSInteger rectEndIndex = MIN(idx + unionSize, itemCounts); 380 | 381 | for (NSInteger i = idx + 1; i < rectEndIndex; i++) { 382 | unionRect = CGRectUnion(unionRect, ((UICollectionViewLayoutAttributes *)self.allItemAttributes[i]).frame); 383 | } 384 | 385 | idx = rectEndIndex; 386 | 387 | [self.unionRects addObject:[NSValue valueWithCGRect:unionRect]]; 388 | } 389 | } 390 | 391 | - (CGSize)collectionViewContentSize { 392 | NSInteger numberOfSections = [self.collectionView numberOfSections]; 393 | if (numberOfSections == 0) { 394 | return CGSizeZero; 395 | } 396 | 397 | CGSize contentSize = self.collectionView.bounds.size; 398 | contentSize.height = [[[self.columnHeights lastObject] firstObject] floatValue]; 399 | 400 | if (contentSize.height < self.minimumContentHeight) { 401 | contentSize.height = self.minimumContentHeight; 402 | } 403 | 404 | return contentSize; 405 | } 406 | 407 | - (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)path { 408 | if (path.section >= [self.sectionItemAttributes count]) { 409 | return nil; 410 | } 411 | if (path.item >= [self.sectionItemAttributes[path.section] count]) { 412 | return nil; 413 | } 414 | return (self.sectionItemAttributes[path.section])[path.item]; 415 | } 416 | 417 | - (UICollectionViewLayoutAttributes *)layoutAttributesForSupplementaryViewOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath { 418 | UICollectionViewLayoutAttributes *attribute = nil; 419 | if ([kind isEqualToString:CHTCollectionElementKindSectionHeader]) { 420 | attribute = self.headersAttribute[@(indexPath.section)]; 421 | } else if ([kind isEqualToString:CHTCollectionElementKindSectionFooter]) { 422 | attribute = self.footersAttribute[@(indexPath.section)]; 423 | } 424 | return attribute; 425 | } 426 | 427 | - (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect { 428 | NSInteger i; 429 | NSInteger begin = 0, end = self.unionRects.count; 430 | NSMutableDictionary *cellAttrDict = [NSMutableDictionary dictionary]; 431 | NSMutableDictionary *supplHeaderAttrDict = [NSMutableDictionary dictionary]; 432 | NSMutableDictionary *supplFooterAttrDict = [NSMutableDictionary dictionary]; 433 | NSMutableDictionary *decorAttrDict = [NSMutableDictionary dictionary]; 434 | 435 | for (i = 0; i < self.unionRects.count; i++) { 436 | if (CGRectIntersectsRect(rect, [self.unionRects[i] CGRectValue])) { 437 | begin = i * unionSize; 438 | break; 439 | } 440 | } 441 | for (i = self.unionRects.count - 1; i >= 0; i--) { 442 | if (CGRectIntersectsRect(rect, [self.unionRects[i] CGRectValue])) { 443 | end = MIN((i + 1) * unionSize, self.allItemAttributes.count); 444 | break; 445 | } 446 | } 447 | for (i = begin; i < end; i++) { 448 | UICollectionViewLayoutAttributes *attr = self.allItemAttributes[i]; 449 | if (CGRectIntersectsRect(rect, attr.frame)) { 450 | switch (attr.representedElementCategory) { 451 | case UICollectionElementCategorySupplementaryView: 452 | if ([attr.representedElementKind isEqualToString:CHTCollectionElementKindSectionHeader]) { 453 | supplHeaderAttrDict[attr.indexPath] = attr; 454 | } else if ([attr.representedElementKind isEqualToString:CHTCollectionElementKindSectionFooter]) { 455 | supplFooterAttrDict[attr.indexPath] = attr; 456 | } 457 | break; 458 | case UICollectionElementCategoryDecorationView: 459 | decorAttrDict[attr.indexPath] = attr; 460 | break; 461 | case UICollectionElementCategoryCell: 462 | cellAttrDict[attr.indexPath] = attr; 463 | break; 464 | } 465 | } 466 | } 467 | 468 | NSArray *result = [cellAttrDict.allValues arrayByAddingObjectsFromArray:supplHeaderAttrDict.allValues]; 469 | result = [result arrayByAddingObjectsFromArray:supplFooterAttrDict.allValues]; 470 | result = [result arrayByAddingObjectsFromArray:decorAttrDict.allValues]; 471 | return result; 472 | } 473 | 474 | - (BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds { 475 | CGRect oldBounds = self.collectionView.bounds; 476 | if (CGRectGetWidth(newBounds) != CGRectGetWidth(oldBounds)) { 477 | return YES; 478 | } 479 | return NO; 480 | } 481 | 482 | #pragma mark - Private Methods 483 | 484 | /** 485 | * Find the shortest column. 486 | * 487 | * @return index for the shortest column 488 | */ 489 | - (NSUInteger)shortestColumnIndexInSection:(NSInteger)section { 490 | __block NSUInteger index = 0; 491 | __block CGFloat shortestHeight = MAXFLOAT; 492 | 493 | [self.columnHeights[section] enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { 494 | CGFloat height = [obj floatValue]; 495 | if (height < shortestHeight) { 496 | shortestHeight = height; 497 | index = idx; 498 | } 499 | }]; 500 | 501 | return index; 502 | } 503 | 504 | /** 505 | * Find the longest column. 506 | * 507 | * @return index for the longest column 508 | */ 509 | - (NSUInteger)longestColumnIndexInSection:(NSInteger)section { 510 | __block NSUInteger index = 0; 511 | __block CGFloat longestHeight = 0; 512 | 513 | [self.columnHeights[section] enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { 514 | CGFloat height = [obj floatValue]; 515 | if (height > longestHeight) { 516 | longestHeight = height; 517 | index = idx; 518 | } 519 | }]; 520 | 521 | return index; 522 | } 523 | 524 | /** 525 | * Find the index for the next column. 526 | * 527 | * @return index for the next column 528 | */ 529 | - (NSUInteger)nextColumnIndexForItem:(NSInteger)item inSection:(NSInteger)section { 530 | NSUInteger index = 0; 531 | NSInteger columnCount = [self columnCountForSection:section]; 532 | switch (self.itemRenderDirection) { 533 | case CHTCollectionViewWaterfallLayoutItemRenderDirectionShortestFirst: 534 | index = [self shortestColumnIndexInSection:section]; 535 | break; 536 | 537 | case CHTCollectionViewWaterfallLayoutItemRenderDirectionLeftToRight: 538 | index = (item % columnCount); 539 | break; 540 | 541 | case CHTCollectionViewWaterfallLayoutItemRenderDirectionRightToLeft: 542 | index = (columnCount - 1) - (item % columnCount); 543 | break; 544 | 545 | default: 546 | index = [self shortestColumnIndexInSection:section]; 547 | break; 548 | } 549 | return index; 550 | } 551 | 552 | @end 553 | -------------------------------------------------------------------------------- /Source/CHTCollectionViewWaterfallLayout.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CHTCollectionViewWaterfallLayout.swift 3 | // PinterestSwift 4 | // 5 | // Created by Nicholas Tau on 6/30/14. 6 | // Copyright (c) 2014 Nicholas Tau. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | private func < (lhs: T?, rhs: T?) -> Bool { 12 | switch (lhs, rhs) { 13 | case let (l?, r?): 14 | return l < r 15 | case (nil, _?): 16 | return true 17 | default: 18 | return false 19 | } 20 | } 21 | 22 | private func > (lhs: T?, rhs: T?) -> Bool { 23 | switch (lhs, rhs) { 24 | case let (l?, r?): 25 | return l > r 26 | default: 27 | return rhs < lhs 28 | } 29 | } 30 | 31 | 32 | @objc public protocol CHTCollectionViewDelegateWaterfallLayout: UICollectionViewDelegate { 33 | func collectionView(_ collectionView: UICollectionView, 34 | layout collectionViewLayout: UICollectionViewLayout, 35 | sizeForItemAt indexPath: IndexPath) -> CGSize 36 | 37 | @objc optional func collectionView(_ collectionView: UICollectionView, 38 | layout collectionViewLayout: UICollectionViewLayout, 39 | heightForHeaderIn section: Int) -> CGFloat 40 | 41 | @objc optional func collectionView(_ collectionView: UICollectionView, 42 | layout collectionViewLayout: UICollectionViewLayout, 43 | heightForFooterIn section: Int) -> CGFloat 44 | 45 | @objc optional func collectionView(_ collectionView: UICollectionView, 46 | layout collectionViewLayout: UICollectionViewLayout, 47 | insetsFor section: Int) -> UIEdgeInsets 48 | 49 | @objc optional func collectionView(_ collectionView: UICollectionView, 50 | layout collectionViewLayout: UICollectionViewLayout, 51 | minimumInteritemSpacingFor section: Int) -> CGFloat 52 | 53 | @objc optional func collectionView(_ collectionView: UICollectionView, 54 | layout collectionViewLayout: UICollectionViewLayout, 55 | columnCountFor section: Int) -> Int 56 | 57 | @available(*, unavailable, renamed: "collectionView(_:layout:sizeForItemAt:)") 58 | @objc optional func collectionView (_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, 59 | sizeForItemAtIndexPath indexPath: IndexPath) -> CGSize 60 | 61 | @available(*, unavailable, renamed: "collectionView(_:layout:heightForHeaderIn:)") 62 | @objc optional func collectionView (_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, 63 | heightForHeaderInSection section: Int) -> CGFloat 64 | 65 | @available(*, unavailable, renamed: "collectionView(_:layout:heightForFooterIn:)") 66 | @objc optional func collectionView (_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, 67 | heightForFooterInSection section: Int) -> CGFloat 68 | 69 | @available(*, unavailable, renamed: "collectionView(_:layout:insetsFor:)") 70 | @objc optional func collectionView (_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, 71 | insetForSectionAtIndex section: Int) -> UIEdgeInsets 72 | 73 | @available(*, unavailable, renamed: "collectionView(_:layout:minimumInteritemSpacingFor:)") 74 | @objc optional func collectionView (_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, 75 | minimumInteritemSpacingForSectionAtIndex section: Int) -> CGFloat 76 | 77 | @available(*, unavailable, renamed: "collectionView(_:layout:columnCountFor:)") 78 | @objc optional func collectionView (_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, 79 | columnCountForSection section: Int) -> Int 80 | } 81 | 82 | @available(*, unavailable, renamed: "CHTCollectionViewWaterfallLayout.ItemRenderDirection") 83 | public enum CHTCollectionViewWaterfallLayoutItemRenderDirection { } 84 | 85 | public extension CHTCollectionViewWaterfallLayout.ItemRenderDirection { 86 | @available(*, unavailable, renamed: "shortestFirst") 87 | static let chtCollectionViewWaterfallLayoutItemRenderDirectionShortestFirst = 0 88 | @available(*, unavailable, renamed: "leftToRight") 89 | static let chtCollectionViewWaterfallLayoutItemRenderDirectionLeftToRight = 1 90 | @available(*, unavailable, renamed: "rightToLeft") 91 | static let chtCollectionViewWaterfallLayoutItemRenderDirectionRightToLeft = 2 92 | } 93 | 94 | extension CHTCollectionViewWaterfallLayout { 95 | public enum ItemRenderDirection: Int { 96 | case shortestFirst 97 | case leftToRight 98 | case rightToLeft 99 | } 100 | 101 | public enum SectionInsetReference { 102 | case fromContentInset 103 | case fromLayoutMargins 104 | @available(iOS 11, *) 105 | case fromSafeArea 106 | } 107 | } 108 | 109 | @available(*, unavailable, renamed: "UICollectionView.elementKindSectionHeader") 110 | public let CHTCollectionElementKindSectionHeader = "CHTCollectionElementKindSectionHeader" 111 | @available(*, unavailable, renamed: "UICollectionView.elementKindSectionFooter") 112 | public let CHTCollectionElementKindSectionFooter = "CHTCollectionElementKindSectionFooter" 113 | public class CHTCollectionViewWaterfallLayout: UICollectionViewLayout { 114 | public var columnCount: Int = 2 { 115 | didSet { 116 | invalidateLayout() 117 | } 118 | } 119 | 120 | public var minimumColumnSpacing: CGFloat = 10 { 121 | didSet { 122 | invalidateLayout() 123 | } 124 | } 125 | 126 | public var minimumInteritemSpacing: CGFloat = 10 { 127 | didSet { 128 | invalidateLayout() 129 | } 130 | } 131 | 132 | public var headerHeight: CGFloat = 0 { 133 | didSet { 134 | invalidateLayout() 135 | } 136 | } 137 | 138 | public var footerHeight: CGFloat = 0 { 139 | didSet { 140 | invalidateLayout() 141 | } 142 | } 143 | 144 | public var sectionInset: UIEdgeInsets = .zero { 145 | didSet { 146 | invalidateLayout() 147 | } 148 | } 149 | 150 | public var itemRenderDirection: ItemRenderDirection = .shortestFirst { 151 | didSet { 152 | invalidateLayout() 153 | } 154 | } 155 | 156 | public var sectionInsetReference: SectionInsetReference = .fromContentInset { 157 | didSet { 158 | invalidateLayout() 159 | } 160 | } 161 | 162 | public var delegate: CHTCollectionViewDelegateWaterfallLayout? { 163 | get { 164 | return collectionView!.delegate as? CHTCollectionViewDelegateWaterfallLayout 165 | } 166 | } 167 | 168 | private var columnHeights: [[CGFloat]] = [] 169 | private var sectionItemAttributes: [[UICollectionViewLayoutAttributes]] = [] 170 | private var allItemAttributes: [UICollectionViewLayoutAttributes] = [] 171 | private var headersAttributes: [Int: UICollectionViewLayoutAttributes] = [:] 172 | private var footersAttributes: [Int: UICollectionViewLayoutAttributes] = [:] 173 | private var unionRects: [CGRect] = [] 174 | private let unionSize = 20 175 | 176 | private func columnCount(forSection section: Int) -> Int { 177 | return delegate?.collectionView?(collectionView!, layout: self, columnCountFor: section) ?? columnCount 178 | } 179 | 180 | private var collectionViewContentWidth: CGFloat { 181 | let insets: UIEdgeInsets 182 | switch sectionInsetReference { 183 | case .fromContentInset: 184 | insets = collectionView!.contentInset 185 | case .fromSafeArea: 186 | if #available(iOS 11.0, *) { 187 | insets = collectionView!.safeAreaInsets 188 | } else { 189 | insets = .zero 190 | } 191 | case .fromLayoutMargins: 192 | insets = collectionView!.layoutMargins 193 | } 194 | return collectionView!.bounds.size.width - insets.left - insets.right 195 | } 196 | 197 | private func collectionViewContentWidth(ofSection section: Int) -> CGFloat { 198 | let insets = delegate?.collectionView?(collectionView!, layout: self, insetsFor: section) ?? sectionInset 199 | return collectionViewContentWidth - insets.left - insets.right 200 | } 201 | 202 | @available(*, unavailable, renamed: "itemWidth(inSection:)") 203 | public func itemWidthInSectionAtIndex(_ section: Int) -> CGFloat { 204 | return itemWidth(inSection: section) 205 | } 206 | 207 | public func itemWidth(inSection section: Int) -> CGFloat { 208 | let columnCount = self.columnCount(forSection: section) 209 | let spaceColumCount = CGFloat(columnCount - 1) 210 | let width = collectionViewContentWidth(ofSection: section) 211 | return floor((width - (spaceColumCount * minimumColumnSpacing)) / CGFloat(columnCount)) 212 | } 213 | 214 | override public func prepare() { 215 | super.prepare() 216 | 217 | let numberOfSections = collectionView!.numberOfSections 218 | if numberOfSections == 0 { 219 | return 220 | } 221 | 222 | headersAttributes = [:] 223 | footersAttributes = [:] 224 | unionRects = [] 225 | allItemAttributes = [] 226 | sectionItemAttributes = [] 227 | columnHeights = (0 ..< numberOfSections).map { section in 228 | let columnCount = self.columnCount(forSection: section) 229 | let sectionColumnHeights = (0 ..< columnCount).map { CGFloat($0) } 230 | return sectionColumnHeights 231 | } 232 | 233 | var top: CGFloat = 0.0 234 | var attributes = UICollectionViewLayoutAttributes() 235 | 236 | for section in 0 ..< numberOfSections { 237 | // MARK: 1. Get section-specific metrics (minimumInteritemSpacing, sectionInset) 238 | let minimumInteritemSpacing = delegate?.collectionView?(collectionView!, layout: self, minimumInteritemSpacingFor: section) 239 | ?? self.minimumInteritemSpacing 240 | let sectionInsets = delegate?.collectionView?(collectionView!, layout: self, insetsFor: section) ?? self.sectionInset 241 | let columnCount = columnHeights[section].count 242 | let itemWidth = self.itemWidth(inSection: section) 243 | 244 | // MARK: 2. Section header 245 | let heightHeader = delegate?.collectionView?(collectionView!, layout: self, heightForHeaderIn: section) 246 | ?? self.headerHeight 247 | if heightHeader > 0 { 248 | attributes = UICollectionViewLayoutAttributes(forSupplementaryViewOfKind: UICollectionView.elementKindSectionHeader, with: IndexPath(row: 0, section: section)) 249 | attributes.frame = CGRect(x: 0, y: top, width: collectionView!.bounds.size.width, height: heightHeader) 250 | headersAttributes[section] = attributes 251 | allItemAttributes.append(attributes) 252 | 253 | top = attributes.frame.maxY 254 | } 255 | top += sectionInsets.top 256 | columnHeights[section] = [CGFloat](repeating: top, count: columnCount) 257 | 258 | // MARK: 3. Section items 259 | let itemCount = collectionView!.numberOfItems(inSection: section) 260 | var itemAttributes: [UICollectionViewLayoutAttributes] = [] 261 | 262 | // Item will be put into shortest column. 263 | for idx in 0 ..< itemCount { 264 | let indexPath = IndexPath(item: idx, section: section) 265 | 266 | let columnIndex = nextColumnIndexForItem(idx, inSection: section) 267 | let xOffset = sectionInsets.left + (itemWidth + minimumColumnSpacing) * CGFloat(columnIndex) 268 | 269 | let yOffset = columnHeights[section][columnIndex] 270 | var itemHeight: CGFloat = 0.0 271 | if let itemSize = delegate?.collectionView(collectionView!, layout: self, sizeForItemAt: indexPath), 272 | itemSize.height > 0 { 273 | itemHeight = itemSize.height 274 | if itemSize.width > 0 { 275 | itemHeight = floor(itemHeight * itemWidth / itemSize.width) 276 | } // else use default item width based on other parameters 277 | } 278 | 279 | attributes = UICollectionViewLayoutAttributes(forCellWith: indexPath) 280 | attributes.frame = CGRect(x: xOffset, y: yOffset, width: itemWidth, height: itemHeight) 281 | itemAttributes.append(attributes) 282 | allItemAttributes.append(attributes) 283 | columnHeights[section][columnIndex] = attributes.frame.maxY + minimumInteritemSpacing 284 | } 285 | sectionItemAttributes.append(itemAttributes) 286 | 287 | // MARK: 4. Section footer 288 | let columnIndex = longestColumnIndex(inSection: section) 289 | top = columnHeights[section][columnIndex] - minimumInteritemSpacing + sectionInsets.bottom 290 | let footerHeight = delegate?.collectionView?(collectionView!, layout: self, heightForFooterIn: section) ?? self.footerHeight 291 | 292 | if footerHeight > 0 { 293 | attributes = UICollectionViewLayoutAttributes(forSupplementaryViewOfKind: UICollectionView.elementKindSectionFooter, with: IndexPath(item: 0, section: section)) 294 | attributes.frame = CGRect(x: 0, y: top, width: collectionView!.bounds.size.width, height: footerHeight) 295 | footersAttributes[section] = attributes 296 | allItemAttributes.append(attributes) 297 | top = attributes.frame.maxY 298 | } 299 | 300 | columnHeights[section] = [CGFloat](repeating: top, count: columnCount) 301 | } 302 | 303 | var idx = 0 304 | let itemCounts = allItemAttributes.count 305 | while idx < itemCounts { 306 | let rect1 = allItemAttributes[idx].frame 307 | idx = min(idx + unionSize, itemCounts) - 1 308 | let rect2 = allItemAttributes[idx].frame 309 | unionRects.append(rect1.union(rect2)) 310 | idx += 1 311 | } 312 | } 313 | 314 | override public var collectionViewContentSize: CGSize { 315 | if collectionView!.numberOfSections == 0 { 316 | return .zero 317 | } 318 | 319 | var contentSize = collectionView!.bounds.size 320 | contentSize.width = collectionViewContentWidth 321 | 322 | if let height = columnHeights.last?.first { 323 | contentSize.height = height 324 | return contentSize 325 | } 326 | return .zero 327 | } 328 | 329 | override public func layoutAttributesForItem(at indexPath: IndexPath) -> UICollectionViewLayoutAttributes? { 330 | if indexPath.section >= sectionItemAttributes.count { 331 | return nil 332 | } 333 | let list = sectionItemAttributes[indexPath.section] 334 | if indexPath.item >= list.count { 335 | return nil 336 | } 337 | return list[indexPath.item] 338 | } 339 | 340 | override public func layoutAttributesForSupplementaryView(ofKind elementKind: String, at indexPath: IndexPath) -> UICollectionViewLayoutAttributes { 341 | var attribute: UICollectionViewLayoutAttributes? 342 | if elementKind == UICollectionView.elementKindSectionHeader { 343 | attribute = headersAttributes[indexPath.section] 344 | } else if elementKind == UICollectionView.elementKindSectionFooter { 345 | attribute = footersAttributes[indexPath.section] 346 | } 347 | return attribute ?? UICollectionViewLayoutAttributes() 348 | } 349 | 350 | override public func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? { 351 | var begin = 0, end = unionRects.count 352 | 353 | if let i = unionRects.firstIndex(where: { rect.intersects($0) }) { 354 | begin = i * unionSize 355 | } 356 | if let i = unionRects.lastIndex(where: { rect.intersects($0) }) { 357 | end = min((i + 1) * unionSize, allItemAttributes.count) 358 | } 359 | return allItemAttributes[begin.. Bool { 364 | return newBounds.width != collectionView?.bounds.width 365 | } 366 | 367 | /// Find the shortest column. 368 | /// 369 | /// - Returns: index for the shortest column 370 | private func shortestColumnIndex(inSection section: Int) -> Int { 371 | return columnHeights[section].enumerated() 372 | .min(by: { $0.element < $1.element })? 373 | .offset ?? 0 374 | } 375 | 376 | /// Find the longest column. 377 | /// 378 | /// - Returns: index for the longest column 379 | private func longestColumnIndex(inSection section: Int) -> Int { 380 | return columnHeights[section].enumerated() 381 | .max(by: { $0.element < $1.element })? 382 | .offset ?? 0 383 | } 384 | 385 | /// Find the index for the next column. 386 | /// 387 | /// - Returns: index for the next column 388 | private func nextColumnIndexForItem(_ item: Int, inSection section: Int) -> Int { 389 | var index = 0 390 | let columnCount = self.columnCount(forSection: section) 391 | switch itemRenderDirection { 392 | case .shortestFirst : 393 | index = shortestColumnIndex(inSection: section) 394 | case .leftToRight : 395 | index = item % columnCount 396 | case .rightToLeft: 397 | index = (columnCount - 1) - (item % columnCount) 398 | } 399 | return index 400 | } 401 | } 402 | --------------------------------------------------------------------------------