├── Classes ├── ios │ └── .gitkeep ├── osx │ └── .gitkeep ├── BMFloatingHeaderCollectionViewLayoutInvalidationContext.h ├── BMReusableContainerView.h ├── BMFloatingHeaderCollectionViewLayoutInvalidationContext.m ├── BMReusableContainerView.m ├── BMFloatingHeaderCollectionViewLayout.h └── BMFloatingHeaderCollectionViewLayout.m ├── Example ├── Pods │ ├── Headers │ │ ├── SSDataSources │ │ │ ├── SSSection.h │ │ │ ├── SSBaseTableCell.h │ │ │ ├── SSDataSources.h │ │ │ ├── SSArrayDataSource.h │ │ │ ├── SSBaseDataSource.h │ │ │ ├── SSCoreDataSource.h │ │ │ ├── SSBaseCollectionCell.h │ │ │ ├── SSBaseHeaderFooterView.h │ │ │ ├── SSSectionedDataSource.h │ │ │ └── SSBaseCollectionReusableView.h │ │ └── BMFloatingHeaderCollectionViewLayout │ │ │ ├── BMReusableContainerView.h │ │ │ ├── BMFloatingHeaderCollectionViewLayout.h │ │ │ └── BMFloatingHeaderCollectionViewLayoutInvalidationContext.h │ ├── BuildHeaders │ │ ├── SSDataSources │ │ │ ├── SSSection.h │ │ │ ├── SSBaseTableCell.h │ │ │ ├── SSDataSources.h │ │ │ ├── SSArrayDataSource.h │ │ │ ├── SSBaseDataSource.h │ │ │ ├── SSCoreDataSource.h │ │ │ ├── SSBaseCollectionCell.h │ │ │ ├── SSBaseHeaderFooterView.h │ │ │ ├── SSSectionedDataSource.h │ │ │ └── SSBaseCollectionReusableView.h │ │ └── BMFloatingHeaderCollectionViewLayout │ │ │ ├── BMReusableContainerView.h │ │ │ ├── BMFloatingHeaderCollectionViewLayout.h │ │ │ └── BMFloatingHeaderCollectionViewLayoutInvalidationContext.h │ ├── Pods-BMCollectionViewLayout.xcconfig │ ├── Pods-LayoutDemo-BMCollectionViewLayout.xcconfig │ ├── Pods-LayoutDemo-SSDataSources.xcconfig │ ├── Pods-BMCollectionViewLayout-prefix.pch │ ├── Pods-dummy.m │ ├── Pods-LayoutDemo-BMFloatingHeaderCollectionViewLayout.xcconfig │ ├── Pods-LayoutDemo-SSDataSources-prefix.pch │ ├── Pods-LayoutDemo-BMCollectionViewLayout-prefix.pch │ ├── Pods-LayoutDemo-BMFloatingHeaderCollectionViewLayout-prefix.pch │ ├── Pods-LayoutDemo-dummy.m │ ├── Pods-BMCollectionViewLayout-dummy.m │ ├── Pods-LayoutDemo-SSDataSources-dummy.m │ ├── Pods-LayoutDemo-BMCollectionViewLayout-dummy.m │ ├── Pods-LayoutDemo-BMFloatingHeaderCollectionViewLayout-dummy.m │ ├── Pods.xcconfig │ ├── Pods-BMCollectionViewLayout-Private.xcconfig │ ├── Pods-LayoutDemo-SSDataSources-Private.xcconfig │ ├── Pods-LayoutDemo-BMCollectionViewLayout-Private.xcconfig │ ├── Pods-LayoutDemo.xcconfig │ ├── Pods-LayoutDemo-BMFloatingHeaderCollectionViewLayout-Private.xcconfig │ ├── Pods-environment.h │ ├── Manifest.lock │ ├── SSDataSources │ │ ├── SSDataSources │ │ │ ├── SSBaseHeaderFooterView.m │ │ │ ├── SSDataSources.h │ │ │ ├── SSBaseHeaderFooterView.h │ │ │ ├── SSBaseCollectionReusableView.m │ │ │ ├── SSBaseCollectionReusableView.h │ │ │ ├── SSBaseCollectionCell.h │ │ │ ├── SSBaseTableCell.m │ │ │ ├── SSBaseTableCell.h │ │ │ ├── SSBaseCollectionCell.m │ │ │ ├── SSCoreDataSource.h │ │ │ ├── SSSection.m │ │ │ ├── SSSection.h │ │ │ ├── SSArrayDataSource.h │ │ │ ├── SSSectionedDataSource.h │ │ │ ├── SSArrayDataSource.m │ │ │ ├── SSBaseDataSource.h │ │ │ ├── SSSectionedDataSource.m │ │ │ ├── SSCoreDataSource.m │ │ │ └── SSBaseDataSource.m │ │ ├── LICENSE │ │ └── README.md │ ├── Pods-LayoutDemo-environment.h │ ├── Pods-acknowledgements.markdown │ ├── Local Podspecs │ │ ├── BMFloatingHeaderCollectionViewLayout.podspec │ │ └── BMCollectionViewLayout.podspec │ ├── Pods-acknowledgements.plist │ ├── Pods-LayoutDemo-acknowledgements.markdown │ ├── Pods-LayoutDemo-acknowledgements.plist │ ├── Pods-resources.sh │ └── Pods-LayoutDemo-resources.sh ├── LayoutDemo │ ├── en.lproj │ │ └── InfoPlist.strings │ ├── BMLViewController.h │ ├── BMLAppDelegate.h │ ├── LayoutDemo-Prefix.pch │ ├── main.m │ ├── Images.xcassets │ │ ├── AppIcon.appiconset │ │ │ └── Contents.json │ │ └── LaunchImage.launchimage │ │ │ └── Contents.json │ ├── LayoutDemo-Info.plist │ ├── BMLAppDelegate.m │ ├── Base.lproj │ │ ├── Main_iPad.storyboard │ │ └── Main_iPhone.storyboard │ └── BMLViewController.m ├── LayoutDemoTests │ ├── en.lproj │ │ └── InfoPlist.strings │ ├── LayoutDemoTests-Info.plist │ └── LayoutDemoTests.m ├── LayoutDemo.xcworkspace │ └── contents.xcworkspacedata ├── LayoutDemo.xcodeproj │ ├── project.xcworkspace │ │ └── contents.xcworkspacedata │ └── project.pbxproj ├── Podfile └── Podfile.lock ├── CHANGELOG.md ├── layout1.gif ├── layout2.gif ├── .gitignore ├── BMFloatingHeaderCollectionViewLayout.podspec ├── LICENSE ├── README.md └── Rakefile /Classes/ios/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Classes/osx/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Example/Pods/Headers/SSDataSources/SSSection.h: -------------------------------------------------------------------------------- 1 | ../../SSDataSources/SSDataSources/SSSection.h -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # BMCollectionViewLayout CHANGELOG 2 | 3 | ## 0.1.0 4 | 5 | Initial release. 6 | -------------------------------------------------------------------------------- /Example/LayoutDemo/en.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* Localized versions of Info.plist keys */ 2 | 3 | -------------------------------------------------------------------------------- /Example/Pods/BuildHeaders/SSDataSources/SSSection.h: -------------------------------------------------------------------------------- 1 | ../../SSDataSources/SSDataSources/SSSection.h -------------------------------------------------------------------------------- /Example/LayoutDemoTests/en.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* Localized versions of Info.plist keys */ 2 | 3 | -------------------------------------------------------------------------------- /Example/Pods/Headers/SSDataSources/SSBaseTableCell.h: -------------------------------------------------------------------------------- 1 | ../../SSDataSources/SSDataSources/SSBaseTableCell.h -------------------------------------------------------------------------------- /Example/Pods/Headers/SSDataSources/SSDataSources.h: -------------------------------------------------------------------------------- 1 | ../../SSDataSources/SSDataSources/SSDataSources.h -------------------------------------------------------------------------------- /Example/Pods/BuildHeaders/SSDataSources/SSBaseTableCell.h: -------------------------------------------------------------------------------- 1 | ../../SSDataSources/SSDataSources/SSBaseTableCell.h -------------------------------------------------------------------------------- /Example/Pods/BuildHeaders/SSDataSources/SSDataSources.h: -------------------------------------------------------------------------------- 1 | ../../SSDataSources/SSDataSources/SSDataSources.h -------------------------------------------------------------------------------- /Example/Pods/Headers/SSDataSources/SSArrayDataSource.h: -------------------------------------------------------------------------------- 1 | ../../SSDataSources/SSDataSources/SSArrayDataSource.h -------------------------------------------------------------------------------- /Example/Pods/Headers/SSDataSources/SSBaseDataSource.h: -------------------------------------------------------------------------------- 1 | ../../SSDataSources/SSDataSources/SSBaseDataSource.h -------------------------------------------------------------------------------- /Example/Pods/Headers/SSDataSources/SSCoreDataSource.h: -------------------------------------------------------------------------------- 1 | ../../SSDataSources/SSDataSources/SSCoreDataSource.h -------------------------------------------------------------------------------- /Example/Pods/BuildHeaders/SSDataSources/SSArrayDataSource.h: -------------------------------------------------------------------------------- 1 | ../../SSDataSources/SSDataSources/SSArrayDataSource.h -------------------------------------------------------------------------------- /Example/Pods/BuildHeaders/SSDataSources/SSBaseDataSource.h: -------------------------------------------------------------------------------- 1 | ../../SSDataSources/SSDataSources/SSBaseDataSource.h -------------------------------------------------------------------------------- /Example/Pods/BuildHeaders/SSDataSources/SSCoreDataSource.h: -------------------------------------------------------------------------------- 1 | ../../SSDataSources/SSDataSources/SSCoreDataSource.h -------------------------------------------------------------------------------- /Example/Pods/Headers/SSDataSources/SSBaseCollectionCell.h: -------------------------------------------------------------------------------- 1 | ../../SSDataSources/SSDataSources/SSBaseCollectionCell.h -------------------------------------------------------------------------------- /Example/Pods/Pods-BMCollectionViewLayout.xcconfig: -------------------------------------------------------------------------------- 1 | PODS_BMCOLLECTIONVIEWLAYOUT_OTHER_LDFLAGS = -framework UIKit -------------------------------------------------------------------------------- /layout1.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ethereal-engineer/BMFloatingHeaderCollectionViewLayout/HEAD/layout1.gif -------------------------------------------------------------------------------- /layout2.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ethereal-engineer/BMFloatingHeaderCollectionViewLayout/HEAD/layout2.gif -------------------------------------------------------------------------------- /Example/Pods/BuildHeaders/SSDataSources/SSBaseCollectionCell.h: -------------------------------------------------------------------------------- 1 | ../../SSDataSources/SSDataSources/SSBaseCollectionCell.h -------------------------------------------------------------------------------- /Example/Pods/Headers/SSDataSources/SSBaseHeaderFooterView.h: -------------------------------------------------------------------------------- 1 | ../../SSDataSources/SSDataSources/SSBaseHeaderFooterView.h -------------------------------------------------------------------------------- /Example/Pods/Headers/SSDataSources/SSSectionedDataSource.h: -------------------------------------------------------------------------------- 1 | ../../SSDataSources/SSDataSources/SSSectionedDataSource.h -------------------------------------------------------------------------------- /Example/Pods/BuildHeaders/SSDataSources/SSBaseHeaderFooterView.h: -------------------------------------------------------------------------------- 1 | ../../SSDataSources/SSDataSources/SSBaseHeaderFooterView.h -------------------------------------------------------------------------------- /Example/Pods/BuildHeaders/SSDataSources/SSSectionedDataSource.h: -------------------------------------------------------------------------------- 1 | ../../SSDataSources/SSDataSources/SSSectionedDataSource.h -------------------------------------------------------------------------------- /Example/Pods/Headers/BMFloatingHeaderCollectionViewLayout/BMReusableContainerView.h: -------------------------------------------------------------------------------- 1 | ../../../../Classes/BMReusableContainerView.h -------------------------------------------------------------------------------- /Example/Pods/BuildHeaders/BMFloatingHeaderCollectionViewLayout/BMReusableContainerView.h: -------------------------------------------------------------------------------- 1 | ../../../../Classes/BMReusableContainerView.h -------------------------------------------------------------------------------- /Example/Pods/Headers/SSDataSources/SSBaseCollectionReusableView.h: -------------------------------------------------------------------------------- 1 | ../../SSDataSources/SSDataSources/SSBaseCollectionReusableView.h -------------------------------------------------------------------------------- /Example/Pods/Pods-LayoutDemo-BMCollectionViewLayout.xcconfig: -------------------------------------------------------------------------------- 1 | PODS_LAYOUTDEMO_BMCOLLECTIONVIEWLAYOUT_OTHER_LDFLAGS = -framework UIKit -------------------------------------------------------------------------------- /Example/Pods/Pods-LayoutDemo-SSDataSources.xcconfig: -------------------------------------------------------------------------------- 1 | PODS_LAYOUTDEMO_SSDATASOURCES_OTHER_LDFLAGS = -framework CoreData -framework UIKit -------------------------------------------------------------------------------- /Example/Pods/BuildHeaders/SSDataSources/SSBaseCollectionReusableView.h: -------------------------------------------------------------------------------- 1 | ../../SSDataSources/SSDataSources/SSBaseCollectionReusableView.h -------------------------------------------------------------------------------- /Example/Pods/Pods-BMCollectionViewLayout-prefix.pch: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #endif 4 | 5 | #import "Pods-environment.h" 6 | -------------------------------------------------------------------------------- /Example/Pods/Headers/BMFloatingHeaderCollectionViewLayout/BMFloatingHeaderCollectionViewLayout.h: -------------------------------------------------------------------------------- 1 | ../../../../Classes/BMFloatingHeaderCollectionViewLayout.h -------------------------------------------------------------------------------- /Example/Pods/Pods-dummy.m: -------------------------------------------------------------------------------- 1 | #import 2 | @interface PodsDummy_Pods : NSObject 3 | @end 4 | @implementation PodsDummy_Pods 5 | @end 6 | -------------------------------------------------------------------------------- /Example/Pods/BuildHeaders/BMFloatingHeaderCollectionViewLayout/BMFloatingHeaderCollectionViewLayout.h: -------------------------------------------------------------------------------- 1 | ../../../../Classes/BMFloatingHeaderCollectionViewLayout.h -------------------------------------------------------------------------------- /Example/Pods/Pods-LayoutDemo-BMFloatingHeaderCollectionViewLayout.xcconfig: -------------------------------------------------------------------------------- 1 | PODS_LAYOUTDEMO_BMFLOATINGHEADERCOLLECTIONVIEWLAYOUT_OTHER_LDFLAGS = -framework UIKit -------------------------------------------------------------------------------- /Example/Pods/Pods-LayoutDemo-SSDataSources-prefix.pch: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #endif 4 | 5 | #import "Pods-LayoutDemo-environment.h" 6 | -------------------------------------------------------------------------------- /Example/Pods/Pods-LayoutDemo-BMCollectionViewLayout-prefix.pch: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #endif 4 | 5 | #import "Pods-LayoutDemo-environment.h" 6 | -------------------------------------------------------------------------------- /Example/Pods/Pods-LayoutDemo-BMFloatingHeaderCollectionViewLayout-prefix.pch: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #endif 4 | 5 | #import "Pods-LayoutDemo-environment.h" 6 | -------------------------------------------------------------------------------- /Example/Pods/Headers/BMFloatingHeaderCollectionViewLayout/BMFloatingHeaderCollectionViewLayoutInvalidationContext.h: -------------------------------------------------------------------------------- 1 | ../../../../Classes/BMFloatingHeaderCollectionViewLayoutInvalidationContext.h -------------------------------------------------------------------------------- /Example/Pods/Pods-LayoutDemo-dummy.m: -------------------------------------------------------------------------------- 1 | #import 2 | @interface PodsDummy_Pods_LayoutDemo : NSObject 3 | @end 4 | @implementation PodsDummy_Pods_LayoutDemo 5 | @end 6 | -------------------------------------------------------------------------------- /Example/Pods/BuildHeaders/BMFloatingHeaderCollectionViewLayout/BMFloatingHeaderCollectionViewLayoutInvalidationContext.h: -------------------------------------------------------------------------------- 1 | ../../../../Classes/BMFloatingHeaderCollectionViewLayoutInvalidationContext.h -------------------------------------------------------------------------------- /Example/LayoutDemo.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Example/Pods/Pods-BMCollectionViewLayout-dummy.m: -------------------------------------------------------------------------------- 1 | #import 2 | @interface PodsDummy_Pods_BMCollectionViewLayout : NSObject 3 | @end 4 | @implementation PodsDummy_Pods_BMCollectionViewLayout 5 | @end 6 | -------------------------------------------------------------------------------- /Example/Pods/Pods-LayoutDemo-SSDataSources-dummy.m: -------------------------------------------------------------------------------- 1 | #import 2 | @interface PodsDummy_Pods_LayoutDemo_SSDataSources : NSObject 3 | @end 4 | @implementation PodsDummy_Pods_LayoutDemo_SSDataSources 5 | @end 6 | -------------------------------------------------------------------------------- /Example/LayoutDemo.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Example/Pods/Pods-LayoutDemo-BMCollectionViewLayout-dummy.m: -------------------------------------------------------------------------------- 1 | #import 2 | @interface PodsDummy_Pods_LayoutDemo_BMCollectionViewLayout : NSObject 3 | @end 4 | @implementation PodsDummy_Pods_LayoutDemo_BMCollectionViewLayout 5 | @end 6 | -------------------------------------------------------------------------------- /Example/Podfile: -------------------------------------------------------------------------------- 1 | platform :ios, '7.1' 2 | 3 | target :LayoutDemo do 4 | 5 | pod 'BMFloatingHeaderCollectionViewLayout', :path => '../BMFloatingHeaderCollectionViewLayout.podspec' 6 | pod 'SSDataSources' 7 | 8 | end 9 | 10 | target :LayoutDemoTests do 11 | end 12 | -------------------------------------------------------------------------------- /Example/Pods/Pods-LayoutDemo-BMFloatingHeaderCollectionViewLayout-dummy.m: -------------------------------------------------------------------------------- 1 | #import 2 | @interface PodsDummy_Pods_LayoutDemo_BMFloatingHeaderCollectionViewLayout : NSObject 3 | @end 4 | @implementation PodsDummy_Pods_LayoutDemo_BMFloatingHeaderCollectionViewLayout 5 | @end 6 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # OS X 2 | .DS_Store 3 | 4 | # Xcode 5 | build/ 6 | *.pbxuser 7 | !default.pbxuser 8 | *.mode1v3 9 | !default.mode1v3 10 | *.mode2v3 11 | !default.mode2v3 12 | *.perspectivev3 13 | !default.perspectivev3 14 | xcuserdata 15 | *.xccheckout 16 | profile 17 | *.moved-aside 18 | DerivedData 19 | *.hmap 20 | *.ipa 21 | 22 | -------------------------------------------------------------------------------- /Example/LayoutDemo/BMLViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // BMLViewController.h 3 | // LayoutDemo 4 | // 5 | // Created by Adam Iredale on 26/03/2014. 6 | // Copyright (c) 2014 Bionic Monocle Pty Ltd. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface BMLViewController : UICollectionViewController 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /Example/Pods/Pods.xcconfig: -------------------------------------------------------------------------------- 1 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 2 | HEADER_SEARCH_PATHS = "${PODS_ROOT}/Headers" "${PODS_ROOT}/Headers/BMCollectionViewLayout" 3 | OTHER_CFLAGS = $(inherited) "-isystem${PODS_ROOT}/Headers" "-isystem${PODS_ROOT}/Headers/BMCollectionViewLayout" 4 | OTHER_LDFLAGS = -ObjC -framework UIKit 5 | PODS_ROOT = ${SRCROOT}/Pods -------------------------------------------------------------------------------- /Example/LayoutDemo/BMLAppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // BMLAppDelegate.h 3 | // LayoutDemo 4 | // 5 | // Created by Adam Iredale on 26/03/2014. 6 | // Copyright (c) 2014 Bionic Monocle Pty Ltd. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface BMLAppDelegate : UIResponder 12 | 13 | @property (strong, nonatomic) UIWindow *window; 14 | 15 | @end 16 | -------------------------------------------------------------------------------- /Example/Pods/Pods-BMCollectionViewLayout-Private.xcconfig: -------------------------------------------------------------------------------- 1 | #include "Pods-BMCollectionViewLayout.xcconfig" 2 | GCC_PREPROCESSOR_DEFINITIONS = COCOAPODS=1 3 | HEADER_SEARCH_PATHS = "${PODS_ROOT}/BuildHeaders" "${PODS_ROOT}/BuildHeaders/BMCollectionViewLayout" "${PODS_ROOT}/Headers" "${PODS_ROOT}/Headers/BMCollectionViewLayout" 4 | OTHER_LDFLAGS = -ObjC ${PODS_BMCOLLECTIONVIEWLAYOUT_OTHER_LDFLAGS} 5 | PODS_ROOT = ${SRCROOT} -------------------------------------------------------------------------------- /Example/LayoutDemo/LayoutDemo-Prefix.pch: -------------------------------------------------------------------------------- 1 | // 2 | // Prefix header 3 | // 4 | // The contents of this file are implicitly included at the beginning of every source file. 5 | // 6 | 7 | #import 8 | 9 | #ifndef __IPHONE_5_0 10 | #warning "This project uses features only available in iOS SDK 5.0 and later." 11 | #endif 12 | 13 | #ifdef __OBJC__ 14 | #import 15 | #import 16 | #endif 17 | -------------------------------------------------------------------------------- /Example/Pods/Pods-LayoutDemo-SSDataSources-Private.xcconfig: -------------------------------------------------------------------------------- 1 | #include "Pods-LayoutDemo-SSDataSources.xcconfig" 2 | GCC_PREPROCESSOR_DEFINITIONS = COCOAPODS=1 3 | HEADER_SEARCH_PATHS = "${PODS_ROOT}/BuildHeaders" "${PODS_ROOT}/BuildHeaders/SSDataSources" "${PODS_ROOT}/Headers" "${PODS_ROOT}/Headers/BMFloatingHeaderCollectionViewLayout" "${PODS_ROOT}/Headers/SSDataSources" 4 | OTHER_LDFLAGS = -ObjC ${PODS_LAYOUTDEMO_SSDATASOURCES_OTHER_LDFLAGS} 5 | PODS_ROOT = ${SRCROOT} -------------------------------------------------------------------------------- /Example/LayoutDemo/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // LayoutDemo 4 | // 5 | // Created by Adam Iredale on 26/03/2014. 6 | // Copyright (c) 2014 Bionic Monocle Pty Ltd. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | #import "BMLAppDelegate.h" 12 | 13 | int main(int argc, char * argv[]) 14 | { 15 | @autoreleasepool { 16 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([BMLAppDelegate class])); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Example/Pods/Pods-LayoutDemo-BMCollectionViewLayout-Private.xcconfig: -------------------------------------------------------------------------------- 1 | #include "Pods-LayoutDemo-BMCollectionViewLayout.xcconfig" 2 | GCC_PREPROCESSOR_DEFINITIONS = COCOAPODS=1 3 | HEADER_SEARCH_PATHS = "${PODS_ROOT}/BuildHeaders" "${PODS_ROOT}/BuildHeaders/BMCollectionViewLayout" "${PODS_ROOT}/Headers" "${PODS_ROOT}/Headers/BMCollectionViewLayout" "${PODS_ROOT}/Headers/SSDataSources" 4 | OTHER_LDFLAGS = -ObjC ${PODS_LAYOUTDEMO_BMCOLLECTIONVIEWLAYOUT_OTHER_LDFLAGS} 5 | PODS_ROOT = ${SRCROOT} -------------------------------------------------------------------------------- /Example/Pods/Pods-LayoutDemo.xcconfig: -------------------------------------------------------------------------------- 1 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 2 | HEADER_SEARCH_PATHS = "${PODS_ROOT}/Headers" "${PODS_ROOT}/Headers/BMFloatingHeaderCollectionViewLayout" "${PODS_ROOT}/Headers/SSDataSources" 3 | OTHER_CFLAGS = $(inherited) -isystem "${PODS_ROOT}/Headers" -isystem "${PODS_ROOT}/Headers/BMFloatingHeaderCollectionViewLayout" -isystem "${PODS_ROOT}/Headers/SSDataSources" 4 | OTHER_LDFLAGS = -ObjC -framework CoreData -framework UIKit 5 | PODS_ROOT = ${SRCROOT}/Pods -------------------------------------------------------------------------------- /Example/Pods/Pods-LayoutDemo-BMFloatingHeaderCollectionViewLayout-Private.xcconfig: -------------------------------------------------------------------------------- 1 | #include "Pods-LayoutDemo-BMFloatingHeaderCollectionViewLayout.xcconfig" 2 | GCC_PREPROCESSOR_DEFINITIONS = COCOAPODS=1 3 | HEADER_SEARCH_PATHS = "${PODS_ROOT}/BuildHeaders" "${PODS_ROOT}/BuildHeaders/BMFloatingHeaderCollectionViewLayout" "${PODS_ROOT}/Headers" "${PODS_ROOT}/Headers/BMFloatingHeaderCollectionViewLayout" "${PODS_ROOT}/Headers/SSDataSources" 4 | OTHER_LDFLAGS = -ObjC ${PODS_LAYOUTDEMO_BMFLOATINGHEADERCOLLECTIONVIEWLAYOUT_OTHER_LDFLAGS} 5 | PODS_ROOT = ${SRCROOT} -------------------------------------------------------------------------------- /Classes/BMFloatingHeaderCollectionViewLayoutInvalidationContext.h: -------------------------------------------------------------------------------- 1 | // 2 | // BMFloatingHeaderCollectionViewLayoutInvalidationContext.h 3 | // Pods 4 | // 5 | // Created by Adam Iredale on 28/03/2014. 6 | // 7 | // 8 | 9 | #import 10 | 11 | @interface BMFloatingHeaderCollectionViewLayoutInvalidationContext : UICollectionViewLayoutInvalidationContext 12 | 13 | @property (nonatomic, assign) BOOL invalidateOnlyDetailHeader; 14 | 15 | - (instancetype)initWithInvalidationForDetailHeaderOnly:(BOOL)invalidateDetailHeaderOnly; 16 | 17 | @end 18 | -------------------------------------------------------------------------------- /Example/Pods/Pods-environment.h: -------------------------------------------------------------------------------- 1 | 2 | // To check if a library is compiled with CocoaPods you 3 | // can use the `COCOAPODS` macro definition which is 4 | // defined in the xcconfigs so it is available in 5 | // headers also when they are imported in the client 6 | // project. 7 | 8 | 9 | // BMCollectionViewLayout 10 | #define COCOAPODS_POD_AVAILABLE_BMCollectionViewLayout 11 | #define COCOAPODS_VERSION_MAJOR_BMCollectionViewLayout 0 12 | #define COCOAPODS_VERSION_MINOR_BMCollectionViewLayout 1 13 | #define COCOAPODS_VERSION_PATCH_BMCollectionViewLayout 0 14 | 15 | -------------------------------------------------------------------------------- /Example/Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - BMFloatingHeaderCollectionViewLayout (0.1.0) 3 | - SSDataSources (0.6.0) 4 | 5 | DEPENDENCIES: 6 | - BMFloatingHeaderCollectionViewLayout (from `../BMFloatingHeaderCollectionViewLayout.podspec`) 7 | - SSDataSources 8 | 9 | EXTERNAL SOURCES: 10 | BMFloatingHeaderCollectionViewLayout: 11 | :path: ../BMFloatingHeaderCollectionViewLayout.podspec 12 | 13 | SPEC CHECKSUMS: 14 | BMFloatingHeaderCollectionViewLayout: f8221691a946c319ee1d76325cf579a782c82126 15 | SSDataSources: cea022d78248c159e8c374161fdf2c8a21fffd41 16 | 17 | COCOAPODS: 0.32.1 18 | -------------------------------------------------------------------------------- /Example/Pods/Manifest.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - BMFloatingHeaderCollectionViewLayout (0.1.0) 3 | - SSDataSources (0.6.0) 4 | 5 | DEPENDENCIES: 6 | - BMFloatingHeaderCollectionViewLayout (from `../BMFloatingHeaderCollectionViewLayout.podspec`) 7 | - SSDataSources 8 | 9 | EXTERNAL SOURCES: 10 | BMFloatingHeaderCollectionViewLayout: 11 | :path: ../BMFloatingHeaderCollectionViewLayout.podspec 12 | 13 | SPEC CHECKSUMS: 14 | BMFloatingHeaderCollectionViewLayout: f8221691a946c319ee1d76325cf579a782c82126 15 | SSDataSources: cea022d78248c159e8c374161fdf2c8a21fffd41 16 | 17 | COCOAPODS: 0.32.1 18 | -------------------------------------------------------------------------------- /Example/Pods/SSDataSources/SSDataSources/SSBaseHeaderFooterView.m: -------------------------------------------------------------------------------- 1 | // 2 | // SSBaseHeaderFooterView.m 3 | // ExampleSSDataSources 4 | // 5 | // Created by Jonathan Hersh on 8/29/13. 6 | // Copyright (c) 2013 Splinesoft. All rights reserved. 7 | // 8 | 9 | #import "SSBaseHeaderFooterView.h" 10 | 11 | @implementation SSBaseHeaderFooterView 12 | 13 | + (NSString *)identifier { 14 | return NSStringFromClass(self); 15 | } 16 | 17 | - (instancetype)init { 18 | if ((self = [self initWithReuseIdentifier:[[self class] identifier]])) { 19 | 20 | } 21 | 22 | return self; 23 | } 24 | 25 | @end 26 | -------------------------------------------------------------------------------- /Example/Pods/SSDataSources/SSDataSources/SSDataSources.h: -------------------------------------------------------------------------------- 1 | // 2 | // SSDataSources.h 3 | // SSDataSources 4 | // 5 | // Created by Jonathan Hersh on 6/8/13. 6 | // Copyright (c) 2013 Jonathan Hersh. All rights reserved. 7 | // 8 | 9 | #ifndef __SS_DATASOURCES__ 10 | #define __SS_DATASOURCES__ 11 | 12 | #import "SSBaseTableCell.h" 13 | #import "SSBaseCollectionCell.h" 14 | #import "SSBaseCollectionReusableView.h" 15 | #import "SSBaseHeaderFooterView.h" 16 | #import "SSSection.h" 17 | 18 | #import "SSBaseDataSource.h" 19 | #import "SSSectionedDataSource.h" 20 | #import "SSArrayDataSource.h" 21 | #import "SSCoreDataSource.h" 22 | 23 | #endif 24 | -------------------------------------------------------------------------------- /Classes/BMReusableContainerView.h: -------------------------------------------------------------------------------- 1 | // 2 | // BMReusableContainerView.h 3 | // Pods 4 | // 5 | // Created by Adam Iredale on 28/03/2014. 6 | // 7 | // 8 | 9 | #import 10 | 11 | /** 12 | * A container view that will embed any other kind of UIView-based class that it is given, 13 | * freeing developers of having to use UICollectionReusableView as a base class for supplementary 14 | * and decoration views 15 | */ 16 | 17 | @interface BMReusableContainerView : UICollectionReusableView 18 | /** 19 | * UIView object embedded and automatically resized to fit the container 20 | */ 21 | @property (nonatomic, strong) UIView *embeddedView; 22 | 23 | @end 24 | -------------------------------------------------------------------------------- /Example/Pods/SSDataSources/SSDataSources/SSBaseHeaderFooterView.h: -------------------------------------------------------------------------------- 1 | // 2 | // SSBaseHeaderFooterView.h 3 | // ExampleSSDataSources 4 | // 5 | // Created by Jonathan Hersh on 8/29/13. 6 | // Copyright (c) 2013 Splinesoft. All rights reserved. 7 | // 8 | 9 | /** 10 | * A simple header/footer class for tableviews. 11 | * Subclass me if necessary. 12 | */ 13 | 14 | #import 15 | 16 | @interface SSBaseHeaderFooterView : UITableViewHeaderFooterView 17 | 18 | /** 19 | * Reuse identifier. You probably don't need to override this. 20 | */ 21 | + (NSString *) identifier; 22 | 23 | /** 24 | * Default constructor. 25 | * Automatically uses a reuse identifier as defined in `+identifier`. 26 | */ 27 | - (instancetype) init; 28 | 29 | @end 30 | -------------------------------------------------------------------------------- /Example/LayoutDemoTests/LayoutDemoTests-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIdentifier 10 | com.bionicmonocle.${PRODUCT_NAME:rfc1034identifier} 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundlePackageType 14 | BNDL 15 | CFBundleShortVersionString 16 | 1.0 17 | CFBundleSignature 18 | ???? 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /Example/LayoutDemoTests/LayoutDemoTests.m: -------------------------------------------------------------------------------- 1 | // 2 | // LayoutDemoTests.m 3 | // LayoutDemoTests 4 | // 5 | // Created by Adam Iredale on 26/03/2014. 6 | // Copyright (c) 2014 Bionic Monocle Pty Ltd. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface LayoutDemoTests : XCTestCase 12 | 13 | @end 14 | 15 | @implementation LayoutDemoTests 16 | 17 | - (void)setUp 18 | { 19 | [super setUp]; 20 | // Put setup code here. This method is called before the invocation of each test method in the class. 21 | } 22 | 23 | - (void)tearDown 24 | { 25 | // Put teardown code here. This method is called after the invocation of each test method in the class. 26 | [super tearDown]; 27 | } 28 | 29 | - (void)testExample 30 | { 31 | XCTFail(@"No implementation for \"%s\"", __PRETTY_FUNCTION__); 32 | } 33 | 34 | @end 35 | -------------------------------------------------------------------------------- /Example/Pods/Pods-LayoutDemo-environment.h: -------------------------------------------------------------------------------- 1 | 2 | // To check if a library is compiled with CocoaPods you 3 | // can use the `COCOAPODS` macro definition which is 4 | // defined in the xcconfigs so it is available in 5 | // headers also when they are imported in the client 6 | // project. 7 | 8 | 9 | // BMFloatingHeaderCollectionViewLayout 10 | #define COCOAPODS_POD_AVAILABLE_BMFloatingHeaderCollectionViewLayout 11 | #define COCOAPODS_VERSION_MAJOR_BMFloatingHeaderCollectionViewLayout 0 12 | #define COCOAPODS_VERSION_MINOR_BMFloatingHeaderCollectionViewLayout 1 13 | #define COCOAPODS_VERSION_PATCH_BMFloatingHeaderCollectionViewLayout 0 14 | 15 | // SSDataSources 16 | #define COCOAPODS_POD_AVAILABLE_SSDataSources 17 | #define COCOAPODS_VERSION_MAJOR_SSDataSources 0 18 | #define COCOAPODS_VERSION_MINOR_SSDataSources 6 19 | #define COCOAPODS_VERSION_PATCH_SSDataSources 0 20 | 21 | -------------------------------------------------------------------------------- /BMFloatingHeaderCollectionViewLayout.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |s| 2 | s.name = "BMFloatingHeaderCollectionViewLayout" 3 | s.version = "1.0.0" 4 | s.summary = "A UICollectionViewLayout subclass that provides a floating header, detail header, orientation and empty view management" 5 | s.homepage = "https://github.com/iosengineer/BMFloatingHeaderCollectionViewLayout" 6 | s.license = 'MIT' 7 | s.author = { "Adam Iredale" => "@iosengineer" } 8 | s.source = { :git => "https://github.com/iosengineer/BMFloatingHeaderCollectionViewLayout.git", :tag => s.version.to_s } 9 | s.social_media_url = 'https://twitter.com/iosengineer' 10 | 11 | s.platform = :ios, '7.1' 12 | s.ios.deployment_target = '6.0' 13 | s.requires_arc = true 14 | 15 | s.source_files = 'Classes' 16 | s.framework = 'UIKit' 17 | end 18 | -------------------------------------------------------------------------------- /Example/Pods/SSDataSources/SSDataSources/SSBaseCollectionReusableView.m: -------------------------------------------------------------------------------- 1 | // 2 | // SSBaseCollectionReusableView.m 3 | // SSDataSources 4 | // 5 | // Created by Jonathan Hersh on 8/8/13. 6 | // Copyright (c) 2013 Splinesoft. All rights reserved. 7 | // 8 | 9 | #import "SSDataSources.h" 10 | 11 | @implementation SSBaseCollectionReusableView 12 | 13 | + (NSString *)identifier { 14 | return NSStringFromClass(self); 15 | } 16 | 17 | + (instancetype)supplementaryViewForCollectionView:(UICollectionView *)cv 18 | kind:(NSString *)kind 19 | indexPath:(NSIndexPath *)indexPath { 20 | 21 | return [cv dequeueReusableSupplementaryViewOfKind:kind 22 | withReuseIdentifier:[self identifier] 23 | forIndexPath:indexPath]; 24 | } 25 | 26 | @end 27 | -------------------------------------------------------------------------------- /Example/Pods/SSDataSources/SSDataSources/SSBaseCollectionReusableView.h: -------------------------------------------------------------------------------- 1 | // 2 | // SSBaseCollectionReusableView.h 3 | // SSDataSources 4 | // 5 | // Created by Jonathan Hersh on 8/8/13. 6 | // Copyright (c) 2013 Splinesoft. All rights reserved. 7 | // 8 | 9 | /** 10 | * A simple base collection reusable view. Subclass me if necessary. 11 | */ 12 | 13 | #import 14 | 15 | @interface SSBaseCollectionReusableView : UICollectionReusableView 16 | 17 | /** 18 | * Dequeues a supplementary view from collectionView, or if there are no cells of the 19 | * receiver's type in the queue, creates a new view. 20 | */ 21 | + (instancetype) supplementaryViewForCollectionView:(UICollectionView *)cv 22 | kind:(NSString *)kind 23 | indexPath:(NSIndexPath *)indexPath; 24 | 25 | + (NSString *) identifier; 26 | 27 | @end 28 | -------------------------------------------------------------------------------- /Classes/BMFloatingHeaderCollectionViewLayoutInvalidationContext.m: -------------------------------------------------------------------------------- 1 | // 2 | // BMFloatingHeaderCollectionViewLayoutInvalidationContext.m 3 | // Pods 4 | // 5 | // Created by Adam Iredale on 28/03/2014. 6 | // 7 | // 8 | 9 | #import "BMFloatingHeaderCollectionViewLayoutInvalidationContext.h" 10 | 11 | @implementation BMFloatingHeaderCollectionViewLayoutInvalidationContext 12 | 13 | #pragma mark - Init 14 | 15 | - (instancetype)initWithInvalidationForDetailHeaderOnly:(BOOL)invalidateDetailHeaderOnly 16 | { 17 | self = [super init]; 18 | if (self) 19 | { 20 | self.invalidateOnlyDetailHeader = invalidateDetailHeaderOnly; 21 | } 22 | return self; 23 | } 24 | 25 | #pragma mark - UICollectionViewLayoutInvalidationContext 26 | 27 | #pragma mark Init 28 | 29 | - (instancetype)init 30 | { 31 | // By default, invalidate everything 32 | self = [self initWithInvalidationForDetailHeaderOnly:NO]; 33 | return self; 34 | } 35 | 36 | @end 37 | -------------------------------------------------------------------------------- /Example/Pods/SSDataSources/SSDataSources/SSBaseCollectionCell.h: -------------------------------------------------------------------------------- 1 | // 2 | // SSBaseCollectionCell.h 3 | // SSDataSources 4 | // 5 | // Created by Jonathan Hersh on 6/24/13. 6 | // Copyright (c) 2013 Splinesoft. All rights reserved. 7 | // 8 | 9 | /** 10 | * Generic collection view cell. Subclass me! 11 | * Override `configureCell` to do one-time setup at cell creation, like creating subviews. 12 | * You probably don't need to override `identifier`. 13 | */ 14 | 15 | #import 16 | 17 | @interface SSBaseCollectionCell : UICollectionViewCell 18 | 19 | /** 20 | * Dequeues a collection cell from collectionView, or if there are no cells of the 21 | * receiver's type in the queue, creates a new cell and calls -configureCell. 22 | */ 23 | + (instancetype) cellForCollectionView:(UICollectionView *)collectionView 24 | indexPath:(NSIndexPath *)indexPath; 25 | 26 | + (NSString *) identifier; 27 | 28 | // Override me! 29 | - (void) configureCell; 30 | 31 | @end 32 | -------------------------------------------------------------------------------- /Example/Pods/SSDataSources/SSDataSources/SSBaseTableCell.m: -------------------------------------------------------------------------------- 1 | // 2 | // SSBaseTableCell.m 3 | // SSDataSources 4 | // 5 | // Created by Jonathan Hersh on 1/5/13. 6 | // Copyright (c) 2013 Jonathan Hersh. All rights reserved. 7 | // 8 | 9 | #import "SSBaseTableCell.h" 10 | 11 | @implementation SSBaseTableCell 12 | 13 | + (NSString *)identifier { 14 | return NSStringFromClass(self); 15 | } 16 | 17 | + (UITableViewCellStyle)cellStyle { 18 | return UITableViewCellStyleDefault; 19 | } 20 | 21 | + (instancetype)cellForTableView:(UITableView *)tableView { 22 | SSBaseTableCell *cell = (SSBaseTableCell *)[tableView dequeueReusableCellWithIdentifier:[self identifier]]; 23 | 24 | if (!cell) { 25 | cell = [[self alloc] initWithStyle:[self cellStyle] 26 | reuseIdentifier:[self identifier]]; 27 | 28 | [cell configureCell]; 29 | } 30 | 31 | return cell; 32 | } 33 | 34 | - (void) configureCell { 35 | // override me! 36 | } 37 | 38 | @end 39 | -------------------------------------------------------------------------------- /Example/Pods/SSDataSources/SSDataSources/SSBaseTableCell.h: -------------------------------------------------------------------------------- 1 | // 2 | // SSBaseTableCell.h 3 | // SSDataSources 4 | // 5 | // Created by Jonathan Hersh on 1/5/13. 6 | // Copyright (c) 2013 Jonathan Hersh. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | /** 12 | * A simple base table cell. Subclass me and override configureCell 13 | * to add custom one-time logic (e.g. creating subviews). 14 | * Override cellStyle to use a different style. 15 | * You probably don't need to override identifier. 16 | */ 17 | 18 | @interface SSBaseTableCell : UITableViewCell 19 | 20 | /** 21 | * Dequeues a table cell from tableView, or if there are no cells of the 22 | * receiver's type in the queue, creates a new cell and calls -configureCell. 23 | */ 24 | + (instancetype) cellForTableView:(UITableView *)tableView; 25 | 26 | + (NSString *) identifier; 27 | 28 | + (UITableViewCellStyle) cellStyle; 29 | 30 | // Called once for each cell after initial creation. Subclass me! 31 | - (void) configureCell; 32 | 33 | @end 34 | -------------------------------------------------------------------------------- /Example/LayoutDemo/Images.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "29x29", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "40x40", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "60x60", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "ipad", 20 | "size" : "29x29", 21 | "scale" : "1x" 22 | }, 23 | { 24 | "idiom" : "ipad", 25 | "size" : "29x29", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "ipad", 30 | "size" : "40x40", 31 | "scale" : "1x" 32 | }, 33 | { 34 | "idiom" : "ipad", 35 | "size" : "40x40", 36 | "scale" : "2x" 37 | }, 38 | { 39 | "idiom" : "ipad", 40 | "size" : "76x76", 41 | "scale" : "1x" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "size" : "76x76", 46 | "scale" : "2x" 47 | } 48 | ], 49 | "info" : { 50 | "version" : 1, 51 | "author" : "xcode" 52 | } 53 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2014 Adam Iredale 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 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all 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 | -------------------------------------------------------------------------------- /Example/Pods/SSDataSources/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013 Jonathan Hersh 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files (the 5 | "Software"), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be 12 | included in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /Example/Pods/SSDataSources/SSDataSources/SSBaseCollectionCell.m: -------------------------------------------------------------------------------- 1 | // 2 | // SSBaseCollectionCell.m 3 | // SSDataSources 4 | // 5 | // Created by Jonathan Hersh on 6/24/13. 6 | // 7 | // 8 | 9 | #import "SSBaseCollectionCell.h" 10 | 11 | @interface SSBaseCollectionCell () 12 | 13 | @property (nonatomic, assign) BOOL didCompleteSetup; 14 | 15 | @end 16 | 17 | @implementation SSBaseCollectionCell 18 | 19 | + (NSString *)identifier { 20 | return NSStringFromClass(self); 21 | } 22 | 23 | + (instancetype)cellForCollectionView:(UICollectionView *)collectionView 24 | indexPath:(NSIndexPath *)indexPath { 25 | 26 | SSBaseCollectionCell *cell = (SSBaseCollectionCell *)[collectionView dequeueReusableCellWithReuseIdentifier:[self identifier] 27 | forIndexPath:indexPath]; 28 | 29 | if (!cell.didCompleteSetup) { 30 | [cell configureCell]; 31 | 32 | cell.didCompleteSetup = YES; 33 | } 34 | 35 | return cell; 36 | } 37 | 38 | - (void)configureCell { 39 | // override me! 40 | } 41 | 42 | @end 43 | -------------------------------------------------------------------------------- /Example/Pods/Pods-acknowledgements.markdown: -------------------------------------------------------------------------------- 1 | # Acknowledgements 2 | This application makes use of the following third party libraries: 3 | 4 | ## BMCollectionViewLayout 5 | 6 | Copyright (c) 2014 Adam Iredale 7 | 8 | Permission is hereby granted, free of charge, to any person obtaining a copy 9 | of this software and associated documentation files (the "Software"), to deal 10 | in the Software without restriction, including without limitation the rights 11 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | copies of the Software, and to permit persons to whom the Software is 13 | furnished to do so, subject to the following conditions: 14 | 15 | The above copyright notice and this permission notice shall be included in 16 | all copies or substantial portions of the Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 24 | THE SOFTWARE. 25 | 26 | Generated by CocoaPods - http://cocoapods.org 27 | -------------------------------------------------------------------------------- /Example/LayoutDemo/Images.xcassets/LaunchImage.launchimage/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "orientation" : "portrait", 5 | "idiom" : "iphone", 6 | "extent" : "full-screen", 7 | "minimum-system-version" : "7.0", 8 | "scale" : "2x" 9 | }, 10 | { 11 | "orientation" : "portrait", 12 | "idiom" : "iphone", 13 | "subtype" : "retina4", 14 | "extent" : "full-screen", 15 | "minimum-system-version" : "7.0", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "orientation" : "portrait", 20 | "idiom" : "ipad", 21 | "extent" : "full-screen", 22 | "minimum-system-version" : "7.0", 23 | "scale" : "1x" 24 | }, 25 | { 26 | "orientation" : "landscape", 27 | "idiom" : "ipad", 28 | "extent" : "full-screen", 29 | "minimum-system-version" : "7.0", 30 | "scale" : "1x" 31 | }, 32 | { 33 | "orientation" : "portrait", 34 | "idiom" : "ipad", 35 | "extent" : "full-screen", 36 | "minimum-system-version" : "7.0", 37 | "scale" : "2x" 38 | }, 39 | { 40 | "orientation" : "landscape", 41 | "idiom" : "ipad", 42 | "extent" : "full-screen", 43 | "minimum-system-version" : "7.0", 44 | "scale" : "2x" 45 | } 46 | ], 47 | "info" : { 48 | "version" : 1, 49 | "author" : "xcode" 50 | } 51 | } -------------------------------------------------------------------------------- /Classes/BMReusableContainerView.m: -------------------------------------------------------------------------------- 1 | // 2 | // BMReusableContainerView.m 3 | // Pods 4 | // 5 | // Created by Adam Iredale on 28/03/2014. 6 | // 7 | // 8 | 9 | #import "BMReusableContainerView.h" 10 | 11 | @implementation BMReusableContainerView 12 | 13 | #pragma mark - Accessors 14 | 15 | - (void)setEmbeddedView:(UIView *)embeddedView 16 | { 17 | [_embeddedView removeFromSuperview]; 18 | _embeddedView = embeddedView; 19 | if (_embeddedView) 20 | { 21 | [_embeddedView setTranslatesAutoresizingMaskIntoConstraints:NO]; 22 | 23 | [self addSubview:_embeddedView]; 24 | 25 | [self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"|[emb]|" 26 | options:0 27 | metrics:nil 28 | views:@{@"emb":_embeddedView}]]; 29 | [self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[emb]|" 30 | options:0 31 | metrics:nil 32 | views:@{@"emb":_embeddedView}]]; 33 | [self setNeedsUpdateConstraints]; 34 | } 35 | } 36 | 37 | @end 38 | -------------------------------------------------------------------------------- /Example/Pods/Local Podspecs/BMFloatingHeaderCollectionViewLayout.podspec: -------------------------------------------------------------------------------- 1 | # 2 | # Be sure to run `pod spec lint NAME.podspec' to ensure this is a 3 | # valid spec and remove all comments before submitting the spec. 4 | # 5 | # To learn more about the attributes see http://guides.cocoapods.org/syntax/podspec.html 6 | # 7 | Pod::Spec.new do |s| 8 | s.name = "BMFloatingHeaderCollectionViewLayout" 9 | s.version = "0.1.0" 10 | s.summary = "A UICollectionViewLayout subclass that provides a floating header, detail header, orientation and empty view management" 11 | s.description = <<-DESC 12 | An optional longer description of BMCollectionViewLayout 13 | 14 | * Markdown format. 15 | * Don't worry about the indent, we strip it! 16 | DESC 17 | s.homepage = "http://EXAMPLE/NAME" 18 | s.screenshots = "www.example.com/screenshots_1", "www.example.com/screenshots_2" 19 | s.license = 'MIT' 20 | s.author = { "Adam Iredale" => "@iosengineer" } 21 | s.source = { :git => "http://EXAMPLE/NAME.git", :tag => s.version.to_s } 22 | s.social_media_url = 'https://twitter.com/iosengineer' 23 | 24 | s.platform = :ios, '7.1' 25 | s.ios.deployment_target = '6.0' 26 | s.requires_arc = true 27 | 28 | s.source_files = 'Classes' 29 | s.framework = 'UIKit' 30 | end 31 | -------------------------------------------------------------------------------- /Classes/BMFloatingHeaderCollectionViewLayout.h: -------------------------------------------------------------------------------- 1 | // 2 | // BMFloatingHeaderCollectionViewLayout.h 3 | // 4 | // Created by Adam Iredale on 15/11/2013. 5 | // Copyright (c) 2013 Bionic Monocle Pty Ltd. All rights reserved. 6 | // 7 | 8 | #import 9 | 10 | #pragma mark - Views 11 | 12 | #import "BMReusableContainerView.h" 13 | 14 | extern NSString *const BMFloatingHeaderCollectionViewLayoutHeaderTitleKind; 15 | extern NSString *const BMFloatingHeaderCollectionViewLayoutHeaderDetailKind; 16 | extern NSString *const BMFloatingHeaderCollectionViewLayoutPlaceholderKind; 17 | 18 | @class BMFloatingHeaderCollectionViewLayout; 19 | 20 | /** 21 | * Used by BMFloatingHeaderCollectionViewLayout to find the predetermined sizes of the items, 22 | * and supplimentary views, if applicable. BMFloatingHeaderCollectionViewLayout checks to see 23 | * if the collection view's datasource supports this protocol. 24 | */ 25 | @protocol BMFloatingHeaderCollectionViewLayoutDelegate 26 | 27 | - (CGFloat)collectionViewLayout:(BMFloatingHeaderCollectionViewLayout *)layout heightForItemAtIndexPath:(NSIndexPath *)indexPath; 28 | 29 | @end 30 | 31 | /** 32 | * Base class for taking care of a bunch of grunt work in UICollectionViewLayout 33 | */ 34 | 35 | @interface BMFloatingHeaderCollectionViewLayout : UICollectionViewLayout 36 | 37 | @property (nonatomic, assign) UIEdgeInsets itemInsets; 38 | @property (nonatomic, assign) UIEdgeInsets contentInsets; 39 | @property (nonatomic, assign) CGFloat headerTitleViewHeight; 40 | @property (nonatomic, assign) CGFloat headerDetailViewHeight; 41 | 42 | @end 43 | -------------------------------------------------------------------------------- /Example/Pods/Local Podspecs/BMCollectionViewLayout.podspec: -------------------------------------------------------------------------------- 1 | # 2 | # Be sure to run `pod spec lint NAME.podspec' to ensure this is a 3 | # valid spec and remove all comments before submitting the spec. 4 | # 5 | # To learn more about the attributes see http://guides.cocoapods.org/syntax/podspec.html 6 | # 7 | Pod::Spec.new do |s| 8 | s.name = "BMCollectionViewLayout" 9 | s.version = "0.1.0" 10 | s.summary = "A short description of BMCollectionViewLayout." 11 | s.description = <<-DESC 12 | An optional longer description of BMCollectionViewLayout 13 | 14 | * Markdown format. 15 | * Don't worry about the indent, we strip it! 16 | DESC 17 | s.homepage = "http://EXAMPLE/NAME" 18 | s.screenshots = "www.example.com/screenshots_1", "www.example.com/screenshots_2" 19 | s.license = 'MIT' 20 | s.author = { "Adam Iredale" => "adam@bionicmonocle.com" } 21 | s.source = { :git => "http://EXAMPLE/NAME.git", :tag => s.version.to_s } 22 | s.social_media_url = 'https://twitter.com/NAME' 23 | 24 | s.platform = :ios, '7.1' 25 | s.ios.deployment_target = '6.0' 26 | # s.osx.deployment_target = '10.7' 27 | s.requires_arc = true 28 | 29 | s.source_files = 'Classes' 30 | # s.resources = 'Resources' 31 | 32 | # s.ios.exclude_files = 'Classes/osx' 33 | # s.osx.exclude_files = 'Classes/ios' 34 | # s.public_header_files = 'Classes/**/*.h' 35 | # s.frameworks = 'SomeFramework', 'AnotherFramework' 36 | s.framework = 'UIKit' 37 | # s.dependency 'JSONKit', '~> 1.4' MASNRY!!! 38 | end 39 | -------------------------------------------------------------------------------- /Example/LayoutDemo/LayoutDemo-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleDisplayName 8 | ${PRODUCT_NAME} 9 | CFBundleExecutable 10 | ${EXECUTABLE_NAME} 11 | CFBundleIdentifier 12 | com.bionicmonocle.${PRODUCT_NAME:rfc1034identifier} 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 | UIMainStoryboardFile 28 | Main_iPhone 29 | UIMainStoryboardFile~ipad 30 | Main_iPad 31 | UIRequiredDeviceCapabilities 32 | 33 | armv7 34 | 35 | UISupportedInterfaceOrientations 36 | 37 | UIInterfaceOrientationPortrait 38 | UIInterfaceOrientationLandscapeLeft 39 | UIInterfaceOrientationLandscapeRight 40 | 41 | UISupportedInterfaceOrientations~ipad 42 | 43 | UIInterfaceOrientationPortrait 44 | UIInterfaceOrientationPortraitUpsideDown 45 | UIInterfaceOrientationLandscapeLeft 46 | UIInterfaceOrientationLandscapeRight 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /Example/Pods/SSDataSources/SSDataSources/SSCoreDataSource.h: -------------------------------------------------------------------------------- 1 | // 2 | // SSCoreDataSource.h 3 | // SSDataSources 4 | // 5 | // Created by Jonathan Hersh on 6/7/13. 6 | // Copyright (c) 2013 Splinesoft. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | #import "SSBaseDataSource.h" 12 | #import 13 | 14 | /** 15 | * Generic table/collectionview data source, useful when your data comes from an NSFetchedResultsController. 16 | * Optional: assign this object to be the FRC's delegate, in which case it'll make 17 | * updates in response to FRC events. 18 | */ 19 | 20 | @interface SSCoreDataSource : SSBaseDataSource 21 | 22 | /** 23 | * The data source's fetched results controller. You probably don't need to set this directly 24 | * as both initializers will do this for you. 25 | */ 26 | @property (nonatomic, strong) NSFetchedResultsController *controller; 27 | 28 | /** 29 | * Any error experienced during the most recent fetch. 30 | * nil if the fetch succeeded. 31 | */ 32 | @property (nonatomic, strong, readonly) NSError *fetchError; 33 | 34 | /** 35 | * Create a data source with a fetched results controller. 36 | * @param controller - the FRC backing this data source 37 | */ 38 | - (instancetype) initWithFetchedResultsController:(NSFetchedResultsController *)controller; 39 | 40 | /** 41 | * Create a data source with a fetch request and a managed object context. 42 | * Optionally, specify a section keypath. 43 | */ 44 | - (instancetype) initWithFetchRequest:(NSFetchRequest *)request 45 | inContext:(NSManagedObjectContext *)context 46 | sectionNameKeyPath:(NSString *)sectionNameKeyPath; 47 | 48 | /** 49 | * Determine an indexpath for the record with a given managed object ID. 50 | */ 51 | - (NSIndexPath *)indexPathForItemWithId:(NSManagedObjectID *)objectId; 52 | 53 | @end 54 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # BMFloatingHeaderCollectionViewLayout 2 | 3 | [![Version](http://cocoapod-badges.herokuapp.com/v/BMFloatingHeaderCollectionViewLayout/badge.png)](http://cocoadocs.org/docsets/BMFloatingHeaderCollectionViewLayout) 4 | [![Platform](http://cocoapod-badges.herokuapp.com/p/BMFloatingHeaderCollectionViewLayout/badge.png)](http://cocoadocs.org/docsets/BMFloatingHeaderCollectionViewLayout) 5 | 6 | ## Usage 7 | 8 | I can't find the words to describe what this does at the moment. I'm not feeling very eloquent today. So refer to the pretty moving pictures for details. 9 | 10 | In short, this UICollectionViewLayout subclass (and helper classes) gives you two slots above a collection view proper. One slot is for a header that will slide away on scroll, the other is for a header that will slide to the top of the screen on scroll but no further. These can contain any UIView subclass. 11 | 12 | It is written with performance in mind, handles all orientations and orientation changes and even has a way for you to specify an "empty" view to show when your dataset has no results. All in all, it's pretty handy. I apologise upfront for the ugly demo app but to make up for it, you can see how beautiful it looks in a real app - that's Kayako for iOS (second screenshot). 13 | 14 | ![Ugly Example App Demonstration](layout1.gif "Ugly Example App Demonstration") 15 | 16 | ![Beautiful App Demonstration](layout2.gif "Beautiful App Demonstration") 17 | 18 | To run the example project; clone the repo, and run `pod install` from the Example directory first. 19 | 20 | ## Installation 21 | 22 | BMFloatingHeaderCollectionViewLayout is available through [CocoaPods](http://cocoapods.org), to install 23 | it simply add the following line to your Podfile: 24 | 25 | pod "BMFloatingHeaderCollectionViewLayout" 26 | 27 | ## Author 28 | 29 | Adam Iredale, [@iosengineer](https://twitter.com/iosengineer) 30 | 31 | ## License 32 | 33 | BMFloatingHeaderCollectionViewLayout is available under the MIT license. See the LICENSE file for more info. 34 | 35 | -------------------------------------------------------------------------------- /Example/LayoutDemo/BMLAppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // BMLAppDelegate.m 3 | // LayoutDemo 4 | // 5 | // Created by Adam Iredale on 26/03/2014. 6 | // Copyright (c) 2014 Bionic Monocle Pty Ltd. All rights reserved. 7 | // 8 | 9 | #import "BMLAppDelegate.h" 10 | 11 | @implementation BMLAppDelegate 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 | -------------------------------------------------------------------------------- /Example/Pods/Pods-acknowledgements.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreferenceSpecifiers 6 | 7 | 8 | FooterText 9 | This application makes use of the following third party libraries: 10 | Title 11 | Acknowledgements 12 | Type 13 | PSGroupSpecifier 14 | 15 | 16 | FooterText 17 | Copyright (c) 2014 Adam Iredale <adam@bionicmonocle.com> 18 | 19 | Permission is hereby granted, free of charge, to any person obtaining a copy 20 | of this software and associated documentation files (the "Software"), to deal 21 | in the Software without restriction, including without limitation the rights 22 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 23 | copies of the Software, and to permit persons to whom the Software is 24 | furnished to do so, subject to the following conditions: 25 | 26 | The above copyright notice and this permission notice shall be included in 27 | all copies or substantial portions of the Software. 28 | 29 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 30 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 31 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 32 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 33 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 34 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 35 | THE SOFTWARE. 36 | 37 | Title 38 | BMCollectionViewLayout 39 | Type 40 | PSGroupSpecifier 41 | 42 | 43 | FooterText 44 | Generated by CocoaPods - http://cocoapods.org 45 | Title 46 | 47 | Type 48 | PSGroupSpecifier 49 | 50 | 51 | StringsTable 52 | Acknowledgements 53 | Title 54 | Acknowledgements 55 | 56 | 57 | -------------------------------------------------------------------------------- /Example/Pods/Pods-LayoutDemo-acknowledgements.markdown: -------------------------------------------------------------------------------- 1 | # Acknowledgements 2 | This application makes use of the following third party libraries: 3 | 4 | ## BMFloatingHeaderCollectionViewLayout 5 | 6 | Copyright (c) 2014 Adam Iredale 7 | 8 | Permission is hereby granted, free of charge, to any person obtaining a copy 9 | of this software and associated documentation files (the "Software"), to deal 10 | in the Software without restriction, including without limitation the rights 11 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | copies of the Software, and to permit persons to whom the Software is 13 | furnished to do so, subject to the following conditions: 14 | 15 | The above copyright notice and this permission notice shall be included in 16 | all copies or substantial portions of the Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 24 | THE SOFTWARE. 25 | 26 | 27 | ## SSDataSources 28 | 29 | Copyright (c) 2013 Jonathan Hersh 30 | 31 | Permission is hereby granted, free of charge, to any person obtaining 32 | a copy of this software and associated documentation files (the 33 | "Software"), to deal in the Software without restriction, including 34 | without limitation the rights to use, copy, modify, merge, publish, 35 | distribute, sublicense, and/or sell copies of the Software, and to 36 | permit persons to whom the Software is furnished to do so, subject to 37 | the following conditions: 38 | 39 | The above copyright notice and this permission notice shall be 40 | included in all copies or substantial portions of the Software. 41 | 42 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 43 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 44 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 45 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 46 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 47 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 48 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 49 | 50 | Generated by CocoaPods - http://cocoapods.org 51 | -------------------------------------------------------------------------------- /Example/Pods/SSDataSources/SSDataSources/SSSection.m: -------------------------------------------------------------------------------- 1 | // 2 | // SSSection.m 3 | // ExampleSSDataSources 4 | // 5 | // Created by Jonathan Hersh on 8/29/13. 6 | // Copyright (c) 2013 Splinesoft. All rights reserved. 7 | // 8 | 9 | #import "SSDataSources.h" 10 | 11 | @implementation SSSection 12 | 13 | - (id)init { 14 | if ((self = [super init])) { 15 | self.items = [NSMutableArray new]; 16 | self.headerClass = [SSBaseHeaderFooterView class]; 17 | self.footerClass = [SSBaseHeaderFooterView class]; 18 | } 19 | 20 | return self; 21 | } 22 | 23 | + (instancetype)sectionWithItems:(NSArray *)items { 24 | return [self sectionWithItems:items 25 | header:nil 26 | footer:nil 27 | identifier:nil]; 28 | } 29 | 30 | + (instancetype)sectionWithItems:(NSArray *)items 31 | header:(NSString *)header 32 | footer:(NSString *)footer 33 | identifier:(id)identifier { 34 | 35 | SSSection *section = [SSSection new]; 36 | 37 | if (items) 38 | [section.items addObjectsFromArray:items]; 39 | 40 | section.header = header; 41 | section.footer = footer; 42 | section.sectionIdentifier = identifier; 43 | 44 | return section; 45 | } 46 | 47 | + (instancetype)sectionWithNumberOfItems:(NSUInteger)numberOfItems { 48 | return [self sectionWithNumberOfItems:numberOfItems 49 | header:nil 50 | footer:nil 51 | identifier:nil]; 52 | } 53 | 54 | + (instancetype)sectionWithNumberOfItems:(NSUInteger)numberOfItems 55 | header:(NSString *)header 56 | footer:(NSString *)footer 57 | identifier:(id)identifier { 58 | 59 | NSMutableArray *array = [NSMutableArray new]; 60 | 61 | for (NSUInteger i = 0; i < numberOfItems; i++) 62 | [array addObject:@(i)]; 63 | 64 | return [self sectionWithItems:array 65 | header:header 66 | footer:footer 67 | identifier:identifier]; 68 | } 69 | 70 | - (NSUInteger)numberOfItems { 71 | return [self.items count]; 72 | } 73 | 74 | - (id)itemAtIndex:(NSUInteger)index { 75 | return self.items[index]; 76 | } 77 | 78 | #pragma mark - NSCopying 79 | 80 | - (id)copyWithZone:(NSZone *)zone { 81 | SSSection *newSection = [SSSection sectionWithItems:self.items]; 82 | newSection.header = self.header; 83 | newSection.footer = self.footer; 84 | newSection.headerClass = self.headerClass; 85 | newSection.footerClass = self.footerClass; 86 | newSection.headerHeight = self.headerHeight; 87 | newSection.footerHeight = self.footerHeight; 88 | newSection.sectionIdentifier = self.sectionIdentifier; 89 | 90 | return newSection; 91 | } 92 | 93 | @end 94 | -------------------------------------------------------------------------------- /Example/Pods/SSDataSources/SSDataSources/SSSection.h: -------------------------------------------------------------------------------- 1 | // 2 | // SSSection.h 3 | // ExampleSSDataSources 4 | // 5 | // Created by Jonathan Hersh on 8/29/13. 6 | // Copyright (c) 2013 Splinesoft. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | /** 12 | * SSSection models a single section in a multi-sectioned table or collection view 13 | * powered by `SSSectionedDataSource`. 14 | * It maintains an array of items appearing within its section, 15 | * plus a header and footer string. 16 | */ 17 | 18 | @interface SSSection : NSObject 19 | 20 | @property (nonatomic, strong) NSMutableArray *items; 21 | 22 | /** 23 | * I find it helpful to assign an identifier to each section 24 | * particularly when constructing tables that have dynamic numbers and types of sections. 25 | * This identifier is used in the `SSSectionedDataSource` helper method 26 | * indexOfSectionWithIdentifier:. 27 | */ 28 | @property (nonatomic, strong) id sectionIdentifier; 29 | 30 | /** 31 | * Simple strings to use for headers and footers. 32 | * Alternatively, you can use an `SSBaseHeaderFooterView`. 33 | * See the headerClass and footerClass properties. 34 | */ 35 | @property (nonatomic, copy) NSString *header; 36 | @property (nonatomic, copy) NSString *footer; 37 | 38 | /** 39 | * Optional custom classes to use for header and footer views. 40 | * Defaults to SSBaseHeaderFooterView. 41 | */ 42 | @property (nonatomic, weak) Class headerClass; 43 | @property (nonatomic, weak) Class footerClass; 44 | 45 | /** 46 | * Optional header and footer height. 47 | * Given that `tableView:heightForHeaderInSection:` is part of 48 | * UITableViewDelegate, NOT UITableViewDataSource, 49 | * SSDataSources does not provide an implementation. These properties 50 | * are merely helpers so that you can write simpler delegate code similar to this: 51 | * 52 | - (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section { 53 | return [mySectionedDataSource heightForHeaderInSection:section]; 54 | } 55 | * 56 | */ 57 | @property (nonatomic, assign) CGFloat headerHeight; 58 | @property (nonatomic, assign) CGFloat footerHeight; 59 | 60 | /** 61 | * Create a section with an array of items. 62 | */ 63 | + (instancetype) sectionWithItems:(NSArray *)items; 64 | + (instancetype) sectionWithItems:(NSArray *)items 65 | header:(NSString *)header 66 | footer:(NSString *)footer 67 | identifier:(id)identifier; 68 | 69 | /** 70 | * Sometimes I just need a section with a given number of cells, 71 | * and all the cell creation and configuration is handled with values stored elsewhere. 72 | * This method creates a section with the specified number of placeholder objects. 73 | */ 74 | + (instancetype) sectionWithNumberOfItems:(NSUInteger)numberOfItems; 75 | + (instancetype) sectionWithNumberOfItems:(NSUInteger)numberOfItems 76 | header:(NSString *)header 77 | footer:(NSString *)footer 78 | identifier:(id)identifier; 79 | 80 | /** 81 | * Return the number of items in this section. 82 | */ 83 | - (NSUInteger) numberOfItems; 84 | 85 | /** 86 | * Return the item at a particular index. 87 | */ 88 | - (id) itemAtIndex:(NSUInteger)index; 89 | 90 | @end 91 | -------------------------------------------------------------------------------- /Example/Pods/SSDataSources/SSDataSources/SSArrayDataSource.h: -------------------------------------------------------------------------------- 1 | // 2 | // SSArrayDataSource.h 3 | // SSDataSources 4 | // 5 | // Created by Jonathan Hersh on 6/7/13. 6 | // Copyright (c) 2013 Splinesoft. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | #import "SSBaseDataSource.h" 12 | #import 13 | 14 | /** 15 | * Data source for single-sectioned table and collection views. 16 | */ 17 | 18 | @interface SSArrayDataSource : SSBaseDataSource 19 | 20 | /** 21 | * Create a new array data source by specifying an array of items. 22 | */ 23 | - (instancetype) initWithItems:(NSArray *)items; 24 | 25 | /** 26 | * Create a new array data source by specifying a target and key path to observe 27 | * for array content. 28 | * 29 | * @param target - the object that the given key path is relative to 30 | * @param keyPath - a key path for that identifiers an NSArray of data for the receiver 31 | * 32 | * @warning The `target` parameter is strongly referenced by the receiver. Make 33 | * sure you don't create a retain cycle by having `target` also hold a 34 | * strong reference to the receiver. 35 | */ 36 | - (instancetype) initWithTarget:(id)target keyPath:(NSString *)keyPath; 37 | 38 | #pragma mark - Item access 39 | 40 | /** 41 | * Return the indexpath for a given item in the data source. 42 | */ 43 | - (NSIndexPath *) indexPathForItem:(id)item; 44 | 45 | /** 46 | * Helper for managed objects. As with `indexPathForItem`, but for managed object IDs. 47 | */ 48 | - (NSIndexPath *) indexPathForItemWithId:(NSManagedObjectID *)itemId; 49 | 50 | #pragma mark - All or None Operations 51 | 52 | /** 53 | * Returns all items in the data source. 54 | */ 55 | - (NSArray *) allItems; 56 | 57 | /** 58 | * Remove all objects in the data source. 59 | */ 60 | - (void) clearItems; 61 | 62 | /** 63 | * Remove all objects in the data source. 64 | * Alias for clearItems. 65 | */ 66 | - (void) removeAllItems; 67 | 68 | /** 69 | * Replace all items in the data source. 70 | * This will reload the table or collection view. 71 | */ 72 | - (void) updateItems:(NSArray *)newItems; 73 | 74 | #pragma mark - Adding Items 75 | 76 | /** 77 | * Append a single item to the end of the items array. 78 | */ 79 | - (void) appendItem:(id)item; 80 | 81 | /** 82 | * Add some more items to the end of the items array. 83 | */ 84 | - (void) appendItems:(NSArray *)newItems; 85 | 86 | /** 87 | * Insert an item at the specified index. 88 | */ 89 | - (void) insertItem:(id)item atIndex:(NSUInteger)index; 90 | 91 | /** 92 | * Insert some items at the specified indexes. 93 | * The count of `items` should be equal to the number of `indexes`. 94 | */ 95 | - (void) insertItems:(NSArray *)items atIndexes:(NSIndexSet *)indexes; 96 | 97 | #pragma mark - Replacing items 98 | 99 | /** 100 | * Replace an item. 101 | */ 102 | - (void) replaceItemAtIndex:(NSUInteger)index withItem:(id)item; 103 | 104 | #pragma mark - Removing items 105 | 106 | /** 107 | * Remove the item at the specified index. 108 | */ 109 | - (void) removeItemAtIndex:(NSUInteger)index; 110 | 111 | /** 112 | * Remove items in the specified range. 113 | */ 114 | - (void) removeItemsInRange:(NSRange)range; 115 | 116 | /** 117 | * Remove items at the specified indexes. 118 | */ 119 | - (void) removeItemsAtIndexes:(NSIndexSet *)indexes; 120 | 121 | #pragma mark - Moving items 122 | 123 | /** 124 | * Move an item to a new index. 125 | */ 126 | - (void) moveItemAtIndex:(NSUInteger)index1 toIndex:(NSUInteger)index2; 127 | 128 | @end 129 | -------------------------------------------------------------------------------- /Example/Pods/Pods-LayoutDemo-acknowledgements.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreferenceSpecifiers 6 | 7 | 8 | FooterText 9 | This application makes use of the following third party libraries: 10 | Title 11 | Acknowledgements 12 | Type 13 | PSGroupSpecifier 14 | 15 | 16 | FooterText 17 | Copyright (c) 2014 Adam Iredale <adam@bionicmonocle.com> 18 | 19 | Permission is hereby granted, free of charge, to any person obtaining a copy 20 | of this software and associated documentation files (the "Software"), to deal 21 | in the Software without restriction, including without limitation the rights 22 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 23 | copies of the Software, and to permit persons to whom the Software is 24 | furnished to do so, subject to the following conditions: 25 | 26 | The above copyright notice and this permission notice shall be included in 27 | all copies or substantial portions of the Software. 28 | 29 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 30 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 31 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 32 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 33 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 34 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 35 | THE SOFTWARE. 36 | 37 | Title 38 | BMFloatingHeaderCollectionViewLayout 39 | Type 40 | PSGroupSpecifier 41 | 42 | 43 | FooterText 44 | Copyright (c) 2013 Jonathan Hersh 45 | 46 | Permission is hereby granted, free of charge, to any person obtaining 47 | a copy of this software and associated documentation files (the 48 | "Software"), to deal in the Software without restriction, including 49 | without limitation the rights to use, copy, modify, merge, publish, 50 | distribute, sublicense, and/or sell copies of the Software, and to 51 | permit persons to whom the Software is furnished to do so, subject to 52 | the following conditions: 53 | 54 | The above copyright notice and this permission notice shall be 55 | included in all copies or substantial portions of the Software. 56 | 57 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 58 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 59 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 60 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 61 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 62 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 63 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 64 | 65 | Title 66 | SSDataSources 67 | Type 68 | PSGroupSpecifier 69 | 70 | 71 | FooterText 72 | Generated by CocoaPods - http://cocoapods.org 73 | Title 74 | 75 | Type 76 | PSGroupSpecifier 77 | 78 | 79 | StringsTable 80 | Acknowledgements 81 | Title 82 | Acknowledgements 83 | 84 | 85 | -------------------------------------------------------------------------------- /Example/Pods/Pods-resources.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | 4 | RESOURCES_TO_COPY=${PODS_ROOT}/resources-to-copy-${TARGETNAME}.txt 5 | > "$RESOURCES_TO_COPY" 6 | 7 | install_resource() 8 | { 9 | case $1 in 10 | *.storyboard) 11 | echo "ibtool --reference-external-strings-file --errors --warnings --notices --output-format human-readable-text --compile ${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$1\" .storyboard`.storyboardc ${PODS_ROOT}/$1 --sdk ${SDKROOT}" 12 | ibtool --reference-external-strings-file --errors --warnings --notices --output-format human-readable-text --compile "${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$1\" .storyboard`.storyboardc" "${PODS_ROOT}/$1" --sdk "${SDKROOT}" 13 | ;; 14 | *.xib) 15 | echo "ibtool --reference-external-strings-file --errors --warnings --notices --output-format human-readable-text --compile ${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$1\" .xib`.nib ${PODS_ROOT}/$1 --sdk ${SDKROOT}" 16 | ibtool --reference-external-strings-file --errors --warnings --notices --output-format human-readable-text --compile "${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$1\" .xib`.nib" "${PODS_ROOT}/$1" --sdk "${SDKROOT}" 17 | ;; 18 | *.framework) 19 | echo "mkdir -p ${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 20 | mkdir -p "${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 21 | echo "rsync -av ${PODS_ROOT}/$1 ${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 22 | rsync -av "${PODS_ROOT}/$1" "${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 23 | ;; 24 | *.xcdatamodel) 25 | echo "xcrun momc \"${PODS_ROOT}/$1\" \"${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$1"`.mom\"" 26 | xcrun momc "${PODS_ROOT}/$1" "${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$1" .xcdatamodel`.mom" 27 | ;; 28 | *.xcdatamodeld) 29 | echo "xcrun momc \"${PODS_ROOT}/$1\" \"${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$1" .xcdatamodeld`.momd\"" 30 | xcrun momc "${PODS_ROOT}/$1" "${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$1" .xcdatamodeld`.momd" 31 | ;; 32 | *.xcassets) 33 | ;; 34 | /*) 35 | echo "$1" 36 | echo "$1" >> "$RESOURCES_TO_COPY" 37 | ;; 38 | *) 39 | echo "${PODS_ROOT}/$1" 40 | echo "${PODS_ROOT}/$1" >> "$RESOURCES_TO_COPY" 41 | ;; 42 | esac 43 | } 44 | 45 | rsync -avr --copy-links --no-relative --exclude '*/.svn/*' --files-from="$RESOURCES_TO_COPY" / "${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 46 | if [[ "${ACTION}" == "install" ]]; then 47 | rsync -avr --copy-links --no-relative --exclude '*/.svn/*' --files-from="$RESOURCES_TO_COPY" / "${INSTALL_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 48 | fi 49 | rm -f "$RESOURCES_TO_COPY" 50 | 51 | if [[ -n "${WRAPPER_EXTENSION}" ]] && [ `xcrun --find actool` ] && [ `find . -name '*.xcassets' | wc -l` -ne 0 ] 52 | then 53 | case "${TARGETED_DEVICE_FAMILY}" in 54 | 1,2) 55 | TARGET_DEVICE_ARGS="--target-device ipad --target-device iphone" 56 | ;; 57 | 1) 58 | TARGET_DEVICE_ARGS="--target-device iphone" 59 | ;; 60 | 2) 61 | TARGET_DEVICE_ARGS="--target-device ipad" 62 | ;; 63 | *) 64 | TARGET_DEVICE_ARGS="--target-device mac" 65 | ;; 66 | esac 67 | find "${PWD}" -name "*.xcassets" -print0 | xargs -0 actool --output-format human-readable-text --notices --warnings --platform "${PLATFORM_NAME}" --minimum-deployment-target "${IPHONEOS_DEPLOYMENT_TARGET}" ${TARGET_DEVICE_ARGS} --compress-pngs --compile "${BUILT_PRODUCTS_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 68 | fi 69 | -------------------------------------------------------------------------------- /Example/Pods/Pods-LayoutDemo-resources.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | 4 | RESOURCES_TO_COPY=${PODS_ROOT}/resources-to-copy-${TARGETNAME}.txt 5 | > "$RESOURCES_TO_COPY" 6 | 7 | install_resource() 8 | { 9 | case $1 in 10 | *.storyboard) 11 | echo "ibtool --reference-external-strings-file --errors --warnings --notices --output-format human-readable-text --compile ${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$1\" .storyboard`.storyboardc ${PODS_ROOT}/$1 --sdk ${SDKROOT}" 12 | ibtool --reference-external-strings-file --errors --warnings --notices --output-format human-readable-text --compile "${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$1\" .storyboard`.storyboardc" "${PODS_ROOT}/$1" --sdk "${SDKROOT}" 13 | ;; 14 | *.xib) 15 | echo "ibtool --reference-external-strings-file --errors --warnings --notices --output-format human-readable-text --compile ${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$1\" .xib`.nib ${PODS_ROOT}/$1 --sdk ${SDKROOT}" 16 | ibtool --reference-external-strings-file --errors --warnings --notices --output-format human-readable-text --compile "${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$1\" .xib`.nib" "${PODS_ROOT}/$1" --sdk "${SDKROOT}" 17 | ;; 18 | *.framework) 19 | echo "mkdir -p ${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 20 | mkdir -p "${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 21 | echo "rsync -av ${PODS_ROOT}/$1 ${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 22 | rsync -av "${PODS_ROOT}/$1" "${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 23 | ;; 24 | *.xcdatamodel) 25 | echo "xcrun momc \"${PODS_ROOT}/$1\" \"${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$1"`.mom\"" 26 | xcrun momc "${PODS_ROOT}/$1" "${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$1" .xcdatamodel`.mom" 27 | ;; 28 | *.xcdatamodeld) 29 | echo "xcrun momc \"${PODS_ROOT}/$1\" \"${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$1" .xcdatamodeld`.momd\"" 30 | xcrun momc "${PODS_ROOT}/$1" "${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$1" .xcdatamodeld`.momd" 31 | ;; 32 | *.xcassets) 33 | ;; 34 | /*) 35 | echo "$1" 36 | echo "$1" >> "$RESOURCES_TO_COPY" 37 | ;; 38 | *) 39 | echo "${PODS_ROOT}/$1" 40 | echo "${PODS_ROOT}/$1" >> "$RESOURCES_TO_COPY" 41 | ;; 42 | esac 43 | } 44 | 45 | rsync -avr --copy-links --no-relative --exclude '*/.svn/*' --files-from="$RESOURCES_TO_COPY" / "${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 46 | if [[ "${ACTION}" == "install" ]]; then 47 | rsync -avr --copy-links --no-relative --exclude '*/.svn/*' --files-from="$RESOURCES_TO_COPY" / "${INSTALL_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 48 | fi 49 | rm -f "$RESOURCES_TO_COPY" 50 | 51 | if [[ -n "${WRAPPER_EXTENSION}" ]] && [ `xcrun --find actool` ] && [ `find . -name '*.xcassets' | wc -l` -ne 0 ] 52 | then 53 | case "${TARGETED_DEVICE_FAMILY}" in 54 | 1,2) 55 | TARGET_DEVICE_ARGS="--target-device ipad --target-device iphone" 56 | ;; 57 | 1) 58 | TARGET_DEVICE_ARGS="--target-device iphone" 59 | ;; 60 | 2) 61 | TARGET_DEVICE_ARGS="--target-device ipad" 62 | ;; 63 | *) 64 | TARGET_DEVICE_ARGS="--target-device mac" 65 | ;; 66 | esac 67 | find "${PWD}" -name "*.xcassets" -print0 | xargs -0 actool --output-format human-readable-text --notices --warnings --platform "${PLATFORM_NAME}" --minimum-deployment-target "${IPHONEOS_DEPLOYMENT_TARGET}" ${TARGET_DEVICE_ARGS} --compress-pngs --compile "${BUILT_PRODUCTS_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 68 | fi 69 | -------------------------------------------------------------------------------- /Example/LayoutDemo/Base.lproj/Main_iPad.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 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | -------------------------------------------------------------------------------- /Example/LayoutDemo/Base.lproj/Main_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 | 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 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | desc "Runs the specs [EMPTY]" 2 | task :spec do 3 | # Provide your own implementation 4 | end 5 | 6 | task :version do 7 | git_remotes = `git remote`.strip.split("\n") 8 | 9 | if git_remotes.count > 0 10 | puts "-- fetching version number from github" 11 | sh 'git fetch' 12 | 13 | remote_version = remote_spec_version 14 | end 15 | 16 | if remote_version.nil? 17 | puts "There is no current released version. You're about to release a new Pod." 18 | version = "0.0.1" 19 | else 20 | puts "The current released version of your pod is " + remote_spec_version.to_s() 21 | version = suggested_version_number 22 | end 23 | 24 | puts "Enter the version you want to release (" + version + ") " 25 | new_version_number = $stdin.gets.strip 26 | if new_version_number == "" 27 | new_version_number = version 28 | end 29 | 30 | replace_version_number(new_version_number) 31 | end 32 | 33 | desc "Release a new version of the Pod" 34 | task :release do 35 | 36 | puts "* Running version" 37 | sh "rake version" 38 | 39 | unless ENV['SKIP_CHECKS'] 40 | if `git symbolic-ref HEAD 2>/dev/null`.strip.split('/').last != 'master' 41 | $stderr.puts "[!] You need to be on the `master' branch in order to be able to do a release." 42 | exit 1 43 | end 44 | 45 | if `git tag`.strip.split("\n").include?(spec_version) 46 | $stderr.puts "[!] A tag for version `#{spec_version}' already exists. Change the version in the podspec" 47 | exit 1 48 | end 49 | 50 | puts "You are about to release `#{spec_version}`, is that correct? [y/n]" 51 | exit if $stdin.gets.strip.downcase != 'y' 52 | end 53 | 54 | puts "* Running specs" 55 | sh "rake spec" 56 | 57 | puts "* Linting the podspec" 58 | sh "pod lib lint" 59 | 60 | # Then release 61 | sh "git commit #{podspec_path} CHANGELOG.md -m 'Release #{spec_version}'" 62 | sh "git tag -a #{spec_version} -m 'Release #{spec_version}'" 63 | sh "git push origin master" 64 | sh "git push origin --tags" 65 | sh "pod push master #{podspec_path}" 66 | end 67 | 68 | # @return [Pod::Version] The version as reported by the Podspec. 69 | # 70 | def spec_version 71 | require 'cocoapods' 72 | spec = Pod::Specification.from_file(podspec_path) 73 | spec.version 74 | end 75 | 76 | # @return [Pod::Version] The version as reported by the Podspec from remote. 77 | # 78 | def remote_spec_version 79 | require 'cocoapods-core' 80 | 81 | if spec_file_exist_on_remote? 82 | remote_spec = eval(`git show origin/master:#{podspec_path}`) 83 | remote_spec.version 84 | else 85 | nil 86 | end 87 | end 88 | 89 | # @return [Bool] If the remote repository has a copy of the podpesc file or not. 90 | # 91 | def spec_file_exist_on_remote? 92 | test_condition = `if git rev-parse --verify --quiet origin/master:#{podspec_path} >/dev/null; 93 | then 94 | echo 'true' 95 | else 96 | echo 'false' 97 | fi` 98 | 99 | 'true' == test_condition.strip 100 | end 101 | 102 | # @return [String] The relative path of the Podspec. 103 | # 104 | def podspec_path 105 | podspecs = Dir.glob('*.podspec') 106 | if podspecs.count == 1 107 | podspecs.first 108 | else 109 | raise "Could not select a podspec" 110 | end 111 | end 112 | 113 | # @return [String] The suggested version number based on the local and remote version numbers. 114 | # 115 | def suggested_version_number 116 | if spec_version != remote_spec_version 117 | spec_version.to_s() 118 | else 119 | next_version(spec_version).to_s() 120 | end 121 | end 122 | 123 | # @param [Pod::Version] version 124 | # the version for which you need the next version 125 | # 126 | # @note It is computed by bumping the last component of the versino string by 1. 127 | # 128 | # @return [Pod::Version] The version that comes next after the version supplied. 129 | # 130 | def next_version(version) 131 | version_components = version.to_s().split("."); 132 | last = (version_components.last.to_i() + 1).to_s 133 | version_components[-1] = last 134 | Pod::Version.new(version_components.join(".")) 135 | end 136 | 137 | # @param [String] new_version_number 138 | # the new version number 139 | # 140 | # @note This methods replaces the version number in the podspec file with a new version number. 141 | # 142 | # @return void 143 | # 144 | def replace_version_number(new_version_number) 145 | text = File.read(podspec_path) 146 | text.gsub!(/(s.version( )*= ")#{spec_version}(")/, "\\1#{new_version_number}\\3") 147 | File.open(podspec_path, "w") { |file| file.puts text } 148 | end -------------------------------------------------------------------------------- /Example/LayoutDemo/BMLViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // BMLViewController.m 3 | // LayoutDemo 4 | // 5 | // Created by Adam Iredale on 26/03/2014. 6 | // Copyright (c) 2014 Bionic Monocle Pty Ltd. All rights reserved. 7 | // 8 | 9 | #import "BMLViewController.h" 10 | 11 | #pragma mark - Pods 12 | 13 | #import 14 | #import 15 | #import 16 | 17 | static NSString *const kDemoTitle = @"HEADER TEXT CAN GO HERE\n(or any other UIView subclass)"; 18 | static NSString *const kDemoDetail = @"Data Empty"; 19 | static NSString *const kDemoEmpty = @"Your empty view here!"; 20 | static NSString *const kDemoData = @"The title area goes away but the detail never does. Cool, huh? ... All " \ 21 | @"orientations are handled automatically and there's even empty view management built in!"; 22 | 23 | @interface BMLViewController () 24 | /** 25 | * Demo datasource 26 | */ 27 | @property (nonatomic, strong) SSArrayDataSource *dataSource; 28 | 29 | @property (nonatomic, strong) UISegmentedControl *detailControl; 30 | 31 | @property (nonatomic, strong) UILabel *titleLabel; 32 | 33 | @property (nonatomic, strong) UILabel *emptyLabel; 34 | 35 | @end 36 | 37 | @implementation BMLViewController 38 | 39 | - (void)viewDidLoad 40 | { 41 | [super viewDidLoad]; 42 | 43 | self.collectionView.backgroundColor = [UIColor lightGrayColor]; 44 | 45 | self.collectionView.alwaysBounceVertical = YES; 46 | 47 | self.titleLabel = 48 | ({ 49 | UILabel *label = [[UILabel alloc] init]; 50 | label.text = kDemoTitle; 51 | label.numberOfLines = 2; 52 | label; 53 | }); 54 | 55 | self.detailControl = 56 | ({ 57 | UISegmentedControl *control = [[UISegmentedControl alloc] initWithItems:[kDemoDetail componentsSeparatedByString:@" "]]; 58 | [control addTarget:self action:@selector(segmentDidChange:) forControlEvents:UIControlEventValueChanged]; 59 | control.selectedSegmentIndex = 0; 60 | control; 61 | }); 62 | 63 | self.emptyLabel = 64 | ({ 65 | UILabel *label = [[UILabel alloc] init]; 66 | label.text = kDemoEmpty; 67 | label.textAlignment = NSTextAlignmentCenter; 68 | label; 69 | }); 70 | 71 | self.dataSource = [[SSArrayDataSource alloc] initWithItems:[kDemoData componentsSeparatedByString:@" "]]; 72 | 73 | _dataSource.cellCreationBlock = ^(id object, 74 | UICollectionView *collectionView, 75 | NSIndexPath *indexPath) 76 | { 77 | return [collectionView dequeueReusableCellWithReuseIdentifier:@"cell" forIndexPath:indexPath]; 78 | }; 79 | 80 | _dataSource.cellConfigureBlock = ^(UICollectionViewCell *cell, 81 | NSString *object, 82 | UICollectionView *collectionView, 83 | NSIndexPath *indexPath) 84 | { 85 | // Ordinarily you'd make your own collection view cell subclass 86 | // This is just lazy demo code 87 | UILabel *label = cell.contentView.subviews.firstObject; 88 | if (!label) 89 | { 90 | label = [[UILabel alloc] init]; 91 | } 92 | label.text = object; 93 | label.autoresizingMask = UIViewAutoresizingFlexibleWidth|UIViewAutoresizingFlexibleHeight; 94 | label.frame = cell.contentView.bounds; 95 | label.textAlignment = NSTextAlignmentCenter; 96 | [cell.contentView addSubview:label]; 97 | }; 98 | 99 | __weak __block typeof(self) weakSelf = self; 100 | 101 | _dataSource.collectionSupplementaryCreationBlock = ^(NSString *kind, 102 | UICollectionView *cv, 103 | NSIndexPath *indexPath) 104 | { 105 | BMReusableContainerView *view = [cv dequeueReusableSupplementaryViewOfKind:kind withReuseIdentifier:kind forIndexPath:indexPath]; 106 | return view; 107 | }; 108 | 109 | _dataSource.collectionSupplementaryConfigureBlock = ^(BMReusableContainerView *view, 110 | NSString *kind, 111 | UICollectionView *cv, 112 | NSIndexPath *indexPath) 113 | { 114 | if ([kind isEqualToString:BMFloatingHeaderCollectionViewLayoutHeaderTitleKind]) 115 | { 116 | // Your header title view here 117 | view.embeddedView = weakSelf.titleLabel; 118 | } 119 | else if ([kind isEqualToString:BMFloatingHeaderCollectionViewLayoutHeaderDetailKind]) 120 | { 121 | // Your header detail view here 122 | view.embeddedView = weakSelf.detailControl; 123 | } 124 | else if ([kind isEqualToString:BMFloatingHeaderCollectionViewLayoutPlaceholderKind]) 125 | { 126 | // Your empty view here 127 | view.embeddedView = weakSelf.emptyLabel; 128 | } 129 | }; 130 | 131 | BMFloatingHeaderCollectionViewLayout *layout = (id)self.collectionView.collectionViewLayout; 132 | 133 | layout.headerTitleViewHeight = 60; 134 | layout.headerDetailViewHeight = 40; 135 | layout.itemInsets = UIEdgeInsetsMake(2, 2, 2, 2); 136 | 137 | _dataSource.collectionView = self.collectionView; 138 | 139 | [_dataSource reloadData]; 140 | 141 | } 142 | 143 | #pragma mark - BMFloatingHeaderCollectionViewLayoutDelegate 144 | 145 | - (CGFloat)collectionViewLayout:(BMFloatingHeaderCollectionViewLayout *)layout heightForItemAtIndexPath:(NSIndexPath *)indexPath 146 | { 147 | // Here you could calculate dynamic heights from data if required (I do) 148 | return 40.0; 149 | } 150 | 151 | #pragma mark - Actions 152 | 153 | - (void)segmentDidChange:(UISegmentedControl *)sender 154 | { 155 | if (!sender.selectedSegmentIndex) 156 | { 157 | [self.dataSource appendItems:[kDemoData componentsSeparatedByString:@" "]]; 158 | } 159 | else 160 | { 161 | [self.dataSource clearItems]; 162 | } 163 | } 164 | 165 | @end 166 | -------------------------------------------------------------------------------- /Example/Pods/SSDataSources/SSDataSources/SSSectionedDataSource.h: -------------------------------------------------------------------------------- 1 | // 2 | // SSSectionedDataSource.h 3 | // SSDataSources 4 | // 5 | // Created by Jonathan Hersh on 8/26/13. 6 | // Copyright (c) 2013 Splinesoft. All rights reserved. 7 | // 8 | 9 | #import "SSBaseDataSource.h" 10 | #import "SSSection.h" 11 | 12 | /** 13 | * A data source for multi-sectioned table and collection views. 14 | * Each section is modeled using an `SSSection` object. 15 | */ 16 | 17 | #pragma mark - SSSectionedDataSource 18 | 19 | @interface SSSectionedDataSource : SSBaseDataSource 20 | 21 | /** 22 | * Create a sectioned data source with a single section. 23 | * Creates the `SSSection` object for you. 24 | */ 25 | - (instancetype) initWithItems:(NSArray *)items; 26 | 27 | /** 28 | * Create a sectioned data source with a single SSSection object. 29 | */ 30 | - (instancetype) initWithSection:(SSSection *)section; 31 | 32 | /** 33 | * Create a sectioned data source with multiple sections. 34 | * Each item in the sections array should be a `SSSection` object. 35 | */ 36 | - (instancetype) initWithSections:(NSArray *)sections; 37 | 38 | /** 39 | * Sections that have 0 items will still display a header and footer 40 | * in their table or collection view. By default, SSSectionedDataSource 41 | * will remove these empty sections for you. 42 | * 43 | * If YES, automatically removes any section that becomes empty -- 44 | * the `SSSection` object contains 0 items -- 45 | * after a call to one of the following: 46 | * 47 | * removeItemAtIndexPath: 48 | * removeItemsAtIndexes:inSection: 49 | * removeItemsInRange:inSection: 50 | * 51 | * Defaults to YES. 52 | */ 53 | @property (nonatomic, assign) BOOL shouldRemoveEmptySections; 54 | 55 | /** 56 | * Sections appearing in the datasource. 57 | * You should not mutate this directly - rather, use the insert/move/remove accessors below. 58 | */ 59 | @property (nonatomic, strong, readonly) NSMutableArray *sections; 60 | 61 | #pragma mark - Section access 62 | 63 | /** 64 | * Return the section object at a particular index. 65 | * Use `itemAtIndexPath:` for items. 66 | */ 67 | - (SSSection *) sectionAtIndex:(NSUInteger)index; 68 | 69 | /** 70 | * Return the first section with a given identifier, or nil if not found. 71 | */ 72 | - (SSSection *) sectionWithIdentifier:(id)identifier; 73 | 74 | /** 75 | * Return the index of the first section with a given identifier, or NSNotFound. 76 | * See `sectionIdentifier` in SSSection. 77 | */ 78 | - (NSUInteger) indexOfSectionWithIdentifier:(id)identifier; 79 | 80 | #pragma mark - Moving sections 81 | 82 | - (void) moveSectionAtIndex:(NSUInteger)fromIndex toIndex:(NSUInteger)toIndex; 83 | 84 | #pragma mark - Inserting sections 85 | 86 | /** 87 | * Add a new section to the end of the table or collectionview. 88 | */ 89 | - (void) appendSection:(SSSection *)newSection; 90 | 91 | /** 92 | * Insert a section at a particular index. 93 | */ 94 | - (void) insertSection:(SSSection *)newSection atIndex:(NSUInteger)index; 95 | 96 | /** 97 | * Insert some new sections at the specified indexes. 98 | * Each item in the `sections` array should itself be an SSSection object 99 | * or an array, in which case an SSSection object will be created for it. 100 | * The number of `sections` should equal the number of `indexes`. 101 | */ 102 | - (void) insertSections:(NSArray *)sections atIndexes:(NSIndexSet *)indexes; 103 | 104 | #pragma mark - Inserting items 105 | 106 | /** 107 | * Insert an item at a particular section (indexPath.section) and row (indexPath.row). 108 | */ 109 | - (void) insertItem:(id)item atIndexPath:(NSIndexPath *)indexPath; 110 | 111 | /** 112 | * Replace an item at a particular section (indexPath.section) and row (indexPath.row). 113 | * Reloads the cell. 114 | */ 115 | - (void) replaceItemAtIndexPath:(NSIndexPath *)indexPath withItem:(id)item; 116 | 117 | /** 118 | * Insert multiple items within a single section. 119 | * The number of `items` should be equal to the number of `indexes`. 120 | */ 121 | - (void) insertItems:(NSArray *)items 122 | atIndexes:(NSIndexSet *)indexes 123 | inSection:(NSUInteger)section; 124 | 125 | /** 126 | * Append multiple items to the end of a single section. 127 | */ 128 | - (void) appendItems:(NSArray *)items toSection:(NSUInteger)section; 129 | 130 | #pragma mark - Removing sections 131 | 132 | /** 133 | * Destroy all sections. 134 | */ 135 | - (void) clearSections; 136 | - (void) removeAllSections; 137 | 138 | /** 139 | * Remove the section at a given index. 140 | */ 141 | - (void) removeSectionAtIndex:(NSUInteger)index; 142 | 143 | /** 144 | * Remove the sections in a given range. 145 | */ 146 | - (void) removeSectionsInRange:(NSRange)range; 147 | 148 | /** 149 | * Remove the sections at specified indexes. 150 | */ 151 | - (void) removeSectionsAtIndexes:(NSIndexSet *)indexes; 152 | 153 | #pragma mark - Removing items 154 | 155 | /** 156 | * Remove the item at a given indexpath. 157 | */ 158 | - (void) removeItemAtIndexPath:(NSIndexPath *)indexPath; 159 | 160 | /** 161 | * Remove multiple items within a single section. 162 | */ 163 | - (void) removeItemsAtIndexes:(NSIndexSet *)indexes inSection:(NSUInteger)section; 164 | 165 | /** 166 | * Remove multiple items in a range within a single section. 167 | */ 168 | - (void) removeItemsInRange:(NSRange)range inSection:(NSUInteger)section; 169 | 170 | #pragma mark - UITableViewDelegate helpers 171 | 172 | /** 173 | * It is UITableViewDelegate, not UITableViewDataSource, 174 | * that provides header and footer views. 175 | * SSDataSources provides these helpers for constructing table header and footer views. 176 | * Assumes your header/footer view is a subclass of SSBaseHeaderFooterView. 177 | * See the Example project for sample usage. 178 | */ 179 | - (SSBaseHeaderFooterView *) viewForHeaderInSection:(NSUInteger)section; 180 | - (SSBaseHeaderFooterView *) viewForFooterInSection:(NSUInteger)section; 181 | 182 | /** 183 | * As above, but for section header/footer heights. 184 | * This is simply a shortcut for 185 | * [myDataSource sectionAtIndex:section].headerHeight; 186 | */ 187 | - (CGFloat) heightForHeaderInSection:(NSUInteger)section; 188 | - (CGFloat) heightForFooterInSection:(NSUInteger)section; 189 | 190 | /** 191 | * As above, for section header/footer titles. 192 | * This is simply a shortcut for 193 | * [myDataSource sectionAtIndex:section].header/footer 194 | */ 195 | - (NSString *) titleForHeaderInSection:(NSUInteger)section; 196 | - (NSString *) titleForFooterInSection:(NSUInteger)section; 197 | 198 | @end 199 | -------------------------------------------------------------------------------- /Example/Pods/SSDataSources/SSDataSources/SSArrayDataSource.m: -------------------------------------------------------------------------------- 1 | // 2 | // SSArrayDataSource.m 3 | // SSDataSources 4 | // 5 | // Created by Jonathan Hersh on 6/7/13. 6 | // Copyright (c) 2013 Splinesoft. All rights reserved. 7 | // 8 | 9 | #import "SSDataSources.h" 10 | #import 11 | 12 | /** 13 | * An internal container to hold the raw items that are handed to SSArrayDataSource 14 | * that can then be KVO'd for changes and manipulation. 15 | * 16 | * See https://github.com/splinesoft/SSDataSources/pull/29 & -[SSArrayDataSource initWithItems:] 17 | */ 18 | @interface SSArrayDataSourceItemsContainer : NSObject 19 | 20 | @property (nonatomic, copy) NSArray *items; 21 | 22 | - (instancetype)initWithItems:(NSArray *)items; 23 | 24 | @end 25 | 26 | @implementation SSArrayDataSourceItemsContainer 27 | 28 | - (instancetype)initWithItems:(NSArray *)items { 29 | if ((self = [self init])) { 30 | self.items = items; 31 | } 32 | 33 | return self; 34 | } 35 | 36 | @end 37 | 38 | #pragma mark - 39 | 40 | static void *SSArrayKeyPathDataSourceContext = &SSArrayKeyPathDataSourceContext; 41 | 42 | @interface SSArrayDataSource () 43 | 44 | /** 45 | * The object that the receiver is observing at the given key path when initialized 46 | * via -initwithitems:. 47 | */ 48 | @property (nonatomic, strong) id target; 49 | 50 | /** 51 | * The key path for an NSArray off of target the receiver is initialized via 52 | * -initwithitems:. 53 | */ 54 | @property (nonatomic, copy) NSString *keyPath; 55 | 56 | @end 57 | 58 | @implementation SSArrayDataSource 59 | 60 | - (instancetype)initWithItems:(NSArray *)anItems { 61 | return [self initWithTarget:[[SSArrayDataSourceItemsContainer alloc] initWithItems:anItems] 62 | keyPath:NSStringFromSelector(@selector(items))]; 63 | } 64 | 65 | - (instancetype)initWithTarget:(id)target keyPath:(NSString *)keyPath { 66 | if ((self = [self init])) { 67 | self.target = target; 68 | self.keyPath = keyPath; 69 | [self registerKVO]; 70 | } 71 | 72 | return self; 73 | } 74 | 75 | - (void)dealloc { 76 | [self unregisterKVO]; 77 | } 78 | 79 | #pragma mark - Internal mutable items 80 | 81 | /** 82 | * An NSMutableArray proxy for whatever source array is backing the receiver 83 | * data source. 84 | */ 85 | - (NSMutableArray *)items { 86 | return [self.target mutableArrayValueForKey:self.keyPath]; 87 | } 88 | 89 | #pragma mark - Base Data source 90 | 91 | - (NSUInteger)numberOfSections { 92 | return 1; 93 | } 94 | 95 | - (NSUInteger)numberOfItemsInSection:(NSUInteger)section { 96 | return [self numberOfItems]; 97 | } 98 | 99 | - (NSUInteger)numberOfItems { 100 | return [self.items count]; 101 | } 102 | 103 | - (id)itemAtIndexPath:(NSIndexPath *)indexPath { 104 | if (!indexPath) { 105 | return nil; 106 | } 107 | 108 | if (indexPath.row < (NSInteger)[self.items count]) { 109 | return self.items[(NSUInteger)indexPath.row]; 110 | } 111 | 112 | return nil; 113 | } 114 | 115 | #pragma mark - Updating items 116 | 117 | - (void)clearItems { 118 | [self.items removeAllObjects]; 119 | [self reloadData]; 120 | } 121 | 122 | - (void)removeAllItems { 123 | [self clearItems]; 124 | } 125 | 126 | - (void)updateItems:(NSArray *)newItems { 127 | [self.items replaceObjectsInRange:NSMakeRange(0, self.items.count) withObjectsFromArray:newItems]; 128 | [self reloadData]; 129 | } 130 | 131 | - (NSArray *)allItems { 132 | return self.items; 133 | } 134 | 135 | #pragma mark - Adding Items 136 | 137 | - (void)appendItem:(id)item { 138 | [self appendItems:@[ item ]]; 139 | } 140 | 141 | - (void)appendItems:(NSArray *)newItems { 142 | [self insertItems:newItems 143 | atIndexes:[NSIndexSet indexSetWithIndexesInRange:NSMakeRange([self numberOfItems], 144 | [newItems count])]]; 145 | } 146 | 147 | - (void)insertItem:(id)item atIndex:(NSUInteger)index { 148 | [self insertItems:@[ item ] 149 | atIndexes:[NSIndexSet indexSetWithIndex:index]]; 150 | } 151 | 152 | - (void)insertItems:(NSArray *)newItems atIndexes:(NSIndexSet *)indexes { 153 | if ([newItems count] == 0 || [newItems count] != [indexes count]) { 154 | return; 155 | } 156 | 157 | [self.items insertObjects:newItems atIndexes:indexes]; 158 | } 159 | 160 | - (void)replaceItemAtIndex:(NSUInteger)index withItem:(id)item { 161 | [self.items replaceObjectAtIndex:index withObject:item]; 162 | } 163 | 164 | #pragma mark - Moving Items 165 | 166 | - (void)moveItemAtIndex:(NSUInteger)index1 toIndex:(NSUInteger)index2 { 167 | NSIndexPath *indexPath1 = [NSIndexPath indexPathForRow:(NSInteger)index1 168 | inSection:0], 169 | *indexPath2 = [NSIndexPath indexPathForRow:(NSInteger)index2 170 | inSection:0]; 171 | 172 | id item = [self itemAtIndexPath:indexPath1]; 173 | [self unregisterKVO]; 174 | [self.items removeObject:item]; 175 | [self.items insertObject:item atIndex:index2]; 176 | 177 | [self moveCellAtIndexPath:indexPath1 178 | toIndexPath:indexPath2]; 179 | [self registerKVO]; 180 | } 181 | 182 | #pragma mark - Removing Items 183 | 184 | - (void)removeItemsInRange:(NSRange)range { 185 | [self removeItemsAtIndexes:[NSIndexSet indexSetWithIndexesInRange:range]]; 186 | } 187 | 188 | - (void)removeItemAtIndex:(NSUInteger)index { 189 | [self removeItemsAtIndexes:[NSIndexSet indexSetWithIndex:index]]; 190 | } 191 | 192 | - (void)removeItemsAtIndexes:(NSIndexSet *)indexes { 193 | [self.items removeObjectsAtIndexes:indexes]; 194 | } 195 | 196 | #pragma mark - Item Searching 197 | 198 | - (NSIndexPath *)indexPathForItem:(id)item { 199 | NSUInteger row = [self.items indexOfObjectIdenticalTo:item]; 200 | 201 | if (row == NSNotFound) { 202 | return nil; 203 | } 204 | 205 | return [NSIndexPath indexPathForRow:(NSInteger)row inSection:0]; 206 | } 207 | 208 | - (NSIndexPath *)indexPathForItemWithId:(NSManagedObjectID *)itemId { 209 | NSUInteger row = [self.items indexOfObjectPassingTest:^BOOL(NSManagedObject *object, 210 | NSUInteger index, 211 | BOOL *stop) { 212 | return [[object objectID] isEqual:itemId]; 213 | }]; 214 | 215 | if (row == NSNotFound) { 216 | return nil; 217 | } 218 | 219 | return [NSIndexPath indexPathForRow:(NSInteger)row inSection:0]; 220 | } 221 | 222 | #pragma mark - UITableViewDataSource 223 | 224 | - (void)tableView:(UITableView *)tableView 225 | moveRowAtIndexPath:(NSIndexPath *)sourceIndexPath 226 | toIndexPath:(NSIndexPath *)destinationIndexPath { 227 | 228 | id item = [self itemAtIndexPath:sourceIndexPath]; 229 | [self unregisterKVO]; 230 | [self.items removeObject:item]; 231 | [self.items insertObject:item 232 | atIndex:(NSUInteger)destinationIndexPath.row]; 233 | [self registerKVO]; 234 | } 235 | 236 | #pragma mark Key-value observing 237 | 238 | - (void)registerKVO { 239 | [self.target addObserver:self 240 | forKeyPath:self.keyPath 241 | options:NSKeyValueObservingOptionInitial 242 | context:&SSArrayKeyPathDataSourceContext]; 243 | } 244 | 245 | - (void)unregisterKVO { 246 | [self.target removeObserver:self 247 | forKeyPath:self.keyPath 248 | context:&SSArrayKeyPathDataSourceContext]; 249 | } 250 | 251 | - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context { 252 | if (context == SSArrayKeyPathDataSourceContext && [keyPath isEqualToString:self.keyPath]) { 253 | NSKeyValueChange changeKind = [change[NSKeyValueChangeKindKey] unsignedIntegerValue]; 254 | NSArray *indexPaths = [self.class indexPathArrayWithIndexSet:change[NSKeyValueChangeIndexesKey] 255 | inSection:0]; 256 | switch (changeKind) { 257 | case NSKeyValueChangeInsertion: 258 | [self insertCellsAtIndexPaths:indexPaths]; 259 | break; 260 | case NSKeyValueChangeRemoval: 261 | [self deleteCellsAtIndexPaths:indexPaths]; 262 | break; 263 | case NSKeyValueChangeReplacement: 264 | [self reloadCellsAtIndexPaths:indexPaths]; 265 | break; 266 | case NSKeyValueChangeSetting: 267 | break; 268 | default: 269 | break; 270 | } 271 | } else { 272 | [super observeValueForKeyPath:keyPath ofObject:object change:change context:context]; 273 | } 274 | } 275 | 276 | @end 277 | -------------------------------------------------------------------------------- /Example/Pods/SSDataSources/SSDataSources/SSBaseDataSource.h: -------------------------------------------------------------------------------- 1 | // 2 | // SSBaseDataSource.h 3 | // SSDataSources 4 | // 5 | // Created by Jonathan Hersh on 6/8/13. 6 | // Copyright (c) 2013 Splinesoft. All rights reserved. 7 | // 8 | 9 | /** 10 | * A generic data source object for table and collection views. Takes care of creating new cells 11 | * and exposes a block interface to configure cells with the object they represent. 12 | * Don't use this class directly except to subclass - instead, see 13 | * SSArrayDataSource, SSSectionedDataSource, and SSCoreDataSource. 14 | */ 15 | 16 | #import 17 | #import 18 | 19 | @interface SSBaseDataSource : NSObject 20 | 21 | #pragma mark - SSDataSources block signatures 22 | 23 | // Block called to configure each table and collection cell. 24 | typedef void (^SSCellConfigureBlock) (id cell, // The cell to configure 25 | id object, // The object being presented in this cell 26 | id parentView, // The parent table or collection view 27 | NSIndexPath *indexPath); // Index path for this cell 28 | 29 | // Optional block used to create a table or collection cell. 30 | typedef id (^SSCellCreationBlock) (id object, // The object being presented in this cell 31 | id parentView, // The parent table or collection view 32 | NSIndexPath *indexPath); // Index path for this cell 33 | 34 | // Optional block used to create a UICollectionView supplementary view. 35 | typedef UICollectionReusableView * (^SSCollectionSupplementaryViewCreationBlock) 36 | (NSString *kind, // the kind of reusable view 37 | UICollectionView *cv, // the parent collection view 38 | NSIndexPath *indexPath); // index path for this view 39 | 40 | // Optional block used to configure UICollectionView supplementary views. 41 | typedef void (^SSCollectionSupplementaryViewConfigureBlock) (id view, // the header/footer view 42 | NSString *kind, // the kind of reusable view 43 | UICollectionView *cv, // the parent collection view 44 | NSIndexPath *indexPath); // index path where this view appears 45 | 46 | // Optional block used to configure table move/edit behavior. 47 | typedef NS_ENUM(NSUInteger, SSCellActionType) { 48 | SSCellActionTypeEdit, 49 | SSCellActionTypeMove 50 | }; 51 | 52 | typedef BOOL (^SSTableCellActionBlock) (SSCellActionType actionType, // The action type requested for this cell (edit or move) 53 | UITableView *parentView, // the parent table view 54 | NSIndexPath *indexPath); // the indexPath being edited or moved 55 | 56 | // Optional block used to handle deletion behavior. 57 | typedef void (^SSTableCellDeletionBlock) (id dataSource, // the datasource performing the deletion 58 | UITableView *parentView, // the parent table view 59 | NSIndexPath *indexPath); // the indexPath being deleted 60 | 61 | #pragma mark - Base Data Source 62 | 63 | /** 64 | * The base class to use to instantiate new cells. 65 | * Assumed to be a subclass of SSBaseTableCell or SSBaseCollectionCell. 66 | * If you use a cell class that does not inherit one of those two, 67 | * or if you want to specify your own custom cell creation logic, 68 | * you can ignore this property and instead specify a cellCreationBlock. 69 | */ 70 | @property (nonatomic, weak) Class cellClass; 71 | 72 | /** 73 | * Cell configuration block, called for each table and collection 74 | * cell with the object to display in that cell. See block signature above. 75 | */ 76 | @property (nonatomic, copy) SSCellConfigureBlock cellConfigureBlock; 77 | 78 | /** 79 | * Optional block to use to instantiate new table and collection cells. 80 | * See block signature above. 81 | */ 82 | @property (nonatomic, copy) SSCellCreationBlock cellCreationBlock; 83 | 84 | /** 85 | * Optional view that will be added to the table or collection view if there 86 | * are no items in the datasource, then removed again once the datasource 87 | * has items. 88 | * 89 | * If this view's frame is equal to CGRectZero, the view's frame 90 | * will be sized to match the parent table or collection view. 91 | */ 92 | @property (nonatomic, strong) UIView *emptyView; 93 | 94 | #pragma mark - UITableView 95 | 96 | /** 97 | * Optional: If the tableview property is assigned, the data source will perform 98 | * insert/reload/delete calls on it as data changes. 99 | * 100 | * Assigning this property will set the reciever as the tableView's `dataSource` 101 | */ 102 | @property (nonatomic, weak) UITableView *tableView; 103 | 104 | /** 105 | * Optional animation to use when updating the table. 106 | * Defaults to UITableViewRowAnimationAutomatic. 107 | */ 108 | @property (nonatomic, assign) UITableViewRowAnimation rowAnimation; 109 | 110 | /** 111 | * In lieu of implementing 112 | * tableView:canEditRowAtIndexPath: and 113 | * tableView:canMoveRowAtIndexPath: 114 | * you may instead specify this block, which will be called to determine whether editing 115 | * and moving is allowed for a given indexPath. 116 | */ 117 | @property (nonatomic, copy) SSTableCellActionBlock tableActionBlock; 118 | 119 | /** 120 | * To implement cell deletion, first specify a `tableActionBlock` that returns 121 | * YES when called with SSCellActionTypeEdit. Then specify this deletion block, 122 | * which can be as simple as removing the item in question. 123 | * See the Example project for a full implementation. 124 | */ 125 | @property (nonatomic, copy) SSTableCellDeletionBlock tableDeletionBlock; 126 | 127 | #pragma mark - UICollectionView 128 | 129 | /** 130 | * Optional: If the collectionview property is assigned, the data source will perform 131 | * insert/reload/delete calls on it as data changes. 132 | * 133 | * Assigning this property will set the reciever as the collectionView's `dataSource` 134 | */ 135 | @property (nonatomic, weak) UICollectionView *collectionView; 136 | 137 | /** 138 | * The base class to use to instantiate new supplementary collection view elements. 139 | * These are assumed to be subclasses of SSBaseCollectionReusableView. 140 | * If you want to use a class that does not inherit SSBaseCollectionReusableView, 141 | * or if you want to use your own custom logic to create supplementary elements, 142 | * then you can ignore this property and instead specify a collectionSupplementaryCreationBlock. 143 | */ 144 | @property (nonatomic, weak) Class collectionViewSupplementaryElementClass; 145 | 146 | /** 147 | * Optional block used to create supplementary collection view elements. 148 | */ 149 | @property (nonatomic, copy) SSCollectionSupplementaryViewCreationBlock collectionSupplementaryCreationBlock; 150 | 151 | /** 152 | * Optional configure block for supplementary collection view elements. 153 | */ 154 | @property (nonatomic, copy) SSCollectionSupplementaryViewConfigureBlock collectionSupplementaryConfigureBlock; 155 | 156 | #pragma mark - Base item access 157 | 158 | /** 159 | * Return the item at a given index path. Override me in your subclass. 160 | */ 161 | - (id) itemAtIndexPath:(NSIndexPath *)indexPath; 162 | 163 | /** 164 | * Return the total number of items in the data source. Override me in your subclass. 165 | */ 166 | - (NSUInteger) numberOfItems; 167 | 168 | /** 169 | * Return the total number of sections in the data source. Override me! 170 | */ 171 | - (NSUInteger) numberOfSections; 172 | 173 | /** 174 | * Return the total number of items in a given section. Override me! 175 | */ 176 | - (NSUInteger) numberOfItemsInSection:(NSUInteger)section; 177 | 178 | #pragma mark - Custom Animations 179 | 180 | /** 181 | * Perform a tableView/collectionView operation, like inserting or deleting rows, 182 | * with a custom animation duration and completion block. 183 | */ 184 | - (void) performAnimations:(void (^)(void))animations 185 | duration:(NSTimeInterval)duration 186 | completion:(void (^)(void))completion; 187 | 188 | #pragma mark - Base tableView/collectionView operations 189 | 190 | - (void) insertCellsAtIndexPaths:(NSArray *)indexPaths; 191 | - (void) deleteCellsAtIndexPaths:(NSArray *)indexPaths; 192 | - (void) reloadCellsAtIndexPaths:(NSArray *)indexPaths; 193 | 194 | - (void) moveCellAtIndexPath:(NSIndexPath *)index1 toIndexPath:(NSIndexPath *)index2; 195 | - (void) moveSectionAtIndex:(NSUInteger)index1 toIndex:(NSUInteger)index2; 196 | 197 | - (void) insertSectionsAtIndexes:(NSIndexSet *)indexes; 198 | - (void) deleteSectionsAtIndexes:(NSIndexSet *)indexes; 199 | 200 | - (void) reloadData; 201 | 202 | #pragma mark - helpers 203 | 204 | /** 205 | * Helper functions to generate arrays of NSIndexPaths. 206 | */ 207 | + (NSArray *) indexPathArrayWithRange:(NSRange)range inSection:(NSUInteger)section; 208 | + (NSArray *) indexPathArrayWithIndexSet:(NSIndexSet *)indexes inSection:(NSUInteger)section; 209 | 210 | @end 211 | -------------------------------------------------------------------------------- /Example/Pods/SSDataSources/SSDataSources/SSSectionedDataSource.m: -------------------------------------------------------------------------------- 1 | // 2 | // SSSectionedDataSource.m 3 | // SSDataSources 4 | // 5 | // Created by Jonathan Hersh on 8/26/13. 6 | // Copyright (c) 2013 Splinesoft. All rights reserved. 7 | // 8 | 9 | #import "SSDataSources.h" 10 | 11 | @interface SSSectionedDataSource () 12 | 13 | - (instancetype)init; 14 | 15 | // Header/footer view helper 16 | - (SSBaseHeaderFooterView *)headerFooterViewWithClass:(Class)class; 17 | 18 | @end 19 | 20 | @implementation SSSectionedDataSource 21 | 22 | - (instancetype)init { 23 | if ((self = [super init])) { 24 | _sections = [NSMutableArray array]; 25 | _shouldRemoveEmptySections = YES; 26 | } 27 | 28 | return self; 29 | } 30 | 31 | - (instancetype)initWithItems:(NSArray *)items { 32 | if ((self = [self init])) { 33 | if ([items count] > 0) { 34 | [self appendSection:[SSSection sectionWithItems:items]]; 35 | } 36 | } 37 | 38 | return self; 39 | } 40 | 41 | - (instancetype)initWithSection:(SSSection *)section { 42 | if ((self = [self init])) { 43 | if (section) { 44 | [self appendSection:section]; 45 | } 46 | } 47 | 48 | return self; 49 | } 50 | 51 | - (instancetype)initWithSections:(NSArray *)newSections { 52 | if ((self = [self init])) { 53 | if ([newSections count] > 0) { 54 | [self insertSections:newSections 55 | atIndexes:[NSIndexSet indexSetWithIndexesInRange: 56 | NSMakeRange(0, [newSections count])]]; 57 | } 58 | } 59 | 60 | return self; 61 | } 62 | 63 | #pragma mark - Base Data Source 64 | 65 | - (NSUInteger)numberOfSections { 66 | return [self.sections count]; 67 | } 68 | 69 | - (NSUInteger)numberOfItemsInSection:(NSUInteger)section { 70 | return [[self sectionAtIndex:section] numberOfItems]; 71 | } 72 | 73 | - (NSUInteger)numberOfItems { 74 | NSUInteger count = 0; 75 | 76 | for (SSSection *section in self.sections) { 77 | count += [section numberOfItems]; 78 | } 79 | 80 | return count; 81 | } 82 | 83 | - (id)itemAtIndexPath:(NSIndexPath *)indexPath { 84 | return [[self sectionAtIndex:indexPath.section] itemAtIndex:indexPath.row]; 85 | } 86 | 87 | #pragma mark - Section access 88 | 89 | - (SSSection *)sectionAtIndex:(NSUInteger)index { 90 | return (SSSection *)[self.sections objectAtIndex:index]; 91 | } 92 | 93 | - (SSSection *)sectionWithIdentifier:(id)identifier { 94 | NSUInteger index = [self indexOfSectionWithIdentifier:identifier]; 95 | 96 | if (index == NSNotFound) { 97 | return nil; 98 | } 99 | 100 | return [self sectionAtIndex:index]; 101 | } 102 | 103 | - (NSUInteger)indexOfSectionWithIdentifier:(id)identifier { 104 | return [self.sections indexOfObjectPassingTest:^BOOL(SSSection *section, 105 | NSUInteger index, 106 | BOOL *stop) { 107 | return [(id)section.sectionIdentifier isEqual:identifier]; 108 | }]; 109 | } 110 | 111 | #pragma mark - UITableViewDataSource 112 | 113 | - (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section { 114 | return [self titleForHeaderInSection:section]; 115 | } 116 | 117 | - (NSString *)tableView:(UITableView *)tableView titleForFooterInSection:(NSInteger)section { 118 | return [self titleForFooterInSection:section]; 119 | } 120 | 121 | - (void)tableView:(UITableView *)tableView 122 | moveRowAtIndexPath:(NSIndexPath *)sourceIndexPath 123 | toIndexPath:(NSIndexPath *)destinationIndexPath { 124 | 125 | id item = [self itemAtIndexPath:sourceIndexPath]; 126 | [[self sectionAtIndex:sourceIndexPath.section].items removeObjectAtIndex:sourceIndexPath.row]; 127 | [[self sectionAtIndex:destinationIndexPath.section].items insertObject:item 128 | atIndex:destinationIndexPath.row]; 129 | 130 | } 131 | 132 | #pragma mark - Moving 133 | 134 | - (void)moveSectionAtIndex:(NSUInteger)fromIndex toIndex:(NSUInteger)toIndex { 135 | SSSection *section = [self sectionAtIndex:fromIndex]; 136 | 137 | [self.sections removeObjectAtIndex:fromIndex]; 138 | [self.sections insertObject:section 139 | atIndex:toIndex]; 140 | 141 | [self moveSectionAtIndex:fromIndex toIndex:toIndex]; 142 | } 143 | 144 | #pragma mark - Adding 145 | 146 | - (void) appendSection:(SSSection *)newSection { 147 | [self insertSection:newSection 148 | atIndex:[self numberOfSections]]; 149 | } 150 | 151 | - (void) insertSection:(SSSection *)newSection atIndex:(NSUInteger)index { 152 | [self.sections insertObject:newSection 153 | atIndex:index]; 154 | 155 | [self insertSectionsAtIndexes:[NSIndexSet indexSetWithIndex:index]]; 156 | } 157 | 158 | - (void) insertSections:(NSArray *)newSections atIndexes:(NSIndexSet *)indexes { 159 | NSMutableArray *mutableSections = [NSMutableArray array]; 160 | 161 | [newSections enumerateObjectsUsingBlock:^(id sectionObject, 162 | NSUInteger sectionIndex, 163 | BOOL *stop) { 164 | if ([sectionObject isKindOfClass:[SSSection class]]) 165 | [mutableSections addObject:sectionObject]; 166 | else if ([sectionObject isKindOfClass:[NSArray class]]) 167 | [mutableSections addObject:[SSSection sectionWithItems:sectionObject]]; 168 | else 169 | NSLog(@"Invalid SSSectionedDataSource section object: %@", sectionObject); 170 | }]; 171 | 172 | [self.sections insertObjects:mutableSections 173 | atIndexes:indexes]; 174 | 175 | [self insertSectionsAtIndexes:indexes]; 176 | } 177 | 178 | - (void)insertItem:(id)item atIndexPath:(NSIndexPath *)indexPath { 179 | [[self sectionAtIndex:indexPath.section].items insertObject:item 180 | atIndex:indexPath.row]; 181 | 182 | [self insertCellsAtIndexPaths:@[ indexPath ]]; 183 | } 184 | 185 | - (void)insertItems:(NSArray *)items 186 | atIndexes:(NSIndexSet *)indexes 187 | inSection:(NSUInteger)section { 188 | 189 | [[self sectionAtIndex:section].items insertObjects:items 190 | atIndexes:indexes]; 191 | 192 | [self insertCellsAtIndexPaths:[self.class indexPathArrayWithIndexSet:indexes 193 | inSection:section]]; 194 | } 195 | 196 | - (void)appendItems:(NSArray *)items toSection:(NSUInteger)section { 197 | NSUInteger sectionCount = [self numberOfItemsInSection:section]; 198 | 199 | [[self sectionAtIndex:section].items addObjectsFromArray:items]; 200 | 201 | [self insertCellsAtIndexPaths:[self.class indexPathArrayWithRange:NSMakeRange(sectionCount, 202 | [items count]) 203 | inSection:section]]; 204 | } 205 | 206 | #pragma mark - Replacing 207 | 208 | - (void)replaceItemAtIndexPath:(NSIndexPath *)indexPath withItem:(id)item { 209 | 210 | [[self sectionAtIndex:indexPath.section].items removeObjectAtIndex:indexPath.row]; 211 | [[self sectionAtIndex:indexPath.section].items insertObject:item 212 | atIndex:indexPath.row]; 213 | 214 | [self reloadCellsAtIndexPaths:@[ indexPath ]]; 215 | } 216 | 217 | #pragma mark - Removing 218 | 219 | - (void)clearSections { 220 | [self.sections removeAllObjects]; 221 | [self reloadData]; 222 | } 223 | 224 | - (void)removeAllSections { 225 | [self clearSections]; 226 | } 227 | 228 | - (void)removeSectionAtIndex:(NSUInteger)index { 229 | [self removeSectionsAtIndexes:[NSIndexSet indexSetWithIndex:index]]; 230 | } 231 | 232 | - (void)removeSectionsInRange:(NSRange)range { 233 | [self removeSectionsAtIndexes:[NSIndexSet indexSetWithIndexesInRange:range]]; 234 | } 235 | 236 | - (void)removeSectionsAtIndexes:(NSIndexSet *)indexes { 237 | [self.sections removeObjectsAtIndexes:indexes]; 238 | [self deleteSectionsAtIndexes:indexes]; 239 | } 240 | 241 | - (void)removeItemAtIndexPath:(NSIndexPath *)indexPath { 242 | [self removeItemsAtIndexes:[NSIndexSet indexSetWithIndex:indexPath.row] 243 | inSection:indexPath.section]; 244 | } 245 | 246 | - (void)removeItemsInRange:(NSRange)range inSection:(NSUInteger)section { 247 | [self removeItemsAtIndexes:[NSIndexSet indexSetWithIndexesInRange:range] 248 | inSection:section]; 249 | } 250 | 251 | - (void)removeItemsAtIndexes:(NSIndexSet *)indexes inSection:(NSUInteger)section { 252 | [[self sectionAtIndex:section].items removeObjectsAtIndexes:indexes]; 253 | 254 | if (self.shouldRemoveEmptySections && [self numberOfItemsInSection:section] == 0) { 255 | [self removeSectionAtIndex:section]; 256 | } else { 257 | [self deleteCellsAtIndexPaths:[self.class indexPathArrayWithIndexSet:indexes 258 | inSection:section]]; 259 | } 260 | } 261 | 262 | #pragma mark - UITableViewDelegate helpers 263 | 264 | - (SSBaseHeaderFooterView *)headerFooterViewWithClass:(Class)class { 265 | SSBaseHeaderFooterView *headerFooterView = [self.tableView dequeueReusableHeaderFooterViewWithIdentifier: 266 | [class identifier]]; 267 | 268 | if (!headerFooterView) 269 | headerFooterView = [class new]; 270 | 271 | return headerFooterView; 272 | } 273 | 274 | - (SSBaseHeaderFooterView *)viewForHeaderInSection:(NSUInteger)section { 275 | return [self headerFooterViewWithClass:[self sectionAtIndex:section].headerClass]; 276 | } 277 | 278 | - (SSBaseHeaderFooterView *)viewForFooterInSection:(NSUInteger)section { 279 | return [self headerFooterViewWithClass:[self sectionAtIndex:section].footerClass]; 280 | } 281 | 282 | - (CGFloat)heightForHeaderInSection:(NSUInteger)section { 283 | return [self sectionAtIndex:section].headerHeight; 284 | } 285 | 286 | - (CGFloat)heightForFooterInSection:(NSUInteger)section { 287 | return [self sectionAtIndex:section].footerHeight; 288 | } 289 | 290 | - (NSString *)titleForHeaderInSection:(NSUInteger)section { 291 | return [self sectionAtIndex:section].header; 292 | } 293 | 294 | - (NSString *)titleForFooterInSection:(NSUInteger)section { 295 | return [self sectionAtIndex:section].footer; 296 | } 297 | 298 | @end 299 | -------------------------------------------------------------------------------- /Example/Pods/SSDataSources/SSDataSources/SSCoreDataSource.m: -------------------------------------------------------------------------------- 1 | // 2 | // SSCoreDataSource.m 3 | // SSDataSources 4 | // 5 | // Created by Jonathan Hersh on 6/7/13. 6 | // Copyright (c) 2013 Splinesoft. All rights reserved. 7 | // 8 | 9 | #import "SSDataSources.h" 10 | 11 | @interface SSCoreDataSource () 12 | 13 | // For UICollectionView 14 | @property (nonatomic, strong) NSMutableArray *sectionUpdates; 15 | @property (nonatomic, strong) NSMutableArray *objectUpdates; 16 | 17 | - (void) _performFetch; 18 | 19 | @end 20 | 21 | @implementation SSCoreDataSource 22 | 23 | - (instancetype)init { 24 | if ((self = [super init])) { 25 | _sectionUpdates = [NSMutableArray new]; 26 | _objectUpdates = [NSMutableArray new]; 27 | } 28 | 29 | return self; 30 | } 31 | 32 | - (instancetype) initWithFetchedResultsController:(NSFetchedResultsController *)aController { 33 | if ((self = [self init])) { 34 | _controller = aController; 35 | _controller.delegate = self; 36 | 37 | if (!_controller.fetchedObjects) { 38 | [self _performFetch]; 39 | } 40 | } 41 | 42 | return self; 43 | } 44 | 45 | - (instancetype)initWithFetchRequest:(NSFetchRequest *)request 46 | inContext:(NSManagedObjectContext *)context 47 | sectionNameKeyPath:(NSString *)sectionNameKeyPath { 48 | 49 | if ((self = [self init])) { 50 | _controller = [[NSFetchedResultsController alloc] initWithFetchRequest:request 51 | managedObjectContext:context 52 | sectionNameKeyPath:sectionNameKeyPath 53 | cacheName:nil]; 54 | 55 | _controller.delegate = self; 56 | 57 | [self _performFetch]; 58 | } 59 | 60 | return self; 61 | } 62 | 63 | - (void)dealloc { 64 | self.controller.delegate = nil; 65 | self.controller = nil; 66 | [self.sectionUpdates removeAllObjects]; 67 | [self.objectUpdates removeAllObjects]; 68 | } 69 | 70 | #pragma mark - Fetching 71 | 72 | - (void)_performFetch { 73 | NSError *fetchErr = nil; 74 | [_controller performFetch:&fetchErr]; 75 | _fetchError = fetchErr; 76 | } 77 | 78 | #pragma mark - Base data source 79 | 80 | - (NSUInteger)numberOfSections { 81 | return (NSUInteger)[[_controller sections] count]; 82 | } 83 | 84 | - (NSUInteger)numberOfItemsInSection:(NSUInteger)section { 85 | id sectionInfo = [_controller sections][(NSUInteger)section]; 86 | return (NSUInteger)[sectionInfo numberOfObjects]; 87 | } 88 | 89 | - (NSUInteger)numberOfItems { 90 | NSUInteger count = 0; 91 | 92 | for (id section in [_controller sections]) 93 | count += [section numberOfObjects]; 94 | 95 | return count; 96 | } 97 | 98 | #pragma mark - item access 99 | 100 | - (id)itemAtIndexPath:(NSIndexPath *)indexPath { 101 | return [_controller objectAtIndexPath:indexPath]; 102 | } 103 | 104 | - (NSIndexPath *)indexPathForItemWithId:(NSManagedObjectID *)objectId { 105 | for (NSUInteger section = 0; section < [self numberOfSections]; section++) { 106 | id sec = [_controller sections][section]; 107 | 108 | NSUInteger index = [[sec objects] indexOfObjectPassingTest:^BOOL(NSManagedObject *object, 109 | NSUInteger idx, 110 | BOOL *stop) { 111 | return [[object objectID] isEqual:objectId]; 112 | }]; 113 | 114 | if (index != NSNotFound) 115 | return [NSIndexPath indexPathForRow:(NSInteger)index inSection:(NSInteger)section]; 116 | } 117 | 118 | return nil; 119 | } 120 | 121 | #pragma mark - UITableViewDataSource 122 | 123 | - (NSInteger)tableView:(UITableView *)tableView 124 | sectionForSectionIndexTitle:(NSString *)title 125 | atIndex:(NSInteger)index { 126 | return [_controller sectionForSectionIndexTitle:title atIndex:index]; 127 | } 128 | 129 | - (NSArray *)sectionIndexTitlesForTableView:(UITableView *)tableView { 130 | return [_controller sectionIndexTitles]; 131 | } 132 | 133 | - (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section { 134 | id sectionInfo = [_controller sections][(NSUInteger)section]; 135 | return [sectionInfo name]; 136 | } 137 | 138 | #pragma mark - NSFetchedResultsControllerDelegate 139 | 140 | - (NSString *)controller:(NSFetchedResultsController *)controller sectionIndexTitleForSectionName:(NSString *)sectionName { 141 | return sectionName; 142 | } 143 | 144 | - (void)controllerWillChangeContent:(NSFetchedResultsController *)controller { 145 | [self.tableView beginUpdates]; 146 | } 147 | 148 | - (void)controller:(NSFetchedResultsController *)controller 149 | didChangeObject:(id)anObject 150 | atIndexPath:(NSIndexPath *)indexPath 151 | forChangeType:(NSFetchedResultsChangeType)type 152 | newIndexPath:(NSIndexPath *)newIndexPath { 153 | 154 | NSMutableDictionary *change = [NSMutableDictionary new]; 155 | 156 | switch (type) { 157 | case NSFetchedResultsChangeInsert: 158 | change[@(type)] = newIndexPath; 159 | [self.tableView insertRowsAtIndexPaths:@[ newIndexPath ] 160 | withRowAnimation:self.rowAnimation]; 161 | break; 162 | 163 | case NSFetchedResultsChangeDelete: 164 | change[@(type)] = indexPath; 165 | [self.tableView deleteRowsAtIndexPaths:@[ indexPath ] 166 | withRowAnimation:self.rowAnimation]; 167 | break; 168 | 169 | case NSFetchedResultsChangeUpdate: 170 | change[@(type)] = indexPath; 171 | [self.tableView reloadRowsAtIndexPaths:@[ indexPath ] 172 | withRowAnimation:self.rowAnimation]; 173 | break; 174 | 175 | case NSFetchedResultsChangeMove: 176 | change[@(type)] = @[ indexPath, newIndexPath ]; 177 | [self.tableView deleteRowsAtIndexPaths:@[ indexPath ] 178 | withRowAnimation:self.rowAnimation]; 179 | [self.tableView insertRowsAtIndexPaths:@[ newIndexPath ] 180 | withRowAnimation:self.rowAnimation]; 181 | break; 182 | } 183 | 184 | [self.objectUpdates addObject:change]; 185 | } 186 | 187 | - (void)controller:(NSFetchedResultsController *)controller 188 | didChangeSection:(id )sectionInfo 189 | atIndex:(NSUInteger)sectionIndex 190 | forChangeType:(NSFetchedResultsChangeType)type { 191 | 192 | NSMutableDictionary *change = [NSMutableDictionary new]; 193 | 194 | switch (type) { 195 | case NSFetchedResultsChangeInsert: 196 | [self.tableView insertSections:[NSIndexSet indexSetWithIndex:sectionIndex] 197 | withRowAnimation:self.rowAnimation]; 198 | change[@(type)] = @[@(sectionIndex)]; 199 | break; 200 | 201 | case NSFetchedResultsChangeDelete: 202 | [self.tableView deleteSections:[NSIndexSet indexSetWithIndex:sectionIndex] 203 | withRowAnimation:self.rowAnimation]; 204 | change[@(type)] = @[@(sectionIndex)]; 205 | break; 206 | } 207 | 208 | [self.sectionUpdates addObject:change]; 209 | } 210 | 211 | - (void)controllerDidChangeContent:(NSFetchedResultsController *)controller { 212 | [self.tableView endUpdates]; 213 | 214 | if (self.collectionView) { 215 | 216 | if ([self.sectionUpdates count] > 0) { 217 | [self.collectionView performBatchUpdates:^{ 218 | for (NSDictionary *change in self.sectionUpdates) { 219 | [change enumerateKeysAndObjectsUsingBlock:^(NSNumber *key, id secnum, BOOL *stop) { 220 | NSFetchedResultsChangeType type = (NSFetchedResultsChangeType)[key unsignedIntegerValue]; 221 | NSIndexSet *section = [NSIndexSet indexSetWithIndex:[secnum unsignedIntegerValue]]; 222 | 223 | switch( type ) { 224 | case NSFetchedResultsChangeInsert: 225 | [self.collectionView insertSections:section]; 226 | break; 227 | case NSFetchedResultsChangeDelete: 228 | [self.collectionView deleteSections:section]; 229 | break; 230 | case NSFetchedResultsChangeUpdate: 231 | [self.collectionView reloadSections:section]; 232 | break; 233 | } 234 | }]; 235 | } 236 | } completion:nil]; 237 | } 238 | 239 | if ([self.objectUpdates count] > 0 && [self.sectionUpdates count] == 0) { 240 | [self.collectionView performBatchUpdates:^{ 241 | for (NSDictionary *change in self.objectUpdates) { 242 | [change enumerateKeysAndObjectsUsingBlock:^(NSNumber *key, id indexPath, BOOL *stop) { 243 | NSFetchedResultsChangeType type = [key unsignedIntegerValue]; 244 | 245 | switch( type ) { 246 | case NSFetchedResultsChangeInsert: 247 | [self.collectionView insertItemsAtIndexPaths:@[ indexPath ]]; 248 | break; 249 | case NSFetchedResultsChangeDelete: 250 | [self.collectionView deleteItemsAtIndexPaths:@[ indexPath ]]; 251 | break; 252 | case NSFetchedResultsChangeUpdate: 253 | [self.collectionView reloadItemsAtIndexPaths:@[ indexPath ]]; 254 | break; 255 | case NSFetchedResultsChangeMove: 256 | [self.collectionView moveItemAtIndexPath:indexPath[0] 257 | toIndexPath:indexPath[1]]; 258 | break; 259 | } 260 | }]; 261 | } 262 | } completion:nil]; 263 | } 264 | } 265 | 266 | [self.sectionUpdates removeAllObjects]; 267 | [self.objectUpdates removeAllObjects]; 268 | 269 | // Hackish; force recalculation of empty view state 270 | self.emptyView = self.emptyView; 271 | } 272 | 273 | @end 274 | -------------------------------------------------------------------------------- /Example/Pods/SSDataSources/README.md: -------------------------------------------------------------------------------- 1 | SSDataSources 2 | ============= 3 | 4 | [![Build Status](https://travis-ci.org/splinesoft/SSDataSources.png?branch=master)](https://travis-ci.org/splinesoft/SSDataSources) 5 | 6 | Flexible data sources for your `UITableView` and `UICollectionView`. *wow, much DRY* 7 | 8 | No doubt you've done the `tableView:cellForRowAtIndexPath:` and `tableView:numberOfRowsInSection:` and `collectionView:cellForItemAtIndexPath:` and `collectionView:numberOfItemsInSection:` dances many times before. You may also have updated your data and forgotten to update the table or collection view. Whoops -- crash! Is there a better way? 9 | 10 | `SSDataSources` is a collection of objects that conform to `UITableViewDataSource` and `UICollectionViewDataSource`. This is my own implementation of ideas featured in [objc.io's wonderful first issue](http://www.objc.io/issue-1/table-views.html). 11 | 12 | `SSDataSources` powers single-section, multi-section, and Core Data-backed tables in my app [MUDRammer - a modern MUD client for iPhone and iPad](https://itunes.apple.com/us/app/mudrammer-a-modern-mud-client/id597157072?mt=8). 13 | 14 | ## Install 15 | 16 | Install with [Cocoapods](http://cocoapods.org). Add to your podfile: 17 | 18 | ``` 19 | pod 'SSDataSources', :head # YOLO 20 | ``` 21 | 22 | ## Samples 23 | 24 | All the tables and collection views in the `Example` project are built with `SSDataSources`. 25 | 26 | ## Array Data Source 27 | 28 | `SSArrayDataSource` powers a table or collection view with a single section. See `SSArrayDataSource.h` for more details. 29 | 30 | Check out the example project for sample table and collection views that use the array data source. 31 | 32 | `SSArrayDataSource` can also observe a target and key path for array content. 33 | 34 | 35 | ```objc 36 | @interface WizardicTableViewController : UITableViewController 37 | 38 | @property (nonatomic, strong) SSArrayDataSource *wizardDataSource; 39 | 40 | @end 41 | 42 | @implementation WizardicTableViewController 43 | 44 | - (void)viewDidLoad { 45 | [super viewDidLoad]; 46 | 47 | _wizardDataSource = [[SSArrayDataSource alloc] initWithItems: 48 | @[ @"Merlyn", @"Gandalf", @"Melisandre" ]]; 49 | 50 | // SSDataSources creates your cell and calls 51 | // this configure block for each cell with 52 | // the object being presented in that cell, 53 | // the parent table or collection view, 54 | // and the index path at which the cell appears. 55 | self.wizardDataSource.cellConfigureBlock = ^(SSBaseTableCell *cell, 56 | NSString *wizard, 57 | UITableView *tableView, 58 | NSIndexPath *indexPath) { 59 | cell.textLabel.text = wizard; 60 | }; 61 | 62 | // Set the tableView property and the data source will perform 63 | // insert/reload/delete calls on the table as its data changes. 64 | // This also assigns the table's `dataSource` property. 65 | self.wizardDataSource.tableView = self.tableView; 66 | } 67 | @end 68 | ``` 69 | 70 | That's it - you're done! 71 | 72 | Perhaps your data changes: 73 | 74 | ```objc 75 | // Sometimes it's nice to add a view that automatically 76 | // shows when the data source is empty and 77 | // hides when the data source has items. 78 | UILabel *noItemsLabel = [UILabel new]; 79 | noItemsLabel.text = @"No Items"; 80 | noItemsLabel.font = [UIFont boldSystemFontOfSize:18.0f]; 81 | noItemsLabel.textAlignment = NSTextAlignmentCenter; 82 | self.wizardDataSource.emptyView = noItemsLabel; 83 | 84 | // Optional - row animation for table updates. 85 | self.wizardDataSource.rowAnimation = UITableViewRowAnimationFade; 86 | 87 | // Automatically inserts two new cells at the end of the table. 88 | [self.wizardDataSource appendItems:@[ @"Saruman", @"Alatar" ]]; 89 | 90 | // Update the fourth item; reloads the fourth row. 91 | [self.wizardDataSource replaceItemAtIndex:3 withItem:@"Pallando"]; 92 | 93 | // Sorry Merlyn :( 94 | [self.wizardDataSource moveItemAtIndex:0 toIndex:1]; 95 | 96 | // Remove the second and third cells. 97 | [self.wizardDataSource removeItemsInRange:NSMakeRange( 1, 2 )]; 98 | ``` 99 | 100 | Perhaps you have custom table cell classes or multiple classes in the same table: 101 | 102 | ```objc 103 | self.wizardDataSource.cellCreationBlock = ^id(NSString *wizard, 104 | UITableView *tableView, 105 | NSIndexPath *indexPath) { 106 | if ([wizard isEqualToString:@"Gandalf"]) { 107 | return [MiddleEarthWizardCell cellForTableView:tableView]; 108 | } else if ([wizard isEqualToString:@"Merlyn"]) { 109 | return [ArthurianWizardCell cellForTableView:tableView]; 110 | } 111 | }; 112 | 113 | ``` 114 | 115 | Your view controller should continue to implement `UITableViewDelegate`. `SSDataSources` can help there too: 116 | 117 | ```objc 118 | - (void)tableView:(UITableView *)tv didSelectRowAtIndexPath:(NSIndexPath *)indexPath { 119 | NSString *wizard = [self.wizardDataSource itemAtIndexPath:indexPath]; 120 | 121 | // do something with `wizard` 122 | } 123 | ``` 124 | 125 | ## Sectioned Data Source 126 | 127 | `SSSectionedDataSource` powers a table or collection view with multiple sections. Each section is modeled with an `SSSection` object, which stores the section's items and a few other configurable bits. See `SSSectionedDataSource.h` and `SSSection.h` for more details. 128 | 129 | Check out the example project for a sample table that uses the sectioned data source. 130 | 131 | ```objc 132 | @interface ElementalTableViewController : UITableViewController 133 | 134 | @property (nonatomic, strong) SSSectionedDataSource *elementDataSource; 135 | 136 | @end 137 | 138 | @implementation ElementalTableViewController 139 | 140 | - (void)viewDidLoad { 141 | [super viewDidLoad]; 142 | 143 | // Let's start with one section 144 | _elementDataSource = [[SSSectionedDataSource alloc] initWithItems:@[ @"Earth" ]]; 145 | 146 | self.elementDataSource.cellConfigureBlock = ^(SSBaseTableCell *cell, 147 | NSString *element, 148 | UITableView *tableView, 149 | NSIndexPath *indexPath) { 150 | cell.textLabel.text = element; 151 | }; 152 | 153 | // Setting the tableView property automatically updates 154 | // the table in response to data changes. 155 | // This also sets the table's `dataSource` property. 156 | self.elementDataSource.tableView = self.tableView; 157 | } 158 | @end 159 | ``` 160 | 161 | `SSSectionedDataSource` has you covered if your data changes: 162 | 163 | ```objc 164 | // Sometimes it's nice to add a view that automatically 165 | // shows when the data source is empty and 166 | // hides when the data source has items. 167 | UILabel *noItemsLabel = [UILabel new]; 168 | noItemsLabel.text = @"No Items"; 169 | noItemsLabel.font = [UIFont boldSystemFontOfSize:18.0f]; 170 | noItemsLabel.textAlignment = NSTextAlignmentCenter; 171 | self.elementDataSource.emptyView = noItemsLabel; 172 | 173 | // Animation for table updates 174 | self.elementDataSource.rowAnimation = UITableViewRowAnimationFade; 175 | 176 | // Add some new sections 177 | [self.elementDataSource appendSection:[SSSection sectionWithItems:@[ @"Fire" ]]]; 178 | [self.elementDataSource appendSection:[SSSection sectionWithItems:@[ @"Wind" ]]]; 179 | [self.elementDataSource appendSection:[SSSection sectionWithItems:@[ @"Water" ]]]; 180 | [self.elementDataSource appendSection:[SSSection sectionWithItems:@[ @"Heart", @"GOOOO PLANET!" ]]]; 181 | 182 | // Are you 4 srs, heart? 183 | [self.elementDataSource removeSectionAtIndex:([elementDataSource numberOfSections] - 1)]; 184 | ``` 185 | 186 | ## Core Data 187 | 188 | You're a modern wo/man-about-Internet and sometimes you want to present a `UITableView` or `UICollectionView` backed by a core data fetch request or fetched results controller. `SSDataSources` has you covered with `SSCoreDataSource`, featured here with a cameo by [MagicalRecord](https://github.com/magicalpanda/MagicalRecord). 189 | 190 | ```objc 191 | @interface SSCoreDataTableViewController : UITableViewController 192 | 193 | @property (nonatomic, strong) SSCoreDataSource *dataSource; 194 | 195 | @end 196 | 197 | @implementation SSCoreDataTableViewController 198 | 199 | - (void) viewDidLoad { 200 | [super viewDidLoad]; 201 | 202 | NSFetchRequest *triggerFetch = [Trigger MR_requestAllSortedBy:[Trigger defaultSortField] 203 | ascending:[Trigger defaultSortAscending]]; 204 | 205 | _dataSource = [[SSCoreDataSource alloc] initWithFetchRequest:triggerFetch 206 | inContext:[NSManagedObjectContext 207 | MR_defaultContext] 208 | sectionNameKeyPath:nil]; 209 | 210 | self.dataSource.cellConfigureBlock = ^(SSBaseTableCell *cell, 211 | Trigger *trigger, 212 | UITableView *tableView, 213 | NSIndexPath *indexPath ) { 214 | cell.textLabel.text = trigger.name; 215 | }; 216 | 217 | // SSCoreDataSource conforms to NSFetchedResultsControllerDelegate. 218 | // Set the `tableView` property to automatically update the table 219 | // after changes in the data source's managed object context. 220 | // This also sets the tableview's `dataSource`. 221 | self.dataSource.tableView = self.tableView; 222 | 223 | // Optional - row animation to use for update events. 224 | self.dataSource.rowAnimation = UITableViewRowAnimationFade; 225 | 226 | // Optional - permissions for editing and moving 227 | self.dataSource.tableActionBlock = ^BOOL(SSCellActionType actionType, 228 | UITableView *tableView, 229 | NSIndexPath *indexPath) { 230 | 231 | // Disallow moving, allow editing 232 | return actionType == SSCellActionTypeEdit; 233 | }; 234 | 235 | // Optional - handle managed object deletion 236 | self.dataSource.tableDeletionBlock = ^(SSCoreDataSource *aDataSource, 237 | UITableView *tableView, 238 | NSIndexPath *indexPath) { 239 | 240 | Trigger *myObject = [aDataSource itemAtIndexPath:indexPath]; 241 | 242 | // SSCoreDataSource conforms to NSFetchedResultsControllerDelegate, 243 | // so saving the object's context will automatically update the table. 244 | [myObject deleteInContext:myObject.managedObjectContext]; 245 | [myObject.managedObjectContext MR_saveToPersistentStoreWithCompletion:nil]; 246 | }; 247 | } 248 | @end 249 | ``` 250 | 251 | ## Thanks! 252 | 253 | `SSDataSources` is a [@jhersh](https://github.com/jhersh) production -- ([electronic mail](mailto:jon@her.sh) | [@jhersh](https://twitter.com/jhersh)) 254 | -------------------------------------------------------------------------------- /Example/Pods/SSDataSources/SSDataSources/SSBaseDataSource.m: -------------------------------------------------------------------------------- 1 | // 2 | // SSBaseDataSource.m 3 | // SSDataSources 4 | // 5 | // Created by Jonathan Hersh on 6/8/13. 6 | // Copyright (c) 2013 Splinesoft. All rights reserved. 7 | // 8 | 9 | #import "SSDataSources.h" 10 | 11 | @interface SSBaseDataSource () 12 | 13 | @property (nonatomic, assign) UITableViewCellSeparatorStyle cachedSeparatorStyle; 14 | 15 | - (void) _updateEmptyView; 16 | 17 | @end 18 | 19 | @implementation SSBaseDataSource 20 | 21 | #pragma mark - init 22 | 23 | - (instancetype)init { 24 | if ((self = [super init])) { 25 | self.cellClass = [SSBaseTableCell class]; 26 | self.collectionViewSupplementaryElementClass = [SSBaseCollectionReusableView class]; 27 | self.rowAnimation = UITableViewRowAnimationAutomatic; 28 | self.cachedSeparatorStyle = UITableViewCellSeparatorStyleSingleLine; 29 | } 30 | 31 | return self; 32 | } 33 | 34 | - (void)dealloc { 35 | if (self.emptyView) { 36 | [self.emptyView removeFromSuperview]; 37 | self.emptyView = nil; 38 | } 39 | 40 | self.cellConfigureBlock = nil; 41 | self.cellCreationBlock = nil; 42 | self.collectionSupplementaryConfigureBlock = nil; 43 | self.collectionSupplementaryCreationBlock = nil; 44 | self.tableActionBlock = nil; 45 | self.tableDeletionBlock = nil; 46 | } 47 | 48 | #pragma mark - Base item access 49 | 50 | - (id)itemAtIndexPath:(NSIndexPath *)indexPath { 51 | // override me! 52 | @throw [NSException exceptionWithName:NSInternalInconsistencyException 53 | reason:[NSString stringWithFormat: 54 | @"Did you forget to override %@?", 55 | NSStringFromSelector(_cmd)] 56 | userInfo:nil]; 57 | } 58 | 59 | - (NSUInteger)numberOfItems { 60 | // override me! 61 | @throw [NSException exceptionWithName:NSInternalInconsistencyException 62 | reason:[NSString stringWithFormat: 63 | @"Did you forget to override %@?", 64 | NSStringFromSelector(_cmd)] 65 | userInfo:nil]; 66 | } 67 | 68 | - (NSUInteger)numberOfSections { 69 | // override me! 70 | @throw [NSException exceptionWithName:NSInternalInconsistencyException 71 | reason:[NSString stringWithFormat: 72 | @"Did you forget to override %@?", 73 | NSStringFromSelector(_cmd)] 74 | userInfo:nil]; 75 | } 76 | 77 | - (NSUInteger)numberOfItemsInSection:(NSUInteger)section { 78 | // override me! 79 | @throw [NSException exceptionWithName:NSInternalInconsistencyException 80 | reason:[NSString stringWithFormat: 81 | @"Did you forget to override %@?", 82 | NSStringFromSelector(_cmd)] 83 | userInfo:nil]; 84 | } 85 | 86 | #pragma mark - Custom Animations 87 | 88 | - (void)performAnimations:(void (^)(void))animations 89 | duration:(NSTimeInterval)duration 90 | completion:(void (^)(void))completion { 91 | 92 | if (!animations) { 93 | return; 94 | } 95 | 96 | [CATransaction begin]; 97 | [CATransaction setCompletionBlock:completion]; 98 | 99 | [UIView animateWithDuration:duration 100 | animations:^{ 101 | [self.tableView beginUpdates]; 102 | animations(); 103 | [self.tableView endUpdates]; 104 | }]; 105 | 106 | [CATransaction commit]; 107 | } 108 | 109 | #pragma mark - Common 110 | 111 | - (void)configureCell:(id)cell 112 | forItem:(id)item 113 | parentView:(id)parentView 114 | indexPath:(NSIndexPath *)indexPath { 115 | 116 | if (self.cellConfigureBlock) 117 | self.cellConfigureBlock( cell, item, parentView, indexPath ); 118 | } 119 | 120 | #pragma mark - UITableViewDataSource 121 | 122 | - (void)setTableView:(UITableView *)tableView { 123 | _tableView = tableView; 124 | 125 | if (tableView) { 126 | tableView.dataSource = self; 127 | } 128 | } 129 | 130 | - (UITableViewCell *)tableView:(UITableView *)tv 131 | cellForRowAtIndexPath:(NSIndexPath *)indexPath { 132 | 133 | id item = [self itemAtIndexPath:indexPath]; 134 | 135 | id cell = (self.cellCreationBlock 136 | ? self.cellCreationBlock(item, tv, indexPath) 137 | : [self.cellClass cellForTableView:tv]); 138 | 139 | [self configureCell:cell 140 | forItem:item 141 | parentView:tv 142 | indexPath:indexPath]; 143 | 144 | return cell; 145 | } 146 | 147 | - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { 148 | return [self numberOfSections]; 149 | } 150 | 151 | - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { 152 | return [self numberOfItemsInSection:section]; 153 | } 154 | 155 | - (BOOL)tableView:(UITableView *)tv canMoveRowAtIndexPath:(NSIndexPath *)indexPath { 156 | 157 | if (self.tableActionBlock) { 158 | return self.tableActionBlock(SSCellActionTypeMove, 159 | tv, 160 | indexPath); 161 | } 162 | 163 | return NO; 164 | } 165 | 166 | - (BOOL)tableView:(UITableView *)tv canEditRowAtIndexPath:(NSIndexPath *)indexPath { 167 | 168 | if (self.tableActionBlock) { 169 | return self.tableActionBlock(SSCellActionTypeEdit, 170 | tv, 171 | indexPath); 172 | } 173 | 174 | return YES; 175 | } 176 | 177 | - (void)tableView:(UITableView *)tv 178 | commitEditingStyle:(UITableViewCellEditingStyle)editingStyle 179 | forRowAtIndexPath:(NSIndexPath *)indexPath { 180 | 181 | if (self.tableDeletionBlock) { 182 | self.tableDeletionBlock(self, 183 | tv, 184 | indexPath); 185 | } 186 | } 187 | 188 | #pragma mark - UICollectionViewDataSource 189 | 190 | - (void)setCollectionView:(UICollectionView *)collectionView { 191 | _collectionView = collectionView; 192 | 193 | if (collectionView) 194 | collectionView.dataSource = self; 195 | } 196 | 197 | - (UICollectionViewCell *)collectionView:(UICollectionView *)cv 198 | cellForItemAtIndexPath:(NSIndexPath *)indexPath { 199 | 200 | id item = [self itemAtIndexPath:indexPath]; 201 | 202 | id cell = (self.cellCreationBlock 203 | ? self.cellCreationBlock(item, cv, indexPath) 204 | : [self.cellClass cellForCollectionView:cv 205 | indexPath:indexPath]); 206 | 207 | [self configureCell:cell 208 | forItem:item 209 | parentView:cv 210 | indexPath:indexPath]; 211 | 212 | return cell; 213 | } 214 | 215 | - (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView { 216 | return [self numberOfSections]; 217 | } 218 | 219 | - (NSInteger)collectionView:(UICollectionView *)collectionView 220 | numberOfItemsInSection:(NSInteger)section { 221 | 222 | return [self numberOfItemsInSection:section]; 223 | } 224 | 225 | - (UICollectionReusableView *)collectionView:(UICollectionView *)cv 226 | viewForSupplementaryElementOfKind:(NSString *)kind 227 | atIndexPath:(NSIndexPath *)indexPath { 228 | 229 | UICollectionReusableView *supplementaryView; 230 | 231 | if (self.collectionSupplementaryCreationBlock) 232 | supplementaryView = self.collectionSupplementaryCreationBlock(kind, cv, indexPath); 233 | else 234 | supplementaryView = [self.collectionViewSupplementaryElementClass 235 | supplementaryViewForCollectionView:cv 236 | kind:kind 237 | indexPath:indexPath]; 238 | 239 | if (self.collectionSupplementaryConfigureBlock) 240 | self.collectionSupplementaryConfigureBlock(supplementaryView, kind, cv, indexPath); 241 | 242 | return supplementaryView; 243 | } 244 | 245 | #pragma mark - Empty Views 246 | 247 | - (void)setEmptyView:(UIView *)emptyView { 248 | if (self.emptyView) { 249 | [self.emptyView removeFromSuperview]; 250 | } 251 | 252 | _emptyView = emptyView; 253 | 254 | _emptyView.hidden = YES; 255 | 256 | [self _updateEmptyView]; 257 | } 258 | 259 | - (void)_updateEmptyView { 260 | if (!self.emptyView) { 261 | return; 262 | } 263 | 264 | UIScrollView *targetView = (self.tableView ?: self.collectionView); 265 | 266 | if (!targetView) { 267 | return; 268 | } 269 | 270 | if (self.emptyView.superview != targetView) { 271 | [targetView addSubview:self.emptyView]; 272 | } 273 | 274 | BOOL shouldShowEmptyView = ([self numberOfItems] == 0); 275 | BOOL isShowingEmptyView = !self.emptyView.hidden; 276 | 277 | if (shouldShowEmptyView == isShowingEmptyView) { 278 | return; 279 | } 280 | 281 | if (shouldShowEmptyView) { 282 | self.cachedSeparatorStyle = self.tableView.separatorStyle; 283 | self.tableView.separatorStyle = UITableViewCellSeparatorStyleNone; 284 | 285 | if (CGRectEqualToRect(self.emptyView.frame, CGRectZero)) { 286 | CGRect frame = UIEdgeInsetsInsetRect(targetView.bounds, targetView.contentInset); 287 | 288 | if (self.tableView.tableHeaderView) { 289 | frame.size.height -= CGRectGetHeight(self.tableView.tableHeaderView.frame); 290 | } 291 | 292 | [self.emptyView setFrame:frame]; 293 | } 294 | 295 | self.emptyView.autoresizingMask = targetView.autoresizingMask; 296 | } else { 297 | self.tableView.separatorStyle = self.cachedSeparatorStyle; 298 | } 299 | 300 | self.emptyView.hidden = !shouldShowEmptyView; 301 | 302 | // Reloading seems to work around an awkward delay where the empty view 303 | // is not immediately visible but the separator lines still are 304 | [self.tableView reloadData]; 305 | [self.collectionView reloadData]; 306 | } 307 | 308 | #pragma mark - UITableView/UICollectionView Operations 309 | 310 | - (void)insertCellsAtIndexPaths:(NSArray *)indexPaths { 311 | [_tableView insertRowsAtIndexPaths:indexPaths 312 | withRowAnimation:self.rowAnimation]; 313 | 314 | [_collectionView insertItemsAtIndexPaths:indexPaths]; 315 | 316 | [self _updateEmptyView]; 317 | } 318 | 319 | - (void)deleteCellsAtIndexPaths:(NSArray *)indexPaths { 320 | [_tableView deleteRowsAtIndexPaths:indexPaths 321 | withRowAnimation:self.rowAnimation]; 322 | 323 | [_collectionView deleteItemsAtIndexPaths:indexPaths]; 324 | 325 | [self _updateEmptyView]; 326 | } 327 | 328 | - (void)reloadCellsAtIndexPaths:(NSArray *)indexPaths { 329 | [_tableView reloadRowsAtIndexPaths:indexPaths 330 | withRowAnimation:self.rowAnimation]; 331 | 332 | [_collectionView reloadItemsAtIndexPaths:indexPaths]; 333 | } 334 | 335 | - (void)moveCellAtIndexPath:(NSIndexPath *)index1 toIndexPath:(NSIndexPath *)index2 { 336 | [_tableView moveRowAtIndexPath:index1 337 | toIndexPath:index2]; 338 | 339 | [_collectionView moveItemAtIndexPath:index1 340 | toIndexPath:index2]; 341 | } 342 | 343 | - (void)moveSectionAtIndex:(NSUInteger)index1 toIndex:(NSUInteger)index2 { 344 | [_tableView moveSection:index1 345 | toSection:index2]; 346 | 347 | [_collectionView moveSection:index1 348 | toSection:index2]; 349 | } 350 | 351 | - (void)insertSectionsAtIndexes:(NSIndexSet *)indexes { 352 | [_tableView insertSections:indexes 353 | withRowAnimation:self.rowAnimation]; 354 | 355 | [_collectionView insertSections:indexes]; 356 | 357 | [self _updateEmptyView]; 358 | } 359 | 360 | - (void)deleteSectionsAtIndexes:(NSIndexSet *)indexes { 361 | [_tableView deleteSections:indexes 362 | withRowAnimation:self.rowAnimation]; 363 | 364 | [_collectionView deleteSections:indexes]; 365 | 366 | [self _updateEmptyView]; 367 | } 368 | 369 | - (void)reloadData { 370 | [self.tableView reloadData]; 371 | [self.collectionView reloadData]; 372 | 373 | [self _updateEmptyView]; 374 | } 375 | 376 | #pragma mark - NSIndexPath helpers 377 | 378 | + (NSArray *)indexPathArrayWithIndexSet:(NSIndexSet *)indexes 379 | inSection:(NSUInteger)section { 380 | 381 | NSMutableArray *ret = [NSMutableArray array]; 382 | 383 | [indexes enumerateIndexesUsingBlock:^(NSUInteger index, BOOL *stop) { 384 | [ret addObject:[NSIndexPath indexPathForRow:index inSection:section]]; 385 | }]; 386 | 387 | return ret; 388 | } 389 | 390 | + (NSArray *)indexPathArrayWithRange:(NSRange)range 391 | inSection:(NSUInteger)section { 392 | 393 | NSMutableArray *ret = [NSMutableArray array]; 394 | 395 | for (NSUInteger i = range.location; i < NSMaxRange(range); i++) 396 | [ret addObject:[NSIndexPath indexPathForRow:i inSection:section]]; 397 | 398 | return ret; 399 | } 400 | 401 | @end 402 | -------------------------------------------------------------------------------- /Classes/BMFloatingHeaderCollectionViewLayout.m: -------------------------------------------------------------------------------- 1 | // 2 | // HSCollectionViewLayout.m 3 | // cvtest 4 | // 5 | // Created by Adam Iredale on 15/11/2013. 6 | // Copyright (c) 2013 Bionic Monocle Pty Ltd. All rights reserved. 7 | // 8 | 9 | #import "BMFloatingHeaderCollectionViewLayout.h" 10 | 11 | #pragma mark - Invalidation Context 12 | 13 | #import "BMFloatingHeaderCollectionViewLayoutInvalidationContext.h" 14 | 15 | NSString *const BMFloatingHeaderCollectionViewLayoutHeaderTitleKind = @"BMFloatingHeaderCollectionViewLayoutHeaderTitleKind"; 16 | NSString *const BMFloatingHeaderCollectionViewLayoutHeaderDetailKind = @"BMFloatingHeaderCollectionViewLayoutHeaderDetailKind"; 17 | NSString *const BMFloatingHeaderCollectionViewLayoutPlaceholderKind = @"BMFloatingHeaderCollectionViewLayoutPlaceholderKind"; 18 | 19 | #pragma mark - BMFloatingHeaderCollectionViewLayout 20 | 21 | @interface BMFloatingHeaderCollectionViewLayout () 22 | 23 | @property (nonatomic, strong) UICollectionViewLayoutAttributes *headerTitleViewAttributes; 24 | @property (nonatomic, strong) UICollectionViewLayoutAttributes *headerDetailViewAttributes; 25 | @property (nonatomic, strong) UICollectionViewLayoutAttributes *placeHolderViewAttributes; 26 | 27 | @property (nonatomic, strong) NSMapTable *cellAttributes; 28 | @property (nonatomic, strong) NSMutableSet *allAttributes; 29 | 30 | @property (nonatomic, assign) CGRect contentFrame; 31 | /** 32 | * Reference bounds size to know when to invalidate all 33 | */ 34 | @property (nonatomic, assign) CGSize boundsSize; 35 | /** 36 | * Set when inspecting the custom invalidation context. If YES, all will be recalculated, 37 | * otherwise, just the detail header 38 | */ 39 | @property (nonatomic, assign) BOOL shouldInvalidateAll; 40 | 41 | @end 42 | 43 | @implementation BMFloatingHeaderCollectionViewLayout 44 | 45 | #pragma mark - UICollectionViewLayout 46 | 47 | #pragma mark Init 48 | 49 | - (instancetype)initWithCoder:(NSCoder *)aDecoder 50 | { 51 | self = [super initWithCoder:aDecoder]; 52 | if (self) 53 | { 54 | [self setupLayout]; 55 | } 56 | return self; 57 | } 58 | 59 | - (instancetype)init 60 | { 61 | self = [super init]; 62 | if (self) 63 | { 64 | [self setupLayout]; 65 | } 66 | return self; 67 | } 68 | 69 | - (void)setupLayout 70 | { 71 | self.itemInsets = UIEdgeInsetsMake(5, 5, 5, 5); 72 | self.contentInsets = _itemInsets; 73 | self.cellAttributes = [NSMapTable strongToStrongObjectsMapTable]; 74 | self.allAttributes = [[NSMutableSet alloc] init]; 75 | 76 | NSIndexPath *zeroIndexPath = [NSIndexPath indexPathForItem:0 inSection:0]; 77 | 78 | self.headerDetailViewAttributes = 79 | ({ 80 | UICollectionViewLayoutAttributes *attributes = [UICollectionViewLayoutAttributes layoutAttributesForSupplementaryViewOfKind:BMFloatingHeaderCollectionViewLayoutHeaderDetailKind 81 | withIndexPath:zeroIndexPath]; 82 | attributes.zIndex = 99; 83 | attributes; 84 | }); 85 | self.headerTitleViewAttributes = 86 | ({ 87 | UICollectionViewLayoutAttributes *attributes = [UICollectionViewLayoutAttributes layoutAttributesForSupplementaryViewOfKind:BMFloatingHeaderCollectionViewLayoutHeaderTitleKind 88 | withIndexPath:zeroIndexPath]; 89 | attributes.zIndex = 99; 90 | attributes; 91 | }); 92 | self.placeHolderViewAttributes = 93 | ({ 94 | UICollectionViewLayoutAttributes *attributes = [UICollectionViewLayoutAttributes layoutAttributesForSupplementaryViewOfKind:BMFloatingHeaderCollectionViewLayoutPlaceholderKind 95 | withIndexPath:zeroIndexPath]; 96 | attributes.zIndex = 98; 97 | attributes; 98 | }); 99 | } 100 | 101 | #pragma mark Attributes 102 | 103 | - (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath 104 | { 105 | return [_cellAttributes objectForKey:indexPath]; 106 | } 107 | 108 | - (UICollectionViewLayoutAttributes *)layoutAttributesForDecorationViewOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath 109 | { 110 | return nil; 111 | } 112 | 113 | - (UICollectionViewLayoutAttributes *)layoutAttributesForSupplementaryViewOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath 114 | { 115 | if ([kind isEqualToString:BMFloatingHeaderCollectionViewLayoutHeaderTitleKind]) 116 | { 117 | return _headerTitleViewAttributes; 118 | } 119 | else if ([kind isEqualToString:BMFloatingHeaderCollectionViewLayoutHeaderDetailKind]) 120 | { 121 | return _headerDetailViewAttributes; 122 | } 123 | else if ([kind isEqualToString:BMFloatingHeaderCollectionViewLayoutPlaceholderKind]) 124 | { 125 | return _placeHolderViewAttributes; 126 | } 127 | else 128 | { 129 | return nil; 130 | } 131 | } 132 | 133 | #pragma mark Invalidation 134 | 135 | - (BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds 136 | { 137 | // Invalidate with custom options - only the detail header (which is calculated in stage two) 138 | // unless the width of the collection view doesn't match the reference width 139 | [self invalidateLayoutWithContext:[[BMFloatingHeaderCollectionViewLayoutInvalidationContext alloc] initWithInvalidationForDetailHeaderOnly:(newBounds.size.width == self.boundsSize.width)]]; 140 | 141 | // We won't use the default invalidation context here 142 | return NO; 143 | } 144 | 145 | - (void)invalidateLayoutWithContext:(BMFloatingHeaderCollectionViewLayoutInvalidationContext *)context 146 | { 147 | [super invalidateLayoutWithContext:context]; 148 | 149 | // If we are invalidating everything then we do that - otherwise we only invalidate the 150 | // detail header 151 | self.shouldInvalidateAll = !context.invalidateOnlyDetailHeader; 152 | } 153 | 154 | + (Class)invalidationContextClass 155 | { 156 | return [BMFloatingHeaderCollectionViewLayoutInvalidationContext class]; 157 | } 158 | 159 | #pragma mark Layout Process 160 | 161 | /** 162 | * STEP ONE 163 | * -------- 164 | * - perform whatever calculations are needed to determine the position of the cells and views 165 | * - compute enough information to be able to deliver the overall size of the content area in STEP TWO 166 | * 167 | * N.B. When your app is not dealing with thousands of items, it makes sense to create layout attribute 168 | * instances while preparing the layout, because the layout information can be cached and referenced rather 169 | * than computed on the fly. If the costs of computing all the attributes up front outweighs the benefits 170 | * of caching in your app, it is just as easy to create attributes in the moment when they are requested. 171 | */ 172 | - (void)prepareLayout 173 | { 174 | // Only prepare the layout from scratch if we are invalidating ALL, otherwise skip this step 175 | if (!_shouldInvalidateAll) 176 | { 177 | return; 178 | } 179 | 180 | // Register supplimentary views 181 | [self.collectionView registerClass:[BMReusableContainerView class] 182 | forSupplementaryViewOfKind:BMFloatingHeaderCollectionViewLayoutHeaderTitleKind 183 | withReuseIdentifier:BMFloatingHeaderCollectionViewLayoutHeaderTitleKind]; 184 | [self.collectionView registerClass:[BMReusableContainerView class] 185 | forSupplementaryViewOfKind:BMFloatingHeaderCollectionViewLayoutHeaderDetailKind 186 | withReuseIdentifier:BMFloatingHeaderCollectionViewLayoutHeaderDetailKind]; 187 | [self.collectionView registerClass:[BMReusableContainerView class] 188 | forSupplementaryViewOfKind:BMFloatingHeaderCollectionViewLayoutPlaceholderKind 189 | withReuseIdentifier:BMFloatingHeaderCollectionViewLayoutPlaceholderKind]; 190 | 191 | // Clear priors 192 | [_cellAttributes removeAllObjects]; 193 | [_allAttributes removeAllObjects]; 194 | // Reset the content frame 195 | self.contentFrame = CGRectZero; 196 | // Calculate the lot 197 | [self calculateHeaderTitleLayoutAttributes]; 198 | [self calculateHeaderDetailLayoutAttributes:NO]; 199 | [self calculateItemLayoutAttributes]; 200 | // Update the reference bounds size 201 | self.boundsSize = self.collectionView.bounds.size; 202 | } 203 | 204 | /** 205 | * STEP TWO 206 | * -------- 207 | * 208 | * @return Size of the entire content panel 209 | */ 210 | - (CGSize)collectionViewContentSize 211 | { 212 | return self.contentFrame.size; 213 | } 214 | 215 | /** 216 | * STEP THREE 217 | * ---------- 218 | * 219 | * @param rect The current (or upcoming) rectangle that will be visible in the collection view 220 | * 221 | * @return Array of layout attributes for all kinds of items in the specified rectangle 222 | */ 223 | - (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect 224 | { 225 | // Get all attributes that intersect the rectangle requested 226 | NSArray *attributes = 227 | ({ 228 | NSSet *intersectingAttributes = [_allAttributes objectsPassingTest:^BOOL(id obj, BOOL *stop) 229 | { 230 | return CGRectIntersectsRect(rect, [obj frame]); 231 | }]; 232 | [intersectingAttributes allObjects]; 233 | }); 234 | 235 | // Recalculate the detail header position 236 | [self calculateHeaderDetailLayoutAttributes:YES]; 237 | // And include it too if it matches 238 | if (CGRectIntersectsRect(rect, _headerDetailViewAttributes.frame)) 239 | { 240 | attributes = [attributes arrayByAddingObject:_headerDetailViewAttributes]; 241 | } 242 | 243 | return attributes; 244 | } 245 | 246 | /** 247 | * N.B. After layout finishes, the attributes of your cells and views remain the same until you or the 248 | * collection view invalidates the layout (then the layout process starts over). 249 | * 250 | * Invalidation happens when (on the next view cycle)... 251 | * 252 | * - invalidateLayout is called manually (it triggers a dirty flag) 253 | * - the user scrolls the content and shouldInvalidateLayoutForBoundsChange: returns YES 254 | */ 255 | 256 | #pragma mark - Accessors 257 | 258 | - (void)setHeaderTitleViewHeight:(CGFloat)headerTitleViewHeight 259 | { 260 | _headerTitleViewHeight = headerTitleViewHeight; 261 | [self invalidateLayout]; 262 | } 263 | 264 | - (void)setHeaderDetailViewHeight:(CGFloat)headerDetailViewHeight 265 | { 266 | _headerDetailViewHeight = headerDetailViewHeight; 267 | [self invalidateLayout]; 268 | } 269 | 270 | - (void)setItemInsets:(UIEdgeInsets)itemInsets 271 | { 272 | _itemInsets = itemInsets; 273 | [self invalidateLayout]; 274 | } 275 | 276 | - (void)setContentInsets:(UIEdgeInsets)contentInsets 277 | { 278 | _contentInsets = contentInsets; 279 | [self invalidateLayout]; 280 | } 281 | 282 | #pragma mark - Calculation (Private) 283 | 284 | /** 285 | * Given the prior item's frame (or starting with zero prior item), calculate the frame for an item 286 | * 287 | * @param indexPath NSIndexPath of the item 288 | * @param priorItemFrame The last calculated frame (item frames are calculated sequentially) 289 | * 290 | * @note After calculating all item frames, the content size must also append one final item inset and 291 | * content inset to arrive at the correct total size 292 | * 293 | * @return The frame for this item 294 | */ 295 | 296 | - (CGRect)calculateFrameForCellAtIndexPath:(NSIndexPath *)indexPath priorItemFrame:(CGRect)priorItemFrame 297 | { 298 | CGRect frame; 299 | 300 | NSAssert([self.collectionView.delegate conformsToProtocol:@protocol(BMFloatingHeaderCollectionViewLayoutDelegate)], 301 | @"UICollectionView delegate must conform to BMFloatingHeaderCollectionViewLayoutDelegate"); 302 | 303 | CGFloat itemHeight = [(id )self.collectionView.delegate collectionViewLayout:self 304 | heightForItemAtIndexPath:indexPath]; 305 | 306 | if (CGRectEqualToRect(priorItemFrame, CGRectZero)) 307 | { 308 | // Fixed total width is the width of the collection view bounds 309 | CGFloat totalWidth = self.collectionView.bounds.size.width; 310 | // Left inset is the sum of left insets 311 | CGFloat leftInset = _contentInsets.left + _itemInsets.left; 312 | // Right inset is the sum of right insets 313 | CGFloat rightInset = _contentInsets.right + _itemInsets.right; 314 | // Top inset is the sum of top insets PLUS the header title height and header detail height 315 | CGFloat topInset = _contentInsets.top + _itemInsets.top + _headerTitleViewHeight + _headerDetailViewHeight; 316 | 317 | // The item width is the total width less the left and right insets 318 | CGFloat itemWidth = totalWidth - leftInset - rightInset; 319 | 320 | // Calculate the first cell's frame 321 | frame = CGRectMake(leftInset, topInset, itemWidth, itemHeight); 322 | } 323 | else 324 | { 325 | // Calculate this cell's frame based on the prior cell's frame 326 | // Offset by the height of the prior frame and then add in its bottom and this item's top inset 327 | frame = CGRectOffset(priorItemFrame, 0.0, priorItemFrame.size.height + _itemInsets.top + _itemInsets.bottom); 328 | // Swap out the height of the frame for the one from the delegate 329 | frame.size.height = itemHeight; 330 | } 331 | 332 | // Store the computed frame 333 | [self storeFrame:frame forIndexPath:indexPath]; 334 | 335 | return frame; 336 | } 337 | 338 | - (void)calculateItemLayoutAttributes 339 | { 340 | // N.B. Assumes headers have already been calculated 341 | NSUInteger numberOfSections = self.collectionView.numberOfSections; 342 | 343 | CGRect itemFrame = CGRectZero; 344 | CGRect totalItemFrame = CGRectZero; 345 | 346 | for (NSUInteger section = 0; section < numberOfSections; section++) 347 | { 348 | NSUInteger numberOfItems = [self.collectionView numberOfItemsInSection:section]; 349 | for (NSUInteger item = 0; item < numberOfItems; item++) 350 | { 351 | itemFrame = [self calculateFrameForCellAtIndexPath:[NSIndexPath indexPathForItem:item inSection:section] 352 | priorItemFrame:itemFrame]; 353 | totalItemFrame = CGRectUnion(totalItemFrame, itemFrame); 354 | } 355 | } 356 | // If there are items, add on the last item and content insets and set the content frame 357 | if (!CGRectEqualToRect(totalItemFrame, CGRectZero)) 358 | { 359 | [self resetPlaceholderAttributes]; 360 | self.contentFrame = CGRectInset(totalItemFrame, 0.0, -(_itemInsets.bottom + _contentInsets.bottom)); 361 | } 362 | else 363 | { 364 | // No items? Just make the content the two header frames 365 | [self calculatePlaceholderLayoutAttributes]; 366 | self.contentFrame = CGRectUnion(CGRectUnion(_headerTitleViewAttributes.frame, _headerDetailViewAttributes.frame), _placeHolderViewAttributes.frame); 367 | } 368 | } 369 | 370 | - (void)resetPlaceholderAttributes 371 | { 372 | // Reset the frame of the placeholder, effectively hiding it 373 | _placeHolderViewAttributes.frame = CGRectZero; 374 | [_allAttributes addObject:_placeHolderViewAttributes]; 375 | } 376 | 377 | - (void)calculatePlaceholderLayoutAttributes 378 | { 379 | // The header title view is always at the top of the content frame and is always the width of the content view 380 | _placeHolderViewAttributes.frame = CGRectMake(0.0, 0.0, 381 | self.collectionView.bounds.size.width - 382 | self.collectionView.contentInset.left - 383 | self.collectionView.contentInset.right, 384 | self.collectionView.bounds.size.height - 385 | self.collectionView.contentInset.top - 386 | self.collectionView.contentInset.bottom); 387 | [_allAttributes addObject:_placeHolderViewAttributes]; 388 | } 389 | 390 | - (void)calculateHeaderTitleLayoutAttributes 391 | { 392 | // The header title view is always at the top of the content frame and is always the width of the content view 393 | _headerTitleViewAttributes.frame = CGRectMake(0.0, 0.0, self.collectionView.bounds.size.width, _headerTitleViewHeight); 394 | [_allAttributes addObject:_headerTitleViewAttributes]; 395 | } 396 | 397 | - (void)calculateHeaderDetailLayoutAttributes:(BOOL)shouldIncludeOffset 398 | { 399 | // WARNING: This is called at every pixel change when the user scrolls so be careful how much work is done here 400 | CGFloat yOffset = MAX(_headerTitleViewHeight, (shouldIncludeOffset ? self.collectionView.bounds.origin.y + self.collectionView.contentInset.top : 0.0)); 401 | _headerDetailViewAttributes.frame = CGRectMake(0.0, yOffset, self.collectionView.bounds.size.width, _headerDetailViewHeight); 402 | } 403 | 404 | - (void)storeFrame:(CGRect)frame forIndexPath:(NSIndexPath *)indexPath 405 | { 406 | // Store the attributes in an accessible AND a fast place 407 | UICollectionViewLayoutAttributes *attributes = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath]; 408 | attributes.frame = frame; 409 | [_cellAttributes setObject:attributes forKey:indexPath]; 410 | [_allAttributes addObject:attributes]; 411 | } 412 | 413 | @end 414 | -------------------------------------------------------------------------------- /Example/LayoutDemo.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | archiveVersion 6 | 1 7 | classes 8 | 9 | objectVersion 10 | 46 11 | objects 12 | 13 | 33676A8A0BAE4F5692795A94 14 | 15 | fileRef 16 | 6AF7712060504B18B14A4048 17 | isa 18 | PBXBuildFile 19 | 20 | 3486A36C27534B79BCDFB14E 21 | 22 | buildActionMask 23 | 2147483647 24 | files 25 | 26 | inputPaths 27 | 28 | isa 29 | PBXShellScriptBuildPhase 30 | name 31 | Copy Pods Resources 32 | outputPaths 33 | 34 | runOnlyForDeploymentPostprocessing 35 | 0 36 | shellPath 37 | /bin/sh 38 | shellScript 39 | "${SRCROOT}/Pods/Pods-LayoutDemo-resources.sh" 40 | 41 | showEnvVarsInLog 42 | 0 43 | 44 | 5F8135AE53EE4107AA2894E3 45 | 46 | buildActionMask 47 | 2147483647 48 | files 49 | 50 | inputPaths 51 | 52 | isa 53 | PBXShellScriptBuildPhase 54 | name 55 | Check Pods Manifest.lock 56 | outputPaths 57 | 58 | runOnlyForDeploymentPostprocessing 59 | 0 60 | shellPath 61 | /bin/sh 62 | shellScript 63 | diff "${PODS_ROOT}/../Podfile.lock" "${PODS_ROOT}/Manifest.lock" > /dev/null 64 | if [[ $? != 0 ]] ; then 65 | cat << EOM 66 | error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation. 67 | EOM 68 | exit 1 69 | fi 70 | 71 | showEnvVarsInLog 72 | 0 73 | 74 | 66F8226218E2BBC800F69680 75 | 76 | children 77 | 78 | 66F8227418E2BBC800F69680 79 | 66F8229618E2BBC800F69680 80 | 66F8226D18E2BBC800F69680 81 | 66F8226C18E2BBC800F69680 82 | B4A5C4E3FB964E3EB16E469F 83 | 84 | isa 85 | PBXGroup 86 | sourceTree 87 | <group> 88 | 89 | 66F8226318E2BBC800F69680 90 | 91 | attributes 92 | 93 | CLASSPREFIX 94 | BML 95 | LastUpgradeCheck 96 | 0510 97 | ORGANIZATIONNAME 98 | Bionic Monocle Pty Ltd 99 | TargetAttributes 100 | 101 | 66F8228E18E2BBC800F69680 102 | 103 | TestTargetID 104 | 66F8226A18E2BBC800F69680 105 | 106 | 107 | 108 | buildConfigurationList 109 | 66F8226618E2BBC800F69680 110 | compatibilityVersion 111 | Xcode 3.2 112 | developmentRegion 113 | English 114 | hasScannedForEncodings 115 | 0 116 | isa 117 | PBXProject 118 | knownRegions 119 | 120 | en 121 | Base 122 | 123 | mainGroup 124 | 66F8226218E2BBC800F69680 125 | productRefGroup 126 | 66F8226C18E2BBC800F69680 127 | projectDirPath 128 | 129 | projectReferences 130 | 131 | projectRoot 132 | 133 | targets 134 | 135 | 66F8226A18E2BBC800F69680 136 | 66F8228E18E2BBC800F69680 137 | 138 | 139 | 66F8226618E2BBC800F69680 140 | 141 | buildConfigurations 142 | 143 | 66F8229E18E2BBC800F69680 144 | 66F8229F18E2BBC800F69680 145 | 146 | defaultConfigurationIsVisible 147 | 0 148 | defaultConfigurationName 149 | Release 150 | isa 151 | XCConfigurationList 152 | 153 | 66F8226718E2BBC800F69680 154 | 155 | buildActionMask 156 | 2147483647 157 | files 158 | 159 | 66F8227F18E2BBC800F69680 160 | 66F8227B18E2BBC800F69680 161 | 66F8228818E2BBC800F69680 162 | 163 | isa 164 | PBXSourcesBuildPhase 165 | runOnlyForDeploymentPostprocessing 166 | 0 167 | 168 | 66F8226818E2BBC800F69680 169 | 170 | buildActionMask 171 | 2147483647 172 | files 173 | 174 | 66F8227118E2BBC800F69680 175 | 66F8227318E2BBC800F69680 176 | 66F8226F18E2BBC800F69680 177 | 33676A8A0BAE4F5692795A94 178 | 179 | isa 180 | PBXFrameworksBuildPhase 181 | runOnlyForDeploymentPostprocessing 182 | 0 183 | 184 | 66F8226918E2BBC800F69680 185 | 186 | buildActionMask 187 | 2147483647 188 | files 189 | 190 | 66F8228518E2BBC800F69680 191 | 66F8228A18E2BBC800F69680 192 | 66F8228218E2BBC800F69680 193 | 66F8227918E2BBC800F69680 194 | 195 | isa 196 | PBXResourcesBuildPhase 197 | runOnlyForDeploymentPostprocessing 198 | 0 199 | 200 | 66F8226A18E2BBC800F69680 201 | 202 | buildConfigurationList 203 | 66F822A018E2BBC800F69680 204 | buildPhases 205 | 206 | 5F8135AE53EE4107AA2894E3 207 | 66F8226718E2BBC800F69680 208 | 66F8226818E2BBC800F69680 209 | 66F8226918E2BBC800F69680 210 | 3486A36C27534B79BCDFB14E 211 | 212 | buildRules 213 | 214 | dependencies 215 | 216 | isa 217 | PBXNativeTarget 218 | name 219 | LayoutDemo 220 | productName 221 | LayoutDemo 222 | productReference 223 | 66F8226B18E2BBC800F69680 224 | productType 225 | com.apple.product-type.application 226 | 227 | 66F8226B18E2BBC800F69680 228 | 229 | explicitFileType 230 | wrapper.application 231 | includeInIndex 232 | 0 233 | isa 234 | PBXFileReference 235 | path 236 | LayoutDemo.app 237 | sourceTree 238 | BUILT_PRODUCTS_DIR 239 | 240 | 66F8226C18E2BBC800F69680 241 | 242 | children 243 | 244 | 66F8226B18E2BBC800F69680 245 | 66F8228F18E2BBC800F69680 246 | 247 | isa 248 | PBXGroup 249 | name 250 | Products 251 | sourceTree 252 | <group> 253 | 254 | 66F8226D18E2BBC800F69680 255 | 256 | children 257 | 258 | 66F8226E18E2BBC800F69680 259 | 66F8227018E2BBC800F69680 260 | 66F8227218E2BBC800F69680 261 | 66F8229018E2BBC800F69680 262 | 6AF7712060504B18B14A4048 263 | 264 | isa 265 | PBXGroup 266 | name 267 | Frameworks 268 | sourceTree 269 | <group> 270 | 271 | 66F8226E18E2BBC800F69680 272 | 273 | isa 274 | PBXFileReference 275 | lastKnownFileType 276 | wrapper.framework 277 | name 278 | Foundation.framework 279 | path 280 | System/Library/Frameworks/Foundation.framework 281 | sourceTree 282 | SDKROOT 283 | 284 | 66F8226F18E2BBC800F69680 285 | 286 | fileRef 287 | 66F8226E18E2BBC800F69680 288 | isa 289 | PBXBuildFile 290 | 291 | 66F8227018E2BBC800F69680 292 | 293 | isa 294 | PBXFileReference 295 | lastKnownFileType 296 | wrapper.framework 297 | name 298 | CoreGraphics.framework 299 | path 300 | System/Library/Frameworks/CoreGraphics.framework 301 | sourceTree 302 | SDKROOT 303 | 304 | 66F8227118E2BBC800F69680 305 | 306 | fileRef 307 | 66F8227018E2BBC800F69680 308 | isa 309 | PBXBuildFile 310 | 311 | 66F8227218E2BBC800F69680 312 | 313 | isa 314 | PBXFileReference 315 | lastKnownFileType 316 | wrapper.framework 317 | name 318 | UIKit.framework 319 | path 320 | System/Library/Frameworks/UIKit.framework 321 | sourceTree 322 | SDKROOT 323 | 324 | 66F8227318E2BBC800F69680 325 | 326 | fileRef 327 | 66F8227218E2BBC800F69680 328 | isa 329 | PBXBuildFile 330 | 331 | 66F8227418E2BBC800F69680 332 | 333 | children 334 | 335 | 66F8227D18E2BBC800F69680 336 | 66F8227E18E2BBC800F69680 337 | 66F8228018E2BBC800F69680 338 | 66F8228318E2BBC800F69680 339 | 66F8228618E2BBC800F69680 340 | 66F8228718E2BBC800F69680 341 | 66F8228918E2BBC800F69680 342 | 66F8227518E2BBC800F69680 343 | 344 | isa 345 | PBXGroup 346 | path 347 | LayoutDemo 348 | sourceTree 349 | <group> 350 | 351 | 66F8227518E2BBC800F69680 352 | 353 | children 354 | 355 | 66F8227618E2BBC800F69680 356 | 66F8227718E2BBC800F69680 357 | 66F8227A18E2BBC800F69680 358 | 66F8227C18E2BBC800F69680 359 | 360 | isa 361 | PBXGroup 362 | name 363 | Supporting Files 364 | sourceTree 365 | <group> 366 | 367 | 66F8227618E2BBC800F69680 368 | 369 | isa 370 | PBXFileReference 371 | lastKnownFileType 372 | text.plist.xml 373 | path 374 | LayoutDemo-Info.plist 375 | sourceTree 376 | <group> 377 | 378 | 66F8227718E2BBC800F69680 379 | 380 | children 381 | 382 | 66F8227818E2BBC800F69680 383 | 384 | isa 385 | PBXVariantGroup 386 | name 387 | InfoPlist.strings 388 | sourceTree 389 | <group> 390 | 391 | 66F8227818E2BBC800F69680 392 | 393 | isa 394 | PBXFileReference 395 | lastKnownFileType 396 | text.plist.strings 397 | name 398 | en 399 | path 400 | en.lproj/InfoPlist.strings 401 | sourceTree 402 | <group> 403 | 404 | 66F8227918E2BBC800F69680 405 | 406 | fileRef 407 | 66F8227718E2BBC800F69680 408 | isa 409 | PBXBuildFile 410 | 411 | 66F8227A18E2BBC800F69680 412 | 413 | isa 414 | PBXFileReference 415 | lastKnownFileType 416 | sourcecode.c.objc 417 | path 418 | main.m 419 | sourceTree 420 | <group> 421 | 422 | 66F8227B18E2BBC800F69680 423 | 424 | fileRef 425 | 66F8227A18E2BBC800F69680 426 | isa 427 | PBXBuildFile 428 | 429 | 66F8227C18E2BBC800F69680 430 | 431 | isa 432 | PBXFileReference 433 | lastKnownFileType 434 | sourcecode.c.h 435 | path 436 | LayoutDemo-Prefix.pch 437 | sourceTree 438 | <group> 439 | 440 | 66F8227D18E2BBC800F69680 441 | 442 | isa 443 | PBXFileReference 444 | lastKnownFileType 445 | sourcecode.c.h 446 | path 447 | BMLAppDelegate.h 448 | sourceTree 449 | <group> 450 | 451 | 66F8227E18E2BBC800F69680 452 | 453 | isa 454 | PBXFileReference 455 | lastKnownFileType 456 | sourcecode.c.objc 457 | path 458 | BMLAppDelegate.m 459 | sourceTree 460 | <group> 461 | 462 | 66F8227F18E2BBC800F69680 463 | 464 | fileRef 465 | 66F8227E18E2BBC800F69680 466 | isa 467 | PBXBuildFile 468 | 469 | 66F8228018E2BBC800F69680 470 | 471 | children 472 | 473 | 66F8228118E2BBC800F69680 474 | 475 | isa 476 | PBXVariantGroup 477 | name 478 | Main_iPhone.storyboard 479 | sourceTree 480 | <group> 481 | 482 | 66F8228118E2BBC800F69680 483 | 484 | isa 485 | PBXFileReference 486 | lastKnownFileType 487 | file.storyboard 488 | name 489 | Base 490 | path 491 | Base.lproj/Main_iPhone.storyboard 492 | sourceTree 493 | <group> 494 | 495 | 66F8228218E2BBC800F69680 496 | 497 | fileRef 498 | 66F8228018E2BBC800F69680 499 | isa 500 | PBXBuildFile 501 | 502 | 66F8228318E2BBC800F69680 503 | 504 | children 505 | 506 | 66F8228418E2BBC800F69680 507 | 508 | isa 509 | PBXVariantGroup 510 | name 511 | Main_iPad.storyboard 512 | sourceTree 513 | <group> 514 | 515 | 66F8228418E2BBC800F69680 516 | 517 | isa 518 | PBXFileReference 519 | lastKnownFileType 520 | file.storyboard 521 | name 522 | Base 523 | path 524 | Base.lproj/Main_iPad.storyboard 525 | sourceTree 526 | <group> 527 | 528 | 66F8228518E2BBC800F69680 529 | 530 | fileRef 531 | 66F8228318E2BBC800F69680 532 | isa 533 | PBXBuildFile 534 | 535 | 66F8228618E2BBC800F69680 536 | 537 | isa 538 | PBXFileReference 539 | lastKnownFileType 540 | sourcecode.c.h 541 | path 542 | BMLViewController.h 543 | sourceTree 544 | <group> 545 | 546 | 66F8228718E2BBC800F69680 547 | 548 | isa 549 | PBXFileReference 550 | lastKnownFileType 551 | sourcecode.c.objc 552 | path 553 | BMLViewController.m 554 | sourceTree 555 | <group> 556 | 557 | 66F8228818E2BBC800F69680 558 | 559 | fileRef 560 | 66F8228718E2BBC800F69680 561 | isa 562 | PBXBuildFile 563 | 564 | 66F8228918E2BBC800F69680 565 | 566 | isa 567 | PBXFileReference 568 | lastKnownFileType 569 | folder.assetcatalog 570 | path 571 | Images.xcassets 572 | sourceTree 573 | <group> 574 | 575 | 66F8228A18E2BBC800F69680 576 | 577 | fileRef 578 | 66F8228918E2BBC800F69680 579 | isa 580 | PBXBuildFile 581 | 582 | 66F8228B18E2BBC800F69680 583 | 584 | buildActionMask 585 | 2147483647 586 | files 587 | 588 | 66F8229D18E2BBC800F69680 589 | 590 | isa 591 | PBXSourcesBuildPhase 592 | runOnlyForDeploymentPostprocessing 593 | 0 594 | 595 | 66F8228C18E2BBC800F69680 596 | 597 | buildActionMask 598 | 2147483647 599 | files 600 | 601 | 66F8229118E2BBC800F69680 602 | 66F8229318E2BBC800F69680 603 | 66F8229218E2BBC800F69680 604 | 605 | isa 606 | PBXFrameworksBuildPhase 607 | runOnlyForDeploymentPostprocessing 608 | 0 609 | 610 | 66F8228D18E2BBC800F69680 611 | 612 | buildActionMask 613 | 2147483647 614 | files 615 | 616 | 66F8229B18E2BBC800F69680 617 | 618 | isa 619 | PBXResourcesBuildPhase 620 | runOnlyForDeploymentPostprocessing 621 | 0 622 | 623 | 66F8228E18E2BBC800F69680 624 | 625 | buildConfigurationList 626 | 66F822A318E2BBC800F69680 627 | buildPhases 628 | 629 | 66F8228B18E2BBC800F69680 630 | 66F8228C18E2BBC800F69680 631 | 66F8228D18E2BBC800F69680 632 | 633 | buildRules 634 | 635 | dependencies 636 | 637 | 66F8229518E2BBC800F69680 638 | 639 | isa 640 | PBXNativeTarget 641 | name 642 | LayoutDemoTests 643 | productName 644 | LayoutDemoTests 645 | productReference 646 | 66F8228F18E2BBC800F69680 647 | productType 648 | com.apple.product-type.bundle.unit-test 649 | 650 | 66F8228F18E2BBC800F69680 651 | 652 | explicitFileType 653 | wrapper.cfbundle 654 | includeInIndex 655 | 0 656 | isa 657 | PBXFileReference 658 | path 659 | LayoutDemoTests.xctest 660 | sourceTree 661 | BUILT_PRODUCTS_DIR 662 | 663 | 66F8229018E2BBC800F69680 664 | 665 | isa 666 | PBXFileReference 667 | lastKnownFileType 668 | wrapper.framework 669 | name 670 | XCTest.framework 671 | path 672 | Library/Frameworks/XCTest.framework 673 | sourceTree 674 | DEVELOPER_DIR 675 | 676 | 66F8229118E2BBC800F69680 677 | 678 | fileRef 679 | 66F8229018E2BBC800F69680 680 | isa 681 | PBXBuildFile 682 | 683 | 66F8229218E2BBC800F69680 684 | 685 | fileRef 686 | 66F8226E18E2BBC800F69680 687 | isa 688 | PBXBuildFile 689 | 690 | 66F8229318E2BBC800F69680 691 | 692 | fileRef 693 | 66F8227218E2BBC800F69680 694 | isa 695 | PBXBuildFile 696 | 697 | 66F8229418E2BBC800F69680 698 | 699 | containerPortal 700 | 66F8226318E2BBC800F69680 701 | isa 702 | PBXContainerItemProxy 703 | proxyType 704 | 1 705 | remoteGlobalIDString 706 | 66F8226A18E2BBC800F69680 707 | remoteInfo 708 | LayoutDemo 709 | 710 | 66F8229518E2BBC800F69680 711 | 712 | isa 713 | PBXTargetDependency 714 | target 715 | 66F8226A18E2BBC800F69680 716 | targetProxy 717 | 66F8229418E2BBC800F69680 718 | 719 | 66F8229618E2BBC800F69680 720 | 721 | children 722 | 723 | 66F8229C18E2BBC800F69680 724 | 66F8229718E2BBC800F69680 725 | 726 | isa 727 | PBXGroup 728 | path 729 | LayoutDemoTests 730 | sourceTree 731 | <group> 732 | 733 | 66F8229718E2BBC800F69680 734 | 735 | children 736 | 737 | 66F8229818E2BBC800F69680 738 | 66F8229918E2BBC800F69680 739 | 740 | isa 741 | PBXGroup 742 | name 743 | Supporting Files 744 | sourceTree 745 | <group> 746 | 747 | 66F8229818E2BBC800F69680 748 | 749 | isa 750 | PBXFileReference 751 | lastKnownFileType 752 | text.plist.xml 753 | path 754 | LayoutDemoTests-Info.plist 755 | sourceTree 756 | <group> 757 | 758 | 66F8229918E2BBC800F69680 759 | 760 | children 761 | 762 | 66F8229A18E2BBC800F69680 763 | 764 | isa 765 | PBXVariantGroup 766 | name 767 | InfoPlist.strings 768 | sourceTree 769 | <group> 770 | 771 | 66F8229A18E2BBC800F69680 772 | 773 | isa 774 | PBXFileReference 775 | lastKnownFileType 776 | text.plist.strings 777 | name 778 | en 779 | path 780 | en.lproj/InfoPlist.strings 781 | sourceTree 782 | <group> 783 | 784 | 66F8229B18E2BBC800F69680 785 | 786 | fileRef 787 | 66F8229918E2BBC800F69680 788 | isa 789 | PBXBuildFile 790 | 791 | 66F8229C18E2BBC800F69680 792 | 793 | isa 794 | PBXFileReference 795 | lastKnownFileType 796 | sourcecode.c.objc 797 | path 798 | LayoutDemoTests.m 799 | sourceTree 800 | <group> 801 | 802 | 66F8229D18E2BBC800F69680 803 | 804 | fileRef 805 | 66F8229C18E2BBC800F69680 806 | isa 807 | PBXBuildFile 808 | 809 | 66F8229E18E2BBC800F69680 810 | 811 | buildSettings 812 | 813 | ALWAYS_SEARCH_USER_PATHS 814 | NO 815 | CLANG_CXX_LANGUAGE_STANDARD 816 | gnu++0x 817 | CLANG_CXX_LIBRARY 818 | libc++ 819 | CLANG_ENABLE_MODULES 820 | YES 821 | CLANG_ENABLE_OBJC_ARC 822 | YES 823 | CLANG_WARN_BOOL_CONVERSION 824 | YES 825 | CLANG_WARN_CONSTANT_CONVERSION 826 | YES 827 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE 828 | YES_ERROR 829 | CLANG_WARN_EMPTY_BODY 830 | YES 831 | CLANG_WARN_ENUM_CONVERSION 832 | YES 833 | CLANG_WARN_INT_CONVERSION 834 | YES 835 | CLANG_WARN_OBJC_ROOT_CLASS 836 | YES_ERROR 837 | CLANG_WARN__DUPLICATE_METHOD_MATCH 838 | YES 839 | CODE_SIGN_IDENTITY[sdk=iphoneos*] 840 | iPhone Developer 841 | COPY_PHASE_STRIP 842 | NO 843 | GCC_C_LANGUAGE_STANDARD 844 | gnu99 845 | GCC_DYNAMIC_NO_PIC 846 | NO 847 | GCC_OPTIMIZATION_LEVEL 848 | 0 849 | GCC_PREPROCESSOR_DEFINITIONS 850 | 851 | DEBUG=1 852 | $(inherited) 853 | 854 | GCC_SYMBOLS_PRIVATE_EXTERN 855 | NO 856 | GCC_WARN_64_TO_32_BIT_CONVERSION 857 | YES 858 | GCC_WARN_ABOUT_RETURN_TYPE 859 | YES_ERROR 860 | GCC_WARN_UNDECLARED_SELECTOR 861 | YES 862 | GCC_WARN_UNINITIALIZED_AUTOS 863 | YES_AGGRESSIVE 864 | GCC_WARN_UNUSED_FUNCTION 865 | YES 866 | GCC_WARN_UNUSED_VARIABLE 867 | YES 868 | IPHONEOS_DEPLOYMENT_TARGET 869 | 7.1 870 | ONLY_ACTIVE_ARCH 871 | YES 872 | SDKROOT 873 | iphoneos 874 | TARGETED_DEVICE_FAMILY 875 | 1,2 876 | 877 | isa 878 | XCBuildConfiguration 879 | name 880 | Debug 881 | 882 | 66F8229F18E2BBC800F69680 883 | 884 | buildSettings 885 | 886 | ALWAYS_SEARCH_USER_PATHS 887 | NO 888 | CLANG_CXX_LANGUAGE_STANDARD 889 | gnu++0x 890 | CLANG_CXX_LIBRARY 891 | libc++ 892 | CLANG_ENABLE_MODULES 893 | YES 894 | CLANG_ENABLE_OBJC_ARC 895 | YES 896 | CLANG_WARN_BOOL_CONVERSION 897 | YES 898 | CLANG_WARN_CONSTANT_CONVERSION 899 | YES 900 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE 901 | YES_ERROR 902 | CLANG_WARN_EMPTY_BODY 903 | YES 904 | CLANG_WARN_ENUM_CONVERSION 905 | YES 906 | CLANG_WARN_INT_CONVERSION 907 | YES 908 | CLANG_WARN_OBJC_ROOT_CLASS 909 | YES_ERROR 910 | CLANG_WARN__DUPLICATE_METHOD_MATCH 911 | YES 912 | CODE_SIGN_IDENTITY[sdk=iphoneos*] 913 | iPhone Developer 914 | COPY_PHASE_STRIP 915 | YES 916 | ENABLE_NS_ASSERTIONS 917 | NO 918 | GCC_C_LANGUAGE_STANDARD 919 | gnu99 920 | GCC_WARN_64_TO_32_BIT_CONVERSION 921 | YES 922 | GCC_WARN_ABOUT_RETURN_TYPE 923 | YES_ERROR 924 | GCC_WARN_UNDECLARED_SELECTOR 925 | YES 926 | GCC_WARN_UNINITIALIZED_AUTOS 927 | YES_AGGRESSIVE 928 | GCC_WARN_UNUSED_FUNCTION 929 | YES 930 | GCC_WARN_UNUSED_VARIABLE 931 | YES 932 | IPHONEOS_DEPLOYMENT_TARGET 933 | 7.1 934 | SDKROOT 935 | iphoneos 936 | TARGETED_DEVICE_FAMILY 937 | 1,2 938 | VALIDATE_PRODUCT 939 | YES 940 | 941 | isa 942 | XCBuildConfiguration 943 | name 944 | Release 945 | 946 | 66F822A018E2BBC800F69680 947 | 948 | buildConfigurations 949 | 950 | 66F822A118E2BBC800F69680 951 | 66F822A218E2BBC800F69680 952 | 953 | defaultConfigurationIsVisible 954 | 0 955 | isa 956 | XCConfigurationList 957 | 958 | 66F822A118E2BBC800F69680 959 | 960 | baseConfigurationReference 961 | B4A5C4E3FB964E3EB16E469F 962 | buildSettings 963 | 964 | ASSETCATALOG_COMPILER_APPICON_NAME 965 | AppIcon 966 | ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME 967 | LaunchImage 968 | GCC_PRECOMPILE_PREFIX_HEADER 969 | YES 970 | GCC_PREFIX_HEADER 971 | LayoutDemo/LayoutDemo-Prefix.pch 972 | INFOPLIST_FILE 973 | LayoutDemo/LayoutDemo-Info.plist 974 | PRODUCT_NAME 975 | $(TARGET_NAME) 976 | WRAPPER_EXTENSION 977 | app 978 | 979 | isa 980 | XCBuildConfiguration 981 | name 982 | Debug 983 | 984 | 66F822A218E2BBC800F69680 985 | 986 | baseConfigurationReference 987 | B4A5C4E3FB964E3EB16E469F 988 | buildSettings 989 | 990 | ASSETCATALOG_COMPILER_APPICON_NAME 991 | AppIcon 992 | ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME 993 | LaunchImage 994 | GCC_PRECOMPILE_PREFIX_HEADER 995 | YES 996 | GCC_PREFIX_HEADER 997 | LayoutDemo/LayoutDemo-Prefix.pch 998 | INFOPLIST_FILE 999 | LayoutDemo/LayoutDemo-Info.plist 1000 | PRODUCT_NAME 1001 | $(TARGET_NAME) 1002 | WRAPPER_EXTENSION 1003 | app 1004 | 1005 | isa 1006 | XCBuildConfiguration 1007 | name 1008 | Release 1009 | 1010 | 66F822A318E2BBC800F69680 1011 | 1012 | buildConfigurations 1013 | 1014 | 66F822A418E2BBC800F69680 1015 | 66F822A518E2BBC800F69680 1016 | 1017 | defaultConfigurationIsVisible 1018 | 0 1019 | isa 1020 | XCConfigurationList 1021 | 1022 | 66F822A418E2BBC800F69680 1023 | 1024 | buildSettings 1025 | 1026 | BUNDLE_LOADER 1027 | $(BUILT_PRODUCTS_DIR)/LayoutDemo.app/LayoutDemo 1028 | FRAMEWORK_SEARCH_PATHS 1029 | 1030 | $(SDKROOT)/Developer/Library/Frameworks 1031 | $(inherited) 1032 | $(DEVELOPER_FRAMEWORKS_DIR) 1033 | 1034 | GCC_PRECOMPILE_PREFIX_HEADER 1035 | YES 1036 | GCC_PREFIX_HEADER 1037 | LayoutDemo/LayoutDemo-Prefix.pch 1038 | GCC_PREPROCESSOR_DEFINITIONS 1039 | 1040 | DEBUG=1 1041 | $(inherited) 1042 | 1043 | INFOPLIST_FILE 1044 | LayoutDemoTests/LayoutDemoTests-Info.plist 1045 | PRODUCT_NAME 1046 | $(TARGET_NAME) 1047 | TEST_HOST 1048 | $(BUNDLE_LOADER) 1049 | WRAPPER_EXTENSION 1050 | xctest 1051 | 1052 | isa 1053 | XCBuildConfiguration 1054 | name 1055 | Debug 1056 | 1057 | 66F822A518E2BBC800F69680 1058 | 1059 | buildSettings 1060 | 1061 | BUNDLE_LOADER 1062 | $(BUILT_PRODUCTS_DIR)/LayoutDemo.app/LayoutDemo 1063 | FRAMEWORK_SEARCH_PATHS 1064 | 1065 | $(SDKROOT)/Developer/Library/Frameworks 1066 | $(inherited) 1067 | $(DEVELOPER_FRAMEWORKS_DIR) 1068 | 1069 | GCC_PRECOMPILE_PREFIX_HEADER 1070 | YES 1071 | GCC_PREFIX_HEADER 1072 | LayoutDemo/LayoutDemo-Prefix.pch 1073 | INFOPLIST_FILE 1074 | LayoutDemoTests/LayoutDemoTests-Info.plist 1075 | PRODUCT_NAME 1076 | $(TARGET_NAME) 1077 | TEST_HOST 1078 | $(BUNDLE_LOADER) 1079 | WRAPPER_EXTENSION 1080 | xctest 1081 | 1082 | isa 1083 | XCBuildConfiguration 1084 | name 1085 | Release 1086 | 1087 | 6AF7712060504B18B14A4048 1088 | 1089 | explicitFileType 1090 | archive.ar 1091 | includeInIndex 1092 | 0 1093 | isa 1094 | PBXFileReference 1095 | path 1096 | libPods-LayoutDemo.a 1097 | sourceTree 1098 | BUILT_PRODUCTS_DIR 1099 | 1100 | B4A5C4E3FB964E3EB16E469F 1101 | 1102 | includeInIndex 1103 | 1 1104 | isa 1105 | PBXFileReference 1106 | lastKnownFileType 1107 | text.xcconfig 1108 | name 1109 | Pods-LayoutDemo.xcconfig 1110 | path 1111 | Pods/Pods-LayoutDemo.xcconfig 1112 | sourceTree 1113 | <group> 1114 | 1115 | 1116 | rootObject 1117 | 66F8226318E2BBC800F69680 1118 | 1119 | 1120 | --------------------------------------------------------------------------------