├── .gitignore ├── CHANGELOG.md ├── Example ├── CenterFlow.xcodeproj │ ├── project.pbxproj │ └── project.xcworkspace │ │ └── contents.xcworkspacedata └── CenterFlow │ ├── Constants.h │ ├── Constants.m │ ├── Default-568h@2x.png │ ├── Images.xcassets │ └── AppIcon.appiconset │ │ └── Contents.json │ ├── Info.plist │ ├── KTAppDelegate.h │ ├── KTAppDelegate.m │ ├── KTAwesomeCell.h │ ├── KTAwesomeCell.m │ ├── KTAwesomeSizingCell.h │ ├── KTAwesomeSizingCell.m │ ├── KTBasicCollectionViewController.h │ ├── KTBasicCollectionViewController.m │ ├── KTHeaderFooterView.h │ ├── KTHeaderFooterView.m │ ├── KTSelfSizingCollectionViewController.h │ ├── KTSelfSizingCollectionViewController.m │ ├── KTVariableHeightCollectionViewController.h │ ├── KTVariableHeightCollectionViewController.m │ ├── Launch.storyboard │ ├── main.m │ ├── tabbar-image.png │ ├── tabbar-image@2x.png │ └── tabbar-image@3x.png ├── KTCenterFlowLayout.h ├── KTCenterFlowLayout.m ├── KTCenterFlowLayout.podspec ├── LICENSE ├── README.md └── example.png /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | # 3 | build/ 4 | *.pbxuser 5 | !default.pbxuser 6 | *.mode1v3 7 | !default.mode1v3 8 | *.mode2v3 9 | !default.mode2v3 10 | *.perspectivev3 11 | !default.perspectivev3 12 | xcuserdata 13 | *.xccheckout 14 | *.moved-aside 15 | DerivedData 16 | *.hmap 17 | *.ipa 18 | *.xcuserstate 19 | 20 | Pods/ 21 | 22 | .DS_Store 23 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to this project will be documented in this file. 4 | 5 | ## 1.3.1 - 2016-05-14 6 | 7 | * Adds important nil check (#25) 8 | 9 | ## 1.3 - 2016-05-14 10 | 11 | * Fixes broken header/footer supplementary views 12 | 13 | ## 1.2 - 2016-05-14 14 | 15 | * Refactoring layout logic 16 | 17 | ## 1.1.1 - 2015-09-15 18 | 19 | * Fixes a caching issue on iOS9 by deep copying `[super layoutAttributesForElementsInRect:rect]`. Thanks [@fwaddle](https://github.com/fwaddle) 20 | 21 | ## 1.1 - 2015-09-15 22 | 23 | * Uses flow layout `minimumInteritemSpacingForSectionAtIndex` from delegate if available. Thanks [@danblakemore](https://github.com/danblakemore) 24 | 25 | ## 1.0.0 - 2015-05-06 26 | 27 | * Fixes issues where variable cell heights on 3x res screen pile up on top of each other 28 | 29 | ## 0.0.2 - 2015-03-21 30 | 31 | * Small style tweaks 32 | 33 | ## 0.0.1 - 2014-10-09 34 | 35 | * Intitial version! 36 | -------------------------------------------------------------------------------- /Example/CenterFlow.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | CF0E9DA41AE914A300754C1D /* Launch.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = CF0E9DA31AE914A300754C1D /* Launch.storyboard */; }; 11 | CF12786A1D10D155007BD3CC /* KTHeaderFooterView.m in Sources */ = {isa = PBXBuildFile; fileRef = CF1278691D10D155007BD3CC /* KTHeaderFooterView.m */; }; 12 | CF1EDF8D1CDA54EA004D7709 /* KTAwesomeSizingCell.m in Sources */ = {isa = PBXBuildFile; fileRef = CF1EDF8C1CDA54EA004D7709 /* KTAwesomeSizingCell.m */; }; 13 | CF2A543419E702E500BAFA7F /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = CF2A543319E702E500BAFA7F /* main.m */; }; 14 | CF2A543719E702E500BAFA7F /* KTAppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = CF2A543619E702E500BAFA7F /* KTAppDelegate.m */; }; 15 | CF2A543F19E702E500BAFA7F /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = CF2A543E19E702E500BAFA7F /* Images.xcassets */; }; 16 | CF2A545919E704C700BAFA7F /* KTBasicCollectionViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = CF2A545819E704C700BAFA7F /* KTBasicCollectionViewController.m */; }; 17 | CF2A545C19E7061300BAFA7F /* KTAwesomeCell.m in Sources */ = {isa = PBXBuildFile; fileRef = CF2A545B19E7061300BAFA7F /* KTAwesomeCell.m */; }; 18 | CF2A545E19E7084100BAFA7F /* Default-568h@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = CF2A545D19E7084100BAFA7F /* Default-568h@2x.png */; }; 19 | CFA1AADE1CDA724B00373D0E /* tabbar-image.png in Resources */ = {isa = PBXBuildFile; fileRef = CFA1AADB1CDA724B00373D0E /* tabbar-image.png */; }; 20 | CFA1AADF1CDA724B00373D0E /* tabbar-image@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = CFA1AADC1CDA724B00373D0E /* tabbar-image@2x.png */; }; 21 | CFA1AAE01CDA724B00373D0E /* tabbar-image@3x.png in Resources */ = {isa = PBXBuildFile; fileRef = CFA1AADD1CDA724B00373D0E /* tabbar-image@3x.png */; }; 22 | CFD354581CD942170009443A /* KTCenterFlowLayout.m in Sources */ = {isa = PBXBuildFile; fileRef = CFD354571CD942170009443A /* KTCenterFlowLayout.m */; }; 23 | CFD3545B1CD9485A0009443A /* Constants.m in Sources */ = {isa = PBXBuildFile; fileRef = CFD3545A1CD9485A0009443A /* Constants.m */; }; 24 | CFD3545E1CD949780009443A /* KTVariableHeightCollectionViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = CFD3545D1CD949780009443A /* KTVariableHeightCollectionViewController.m */; }; 25 | CFD354611CD94AA70009443A /* KTSelfSizingCollectionViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = CFD354601CD94AA70009443A /* KTSelfSizingCollectionViewController.m */; }; 26 | /* End PBXBuildFile section */ 27 | 28 | /* Begin PBXFileReference section */ 29 | 19FE6FBDF1A7F0531B0558FC /* libPods-CenterFlow.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-CenterFlow.a"; sourceTree = BUILT_PRODUCTS_DIR; }; 30 | BA0FF2F233AC4708DDDF959F /* libPods-CenterFlowTests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-CenterFlowTests.a"; sourceTree = BUILT_PRODUCTS_DIR; }; 31 | CF0E9DA31AE914A300754C1D /* Launch.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = Launch.storyboard; sourceTree = ""; }; 32 | CF1278681D10D155007BD3CC /* KTHeaderFooterView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = KTHeaderFooterView.h; sourceTree = ""; }; 33 | CF1278691D10D155007BD3CC /* KTHeaderFooterView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = KTHeaderFooterView.m; sourceTree = ""; }; 34 | CF1EDF8B1CDA54EA004D7709 /* KTAwesomeSizingCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = KTAwesomeSizingCell.h; sourceTree = ""; }; 35 | CF1EDF8C1CDA54EA004D7709 /* KTAwesomeSizingCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = KTAwesomeSizingCell.m; sourceTree = ""; }; 36 | CF2A542E19E702E500BAFA7F /* CenterFlow.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = CenterFlow.app; sourceTree = BUILT_PRODUCTS_DIR; }; 37 | CF2A543219E702E500BAFA7F /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 38 | CF2A543319E702E500BAFA7F /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 39 | CF2A543519E702E500BAFA7F /* KTAppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = KTAppDelegate.h; sourceTree = ""; }; 40 | CF2A543619E702E500BAFA7F /* KTAppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = KTAppDelegate.m; sourceTree = ""; }; 41 | CF2A543E19E702E500BAFA7F /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; 42 | CF2A545719E704C700BAFA7F /* KTBasicCollectionViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = KTBasicCollectionViewController.h; sourceTree = ""; }; 43 | CF2A545819E704C700BAFA7F /* KTBasicCollectionViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = KTBasicCollectionViewController.m; sourceTree = ""; }; 44 | CF2A545A19E7061300BAFA7F /* KTAwesomeCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = KTAwesomeCell.h; sourceTree = ""; }; 45 | CF2A545B19E7061300BAFA7F /* KTAwesomeCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = KTAwesomeCell.m; sourceTree = ""; }; 46 | CF2A545D19E7084100BAFA7F /* Default-568h@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Default-568h@2x.png"; sourceTree = ""; }; 47 | CFA1AADB1CDA724B00373D0E /* tabbar-image.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "tabbar-image.png"; sourceTree = ""; }; 48 | CFA1AADC1CDA724B00373D0E /* tabbar-image@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "tabbar-image@2x.png"; sourceTree = ""; }; 49 | CFA1AADD1CDA724B00373D0E /* tabbar-image@3x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "tabbar-image@3x.png"; sourceTree = ""; }; 50 | CFD354561CD942170009443A /* KTCenterFlowLayout.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = KTCenterFlowLayout.h; path = ../../KTCenterFlowLayout.h; sourceTree = ""; }; 51 | CFD354571CD942170009443A /* KTCenterFlowLayout.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = KTCenterFlowLayout.m; path = ../../KTCenterFlowLayout.m; sourceTree = ""; }; 52 | CFD354591CD9485A0009443A /* Constants.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Constants.h; sourceTree = ""; }; 53 | CFD3545A1CD9485A0009443A /* Constants.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Constants.m; sourceTree = ""; }; 54 | CFD3545C1CD949780009443A /* KTVariableHeightCollectionViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = KTVariableHeightCollectionViewController.h; sourceTree = ""; }; 55 | CFD3545D1CD949780009443A /* KTVariableHeightCollectionViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = KTVariableHeightCollectionViewController.m; sourceTree = ""; }; 56 | CFD3545F1CD94AA70009443A /* KTSelfSizingCollectionViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = KTSelfSizingCollectionViewController.h; sourceTree = ""; }; 57 | CFD354601CD94AA70009443A /* KTSelfSizingCollectionViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = KTSelfSizingCollectionViewController.m; sourceTree = ""; }; 58 | D7C963BB7B28CF1B2966D389 /* libPods.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libPods.a; sourceTree = BUILT_PRODUCTS_DIR; }; 59 | /* End PBXFileReference section */ 60 | 61 | /* Begin PBXFrameworksBuildPhase section */ 62 | CF2A542B19E702E500BAFA7F /* Frameworks */ = { 63 | isa = PBXFrameworksBuildPhase; 64 | buildActionMask = 2147483647; 65 | files = ( 66 | ); 67 | runOnlyForDeploymentPostprocessing = 0; 68 | }; 69 | /* End PBXFrameworksBuildPhase section */ 70 | 71 | /* Begin PBXGroup section */ 72 | 0405686C57B14B068693DE2D /* Frameworks */ = { 73 | isa = PBXGroup; 74 | children = ( 75 | 19FE6FBDF1A7F0531B0558FC /* libPods-CenterFlow.a */, 76 | BA0FF2F233AC4708DDDF959F /* libPods-CenterFlowTests.a */, 77 | D7C963BB7B28CF1B2966D389 /* libPods.a */, 78 | ); 79 | name = Frameworks; 80 | sourceTree = ""; 81 | }; 82 | CF2A542519E702E500BAFA7F = { 83 | isa = PBXGroup; 84 | children = ( 85 | CF2A543019E702E500BAFA7F /* CenterFlow */, 86 | CF2A542F19E702E500BAFA7F /* Products */, 87 | 0405686C57B14B068693DE2D /* Frameworks */, 88 | ); 89 | sourceTree = ""; 90 | }; 91 | CF2A542F19E702E500BAFA7F /* Products */ = { 92 | isa = PBXGroup; 93 | children = ( 94 | CF2A542E19E702E500BAFA7F /* CenterFlow.app */, 95 | ); 96 | name = Products; 97 | sourceTree = ""; 98 | }; 99 | CF2A543019E702E500BAFA7F /* CenterFlow */ = { 100 | isa = PBXGroup; 101 | children = ( 102 | CFA1AADB1CDA724B00373D0E /* tabbar-image.png */, 103 | CFA1AADC1CDA724B00373D0E /* tabbar-image@2x.png */, 104 | CFA1AADD1CDA724B00373D0E /* tabbar-image@3x.png */, 105 | CFD354561CD942170009443A /* KTCenterFlowLayout.h */, 106 | CFD354571CD942170009443A /* KTCenterFlowLayout.m */, 107 | CF2A545D19E7084100BAFA7F /* Default-568h@2x.png */, 108 | CF2A543519E702E500BAFA7F /* KTAppDelegate.h */, 109 | CF2A543619E702E500BAFA7F /* KTAppDelegate.m */, 110 | CF2A543E19E702E500BAFA7F /* Images.xcassets */, 111 | CF2A543119E702E500BAFA7F /* Supporting Files */, 112 | CF2A545719E704C700BAFA7F /* KTBasicCollectionViewController.h */, 113 | CF2A545819E704C700BAFA7F /* KTBasicCollectionViewController.m */, 114 | CF2A545A19E7061300BAFA7F /* KTAwesomeCell.h */, 115 | CF2A545B19E7061300BAFA7F /* KTAwesomeCell.m */, 116 | CF0E9DA31AE914A300754C1D /* Launch.storyboard */, 117 | CFD354591CD9485A0009443A /* Constants.h */, 118 | CFD3545A1CD9485A0009443A /* Constants.m */, 119 | CFD3545C1CD949780009443A /* KTVariableHeightCollectionViewController.h */, 120 | CFD3545D1CD949780009443A /* KTVariableHeightCollectionViewController.m */, 121 | CFD3545F1CD94AA70009443A /* KTSelfSizingCollectionViewController.h */, 122 | CFD354601CD94AA70009443A /* KTSelfSizingCollectionViewController.m */, 123 | CF1EDF8B1CDA54EA004D7709 /* KTAwesomeSizingCell.h */, 124 | CF1EDF8C1CDA54EA004D7709 /* KTAwesomeSizingCell.m */, 125 | CF1278681D10D155007BD3CC /* KTHeaderFooterView.h */, 126 | CF1278691D10D155007BD3CC /* KTHeaderFooterView.m */, 127 | ); 128 | path = CenterFlow; 129 | sourceTree = ""; 130 | }; 131 | CF2A543119E702E500BAFA7F /* Supporting Files */ = { 132 | isa = PBXGroup; 133 | children = ( 134 | CF2A543219E702E500BAFA7F /* Info.plist */, 135 | CF2A543319E702E500BAFA7F /* main.m */, 136 | ); 137 | name = "Supporting Files"; 138 | sourceTree = ""; 139 | }; 140 | /* End PBXGroup section */ 141 | 142 | /* Begin PBXNativeTarget section */ 143 | CF2A542D19E702E500BAFA7F /* CenterFlow */ = { 144 | isa = PBXNativeTarget; 145 | buildConfigurationList = CF2A545119E702E500BAFA7F /* Build configuration list for PBXNativeTarget "CenterFlow" */; 146 | buildPhases = ( 147 | CF2A542A19E702E500BAFA7F /* Sources */, 148 | CF2A542B19E702E500BAFA7F /* Frameworks */, 149 | CF2A542C19E702E500BAFA7F /* Resources */, 150 | ); 151 | buildRules = ( 152 | ); 153 | dependencies = ( 154 | ); 155 | name = CenterFlow; 156 | productName = CenterFlow; 157 | productReference = CF2A542E19E702E500BAFA7F /* CenterFlow.app */; 158 | productType = "com.apple.product-type.application"; 159 | }; 160 | /* End PBXNativeTarget section */ 161 | 162 | /* Begin PBXProject section */ 163 | CF2A542619E702E500BAFA7F /* Project object */ = { 164 | isa = PBXProject; 165 | attributes = { 166 | LastUpgradeCheck = 0600; 167 | ORGANIZATIONNAME = keighl; 168 | TargetAttributes = { 169 | CF2A542D19E702E500BAFA7F = { 170 | CreatedOnToolsVersion = 6.0.1; 171 | }; 172 | }; 173 | }; 174 | buildConfigurationList = CF2A542919E702E500BAFA7F /* Build configuration list for PBXProject "CenterFlow" */; 175 | compatibilityVersion = "Xcode 3.2"; 176 | developmentRegion = English; 177 | hasScannedForEncodings = 0; 178 | knownRegions = ( 179 | en, 180 | Base, 181 | ); 182 | mainGroup = CF2A542519E702E500BAFA7F; 183 | productRefGroup = CF2A542F19E702E500BAFA7F /* Products */; 184 | projectDirPath = ""; 185 | projectRoot = ""; 186 | targets = ( 187 | CF2A542D19E702E500BAFA7F /* CenterFlow */, 188 | ); 189 | }; 190 | /* End PBXProject section */ 191 | 192 | /* Begin PBXResourcesBuildPhase section */ 193 | CF2A542C19E702E500BAFA7F /* Resources */ = { 194 | isa = PBXResourcesBuildPhase; 195 | buildActionMask = 2147483647; 196 | files = ( 197 | CFA1AADF1CDA724B00373D0E /* tabbar-image@2x.png in Resources */, 198 | CF2A543F19E702E500BAFA7F /* Images.xcassets in Resources */, 199 | CF2A545E19E7084100BAFA7F /* Default-568h@2x.png in Resources */, 200 | CF0E9DA41AE914A300754C1D /* Launch.storyboard in Resources */, 201 | CFA1AADE1CDA724B00373D0E /* tabbar-image.png in Resources */, 202 | CFA1AAE01CDA724B00373D0E /* tabbar-image@3x.png in Resources */, 203 | ); 204 | runOnlyForDeploymentPostprocessing = 0; 205 | }; 206 | /* End PBXResourcesBuildPhase section */ 207 | 208 | /* Begin PBXSourcesBuildPhase section */ 209 | CF2A542A19E702E500BAFA7F /* Sources */ = { 210 | isa = PBXSourcesBuildPhase; 211 | buildActionMask = 2147483647; 212 | files = ( 213 | CF2A543719E702E500BAFA7F /* KTAppDelegate.m in Sources */, 214 | CFD354611CD94AA70009443A /* KTSelfSizingCollectionViewController.m in Sources */, 215 | CF1EDF8D1CDA54EA004D7709 /* KTAwesomeSizingCell.m in Sources */, 216 | CF12786A1D10D155007BD3CC /* KTHeaderFooterView.m in Sources */, 217 | CFD354581CD942170009443A /* KTCenterFlowLayout.m in Sources */, 218 | CF2A545C19E7061300BAFA7F /* KTAwesomeCell.m in Sources */, 219 | CFD3545E1CD949780009443A /* KTVariableHeightCollectionViewController.m in Sources */, 220 | CF2A545919E704C700BAFA7F /* KTBasicCollectionViewController.m in Sources */, 221 | CF2A543419E702E500BAFA7F /* main.m in Sources */, 222 | CFD3545B1CD9485A0009443A /* Constants.m in Sources */, 223 | ); 224 | runOnlyForDeploymentPostprocessing = 0; 225 | }; 226 | /* End PBXSourcesBuildPhase section */ 227 | 228 | /* Begin XCBuildConfiguration section */ 229 | CF2A544F19E702E500BAFA7F /* Debug */ = { 230 | isa = XCBuildConfiguration; 231 | buildSettings = { 232 | ALWAYS_SEARCH_USER_PATHS = NO; 233 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 234 | CLANG_CXX_LIBRARY = "libc++"; 235 | CLANG_ENABLE_MODULES = YES; 236 | CLANG_ENABLE_OBJC_ARC = YES; 237 | CLANG_WARN_BOOL_CONVERSION = YES; 238 | CLANG_WARN_CONSTANT_CONVERSION = YES; 239 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 240 | CLANG_WARN_EMPTY_BODY = YES; 241 | CLANG_WARN_ENUM_CONVERSION = YES; 242 | CLANG_WARN_INT_CONVERSION = YES; 243 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 244 | CLANG_WARN_UNREACHABLE_CODE = YES; 245 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 246 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 247 | COPY_PHASE_STRIP = NO; 248 | ENABLE_STRICT_OBJC_MSGSEND = YES; 249 | GCC_C_LANGUAGE_STANDARD = gnu99; 250 | GCC_DYNAMIC_NO_PIC = NO; 251 | GCC_OPTIMIZATION_LEVEL = 0; 252 | GCC_PREPROCESSOR_DEFINITIONS = ( 253 | "DEBUG=1", 254 | "$(inherited)", 255 | ); 256 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 257 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 258 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 259 | GCC_WARN_UNDECLARED_SELECTOR = YES; 260 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 261 | GCC_WARN_UNUSED_FUNCTION = YES; 262 | GCC_WARN_UNUSED_VARIABLE = YES; 263 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 264 | MTL_ENABLE_DEBUG_INFO = YES; 265 | ONLY_ACTIVE_ARCH = YES; 266 | SDKROOT = iphoneos; 267 | }; 268 | name = Debug; 269 | }; 270 | CF2A545019E702E500BAFA7F /* Release */ = { 271 | isa = XCBuildConfiguration; 272 | buildSettings = { 273 | ALWAYS_SEARCH_USER_PATHS = NO; 274 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 275 | CLANG_CXX_LIBRARY = "libc++"; 276 | CLANG_ENABLE_MODULES = YES; 277 | CLANG_ENABLE_OBJC_ARC = YES; 278 | CLANG_WARN_BOOL_CONVERSION = YES; 279 | CLANG_WARN_CONSTANT_CONVERSION = YES; 280 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 281 | CLANG_WARN_EMPTY_BODY = YES; 282 | CLANG_WARN_ENUM_CONVERSION = YES; 283 | CLANG_WARN_INT_CONVERSION = YES; 284 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 285 | CLANG_WARN_UNREACHABLE_CODE = YES; 286 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 287 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 288 | COPY_PHASE_STRIP = YES; 289 | ENABLE_NS_ASSERTIONS = NO; 290 | ENABLE_STRICT_OBJC_MSGSEND = YES; 291 | GCC_C_LANGUAGE_STANDARD = gnu99; 292 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 293 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 294 | GCC_WARN_UNDECLARED_SELECTOR = YES; 295 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 296 | GCC_WARN_UNUSED_FUNCTION = YES; 297 | GCC_WARN_UNUSED_VARIABLE = YES; 298 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 299 | MTL_ENABLE_DEBUG_INFO = NO; 300 | SDKROOT = iphoneos; 301 | VALIDATE_PRODUCT = YES; 302 | }; 303 | name = Release; 304 | }; 305 | CF2A545219E702E500BAFA7F /* Debug */ = { 306 | isa = XCBuildConfiguration; 307 | buildSettings = { 308 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 309 | INFOPLIST_FILE = CenterFlow/Info.plist; 310 | IPHONEOS_DEPLOYMENT_TARGET = 7.0; 311 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 312 | PRODUCT_NAME = "$(TARGET_NAME)"; 313 | }; 314 | name = Debug; 315 | }; 316 | CF2A545319E702E500BAFA7F /* Release */ = { 317 | isa = XCBuildConfiguration; 318 | buildSettings = { 319 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 320 | INFOPLIST_FILE = CenterFlow/Info.plist; 321 | IPHONEOS_DEPLOYMENT_TARGET = 7.0; 322 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 323 | PRODUCT_NAME = "$(TARGET_NAME)"; 324 | }; 325 | name = Release; 326 | }; 327 | /* End XCBuildConfiguration section */ 328 | 329 | /* Begin XCConfigurationList section */ 330 | CF2A542919E702E500BAFA7F /* Build configuration list for PBXProject "CenterFlow" */ = { 331 | isa = XCConfigurationList; 332 | buildConfigurations = ( 333 | CF2A544F19E702E500BAFA7F /* Debug */, 334 | CF2A545019E702E500BAFA7F /* Release */, 335 | ); 336 | defaultConfigurationIsVisible = 0; 337 | defaultConfigurationName = Release; 338 | }; 339 | CF2A545119E702E500BAFA7F /* Build configuration list for PBXNativeTarget "CenterFlow" */ = { 340 | isa = XCConfigurationList; 341 | buildConfigurations = ( 342 | CF2A545219E702E500BAFA7F /* Debug */, 343 | CF2A545319E702E500BAFA7F /* Release */, 344 | ); 345 | defaultConfigurationIsVisible = 0; 346 | defaultConfigurationName = Release; 347 | }; 348 | /* End XCConfigurationList section */ 349 | }; 350 | rootObject = CF2A542619E702E500BAFA7F /* Project object */; 351 | } 352 | -------------------------------------------------------------------------------- /Example/CenterFlow.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Example/CenterFlow/Constants.h: -------------------------------------------------------------------------------- 1 | // 2 | // Constants.h 3 | // CenterFlow 4 | // 5 | // Created by Kyle Truscott on 5/3/16. 6 | // Copyright © 2016 keighl. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | static NSString *headerCellID = @"HeaderCell"; 12 | static NSString *footerCellID = @"FooterCell"; 13 | static NSString *stateCellID = @"StateCell"; 14 | 15 | @interface Constants : NSObject 16 | + (NSArray *)states; 17 | @end 18 | -------------------------------------------------------------------------------- /Example/CenterFlow/Constants.m: -------------------------------------------------------------------------------- 1 | // 2 | // Constants.m 3 | // CenterFlow 4 | // 5 | // Created by Kyle Truscott on 5/3/16. 6 | // Copyright © 2016 keighl. All rights reserved. 7 | // 8 | 9 | #import "Constants.h" 10 | 11 | @implementation Constants 12 | 13 | + (NSArray *)states 14 | { 15 | return @[ 16 | @"Alabama", 17 | @"Alaska", 18 | @"Arizona", 19 | @"Arkansas", 20 | @"California", 21 | @"Colorado", 22 | @"Connecticut", 23 | @"Delaware", 24 | @"District Of Columbia", 25 | @"Florida", 26 | @"Georgia", 27 | @"Hawaii", 28 | @"Idaho", 29 | @"Illinois", 30 | @"Indiana", 31 | @"Iowa", 32 | @"Kansas", 33 | @"Kentucky", 34 | @"Louisiana", 35 | @"Maine", 36 | @"Maryland", 37 | @"Massachusetts", 38 | @"Michigan", 39 | @"Minnesota", 40 | @"Mississippi", 41 | @"Missouri", 42 | @"Montana", 43 | @"Nebraska", 44 | @"Nevada", 45 | @"New Hampshire", 46 | @"New Jersey", 47 | @"New Mexico", 48 | @"New York", 49 | @"North Carolina", 50 | @"North Dakota", 51 | @"Ohio", 52 | @"Oklahoma", 53 | @"Oregon", 54 | @"Pennsylvania", 55 | @"Rhode Island", 56 | @"South Carolina", 57 | @"South Dakota", 58 | @"Tennessee", 59 | @"Texas", 60 | @"Utah", 61 | @"Vermont", 62 | @"Virginia", 63 | @"Washington", 64 | @"West Virginia", 65 | @"Wisconsin", 66 | @"Wyoming" 67 | ]; 68 | } 69 | 70 | @end 71 | -------------------------------------------------------------------------------- /Example/CenterFlow/Default-568h@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/keighl/KTCenterFlowLayout/bf94728bc1d4efdb5c2a839c7bf29cb0da69bc06/Example/CenterFlow/Default-568h@2x.png -------------------------------------------------------------------------------- /Example/CenterFlow/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" : "29x29", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "40x40", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "40x40", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "60x60", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "60x60", 31 | "scale" : "3x" 32 | } 33 | ], 34 | "info" : { 35 | "version" : 1, 36 | "author" : "xcode" 37 | } 38 | } -------------------------------------------------------------------------------- /Example/CenterFlow/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | com.keighl.$(PRODUCT_NAME:rfc1034identifier) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | Launch 27 | UIRequiredDeviceCapabilities 28 | 29 | armv7 30 | 31 | UISupportedInterfaceOrientations 32 | 33 | UIInterfaceOrientationPortrait 34 | UIInterfaceOrientationPortraitUpsideDown 35 | UIInterfaceOrientationLandscapeLeft 36 | UIInterfaceOrientationLandscapeRight 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /Example/CenterFlow/KTAppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.h 3 | // CenterFlow 4 | // 5 | // Created by Kyle Truscott on 10/9/14. 6 | // Copyright (c) 2014 keighl. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface KTAppDelegate : UIResponder 12 | 13 | @property (strong, nonatomic) UIWindow *window; 14 | 15 | @end 16 | 17 | -------------------------------------------------------------------------------- /Example/CenterFlow/KTAppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.m 3 | // CenterFlow 4 | // 5 | // Created by Kyle Truscott on 10/9/14. 6 | // Copyright (c) 2014 keighl. All rights reserved. 7 | // 8 | 9 | #import "KTAppDelegate.h" 10 | #import "KTBasicCollectionViewController.h" 11 | #import "KTVariableHeightCollectionViewController.h" 12 | #import "KTSelfSizingCollectionViewController.h" 13 | #import "KTCenterFlowLayout.h" 14 | 15 | @interface KTAppDelegate () 16 | 17 | @end 18 | 19 | @implementation KTAppDelegate 20 | 21 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions 22 | { 23 | self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; 24 | self.window.backgroundColor = [UIColor whiteColor]; 25 | 26 | UINavigationController *basicNavController = [UINavigationController new]; 27 | basicNavController.tabBarItem = [[UITabBarItem alloc] initWithTitle:@"Vanilla" image:[UIImage imageNamed:@"tabbar-image"] tag:2]; 28 | KTBasicCollectionViewController *basicController = [[KTBasicCollectionViewController alloc] initWithCollectionViewLayout:[UICollectionViewFlowLayout new]]; 29 | basicController.title = @"UICollectionViewFlowLayout"; 30 | [basicNavController setViewControllers:@[basicController]]; 31 | 32 | UINavigationController *centerNavController = [UINavigationController new]; 33 | KTBasicCollectionViewController *centerController = [[KTBasicCollectionViewController alloc] initWithCollectionViewLayout:[KTCenterFlowLayout new]]; 34 | centerController.title = @"KTCenterFlowLayout"; 35 | centerNavController.tabBarItem = [[UITabBarItem alloc] initWithTitle:@"Centered" image:[UIImage imageNamed:@"tabbar-image"] tag:2]; 36 | [centerNavController setViewControllers:@[centerController]]; 37 | 38 | UINavigationController *heightsNavController = [UINavigationController new]; 39 | heightsNavController.tabBarItem = [[UITabBarItem alloc] initWithTitle:@"Variable Height" image:[UIImage imageNamed:@"tabbar-image"] tag:1]; 40 | KTVariableHeightCollectionViewController *heightsController = [[KTVariableHeightCollectionViewController alloc] initWithCollectionViewLayout:[KTCenterFlowLayout new]]; 41 | heightsController.title = @"Variable Height"; 42 | [heightsNavController setViewControllers:@[heightsController]]; 43 | 44 | UINavigationController *selfsizeNavController = [UINavigationController new]; 45 | selfsizeNavController.tabBarItem = [[UITabBarItem alloc] initWithTitle:@"Self Sizing" image:[UIImage imageNamed:@"tabbar-image"] tag:2]; 46 | KTSelfSizingCollectionViewController *selfsizeController = [[KTSelfSizingCollectionViewController alloc] initWithCollectionViewLayout:[KTCenterFlowLayout new]]; 47 | selfsizeController.title = @"Self Sizing"; 48 | [selfsizeNavController setViewControllers:@[selfsizeController]]; 49 | 50 | UITabBarController *tabBarController = [[UITabBarController alloc] init]; 51 | [tabBarController setViewControllers:@[ 52 | basicNavController, 53 | centerNavController, 54 | heightsNavController, 55 | //selfsizeNavController 56 | ]]; 57 | 58 | self.window.rootViewController = tabBarController; 59 | [self.window makeKeyAndVisible]; 60 | return YES; 61 | } 62 | 63 | - (void)applicationWillResignActive:(UIApplication *)application 64 | { 65 | } 66 | 67 | - (void)applicationDidEnterBackground:(UIApplication *)application 68 | { 69 | } 70 | 71 | - (void)applicationWillEnterForeground:(UIApplication *)application 72 | { 73 | 74 | } 75 | 76 | - (void)applicationDidBecomeActive:(UIApplication *)application 77 | { 78 | } 79 | 80 | - (void)applicationWillTerminate:(UIApplication *)application 81 | { 82 | } 83 | 84 | @end 85 | -------------------------------------------------------------------------------- /Example/CenterFlow/KTAwesomeCell.h: -------------------------------------------------------------------------------- 1 | // 2 | // AwesomeCell.h 3 | // CenterFlow 4 | // 5 | // Created by Kyle Truscott on 10/9/14. 6 | // Copyright (c) 2014 keighl. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface KTAwesomeCell : UICollectionViewCell 12 | @property (strong) UILabel *label; 13 | @end 14 | -------------------------------------------------------------------------------- /Example/CenterFlow/KTAwesomeCell.m: -------------------------------------------------------------------------------- 1 | // 2 | // AwesomeCell.m 3 | // CenterFlow 4 | // 5 | // Created by Kyle Truscott on 10/9/14. 6 | // Copyright (c) 2014 keighl. All rights reserved. 7 | // 8 | 9 | #import "KTAwesomeCell.h" 10 | 11 | @implementation KTAwesomeCell 12 | 13 | - (instancetype)initWithFrame:(CGRect)frame 14 | { 15 | self = [super initWithFrame:frame]; 16 | 17 | if (self) 18 | { 19 | self.clipsToBounds = NO; 20 | self.contentView.clipsToBounds = NO; 21 | 22 | self.contentView.layer.borderColor = [UIColor blueColor].CGColor; 23 | self.contentView.layer.borderWidth = 1.f; 24 | 25 | self.label = [UILabel new]; 26 | self.label.translatesAutoresizingMaskIntoConstraints = NO; 27 | self.label.font = [UIFont systemFontOfSize:14]; 28 | self.label.backgroundColor = [UIColor clearColor]; 29 | self.label.textColor = [UIColor blueColor]; 30 | [self.contentView addSubview:self.label]; 31 | 32 | NSDictionary *views = NSDictionaryOfVariableBindings(_label); 33 | 34 | [self.contentView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|-10-[_label]" options:0 metrics:nil views:views]]; 35 | [self.contentView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"|-10-[_label]" options:0 metrics:nil views:views]]; 36 | 37 | } 38 | 39 | return self; 40 | } 41 | 42 | - (CGSize)intrinsicContentSize 43 | { 44 | CGSize size = self.label.intrinsicContentSize; 45 | size.width += 20; 46 | size.height += 20; 47 | return size; 48 | } 49 | 50 | @end 51 | -------------------------------------------------------------------------------- /Example/CenterFlow/KTAwesomeSizingCell.h: -------------------------------------------------------------------------------- 1 | // 2 | // KTAwesomeSizingCell.h 3 | // CenterFlow 4 | // 5 | // Created by Kyle Truscott on 5/4/16. 6 | // Copyright © 2016 keighl. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface KTAwesomeSizingCell : UICollectionViewCell 12 | @property (strong) UILabel *label; 13 | @end 14 | -------------------------------------------------------------------------------- /Example/CenterFlow/KTAwesomeSizingCell.m: -------------------------------------------------------------------------------- 1 | // 2 | // KTAwesomeSizingCell.m 3 | // CenterFlow 4 | // 5 | // Created by Kyle Truscott on 5/4/16. 6 | // Copyright © 2016 keighl. All rights reserved. 7 | // 8 | 9 | #import "KTAwesomeSizingCell.h" 10 | 11 | @implementation KTAwesomeSizingCell 12 | 13 | - (instancetype)initWithFrame:(CGRect)frame 14 | { 15 | self = [super initWithFrame:frame]; 16 | 17 | if (self) 18 | { 19 | self.clipsToBounds = NO; 20 | self.contentView.clipsToBounds = NO; 21 | 22 | self.contentView.layer.borderColor = [UIColor blueColor].CGColor; 23 | self.contentView.layer.borderWidth = 1.f; 24 | 25 | self.label = [UILabel new]; 26 | self.label.translatesAutoresizingMaskIntoConstraints = NO; 27 | self.label.font = [UIFont systemFontOfSize:14]; 28 | self.label.backgroundColor = [UIColor clearColor]; 29 | self.label.textColor = [UIColor blueColor]; 30 | self.label.preferredMaxLayoutWidth = 100; 31 | [self.contentView addSubview:self.label]; 32 | 33 | NSDictionary *views = NSDictionaryOfVariableBindings(_label); 34 | 35 | [self.contentView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|-10-[_label]-10-|" options:0 metrics:nil views:views]]; 36 | [self.contentView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"|-10-[_label]-10-|" options:0 metrics:nil views:views]]; 37 | 38 | } 39 | 40 | return self; 41 | } 42 | 43 | @end 44 | -------------------------------------------------------------------------------- /Example/CenterFlow/KTBasicCollectionViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // MainController.h 3 | // CenterFlow 4 | // 5 | // Created by Kyle Truscott on 10/9/14. 6 | // Copyright (c) 2014 keighl. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "KTAwesomeCell.h" 11 | 12 | @interface KTBasicCollectionViewController : UICollectionViewController 13 | @property (strong) NSArray *states; 14 | @property (strong) KTAwesomeCell *sizingCell; 15 | @end 16 | -------------------------------------------------------------------------------- /Example/CenterFlow/KTBasicCollectionViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // MainController.m 3 | // CenterFlow 4 | // 5 | // Created by Kyle Truscott on 10/9/14. 6 | // Copyright (c) 2014 keighl. All rights reserved. 7 | // 8 | 9 | #import "KTBasicCollectionViewController.h" 10 | #import "KTCenterFlowLayout.h" 11 | #import "Constants.h" 12 | #import "KTHeaderFooterView.h" 13 | 14 | @interface KTBasicCollectionViewController () 15 | @end 16 | 17 | @implementation KTBasicCollectionViewController 18 | 19 | - (void)viewDidLoad 20 | { 21 | [super viewDidLoad]; 22 | 23 | self.collectionView.backgroundColor = [UIColor whiteColor]; 24 | 25 | [(KTCenterFlowLayout *)self.collectionViewLayout setMinimumInteritemSpacing:15.f]; 26 | [(KTCenterFlowLayout *)self.collectionViewLayout setMinimumLineSpacing:15.f]; 27 | [(KTCenterFlowLayout *)self.collectionViewLayout setSectionInset:UIEdgeInsetsMake(20, 20, 20, 20)]; 28 | 29 | [self.collectionView registerClass:[KTHeaderFooterView class] forSupplementaryViewOfKind:UICollectionElementKindSectionHeader withReuseIdentifier:headerCellID]; 30 | [self.collectionView registerClass:[KTHeaderFooterView class] forSupplementaryViewOfKind:UICollectionElementKindSectionFooter withReuseIdentifier:footerCellID]; 31 | [self.collectionView registerClass:[KTAwesomeCell class] forCellWithReuseIdentifier:stateCellID]; 32 | 33 | self.sizingCell = [KTAwesomeCell new]; 34 | 35 | self.states = [Constants states]; 36 | } 37 | 38 | #pragma mark - UICollectionViewDataSource 39 | 40 | - (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView 41 | { 42 | return 1; 43 | } 44 | 45 | - (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section 46 | { 47 | return self.states.count; 48 | } 49 | 50 | #pragma mark - UICollectionViewDelegate 51 | 52 | - (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath 53 | { 54 | KTAwesomeCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:stateCellID 55 | forIndexPath:indexPath]; 56 | 57 | cell.label.text = self.states[indexPath.row]; 58 | return cell; 59 | } 60 | 61 | - (UICollectionReusableView *)collectionView:(UICollectionView *)collectionView 62 | viewForSupplementaryElementOfKind:(NSString *)kind 63 | atIndexPath:(NSIndexPath *)indexPath 64 | { 65 | if (kind == UICollectionElementKindSectionHeader) 66 | { 67 | KTHeaderFooterView *header = [collectionView dequeueReusableSupplementaryViewOfKind:kind withReuseIdentifier:headerCellID forIndexPath:indexPath]; 68 | header.label.text = @"50 states..."; 69 | return header; 70 | } 71 | 72 | if (kind == UICollectionElementKindSectionFooter) 73 | { 74 | KTHeaderFooterView *header = [collectionView dequeueReusableSupplementaryViewOfKind:kind withReuseIdentifier:footerCellID forIndexPath:indexPath]; 75 | header.label.text = @"...that's all of 'em."; 76 | return header; 77 | } 78 | 79 | return nil; 80 | } 81 | 82 | #pragma mark - UICollectionViewDelegateFlowLayout 83 | 84 | - (CGSize)collectionView:(UICollectionView *)collectionView 85 | layout:(UICollectionViewFlowLayout *)collectionViewLayout 86 | sizeForItemAtIndexPath:(NSIndexPath *)indexPath 87 | { 88 | self.sizingCell.label.text = self.states[indexPath.row]; 89 | return self.sizingCell.intrinsicContentSize; 90 | } 91 | 92 | - (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout referenceSizeForHeaderInSection:(NSInteger)section; 93 | { 94 | CGFloat width = collectionView.bounds.size.width; 95 | return CGSizeMake(width, 40); 96 | } 97 | 98 | - (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout referenceSizeForFooterInSection:(NSInteger)section; 99 | { 100 | CGFloat width = collectionView.bounds.size.width; 101 | return CGSizeMake(width, 40); 102 | } 103 | 104 | @end 105 | -------------------------------------------------------------------------------- /Example/CenterFlow/KTHeaderFooterView.h: -------------------------------------------------------------------------------- 1 | // 2 | // KTHeaderFooterView.h 3 | // CenterFlow 4 | // 5 | // Created by Kyle Truscott on 6/14/16. 6 | // Copyright © 2016 keighl. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | static NSString *KTHeaderFooterViewID = @"KTHeaderFooterView"; 12 | 13 | @interface KTHeaderFooterView : UICollectionReusableView 14 | @property (strong, nonatomic) UILabel *label; 15 | @end 16 | -------------------------------------------------------------------------------- /Example/CenterFlow/KTHeaderFooterView.m: -------------------------------------------------------------------------------- 1 | // 2 | // KTHeaderFooterView.m 3 | // CenterFlow 4 | // 5 | // Created by Kyle Truscott on 6/14/16. 6 | // Copyright © 2016 keighl. All rights reserved. 7 | // 8 | 9 | #import "KTHeaderFooterView.h" 10 | 11 | @implementation KTHeaderFooterView 12 | 13 | - (instancetype)initWithFrame:(CGRect)frame 14 | { 15 | self = [super initWithFrame:frame]; 16 | 17 | if (self) 18 | { 19 | self.backgroundColor = [UIColor colorWithWhite:0.97 alpha:1]; 20 | 21 | self.label = [UILabel new]; 22 | self.label.translatesAutoresizingMaskIntoConstraints = NO; 23 | self.label.font = [UIFont systemFontOfSize:14]; 24 | self.label.backgroundColor = [UIColor clearColor]; 25 | self.label.preferredMaxLayoutWidth = 100; 26 | self.label.textAlignment = NSTextAlignmentCenter; 27 | [self addSubview:self.label]; 28 | 29 | NSDictionary *views = NSDictionaryOfVariableBindings(_label); 30 | 31 | [self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[_label]|" options:0 metrics:nil views:views]]; 32 | [self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"|[_label]|" options:0 metrics:nil views:views]]; 33 | } 34 | 35 | return self; 36 | } 37 | 38 | 39 | @end 40 | -------------------------------------------------------------------------------- /Example/CenterFlow/KTSelfSizingCollectionViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // KTSelfSizingCollectionViewController.h 3 | // CenterFlow 4 | // 5 | // Created by Kyle Truscott on 5/3/16. 6 | // Copyright © 2016 keighl. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface KTSelfSizingCollectionViewController : UICollectionViewController 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /Example/CenterFlow/KTSelfSizingCollectionViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // KTSelfSizingCollectionViewController.m 3 | // CenterFlow 4 | // 5 | // Created by Kyle Truscott on 5/3/16. 6 | // Copyright © 2016 keighl. All rights reserved. 7 | // 8 | 9 | #import "KTSelfSizingCollectionViewController.h" 10 | #import "KTCenterFlowLayout.h" 11 | #import "KTAwesomeSizingCell.h" 12 | #import "Constants.h" 13 | 14 | @interface KTSelfSizingCollectionViewController () 15 | @property (strong) NSArray *states; 16 | @property (strong) KTAwesomeSizingCell *sizingCell; 17 | @end 18 | 19 | @implementation KTSelfSizingCollectionViewController 20 | 21 | - (void)viewDidLoad 22 | { 23 | [super viewDidLoad]; 24 | 25 | self.collectionView.backgroundColor = [UIColor whiteColor]; 26 | 27 | [(KTCenterFlowLayout *)self.collectionViewLayout setEstimatedItemSize:CGSizeMake(100, 50)]; 28 | [(KTCenterFlowLayout *)self.collectionViewLayout setMinimumInteritemSpacing:15.f]; 29 | [(KTCenterFlowLayout *)self.collectionViewLayout setMinimumLineSpacing:15.f]; 30 | [(KTCenterFlowLayout *)self.collectionViewLayout setSectionInset:UIEdgeInsetsMake(20, 20, 20, 20)]; 31 | 32 | [self.collectionView registerClass:[KTAwesomeSizingCell class] forCellWithReuseIdentifier:stateCellID]; 33 | 34 | self.sizingCell = [KTAwesomeSizingCell new]; 35 | 36 | self.states = [Constants states]; 37 | } 38 | 39 | #pragma mark - UICollectionViewDataSource 40 | 41 | - (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView 42 | { 43 | return 1; 44 | } 45 | 46 | - (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section 47 | { 48 | return self.states.count; 49 | } 50 | 51 | #pragma mark - UICollectionViewDelegate 52 | 53 | - (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath 54 | { 55 | KTAwesomeSizingCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:stateCellID 56 | forIndexPath:indexPath]; 57 | 58 | cell.label.text = self.states[indexPath.row]; 59 | return cell; 60 | } 61 | 62 | @end 63 | -------------------------------------------------------------------------------- /Example/CenterFlow/KTVariableHeightCollectionViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // KTVariableHeightCollectionViewController.h 3 | // CenterFlow 4 | // 5 | // Created by Kyle Truscott on 5/3/16. 6 | // Copyright © 2016 keighl. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "KTBasicCollectionViewController.h" 11 | 12 | @interface KTVariableHeightCollectionViewController : KTBasicCollectionViewController 13 | @end 14 | -------------------------------------------------------------------------------- /Example/CenterFlow/KTVariableHeightCollectionViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // KTVariableHeightCollectionViewController.m 3 | // CenterFlow 4 | // 5 | // Created by Kyle Truscott on 5/3/16. 6 | // Copyright © 2016 keighl. All rights reserved. 7 | // 8 | 9 | #import "KTVariableHeightCollectionViewController.h" 10 | #import "KTCenterFlowLayout.h" 11 | #import "KTAwesomeCell.h" 12 | #import "Constants.h" 13 | 14 | @interface KTVariableHeightCollectionViewController () 15 | @end 16 | 17 | @implementation KTVariableHeightCollectionViewController 18 | 19 | #pragma mark - UICollectionViewDelegate 20 | 21 | - (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath 22 | { 23 | KTAwesomeCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:stateCellID 24 | forIndexPath:indexPath]; 25 | 26 | NSString *text = self.states[indexPath.row]; 27 | cell.label.text = text; 28 | cell.label.font = [self fontForText:text]; 29 | return cell; 30 | } 31 | 32 | 33 | #pragma mark - UICollectionViewDelegateFlowLayout 34 | 35 | - (CGSize)collectionView:(UICollectionView *)collectionView 36 | layout:(UICollectionViewFlowLayout *)collectionViewLayout 37 | sizeForItemAtIndexPath:(NSIndexPath *)indexPath 38 | { 39 | NSString *text = self.states[indexPath.row]; 40 | self.sizingCell.label.text = self.states[indexPath.row]; 41 | self.sizingCell.label.font = [self fontForText:text]; 42 | return self.sizingCell.intrinsicContentSize; 43 | } 44 | 45 | - (UIFont *)fontForText:(NSString *)text 46 | { 47 | if (text.length == 7) 48 | { 49 | return [UIFont systemFontOfSize:16]; 50 | } 51 | 52 | if (text.length == 8) 53 | { 54 | return [UIFont systemFontOfSize:24]; 55 | } 56 | 57 | if (text.length == 5) 58 | { 59 | return [UIFont systemFontOfSize:10]; 60 | } 61 | 62 | return [UIFont systemFontOfSize:14]; 63 | } 64 | 65 | @end 66 | -------------------------------------------------------------------------------- /Example/CenterFlow/Launch.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /Example/CenterFlow/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // CenterFlow 4 | // 5 | // Created by Kyle Truscott on 10/9/14. 6 | // Copyright (c) 2014 keighl. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "KTAppDelegate.h" 11 | 12 | int main(int argc, char * argv[]) { 13 | @autoreleasepool { 14 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([KTAppDelegate class])); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Example/CenterFlow/tabbar-image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/keighl/KTCenterFlowLayout/bf94728bc1d4efdb5c2a839c7bf29cb0da69bc06/Example/CenterFlow/tabbar-image.png -------------------------------------------------------------------------------- /Example/CenterFlow/tabbar-image@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/keighl/KTCenterFlowLayout/bf94728bc1d4efdb5c2a839c7bf29cb0da69bc06/Example/CenterFlow/tabbar-image@2x.png -------------------------------------------------------------------------------- /Example/CenterFlow/tabbar-image@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/keighl/KTCenterFlowLayout/bf94728bc1d4efdb5c2a839c7bf29cb0da69bc06/Example/CenterFlow/tabbar-image@3x.png -------------------------------------------------------------------------------- /KTCenterFlowLayout.h: -------------------------------------------------------------------------------- 1 | // 2 | // KTCenterFlowLayout.h 3 | // 4 | // Created by Kyle Truscott on 10/9/14. 5 | // Copyright (c) 2014 keighl. All rights reserved. 6 | // 7 | 8 | #import 9 | 10 | /** 11 | * Aligns cells to the center of a collection view. 12 | */ 13 | @interface KTCenterFlowLayout : UICollectionViewFlowLayout 14 | 15 | @end 16 | -------------------------------------------------------------------------------- /KTCenterFlowLayout.m: -------------------------------------------------------------------------------- 1 | // 2 | // KTCenterFlowLayout.m 3 | // 4 | // Created by Kyle Truscott on 10/9/14. 5 | // Copyright (c) 2014 keighl. All rights reserved. 6 | // 7 | 8 | #import "KTCenterFlowLayout.h" 9 | 10 | @interface KTCenterFlowLayout () 11 | @property (nonatomic) NSMutableDictionary *attrCache; 12 | @end 13 | 14 | @implementation KTCenterFlowLayout 15 | 16 | - (void)prepareLayout 17 | { 18 | // Clear the attrCache 19 | self.attrCache = [NSMutableDictionary new]; 20 | } 21 | 22 | - (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect 23 | { 24 | NSMutableArray *updatedAttributes = [NSMutableArray new]; 25 | 26 | NSInteger sections = [self.collectionView numberOfSections]; 27 | NSInteger s = 0; 28 | while (s < sections) 29 | { 30 | NSInteger rows = [self.collectionView numberOfItemsInSection:s]; 31 | NSInteger r = 0; 32 | while (r < rows) 33 | { 34 | NSIndexPath *indexPath = [NSIndexPath indexPathForRow:r inSection:s]; 35 | 36 | UICollectionViewLayoutAttributes *attrs = [self layoutAttributesForItemAtIndexPath:indexPath]; 37 | 38 | if (attrs && CGRectIntersectsRect(attrs.frame, rect)) 39 | { 40 | [updatedAttributes addObject:attrs]; 41 | } 42 | 43 | UICollectionViewLayoutAttributes *headerAttrs = [super layoutAttributesForSupplementaryViewOfKind:UICollectionElementKindSectionHeader 44 | atIndexPath:indexPath]; 45 | 46 | if (headerAttrs && CGRectIntersectsRect(headerAttrs.frame, rect)) 47 | { 48 | [updatedAttributes addObject:headerAttrs]; 49 | } 50 | 51 | UICollectionViewLayoutAttributes *footerAttrs = [super layoutAttributesForSupplementaryViewOfKind:UICollectionElementKindSectionFooter 52 | atIndexPath:indexPath]; 53 | 54 | if (footerAttrs && CGRectIntersectsRect(footerAttrs.frame, rect)) 55 | { 56 | [updatedAttributes addObject:footerAttrs]; 57 | } 58 | 59 | r++; 60 | } 61 | s++; 62 | } 63 | 64 | return updatedAttributes; 65 | } 66 | 67 | - (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath 68 | { 69 | if (self.attrCache[indexPath]) 70 | { 71 | return self.attrCache[indexPath]; 72 | } 73 | 74 | // Find the other items in the same "row" 75 | NSMutableArray *rowBuddies = [NSMutableArray new]; 76 | 77 | // Calculate the available width to center stuff within 78 | // sectionInset is NOT applicable here because a) we're centering stuff 79 | // and b) Flow layout has arranged the cells to respect the inset. We're 80 | // just hijacking the X position. 81 | CGFloat collectionViewWidth = CGRectGetWidth(self.collectionView.bounds) - 82 | self.collectionView.contentInset.left - 83 | self.collectionView.contentInset.right; 84 | 85 | // To find other items in the "row", we need a rect to check intersects against. 86 | // Take the item attributes frame (from vanilla flow layout), and stretch it out 87 | CGRect rowTestFrame = [super layoutAttributesForItemAtIndexPath:indexPath].frame; 88 | rowTestFrame.origin.x = 0; 89 | rowTestFrame.size.width = collectionViewWidth; 90 | 91 | NSInteger totalRows = [self.collectionView numberOfItemsInSection:indexPath.section]; 92 | 93 | // From this item, work backwards to find the first item in the row 94 | // Decrement the row index until a) we get to 0, b) we reach a previous row 95 | NSInteger rowStartIDX = indexPath.row; 96 | while (true) 97 | { 98 | NSInteger prevIDX = rowStartIDX - 1; 99 | 100 | if (prevIDX < 0) 101 | { 102 | break; 103 | } 104 | 105 | NSIndexPath *prevPath = [NSIndexPath indexPathForRow:prevIDX inSection:indexPath.section]; 106 | CGRect prevFrame = [super layoutAttributesForItemAtIndexPath:prevPath].frame; 107 | 108 | // If the item intersects the test frame, it's in the same row 109 | if (CGRectIntersectsRect(prevFrame, rowTestFrame)) 110 | rowStartIDX = prevIDX; 111 | else 112 | // Found previous row, escape! 113 | break; 114 | } 115 | 116 | // Now, work back UP to find the last item in the row 117 | // For each item in the row, add it's attributes to rowBuddies 118 | NSInteger buddyIDX = rowStartIDX; 119 | while (true) 120 | { 121 | if (buddyIDX > (totalRows-1)) 122 | { 123 | break; 124 | } 125 | 126 | NSIndexPath *buddyPath = [NSIndexPath indexPathForRow:buddyIDX inSection:indexPath.section]; 127 | 128 | UICollectionViewLayoutAttributes *buddyAttributes = [super layoutAttributesForItemAtIndexPath:buddyPath]; 129 | 130 | if (CGRectIntersectsRect(buddyAttributes.frame, rowTestFrame)) 131 | { 132 | // If the item intersects the test frame, it's in the same row 133 | [rowBuddies addObject:[buddyAttributes copy]]; 134 | buddyIDX++; 135 | } 136 | else 137 | { 138 | // Encountered next row 139 | break; 140 | } 141 | } 142 | 143 | id flowDelegate = (id) [[self collectionView] delegate]; 144 | BOOL delegateSupportsInteritemSpacing = [flowDelegate respondsToSelector:@selector(collectionView:layout:minimumInteritemSpacingForSectionAtIndex:)]; 145 | 146 | // x-x-x-x ... sum up the interim space 147 | CGFloat interitemSpacing = [self minimumInteritemSpacing]; 148 | 149 | // Check for minimumInteritemSpacingForSectionAtIndex support 150 | if (delegateSupportsInteritemSpacing && rowBuddies.count > 0) 151 | { 152 | interitemSpacing = [flowDelegate collectionView:self.collectionView 153 | layout:self 154 | minimumInteritemSpacingForSectionAtIndex:indexPath.section]; 155 | } 156 | 157 | CGFloat aggregateInteritemSpacing = interitemSpacing * (rowBuddies.count -1); 158 | 159 | // Sum the width of all elements in the row 160 | CGFloat aggregateItemWidths = 0.f; 161 | for (UICollectionViewLayoutAttributes *itemAttributes in rowBuddies) 162 | aggregateItemWidths += CGRectGetWidth(itemAttributes.frame); 163 | 164 | // Build an alignment rect 165 | // | |x-x-x-x| | 166 | CGFloat alignmentWidth = aggregateItemWidths + aggregateInteritemSpacing; 167 | CGFloat alignmentXOffset = (collectionViewWidth - alignmentWidth) / 2.f; 168 | 169 | // Adjust each item's position to be centered 170 | CGRect previousFrame = CGRectZero; 171 | for (UICollectionViewLayoutAttributes *itemAttributes in rowBuddies) 172 | { 173 | CGRect itemFrame = itemAttributes.frame; 174 | 175 | if (CGRectEqualToRect(previousFrame, CGRectZero)) 176 | itemFrame.origin.x = alignmentXOffset; 177 | else 178 | itemFrame.origin.x = CGRectGetMaxX(previousFrame) + interitemSpacing; 179 | 180 | itemAttributes.frame = itemFrame; 181 | previousFrame = itemFrame; 182 | 183 | // Finally, add it to the cache 184 | self.attrCache[itemAttributes.indexPath] = itemAttributes; 185 | } 186 | 187 | return self.attrCache[indexPath]; 188 | } 189 | 190 | @end 191 | -------------------------------------------------------------------------------- /KTCenterFlowLayout.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |s| 2 | s.name = "KTCenterFlowLayout" 3 | s.version = "1.3.1" 4 | s.summary = "Aligns cells to the center of a collection view." 5 | 6 | s.homepage = "https://github.com/keighl/KTCenterFlowLayout" 7 | s.license = 'MIT' 8 | s.author = { "keighl" => "keighl@keighl.com" } 9 | s.source = { :git => "https://github.com/keighl/KTCenterFlowLayout.git", :tag => s.version.to_s } 10 | s.social_media_url = 'https://twitter.com/keighl' 11 | 12 | s.ios.deployment_target = '7.0' 13 | s.tvos.deployment_target = '9.0' 14 | s.requires_arc = true 15 | 16 | s.source_files = 'KTCenterFlowLayout.{h,m}' 17 | 18 | s.frameworks = 'UIKit' 19 | 20 | end 21 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) keighl (http://github.com/keighl) 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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # KTCenterFlowLayout 2 | 3 | [![CocoaPods](https://img.shields.io/cocoapods/v/KTCenterFlowLayout.svg)]() 4 | 5 | KTCenterFlowLayout is a subclass of UICollectionViewFlowLayout which Aligns cells to the center of a collection view. It effectively enforces the `minimumInteritemSpacing`. 6 | 7 | ### Usage 8 | 9 | ```ruby 10 | # In your Podfile 11 | 12 | pod 'KTCenterFlowLayout' 13 | ``` 14 | 15 | Objective-C: 16 | 17 | ```objective-c 18 | KTCenterFlowLayout *layout = [KTCenterFlowLayout new]; 19 | layout.minimumInteritemSpacing = 10.f; 20 | layout.minimumLineSpacing = 10.f; 21 | 22 | [[UICollectionViewController alloc] initWithCollectionViewLayout:layout]; 23 | ``` 24 | 25 | Swift: 26 | 27 | ```swift 28 | let layout = KTCenterFlowLayout() 29 | layout.minimumInteritemSpacing = 10.0 30 | layout.minimumLineSpacing = 10.0 31 | 32 | UICollectionViewController(collectionViewLayout: layout) 33 | ``` 34 | 35 | ### Example 36 | 37 | See the layout in use in the `./Example` app: 38 | 39 | 40 | 41 | ### Horizontal Scrolling 42 | 43 | This layout **doesn't** work with `UICollectionViewScrollDirection.horizontal`. 44 | 45 | ### Self-sizing cells 46 | 47 | This layout **doesn't** work with self-sizing collection view cells (`estimatedItemSize`). Of course, neither does the vanilla `UICollectionViewFlowLayout`. Pull requests appreciated from anybody interested in solving self-sizing layouts! 48 | -------------------------------------------------------------------------------- /example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/keighl/KTCenterFlowLayout/bf94728bc1d4efdb5c2a839c7bf29cb0da69bc06/example.png --------------------------------------------------------------------------------