├── CollectionViewTutorial.xcodeproj ├── project.pbxproj └── project.xcworkspace │ └── contents.xcworkspacedata ├── CollectionViewTutorial ├── BHAlbum.h ├── BHAlbum.m ├── BHAlbumPhotoCell.h ├── BHAlbumPhotoCell.m ├── BHAlbumTitleReusableView.h ├── BHAlbumTitleReusableView.m ├── BHAppDelegate.h ├── BHAppDelegate.m ├── BHCollectionViewController.h ├── BHCollectionViewController.m ├── BHEmblemView.h ├── BHEmblemView.m ├── BHPhoto.h ├── BHPhoto.m ├── BHPhotoAlbumLayout.h ├── BHPhotoAlbumLayout.m ├── CollectionViewTutorial-Info.plist ├── CollectionViewTutorial-Prefix.pch ├── Default-568h@2x.png ├── Default.png ├── Default@2x.png ├── en.lproj │ ├── BHCollectionViewController.xib │ └── InfoPlist.strings └── main.m ├── Images ├── concrete_wall.png ├── concrete_wall@2X.png ├── emblem.png └── emblem@2x.png ├── LICENSE.md ├── Photos ├── thumbnail0.jpg ├── thumbnail1.jpg ├── thumbnail10.jpg ├── thumbnail11.jpg ├── thumbnail12.jpg ├── thumbnail13.jpg ├── thumbnail14.jpg ├── thumbnail15.jpg ├── thumbnail16.jpg ├── thumbnail17.jpg ├── thumbnail18.jpg ├── thumbnail19.jpg ├── thumbnail2.jpg ├── thumbnail20.jpg ├── thumbnail21.jpg ├── thumbnail22.jpg ├── thumbnail23.jpg ├── thumbnail24.jpg ├── thumbnail3.jpg ├── thumbnail4.jpg ├── thumbnail5.jpg ├── thumbnail6.jpg ├── thumbnail7.jpg ├── thumbnail8.jpg └── thumbnail9.jpg └── README.md /CollectionViewTutorial.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 0D27363D164908EB00180485 /* BHEmblemView.m in Sources */ = {isa = PBXBuildFile; fileRef = 0D27363C164908EB00180485 /* BHEmblemView.m */; }; 11 | 0D27364016490E6F00180485 /* emblem.png in Resources */ = {isa = PBXBuildFile; fileRef = 0D27363E16490E6F00180485 /* emblem.png */; }; 12 | 0D27364116490E6F00180485 /* emblem@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 0D27363F16490E6F00180485 /* emblem@2x.png */; }; 13 | 0D4F141716466A4D0015AD5E /* concrete_wall.png in Resources */ = {isa = PBXBuildFile; fileRef = 0D4F141516466A4D0015AD5E /* concrete_wall.png */; }; 14 | 0D4F141816466A4D0015AD5E /* concrete_wall@2X.png in Resources */ = {isa = PBXBuildFile; fileRef = 0D4F141616466A4D0015AD5E /* concrete_wall@2X.png */; }; 15 | 0D6599C21646446300A7C770 /* BHAlbumTitleReusableView.m in Sources */ = {isa = PBXBuildFile; fileRef = 0D6599C11646446300A7C770 /* BHAlbumTitleReusableView.m */; }; 16 | 0DD9B1AE1645B7AE008EE1C5 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0DD9B1AD1645B7AE008EE1C5 /* UIKit.framework */; }; 17 | 0DD9B1B01645B7AE008EE1C5 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0DD9B1AF1645B7AE008EE1C5 /* Foundation.framework */; }; 18 | 0DD9B1B21645B7AE008EE1C5 /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0DD9B1B11645B7AE008EE1C5 /* CoreGraphics.framework */; }; 19 | 0DD9B1B81645B7AE008EE1C5 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 0DD9B1B61645B7AE008EE1C5 /* InfoPlist.strings */; }; 20 | 0DD9B1BA1645B7AE008EE1C5 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 0DD9B1B91645B7AE008EE1C5 /* main.m */; }; 21 | 0DD9B1BE1645B7AE008EE1C5 /* BHAppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 0DD9B1BD1645B7AE008EE1C5 /* BHAppDelegate.m */; }; 22 | 0DD9B1C01645B7AE008EE1C5 /* Default.png in Resources */ = {isa = PBXBuildFile; fileRef = 0DD9B1BF1645B7AE008EE1C5 /* Default.png */; }; 23 | 0DD9B1C21645B7AE008EE1C5 /* Default@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 0DD9B1C11645B7AE008EE1C5 /* Default@2x.png */; }; 24 | 0DD9B1C41645B7AE008EE1C5 /* Default-568h@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 0DD9B1C31645B7AE008EE1C5 /* Default-568h@2x.png */; }; 25 | 0DD9B1C71645B7AE008EE1C5 /* BHCollectionViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 0DD9B1C61645B7AE008EE1C5 /* BHCollectionViewController.m */; }; 26 | 0DD9B1CA1645B7AE008EE1C5 /* BHCollectionViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 0DD9B1C81645B7AE008EE1C5 /* BHCollectionViewController.xib */; }; 27 | 0DD9B1D31645B97F008EE1C5 /* BHPhotoAlbumLayout.m in Sources */ = {isa = PBXBuildFile; fileRef = 0DD9B1D21645B97F008EE1C5 /* BHPhotoAlbumLayout.m */; }; 28 | 0DD9B1D61645BBFA008EE1C5 /* BHAlbumPhotoCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 0DD9B1D51645BBFA008EE1C5 /* BHAlbumPhotoCell.m */; }; 29 | 0DD9B1DE1645BDE0008EE1C5 /* BHPhoto.m in Sources */ = {isa = PBXBuildFile; fileRef = 0DD9B1DD1645BDE0008EE1C5 /* BHPhoto.m */; }; 30 | 0DD9B1E11645BF26008EE1C5 /* BHAlbum.m in Sources */ = {isa = PBXBuildFile; fileRef = 0DD9B1E01645BF26008EE1C5 /* BHAlbum.m */; }; 31 | 0DD9B20616462154008EE1C5 /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0DD9B20516462154008EE1C5 /* QuartzCore.framework */; }; 32 | /* End PBXBuildFile section */ 33 | 34 | /* Begin PBXFileReference section */ 35 | 0D27363B164908EB00180485 /* BHEmblemView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BHEmblemView.h; sourceTree = ""; }; 36 | 0D27363C164908EB00180485 /* BHEmblemView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BHEmblemView.m; sourceTree = ""; }; 37 | 0D27363E16490E6F00180485 /* emblem.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = emblem.png; path = Images/emblem.png; sourceTree = SOURCE_ROOT; }; 38 | 0D27363F16490E6F00180485 /* emblem@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "emblem@2x.png"; path = "Images/emblem@2x.png"; sourceTree = SOURCE_ROOT; }; 39 | 0D4F141516466A4D0015AD5E /* concrete_wall.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = concrete_wall.png; path = Images/concrete_wall.png; sourceTree = SOURCE_ROOT; }; 40 | 0D4F141616466A4D0015AD5E /* concrete_wall@2X.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "concrete_wall@2X.png"; path = "Images/concrete_wall@2X.png"; sourceTree = SOURCE_ROOT; }; 41 | 0D6599C01646446300A7C770 /* BHAlbumTitleReusableView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BHAlbumTitleReusableView.h; sourceTree = ""; }; 42 | 0D6599C11646446300A7C770 /* BHAlbumTitleReusableView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BHAlbumTitleReusableView.m; sourceTree = ""; }; 43 | 0DD9B1A91645B7AE008EE1C5 /* CollectionViewTutorial.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = CollectionViewTutorial.app; sourceTree = BUILT_PRODUCTS_DIR; }; 44 | 0DD9B1AD1645B7AE008EE1C5 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; }; 45 | 0DD9B1AF1645B7AE008EE1C5 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; 46 | 0DD9B1B11645B7AE008EE1C5 /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; }; 47 | 0DD9B1B51645B7AE008EE1C5 /* CollectionViewTutorial-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "CollectionViewTutorial-Info.plist"; sourceTree = ""; }; 48 | 0DD9B1B71645B7AE008EE1C5 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; 49 | 0DD9B1B91645B7AE008EE1C5 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 50 | 0DD9B1BB1645B7AE008EE1C5 /* CollectionViewTutorial-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "CollectionViewTutorial-Prefix.pch"; sourceTree = ""; }; 51 | 0DD9B1BC1645B7AE008EE1C5 /* BHAppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = BHAppDelegate.h; sourceTree = ""; }; 52 | 0DD9B1BD1645B7AE008EE1C5 /* BHAppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = BHAppDelegate.m; sourceTree = ""; }; 53 | 0DD9B1BF1645B7AE008EE1C5 /* Default.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = Default.png; sourceTree = ""; }; 54 | 0DD9B1C11645B7AE008EE1C5 /* Default@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Default@2x.png"; sourceTree = ""; }; 55 | 0DD9B1C31645B7AE008EE1C5 /* Default-568h@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Default-568h@2x.png"; sourceTree = ""; }; 56 | 0DD9B1C51645B7AE008EE1C5 /* BHCollectionViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = BHCollectionViewController.h; sourceTree = ""; }; 57 | 0DD9B1C61645B7AE008EE1C5 /* BHCollectionViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = BHCollectionViewController.m; sourceTree = ""; }; 58 | 0DD9B1C91645B7AE008EE1C5 /* en */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = en; path = en.lproj/BHCollectionViewController.xib; sourceTree = ""; }; 59 | 0DD9B1D11645B97F008EE1C5 /* BHPhotoAlbumLayout.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BHPhotoAlbumLayout.h; sourceTree = ""; }; 60 | 0DD9B1D21645B97F008EE1C5 /* BHPhotoAlbumLayout.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BHPhotoAlbumLayout.m; sourceTree = ""; }; 61 | 0DD9B1D41645BBFA008EE1C5 /* BHAlbumPhotoCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BHAlbumPhotoCell.h; sourceTree = ""; }; 62 | 0DD9B1D51645BBFA008EE1C5 /* BHAlbumPhotoCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BHAlbumPhotoCell.m; sourceTree = ""; }; 63 | 0DD9B1DC1645BDE0008EE1C5 /* BHPhoto.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BHPhoto.h; sourceTree = ""; }; 64 | 0DD9B1DD1645BDE0008EE1C5 /* BHPhoto.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BHPhoto.m; sourceTree = ""; }; 65 | 0DD9B1DF1645BF26008EE1C5 /* BHAlbum.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BHAlbum.h; sourceTree = ""; }; 66 | 0DD9B1E01645BF26008EE1C5 /* BHAlbum.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BHAlbum.m; sourceTree = ""; }; 67 | 0DD9B20516462154008EE1C5 /* QuartzCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuartzCore.framework; path = System/Library/Frameworks/QuartzCore.framework; sourceTree = SDKROOT; }; 68 | /* End PBXFileReference section */ 69 | 70 | /* Begin PBXFrameworksBuildPhase section */ 71 | 0DD9B1A61645B7AE008EE1C5 /* Frameworks */ = { 72 | isa = PBXFrameworksBuildPhase; 73 | buildActionMask = 2147483647; 74 | files = ( 75 | 0DD9B20616462154008EE1C5 /* QuartzCore.framework in Frameworks */, 76 | 0DD9B1AE1645B7AE008EE1C5 /* UIKit.framework in Frameworks */, 77 | 0DD9B1B01645B7AE008EE1C5 /* Foundation.framework in Frameworks */, 78 | 0DD9B1B21645B7AE008EE1C5 /* CoreGraphics.framework in Frameworks */, 79 | ); 80 | runOnlyForDeploymentPostprocessing = 0; 81 | }; 82 | /* End PBXFrameworksBuildPhase section */ 83 | 84 | /* Begin PBXGroup section */ 85 | 0DD9B19E1645B7AE008EE1C5 = { 86 | isa = PBXGroup; 87 | children = ( 88 | 0DD9B1B31645B7AE008EE1C5 /* CollectionViewTutorial */, 89 | 0DD9B1AC1645B7AE008EE1C5 /* Frameworks */, 90 | 0DD9B1AA1645B7AE008EE1C5 /* Products */, 91 | ); 92 | sourceTree = ""; 93 | }; 94 | 0DD9B1AA1645B7AE008EE1C5 /* Products */ = { 95 | isa = PBXGroup; 96 | children = ( 97 | 0DD9B1A91645B7AE008EE1C5 /* CollectionViewTutorial.app */, 98 | ); 99 | name = Products; 100 | sourceTree = ""; 101 | }; 102 | 0DD9B1AC1645B7AE008EE1C5 /* Frameworks */ = { 103 | isa = PBXGroup; 104 | children = ( 105 | 0DD9B20516462154008EE1C5 /* QuartzCore.framework */, 106 | 0DD9B1AD1645B7AE008EE1C5 /* UIKit.framework */, 107 | 0DD9B1AF1645B7AE008EE1C5 /* Foundation.framework */, 108 | 0DD9B1B11645B7AE008EE1C5 /* CoreGraphics.framework */, 109 | ); 110 | name = Frameworks; 111 | sourceTree = ""; 112 | }; 113 | 0DD9B1B31645B7AE008EE1C5 /* CollectionViewTutorial */ = { 114 | isa = PBXGroup; 115 | children = ( 116 | 0DD9B1BC1645B7AE008EE1C5 /* BHAppDelegate.h */, 117 | 0DD9B1BD1645B7AE008EE1C5 /* BHAppDelegate.m */, 118 | 0DD9B1DB1645BDCA008EE1C5 /* Model */, 119 | 0DD9B1D01645B8EA008EE1C5 /* View */, 120 | 0DD9B1B41645B7AE008EE1C5 /* Supporting Files */, 121 | ); 122 | path = CollectionViewTutorial; 123 | sourceTree = ""; 124 | }; 125 | 0DD9B1B41645B7AE008EE1C5 /* Supporting Files */ = { 126 | isa = PBXGroup; 127 | children = ( 128 | 0DD9B1E41645C16C008EE1C5 /* Images */, 129 | 0DD9B1B51645B7AE008EE1C5 /* CollectionViewTutorial-Info.plist */, 130 | 0DD9B1B61645B7AE008EE1C5 /* InfoPlist.strings */, 131 | 0DD9B1B91645B7AE008EE1C5 /* main.m */, 132 | 0DD9B1BB1645B7AE008EE1C5 /* CollectionViewTutorial-Prefix.pch */, 133 | 0DD9B1BF1645B7AE008EE1C5 /* Default.png */, 134 | 0DD9B1C11645B7AE008EE1C5 /* Default@2x.png */, 135 | 0DD9B1C31645B7AE008EE1C5 /* Default-568h@2x.png */, 136 | ); 137 | name = "Supporting Files"; 138 | sourceTree = ""; 139 | }; 140 | 0DD9B1D01645B8EA008EE1C5 /* View */ = { 141 | isa = PBXGroup; 142 | children = ( 143 | 0DD9B1C51645B7AE008EE1C5 /* BHCollectionViewController.h */, 144 | 0DD9B1C61645B7AE008EE1C5 /* BHCollectionViewController.m */, 145 | 0DD9B1C81645B7AE008EE1C5 /* BHCollectionViewController.xib */, 146 | 0DD9B1D11645B97F008EE1C5 /* BHPhotoAlbumLayout.h */, 147 | 0DD9B1D21645B97F008EE1C5 /* BHPhotoAlbumLayout.m */, 148 | 0DD9B1D41645BBFA008EE1C5 /* BHAlbumPhotoCell.h */, 149 | 0DD9B1D51645BBFA008EE1C5 /* BHAlbumPhotoCell.m */, 150 | 0D6599C01646446300A7C770 /* BHAlbumTitleReusableView.h */, 151 | 0D6599C11646446300A7C770 /* BHAlbumTitleReusableView.m */, 152 | 0D27363B164908EB00180485 /* BHEmblemView.h */, 153 | 0D27363C164908EB00180485 /* BHEmblemView.m */, 154 | ); 155 | name = View; 156 | sourceTree = ""; 157 | }; 158 | 0DD9B1DB1645BDCA008EE1C5 /* Model */ = { 159 | isa = PBXGroup; 160 | children = ( 161 | 0DD9B1DC1645BDE0008EE1C5 /* BHPhoto.h */, 162 | 0DD9B1DD1645BDE0008EE1C5 /* BHPhoto.m */, 163 | 0DD9B1DF1645BF26008EE1C5 /* BHAlbum.h */, 164 | 0DD9B1E01645BF26008EE1C5 /* BHAlbum.m */, 165 | ); 166 | name = Model; 167 | sourceTree = ""; 168 | }; 169 | 0DD9B1E41645C16C008EE1C5 /* Images */ = { 170 | isa = PBXGroup; 171 | children = ( 172 | 0D27363E16490E6F00180485 /* emblem.png */, 173 | 0D27363F16490E6F00180485 /* emblem@2x.png */, 174 | 0D4F141516466A4D0015AD5E /* concrete_wall.png */, 175 | 0D4F141616466A4D0015AD5E /* concrete_wall@2X.png */, 176 | ); 177 | name = Images; 178 | sourceTree = ""; 179 | }; 180 | /* End PBXGroup section */ 181 | 182 | /* Begin PBXNativeTarget section */ 183 | 0DD9B1A81645B7AE008EE1C5 /* CollectionViewTutorial */ = { 184 | isa = PBXNativeTarget; 185 | buildConfigurationList = 0DD9B1CD1645B7AE008EE1C5 /* Build configuration list for PBXNativeTarget "CollectionViewTutorial" */; 186 | buildPhases = ( 187 | 0DD9B1A51645B7AE008EE1C5 /* Sources */, 188 | 0DD9B1A61645B7AE008EE1C5 /* Frameworks */, 189 | 0DD9B1A71645B7AE008EE1C5 /* Resources */, 190 | ); 191 | buildRules = ( 192 | ); 193 | dependencies = ( 194 | ); 195 | name = CollectionViewTutorial; 196 | productName = CollectionViewTutorial; 197 | productReference = 0DD9B1A91645B7AE008EE1C5 /* CollectionViewTutorial.app */; 198 | productType = "com.apple.product-type.application"; 199 | }; 200 | /* End PBXNativeTarget section */ 201 | 202 | /* Begin PBXProject section */ 203 | 0DD9B1A01645B7AE008EE1C5 /* Project object */ = { 204 | isa = PBXProject; 205 | attributes = { 206 | CLASSPREFIX = BH; 207 | LastUpgradeCheck = 0450; 208 | ORGANIZATIONNAME = "Bryan Hansen"; 209 | }; 210 | buildConfigurationList = 0DD9B1A31645B7AE008EE1C5 /* Build configuration list for PBXProject "CollectionViewTutorial" */; 211 | compatibilityVersion = "Xcode 3.2"; 212 | developmentRegion = English; 213 | hasScannedForEncodings = 0; 214 | knownRegions = ( 215 | en, 216 | ); 217 | mainGroup = 0DD9B19E1645B7AE008EE1C5; 218 | productRefGroup = 0DD9B1AA1645B7AE008EE1C5 /* Products */; 219 | projectDirPath = ""; 220 | projectRoot = ""; 221 | targets = ( 222 | 0DD9B1A81645B7AE008EE1C5 /* CollectionViewTutorial */, 223 | ); 224 | }; 225 | /* End PBXProject section */ 226 | 227 | /* Begin PBXResourcesBuildPhase section */ 228 | 0DD9B1A71645B7AE008EE1C5 /* Resources */ = { 229 | isa = PBXResourcesBuildPhase; 230 | buildActionMask = 2147483647; 231 | files = ( 232 | 0DD9B1B81645B7AE008EE1C5 /* InfoPlist.strings in Resources */, 233 | 0DD9B1C01645B7AE008EE1C5 /* Default.png in Resources */, 234 | 0DD9B1C21645B7AE008EE1C5 /* Default@2x.png in Resources */, 235 | 0DD9B1C41645B7AE008EE1C5 /* Default-568h@2x.png in Resources */, 236 | 0DD9B1CA1645B7AE008EE1C5 /* BHCollectionViewController.xib in Resources */, 237 | 0D4F141716466A4D0015AD5E /* concrete_wall.png in Resources */, 238 | 0D4F141816466A4D0015AD5E /* concrete_wall@2X.png in Resources */, 239 | 0D27364016490E6F00180485 /* emblem.png in Resources */, 240 | 0D27364116490E6F00180485 /* emblem@2x.png in Resources */, 241 | ); 242 | runOnlyForDeploymentPostprocessing = 0; 243 | }; 244 | /* End PBXResourcesBuildPhase section */ 245 | 246 | /* Begin PBXSourcesBuildPhase section */ 247 | 0DD9B1A51645B7AE008EE1C5 /* Sources */ = { 248 | isa = PBXSourcesBuildPhase; 249 | buildActionMask = 2147483647; 250 | files = ( 251 | 0DD9B1BA1645B7AE008EE1C5 /* main.m in Sources */, 252 | 0DD9B1BE1645B7AE008EE1C5 /* BHAppDelegate.m in Sources */, 253 | 0DD9B1C71645B7AE008EE1C5 /* BHCollectionViewController.m in Sources */, 254 | 0DD9B1D31645B97F008EE1C5 /* BHPhotoAlbumLayout.m in Sources */, 255 | 0DD9B1D61645BBFA008EE1C5 /* BHAlbumPhotoCell.m in Sources */, 256 | 0DD9B1DE1645BDE0008EE1C5 /* BHPhoto.m in Sources */, 257 | 0DD9B1E11645BF26008EE1C5 /* BHAlbum.m in Sources */, 258 | 0D6599C21646446300A7C770 /* BHAlbumTitleReusableView.m in Sources */, 259 | 0D27363D164908EB00180485 /* BHEmblemView.m in Sources */, 260 | ); 261 | runOnlyForDeploymentPostprocessing = 0; 262 | }; 263 | /* End PBXSourcesBuildPhase section */ 264 | 265 | /* Begin PBXVariantGroup section */ 266 | 0DD9B1B61645B7AE008EE1C5 /* InfoPlist.strings */ = { 267 | isa = PBXVariantGroup; 268 | children = ( 269 | 0DD9B1B71645B7AE008EE1C5 /* en */, 270 | ); 271 | name = InfoPlist.strings; 272 | sourceTree = ""; 273 | }; 274 | 0DD9B1C81645B7AE008EE1C5 /* BHCollectionViewController.xib */ = { 275 | isa = PBXVariantGroup; 276 | children = ( 277 | 0DD9B1C91645B7AE008EE1C5 /* en */, 278 | ); 279 | name = BHCollectionViewController.xib; 280 | sourceTree = ""; 281 | }; 282 | /* End PBXVariantGroup section */ 283 | 284 | /* Begin XCBuildConfiguration section */ 285 | 0DD9B1CB1645B7AE008EE1C5 /* Debug */ = { 286 | isa = XCBuildConfiguration; 287 | buildSettings = { 288 | ALWAYS_SEARCH_USER_PATHS = NO; 289 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 290 | CLANG_CXX_LIBRARY = "libc++"; 291 | CLANG_ENABLE_OBJC_ARC = YES; 292 | CLANG_WARN_EMPTY_BODY = YES; 293 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 294 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 295 | COPY_PHASE_STRIP = NO; 296 | GCC_C_LANGUAGE_STANDARD = gnu99; 297 | GCC_DYNAMIC_NO_PIC = NO; 298 | GCC_OPTIMIZATION_LEVEL = 0; 299 | GCC_PREPROCESSOR_DEFINITIONS = ( 300 | "DEBUG=1", 301 | "$(inherited)", 302 | ); 303 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 304 | GCC_WARN_ABOUT_RETURN_TYPE = YES; 305 | GCC_WARN_UNINITIALIZED_AUTOS = YES; 306 | GCC_WARN_UNUSED_VARIABLE = YES; 307 | IPHONEOS_DEPLOYMENT_TARGET = 6.0; 308 | SDKROOT = iphoneos; 309 | }; 310 | name = Debug; 311 | }; 312 | 0DD9B1CC1645B7AE008EE1C5 /* Release */ = { 313 | isa = XCBuildConfiguration; 314 | buildSettings = { 315 | ALWAYS_SEARCH_USER_PATHS = NO; 316 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 317 | CLANG_CXX_LIBRARY = "libc++"; 318 | CLANG_ENABLE_OBJC_ARC = YES; 319 | CLANG_WARN_EMPTY_BODY = YES; 320 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 321 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 322 | COPY_PHASE_STRIP = YES; 323 | GCC_C_LANGUAGE_STANDARD = gnu99; 324 | GCC_WARN_ABOUT_RETURN_TYPE = YES; 325 | GCC_WARN_UNINITIALIZED_AUTOS = YES; 326 | GCC_WARN_UNUSED_VARIABLE = YES; 327 | IPHONEOS_DEPLOYMENT_TARGET = 6.0; 328 | OTHER_CFLAGS = "-DNS_BLOCK_ASSERTIONS=1"; 329 | SDKROOT = iphoneos; 330 | VALIDATE_PRODUCT = YES; 331 | }; 332 | name = Release; 333 | }; 334 | 0DD9B1CE1645B7AE008EE1C5 /* Debug */ = { 335 | isa = XCBuildConfiguration; 336 | buildSettings = { 337 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 338 | GCC_PREFIX_HEADER = "CollectionViewTutorial/CollectionViewTutorial-Prefix.pch"; 339 | INFOPLIST_FILE = "CollectionViewTutorial/CollectionViewTutorial-Info.plist"; 340 | PRODUCT_NAME = "$(TARGET_NAME)"; 341 | WRAPPER_EXTENSION = app; 342 | }; 343 | name = Debug; 344 | }; 345 | 0DD9B1CF1645B7AE008EE1C5 /* Release */ = { 346 | isa = XCBuildConfiguration; 347 | buildSettings = { 348 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 349 | GCC_PREFIX_HEADER = "CollectionViewTutorial/CollectionViewTutorial-Prefix.pch"; 350 | INFOPLIST_FILE = "CollectionViewTutorial/CollectionViewTutorial-Info.plist"; 351 | PRODUCT_NAME = "$(TARGET_NAME)"; 352 | WRAPPER_EXTENSION = app; 353 | }; 354 | name = Release; 355 | }; 356 | /* End XCBuildConfiguration section */ 357 | 358 | /* Begin XCConfigurationList section */ 359 | 0DD9B1A31645B7AE008EE1C5 /* Build configuration list for PBXProject "CollectionViewTutorial" */ = { 360 | isa = XCConfigurationList; 361 | buildConfigurations = ( 362 | 0DD9B1CB1645B7AE008EE1C5 /* Debug */, 363 | 0DD9B1CC1645B7AE008EE1C5 /* Release */, 364 | ); 365 | defaultConfigurationIsVisible = 0; 366 | defaultConfigurationName = Release; 367 | }; 368 | 0DD9B1CD1645B7AE008EE1C5 /* Build configuration list for PBXNativeTarget "CollectionViewTutorial" */ = { 369 | isa = XCConfigurationList; 370 | buildConfigurations = ( 371 | 0DD9B1CE1645B7AE008EE1C5 /* Debug */, 372 | 0DD9B1CF1645B7AE008EE1C5 /* Release */, 373 | ); 374 | defaultConfigurationIsVisible = 0; 375 | defaultConfigurationName = Release; 376 | }; 377 | /* End XCConfigurationList section */ 378 | }; 379 | rootObject = 0DD9B1A01645B7AE008EE1C5 /* Project object */; 380 | } 381 | -------------------------------------------------------------------------------- /CollectionViewTutorial.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /CollectionViewTutorial/BHAlbum.h: -------------------------------------------------------------------------------- 1 | // 2 | // BHAlbum.h 3 | // CollectionViewTutorial 4 | // 5 | // Created by Bryan Hansen on 11/3/12. 6 | // Copyright (c) 2012 Bryan Hansen. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @class BHPhoto; 12 | 13 | @interface BHAlbum : NSObject 14 | 15 | @property (nonatomic, strong) NSString *name; 16 | @property (nonatomic, strong, readonly) NSArray *photos; 17 | 18 | - (void)addPhoto:(BHPhoto *)photo; 19 | - (BOOL)removePhoto:(BHPhoto *)photo; 20 | 21 | @end 22 | -------------------------------------------------------------------------------- /CollectionViewTutorial/BHAlbum.m: -------------------------------------------------------------------------------- 1 | // 2 | // BHAlbum.m 3 | // CollectionViewTutorial 4 | // 5 | // Created by Bryan Hansen on 11/3/12. 6 | // Copyright (c) 2012 Bryan Hansen. All rights reserved. 7 | // 8 | 9 | #import "BHAlbum.h" 10 | #import "BHPhoto.h" 11 | 12 | @interface BHAlbum () 13 | 14 | @property (nonatomic, strong) NSMutableArray *mutablePhotos; 15 | 16 | @end 17 | 18 | @implementation BHAlbum 19 | 20 | #pragma mark - Properties 21 | 22 | - (NSArray *)photos 23 | { 24 | return [self.mutablePhotos copy]; 25 | } 26 | 27 | 28 | #pragma mark - Lifecycle 29 | 30 | - (id)init 31 | { 32 | self = [super init]; 33 | if (self) { 34 | self.mutablePhotos = [NSMutableArray array]; 35 | } 36 | return self; 37 | } 38 | 39 | #pragma mark - Photos 40 | 41 | - (void)addPhoto:(BHPhoto *)photo 42 | { 43 | [self.mutablePhotos addObject:photo]; 44 | } 45 | 46 | - (BOOL)removePhoto:(BHPhoto *)photo 47 | { 48 | if ([self.mutablePhotos indexOfObject:photo] == NSNotFound) { 49 | return NO; 50 | } 51 | 52 | [self.mutablePhotos removeObject:photo]; 53 | 54 | return YES; 55 | } 56 | 57 | 58 | 59 | @end 60 | -------------------------------------------------------------------------------- /CollectionViewTutorial/BHAlbumPhotoCell.h: -------------------------------------------------------------------------------- 1 | // 2 | // BHAlbumPhotoCell.h 3 | // CollectionViewTutorial 4 | // 5 | // Created by Bryan Hansen on 11/3/12. 6 | // Copyright (c) 2012 Bryan Hansen. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface BHAlbumPhotoCell : UICollectionViewCell 12 | 13 | @property (nonatomic, strong, readonly) UIImageView *imageView; 14 | 15 | @end 16 | -------------------------------------------------------------------------------- /CollectionViewTutorial/BHAlbumPhotoCell.m: -------------------------------------------------------------------------------- 1 | // 2 | // BHAlbumPhotoCell.m 3 | // CollectionViewTutorial 4 | // 5 | // Created by Bryan Hansen on 11/3/12. 6 | // Copyright (c) 2012 Bryan Hansen. All rights reserved. 7 | // 8 | 9 | #import "BHAlbumPhotoCell.h" 10 | #import 11 | 12 | @interface BHAlbumPhotoCell () 13 | 14 | @property (nonatomic, strong, readwrite) UIImageView *imageView; 15 | 16 | @end 17 | 18 | @implementation BHAlbumPhotoCell 19 | 20 | #pragma mark - Lifecycle 21 | 22 | - (id)initWithFrame:(CGRect)frame 23 | { 24 | self = [super initWithFrame:frame]; 25 | if (self) { 26 | self.backgroundColor = [UIColor colorWithWhite:0.85f alpha:1.0f]; 27 | 28 | self.layer.borderColor = [UIColor whiteColor].CGColor; 29 | self.layer.borderWidth = 3.0f; 30 | self.layer.shadowColor = [UIColor blackColor].CGColor; 31 | self.layer.shadowRadius = 3.0f; 32 | self.layer.shadowOffset = CGSizeMake(0.0f, 2.0f); 33 | self.layer.shadowOpacity = 0.5f; 34 | // make sure we rasterize nicely for retina 35 | self.layer.rasterizationScale = [UIScreen mainScreen].scale; 36 | self.layer.shouldRasterize = YES; 37 | 38 | self.imageView = [[UIImageView alloc] initWithFrame:self.bounds]; 39 | self.imageView.contentMode = UIViewContentModeScaleAspectFill; 40 | self.imageView.clipsToBounds = YES; 41 | 42 | [self.contentView addSubview:self.imageView]; 43 | } 44 | 45 | return self; 46 | } 47 | 48 | - (void)prepareForReuse 49 | { 50 | [super prepareForReuse]; 51 | 52 | self.imageView.image = nil; 53 | } 54 | 55 | @end 56 | -------------------------------------------------------------------------------- /CollectionViewTutorial/BHAlbumTitleReusableView.h: -------------------------------------------------------------------------------- 1 | // 2 | // BHAlbumTitleReusableView.h 3 | // CollectionViewTutorial 4 | // 5 | // Created by Bryan Hansen on 11/3/12. 6 | // Copyright (c) 2012 Bryan Hansen. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface BHAlbumTitleReusableView : UICollectionReusableView 12 | 13 | @property (nonatomic, strong, readonly) UILabel *titleLabel; 14 | 15 | @end 16 | -------------------------------------------------------------------------------- /CollectionViewTutorial/BHAlbumTitleReusableView.m: -------------------------------------------------------------------------------- 1 | // 2 | // BHAlbumTitleReusableView.m 3 | // CollectionViewTutorial 4 | // 5 | // Created by Bryan Hansen on 11/3/12. 6 | // Copyright (c) 2012 Bryan Hansen. All rights reserved. 7 | // 8 | 9 | #import "BHAlbumTitleReusableView.h" 10 | 11 | @interface BHAlbumTitleReusableView () 12 | 13 | @property (nonatomic, strong, readwrite) UILabel *titleLabel; 14 | 15 | @end 16 | 17 | @implementation BHAlbumTitleReusableView 18 | 19 | - (id)initWithFrame:(CGRect)frame 20 | { 21 | self = [super initWithFrame:frame]; 22 | if (self) { 23 | self.titleLabel = [[UILabel alloc] initWithFrame:self.bounds]; 24 | self.titleLabel.autoresizingMask = UIViewAutoresizingFlexibleWidth | 25 | UIViewAutoresizingFlexibleHeight; 26 | self.titleLabel.backgroundColor = [UIColor clearColor]; 27 | self.titleLabel.textAlignment = NSTextAlignmentCenter; 28 | self.titleLabel.font = [UIFont boldSystemFontOfSize:13.0f]; 29 | self.titleLabel.textColor = [UIColor colorWithWhite:1.0f alpha:1.0f]; 30 | self.titleLabel.shadowColor = [UIColor colorWithWhite:0.0f alpha:0.3f]; 31 | self.titleLabel.shadowOffset = CGSizeMake(0.0f, 1.0f); 32 | 33 | [self addSubview:self.titleLabel]; 34 | } 35 | return self; 36 | } 37 | 38 | - (void)prepareForReuse 39 | { 40 | [super prepareForReuse]; 41 | 42 | self.titleLabel.text = nil; 43 | } 44 | 45 | @end 46 | -------------------------------------------------------------------------------- /CollectionViewTutorial/BHAppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // BHAppDelegate.h 3 | // CollectionViewTutorial 4 | // 5 | // Created by Bryan Hansen on 11/3/12. 6 | // Copyright (c) 2012 Bryan Hansen. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @class BHCollectionViewController; 12 | 13 | @interface BHAppDelegate : UIResponder 14 | 15 | @property (strong, nonatomic) UIWindow *window; 16 | 17 | @property (strong, nonatomic) BHCollectionViewController *viewController; 18 | 19 | @end 20 | -------------------------------------------------------------------------------- /CollectionViewTutorial/BHAppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // BHAppDelegate.m 3 | // CollectionViewTutorial 4 | // 5 | // Created by Bryan Hansen on 11/3/12. 6 | // Copyright (c) 2012 Bryan Hansen. All rights reserved. 7 | // 8 | 9 | #import "BHAppDelegate.h" 10 | 11 | #import "BHCollectionViewController.h" 12 | 13 | @implementation BHAppDelegate 14 | 15 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions 16 | { 17 | self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; 18 | // Override point for customization after application launch. 19 | self.viewController = [[BHCollectionViewController alloc] initWithNibName:@"BHCollectionViewController" bundle:nil]; 20 | self.window.rootViewController = self.viewController; 21 | [self.window makeKeyAndVisible]; 22 | return YES; 23 | } 24 | 25 | - (void)applicationWillResignActive:(UIApplication *)application 26 | { 27 | // 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. 28 | // 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. 29 | } 30 | 31 | - (void)applicationDidEnterBackground:(UIApplication *)application 32 | { 33 | // 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. 34 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 35 | } 36 | 37 | - (void)applicationWillEnterForeground:(UIApplication *)application 38 | { 39 | // 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. 40 | } 41 | 42 | - (void)applicationDidBecomeActive:(UIApplication *)application 43 | { 44 | // 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. 45 | } 46 | 47 | - (void)applicationWillTerminate:(UIApplication *)application 48 | { 49 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 50 | } 51 | 52 | @end 53 | -------------------------------------------------------------------------------- /CollectionViewTutorial/BHCollectionViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // BHCollectionViewController.h 3 | // CollectionViewTutorial 4 | // 5 | // Created by Bryan Hansen on 11/3/12. 6 | // Copyright (c) 2012 Bryan Hansen. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface BHCollectionViewController : UICollectionViewController 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /CollectionViewTutorial/BHCollectionViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // BHCollectionViewController.m 3 | // CollectionViewTutorial 4 | // 5 | // Created by Bryan Hansen on 11/3/12. 6 | // Copyright (c) 2012 Bryan Hansen. All rights reserved. 7 | // 8 | 9 | #import "BHCollectionViewController.h" 10 | #import "BHPhotoAlbumLayout.h" 11 | #import "BHAlbumPhotoCell.h" 12 | #import "BHAlbum.h" 13 | #import "BHPhoto.h" 14 | #import "BHAlbumTitleReusableView.h" 15 | 16 | static NSString * const PhotoCellIdentifier = @"PhotoCell"; 17 | static NSString * const AlbumTitleIdentifier = @"AlbumTitle"; 18 | 19 | @interface BHCollectionViewController () 20 | 21 | @property (nonatomic, strong) NSMutableArray *albums; 22 | @property (nonatomic, weak) IBOutlet BHPhotoAlbumLayout *photoAlbumLayout; 23 | @property (nonatomic, strong) NSOperationQueue *thumbnailQueue; 24 | 25 | @end 26 | 27 | @implementation BHCollectionViewController 28 | 29 | #pragma mark - Lifecycle 30 | 31 | - (void)viewDidLoad 32 | { 33 | [super viewDidLoad]; 34 | 35 | UIImage *patternImage = [UIImage imageNamed:@"concrete_wall"]; 36 | self.collectionView.backgroundColor = [UIColor colorWithPatternImage:patternImage]; 37 | 38 | self.albums = [NSMutableArray array]; 39 | 40 | NSURL *urlPrefix = [NSURL URLWithString:@"https://raw.github.com/ShadoFlameX/PhotoCollectionView/master/Photos/"]; 41 | 42 | NSInteger photoIndex = 0; 43 | 44 | for (NSInteger a = 0; a < 12; a++) { 45 | BHAlbum *album = [[BHAlbum alloc] init]; 46 | album.name = [NSString stringWithFormat:@"Photo Album %d",a + 1]; 47 | 48 | NSUInteger photoCount = arc4random()%4 + 2; 49 | for (NSInteger p = 0; p < photoCount; p++) { 50 | // there are up to 25 photos available to load from the code repository 51 | NSString *photoFilename = [NSString stringWithFormat:@"thumbnail%d.jpg",photoIndex % 25]; 52 | NSURL *photoURL = [urlPrefix URLByAppendingPathComponent:photoFilename]; 53 | BHPhoto *photo = [BHPhoto photoWithImageURL:photoURL]; 54 | [album addPhoto:photo]; 55 | 56 | photoIndex++; 57 | } 58 | 59 | [self.albums addObject:album]; 60 | } 61 | 62 | [self.collectionView registerClass:[BHAlbumPhotoCell class] 63 | forCellWithReuseIdentifier:PhotoCellIdentifier]; 64 | [self.collectionView registerClass:[BHAlbumTitleReusableView class] 65 | forSupplementaryViewOfKind:BHPhotoAlbumLayoutAlbumTitleKind 66 | withReuseIdentifier:AlbumTitleIdentifier]; 67 | 68 | self.thumbnailQueue = [[NSOperationQueue alloc] init]; 69 | self.thumbnailQueue.maxConcurrentOperationCount = 3; 70 | } 71 | 72 | - (void)didReceiveMemoryWarning 73 | { 74 | [super didReceiveMemoryWarning]; 75 | // Dispose of any resources that can be recreated. 76 | } 77 | 78 | 79 | #pragma mark - View Rotation 80 | 81 | - (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation 82 | duration:(NSTimeInterval)duration 83 | { 84 | if (UIInterfaceOrientationIsLandscape(toInterfaceOrientation)) { 85 | self.photoAlbumLayout.numberOfColumns = 3; 86 | 87 | // handle insets for iPhone 4 or 5 88 | CGFloat sideInset = [UIScreen mainScreen].preferredMode.size.width == 1136.0f ? 89 | 45.0f : 25.0f; 90 | 91 | self.photoAlbumLayout.itemInsets = UIEdgeInsetsMake(22.0f, sideInset, 13.0f, sideInset); 92 | 93 | } else { 94 | self.photoAlbumLayout.numberOfColumns = 2; 95 | self.photoAlbumLayout.itemInsets = UIEdgeInsetsMake(22.0f, 22.0f, 13.0f, 22.0f); 96 | } 97 | } 98 | 99 | #pragma mark - UICollectionViewDataSource 100 | 101 | - (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView 102 | { 103 | return self.albums.count; 104 | } 105 | 106 | - (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section 107 | { 108 | BHAlbum *album = self.albums[section]; 109 | 110 | return album.photos.count; 111 | } 112 | 113 | - (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView 114 | cellForItemAtIndexPath:(NSIndexPath *)indexPath 115 | { 116 | BHAlbumPhotoCell *photoCell = 117 | [collectionView dequeueReusableCellWithReuseIdentifier:PhotoCellIdentifier 118 | forIndexPath:indexPath]; 119 | 120 | BHAlbum *album = self.albums[indexPath.section]; 121 | BHPhoto *photo = album.photos[indexPath.item]; 122 | 123 | // load photo images in the background 124 | __weak BHCollectionViewController *weakSelf = self; 125 | NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{ 126 | UIImage *image = [photo image]; 127 | 128 | dispatch_async(dispatch_get_main_queue(), ^{ 129 | // then set them via the main queue if the cell is still visible. 130 | if ([weakSelf.collectionView.indexPathsForVisibleItems containsObject:indexPath]) { 131 | BHAlbumPhotoCell *cell = 132 | (BHAlbumPhotoCell *)[weakSelf.collectionView cellForItemAtIndexPath:indexPath]; 133 | cell.imageView.image = image; 134 | } 135 | }); 136 | }]; 137 | 138 | operation.queuePriority = (indexPath.item == 0) ? 139 | NSOperationQueuePriorityHigh : NSOperationQueuePriorityNormal; 140 | 141 | [self.thumbnailQueue addOperation:operation]; 142 | 143 | return photoCell; 144 | } 145 | 146 | - (UICollectionReusableView *)collectionView:(UICollectionView *)collectionView 147 | viewForSupplementaryElementOfKind:(NSString *)kind 148 | atIndexPath:(NSIndexPath *)indexPath; 149 | { 150 | BHAlbumTitleReusableView *titleView = 151 | [collectionView dequeueReusableSupplementaryViewOfKind:kind 152 | withReuseIdentifier:AlbumTitleIdentifier 153 | forIndexPath:indexPath]; 154 | 155 | BHAlbum *album = self.albums[indexPath.section]; 156 | 157 | titleView.titleLabel.text = album.name; 158 | 159 | return titleView; 160 | } 161 | 162 | @end 163 | -------------------------------------------------------------------------------- /CollectionViewTutorial/BHEmblemView.h: -------------------------------------------------------------------------------- 1 | // 2 | // BHEmblemView.h 3 | // CollectionViewTutorial 4 | // 5 | // Created by Bryan Hansen on 11/6/12. 6 | // Copyright (c) 2012 Bryan Hansen. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface BHEmblemView : UICollectionReusableView 12 | 13 | + (CGSize)defaultSize; 14 | 15 | @end 16 | -------------------------------------------------------------------------------- /CollectionViewTutorial/BHEmblemView.m: -------------------------------------------------------------------------------- 1 | // 2 | // BHEmblemView.m 3 | // CollectionViewTutorial 4 | // 5 | // Created by Bryan Hansen on 11/6/12. 6 | // Copyright (c) 2012 Bryan Hansen. All rights reserved. 7 | // 8 | 9 | #import "BHEmblemView.h" 10 | 11 | static NSString * const BHEmblemViewImageName = @"emblem"; 12 | 13 | @implementation BHEmblemView 14 | 15 | + (CGSize)defaultSize 16 | { 17 | return [UIImage imageNamed:BHEmblemViewImageName].size; 18 | } 19 | 20 | - (id)initWithFrame:(CGRect)frame 21 | { 22 | self = [super initWithFrame:frame]; 23 | if (self) { 24 | UIImage *image = [UIImage imageNamed:BHEmblemViewImageName]; 25 | UIImageView *imageView = [[UIImageView alloc] initWithImage:image]; 26 | imageView.frame = self.bounds; 27 | 28 | [self addSubview:imageView]; 29 | } 30 | return self; 31 | } 32 | 33 | @end 34 | -------------------------------------------------------------------------------- /CollectionViewTutorial/BHPhoto.h: -------------------------------------------------------------------------------- 1 | // 2 | // BHPhoto.h 3 | // CollectionViewTutorial 4 | // 5 | // Created by Bryan Hansen on 11/3/12. 6 | // Copyright (c) 2012 Bryan Hansen. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface BHPhoto : NSObject 12 | 13 | @property (nonatomic, strong, readonly) NSURL *imageURL; 14 | @property (nonatomic, strong, readonly) UIImage *image; 15 | 16 | + (BHPhoto *)photoWithImageURL:(NSURL *)imageURL; 17 | 18 | - (id)initWithImageURL:(NSURL *)imageURL; 19 | 20 | @end 21 | -------------------------------------------------------------------------------- /CollectionViewTutorial/BHPhoto.m: -------------------------------------------------------------------------------- 1 | // 2 | // BHPhoto.m 3 | // CollectionViewTutorial 4 | // 5 | // Created by Bryan Hansen on 11/3/12. 6 | // Copyright (c) 2012 Bryan Hansen. All rights reserved. 7 | // 8 | 9 | #import "BHPhoto.h" 10 | 11 | @interface BHPhoto () 12 | 13 | @property (nonatomic, strong, readwrite) NSURL *imageURL; 14 | @property (nonatomic, strong, readwrite) UIImage *image; 15 | 16 | @end 17 | 18 | @implementation BHPhoto 19 | 20 | #pragma mark - Properties 21 | 22 | - (UIImage *)image 23 | { 24 | if (!_image && self.imageURL) { 25 | NSData *imageData = [NSData dataWithContentsOfURL:self.imageURL]; 26 | UIImage *image = [UIImage imageWithData:imageData scale:[UIScreen mainScreen].scale]; 27 | 28 | _image = image; 29 | } 30 | 31 | return _image; 32 | } 33 | 34 | #pragma mark - Lifecycle 35 | 36 | + (BHPhoto *)photoWithImageURL:(NSURL *)imageURL 37 | { 38 | return [[self alloc] initWithImageURL:imageURL]; 39 | } 40 | 41 | - (id)initWithImageURL:(NSURL *)imageURL 42 | { 43 | self = [super init]; 44 | if (self) { 45 | self.imageURL = imageURL; 46 | } 47 | return self; 48 | } 49 | 50 | @end 51 | -------------------------------------------------------------------------------- /CollectionViewTutorial/BHPhotoAlbumLayout.h: -------------------------------------------------------------------------------- 1 | // 2 | // BHPhotoAlbumLayout.h 3 | // CollectionViewTutorial 4 | // 5 | // Created by Bryan Hansen on 11/3/12. 6 | // Copyright (c) 2012 Bryan Hansen. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | UIKIT_EXTERN NSString * const BHPhotoAlbumLayoutAlbumTitleKind; 12 | 13 | @interface BHPhotoAlbumLayout : UICollectionViewLayout 14 | 15 | @property (nonatomic) UIEdgeInsets itemInsets; 16 | @property (nonatomic) CGSize itemSize; 17 | @property (nonatomic) CGFloat interItemSpacingY; 18 | @property (nonatomic) NSInteger numberOfColumns; 19 | @property (nonatomic) CGFloat titleHeight; 20 | 21 | @end 22 | -------------------------------------------------------------------------------- /CollectionViewTutorial/BHPhotoAlbumLayout.m: -------------------------------------------------------------------------------- 1 | // 2 | // BHPhotoAlbumLayout.m 3 | // CollectionViewTutorial 4 | // 5 | // Created by Bryan Hansen on 11/3/12. 6 | // Copyright (c) 2012 Bryan Hansen. All rights reserved. 7 | // 8 | 9 | #import "BHPhotoAlbumLayout.h" 10 | #import "BHEmblemView.h" 11 | 12 | static NSUInteger const RotationCount = 32; 13 | static NSUInteger const RotationStride = 3; 14 | static NSUInteger const PhotoCellBaseZIndex = 100; 15 | 16 | static NSString * const BHPhotoAlbumLayoutPhotoCellKind = @"PhotoCell"; 17 | NSString * const BHPhotoAlbumLayoutAlbumTitleKind = @"AlbumTitle"; 18 | static NSString * const BHPhotoEmblemKind = @"Emblem"; 19 | 20 | @interface BHPhotoAlbumLayout () 21 | 22 | @property (nonatomic, strong) NSDictionary *layoutInfo; 23 | @property (nonatomic, strong) NSArray *rotations; 24 | 25 | - (CGRect)frameForAlbumPhotoAtIndexPath:(NSIndexPath *)indexPath; 26 | - (CGRect)frameForAlbumTitleAtIndexPath:(NSIndexPath *)indexPath; 27 | - (CGRect)frameForEmblem; 28 | - (CATransform3D)transformForAlbumPhotoAtIndex:(NSIndexPath *)indexPath; 29 | 30 | @end 31 | 32 | @implementation BHPhotoAlbumLayout 33 | 34 | #pragma mark - Properties 35 | 36 | - (void)setItemInsets:(UIEdgeInsets)itemInsets 37 | { 38 | if (UIEdgeInsetsEqualToEdgeInsets(_itemInsets, itemInsets)) return; 39 | 40 | _itemInsets = itemInsets; 41 | 42 | [self invalidateLayout]; 43 | } 44 | 45 | - (void)setItemSize:(CGSize)itemSize 46 | { 47 | if (CGSizeEqualToSize(_itemSize, itemSize)) return; 48 | 49 | _itemSize = itemSize; 50 | 51 | [self invalidateLayout]; 52 | } 53 | 54 | - (void)setInterItemSpacingY:(CGFloat)interItemSpacingY 55 | { 56 | if (_interItemSpacingY == interItemSpacingY) return; 57 | 58 | _interItemSpacingY = interItemSpacingY; 59 | 60 | [self invalidateLayout]; 61 | } 62 | 63 | - (void)setNumberOfColumns:(NSInteger)numberOfColumns 64 | { 65 | if (_numberOfColumns == numberOfColumns) return; 66 | 67 | _numberOfColumns = numberOfColumns; 68 | 69 | [self invalidateLayout]; 70 | } 71 | 72 | - (void)setTitleHeight:(CGFloat)titleHeight 73 | { 74 | if (_titleHeight == titleHeight) return; 75 | 76 | _titleHeight = titleHeight; 77 | 78 | [self invalidateLayout]; 79 | } 80 | 81 | 82 | #pragma mark - Lifecycle 83 | 84 | - (id)init 85 | { 86 | self = [super init]; 87 | if (self) { 88 | [self setup]; 89 | } 90 | 91 | return self; 92 | } 93 | 94 | - (id)initWithCoder:(NSCoder *)aDecoder 95 | { 96 | self = [super init]; 97 | if (self) { 98 | [self setup]; 99 | } 100 | 101 | return self; 102 | } 103 | 104 | - (void)setup 105 | { 106 | self.itemInsets = UIEdgeInsetsMake(22.0f, 22.0f, 13.0f, 22.0f); 107 | self.itemSize = CGSizeMake(125.0f, 125.0f); 108 | self.interItemSpacingY = 12.0f; 109 | self.numberOfColumns = 2; 110 | self.titleHeight = 26.0f; 111 | 112 | // create rotations at load so that they are consistent during prepareLayout 113 | NSMutableArray *rotations = [NSMutableArray arrayWithCapacity:RotationCount]; 114 | 115 | CGFloat percentage = 0.0f; 116 | for (NSInteger i = 0; i < RotationCount; i++) { 117 | // ensure that each angle is different enough to be seen 118 | CGFloat newPercentage = 0.0f; 119 | do { 120 | newPercentage = ((CGFloat)(arc4random() % 220) - 110) * 0.0001f; 121 | } while (fabsf(percentage - newPercentage) < 0.006); 122 | percentage = newPercentage; 123 | 124 | CGFloat angle = 2 * M_PI * (1.0f + percentage); 125 | CATransform3D transform = CATransform3DMakeRotation(angle, 0.0f, 0.0f, 1.0f); 126 | 127 | [rotations addObject:[NSValue valueWithCATransform3D:transform]]; 128 | } 129 | 130 | self.rotations = rotations; 131 | 132 | [self registerClass:[BHEmblemView class] forDecorationViewOfKind:BHPhotoEmblemKind]; 133 | } 134 | 135 | 136 | #pragma mark - Layout 137 | 138 | - (void)prepareLayout 139 | { 140 | NSMutableDictionary *newLayoutInfo = [NSMutableDictionary dictionary]; 141 | NSMutableDictionary *cellLayoutInfo = [NSMutableDictionary dictionary]; 142 | NSMutableDictionary *titleLayoutInfo = [NSMutableDictionary dictionary]; 143 | 144 | NSInteger sectionCount = [self.collectionView numberOfSections]; 145 | NSIndexPath *indexPath = [NSIndexPath indexPathForItem:0 inSection:0]; 146 | 147 | UICollectionViewLayoutAttributes *emblemAttributes = [UICollectionViewLayoutAttributes 148 | layoutAttributesForDecorationViewOfKind:BHPhotoEmblemKind withIndexPath:indexPath]; 149 | emblemAttributes.frame = [self frameForEmblem]; 150 | 151 | newLayoutInfo[BHPhotoEmblemKind] = @{indexPath: emblemAttributes}; 152 | 153 | for (NSInteger section = 0; section < sectionCount; section++) { 154 | NSInteger itemCount = [self.collectionView numberOfItemsInSection:section]; 155 | 156 | for (NSInteger item = 0; item < itemCount; item++) { 157 | indexPath = [NSIndexPath indexPathForItem:item inSection:section]; 158 | 159 | UICollectionViewLayoutAttributes *itemAttributes = 160 | [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath]; 161 | itemAttributes.frame = [self frameForAlbumPhotoAtIndexPath:indexPath]; 162 | itemAttributes.transform3D = [self transformForAlbumPhotoAtIndex:indexPath]; 163 | itemAttributes.zIndex = PhotoCellBaseZIndex + itemCount - item; 164 | 165 | cellLayoutInfo[indexPath] = itemAttributes; 166 | 167 | if (indexPath.item == 0) { 168 | UICollectionViewLayoutAttributes *titleAttributes = 169 | [UICollectionViewLayoutAttributes layoutAttributesForSupplementaryViewOfKind:BHPhotoAlbumLayoutAlbumTitleKind withIndexPath:indexPath]; 170 | titleAttributes.frame = [self frameForAlbumTitleAtIndexPath:indexPath]; 171 | 172 | titleLayoutInfo[indexPath] = titleAttributes; 173 | } 174 | } 175 | } 176 | 177 | newLayoutInfo[BHPhotoAlbumLayoutPhotoCellKind] = cellLayoutInfo; 178 | newLayoutInfo[BHPhotoAlbumLayoutAlbumTitleKind] = titleLayoutInfo; 179 | 180 | self.layoutInfo = newLayoutInfo; 181 | } 182 | 183 | - (CGSize)collectionViewContentSize 184 | { 185 | NSInteger rowCount = [self.collectionView numberOfSections] / self.numberOfColumns; 186 | // make sure we count another row if one is only partially filled 187 | if ([self.collectionView numberOfSections] % self.numberOfColumns) rowCount++; 188 | 189 | CGFloat height = self.itemInsets.top + 190 | rowCount * self.itemSize.height + (rowCount - 1) * self.interItemSpacingY + 191 | rowCount * self.titleHeight + 192 | self.itemInsets.bottom; 193 | 194 | return CGSizeMake(self.collectionView.bounds.size.width, height); 195 | } 196 | 197 | - (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect 198 | { 199 | NSMutableArray *allAttributes = [NSMutableArray arrayWithCapacity:self.layoutInfo.count]; 200 | 201 | [self.layoutInfo enumerateKeysAndObjectsUsingBlock:^(NSString *elementIdentifier, NSDictionary *elementsInfo, BOOL *stop) { 202 | [elementsInfo enumerateKeysAndObjectsUsingBlock:^(NSIndexPath *indexPath, UICollectionViewLayoutAttributes *attributes, BOOL *innerStop) { 203 | if (CGRectIntersectsRect(rect, attributes.frame)) { 204 | [allAttributes addObject:attributes]; 205 | } 206 | }]; 207 | }]; 208 | 209 | return allAttributes; 210 | } 211 | 212 | - (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath 213 | { 214 | return self.layoutInfo[BHPhotoAlbumLayoutPhotoCellKind][indexPath]; 215 | } 216 | 217 | - (UICollectionViewLayoutAttributes *)layoutAttributesForSupplementaryViewOfKind:(NSString *)kind 218 | atIndexPath:(NSIndexPath *)indexPath 219 | { 220 | return self.layoutInfo[BHPhotoAlbumLayoutAlbumTitleKind][indexPath]; 221 | } 222 | 223 | - (UICollectionViewLayoutAttributes *)layoutAttributesForDecorationViewOfKind:(NSString*)decorationViewKind atIndexPath:(NSIndexPath *)indexPath 224 | { 225 | return self.layoutInfo[BHPhotoEmblemKind][indexPath]; 226 | } 227 | 228 | 229 | #pragma mark - Private 230 | 231 | - (CGRect)frameForAlbumPhotoAtIndexPath:(NSIndexPath *)indexPath 232 | { 233 | NSInteger row = indexPath.section / self.numberOfColumns; 234 | NSInteger column = indexPath.section % self.numberOfColumns; 235 | 236 | CGFloat spacingX = self.collectionView.bounds.size.width - 237 | self.itemInsets.left - 238 | self.itemInsets.right - 239 | (self.numberOfColumns * self.itemSize.width); 240 | 241 | if (self.numberOfColumns > 1) spacingX = spacingX / (self.numberOfColumns - 1); 242 | 243 | CGFloat originX = floorf(self.itemInsets.left + (self.itemSize.width + spacingX) * column); 244 | 245 | CGFloat originY = floor(self.itemInsets.top + 246 | (self.itemSize.height + self.titleHeight + self.interItemSpacingY) * row); 247 | 248 | return CGRectMake(originX, originY, self.itemSize.width, self.itemSize.height); 249 | } 250 | 251 | - (CGRect)frameForAlbumTitleAtIndexPath:(NSIndexPath *)indexPath 252 | { 253 | CGRect frame = [self frameForAlbumPhotoAtIndexPath:indexPath]; 254 | frame.origin.y += frame.size.height; 255 | frame.size.height = self.titleHeight; 256 | 257 | return frame; 258 | } 259 | 260 | - (CGRect)frameForEmblem 261 | { 262 | CGSize size = [BHEmblemView defaultSize]; 263 | 264 | CGFloat originX = floorf((self.collectionView.bounds.size.width - size.width) * 0.5f); 265 | CGFloat originY = -size.height - 30.0f; 266 | 267 | return CGRectMake(originX, originY, size.width, size.height); 268 | } 269 | 270 | - (CATransform3D)transformForAlbumPhotoAtIndex:(NSIndexPath *)indexPath 271 | { 272 | NSInteger offset = (indexPath.section * RotationStride + indexPath.item); 273 | return [self.rotations[offset % RotationCount] CATransform3DValue]; 274 | } 275 | 276 | @end 277 | -------------------------------------------------------------------------------- /CollectionViewTutorial/CollectionViewTutorial-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleDisplayName 8 | ${PRODUCT_NAME} 9 | CFBundleExecutable 10 | ${EXECUTABLE_NAME} 11 | CFBundleIdentifier 12 | com.skeuo.${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 | UIRequiredDeviceCapabilities 28 | 29 | armv7 30 | 31 | UISupportedInterfaceOrientations 32 | 33 | UIInterfaceOrientationPortrait 34 | UIInterfaceOrientationLandscapeLeft 35 | UIInterfaceOrientationLandscapeRight 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /CollectionViewTutorial/CollectionViewTutorial-Prefix.pch: -------------------------------------------------------------------------------- 1 | // 2 | // Prefix header for all source files of the 'CollectionViewTutorial' target in the 'CollectionViewTutorial' project 3 | // 4 | 5 | #import 6 | 7 | #ifndef __IPHONE_4_0 8 | #warning "This project uses features only available in iOS SDK 4.0 and later." 9 | #endif 10 | 11 | #ifdef __OBJC__ 12 | #import 13 | #import 14 | #endif 15 | -------------------------------------------------------------------------------- /CollectionViewTutorial/Default-568h@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ShadoFlameX/PhotoCollectionView/426944fe41dfb5e26b3302b425b3e52e6d41c8d6/CollectionViewTutorial/Default-568h@2x.png -------------------------------------------------------------------------------- /CollectionViewTutorial/Default.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ShadoFlameX/PhotoCollectionView/426944fe41dfb5e26b3302b425b3e52e6d41c8d6/CollectionViewTutorial/Default.png -------------------------------------------------------------------------------- /CollectionViewTutorial/Default@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ShadoFlameX/PhotoCollectionView/426944fe41dfb5e26b3302b425b3e52e6d41c8d6/CollectionViewTutorial/Default@2x.png -------------------------------------------------------------------------------- /CollectionViewTutorial/en.lproj/BHCollectionViewController.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 1536 5 | 12C60 6 | 2844 7 | 1187.34 8 | 625.00 9 | 10 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 11 | 1930 12 | 13 | 14 | IBProxyObject 15 | IBUICollectionView 16 | IBUICollectionViewLayout 17 | 18 | 19 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 20 | 21 | 22 | PluginDependencyRecalculationVersion 23 | 24 | 25 | 26 | 27 | IBFilesOwner 28 | IBCocoaTouchFramework 29 | 30 | 31 | IBFirstResponder 32 | IBCocoaTouchFramework 33 | 34 | 35 | 36 | 1298 37 | 38 | 39 | {{0, 20}, {320, 548}} 40 | 41 | 42 | _NS:9 43 | NO 44 | YES 45 | YES 46 | 47 | 2 48 | 49 | 50 | IBUIScreenMetrics 51 | 52 | YES 53 | 54 | 55 | 56 | 57 | 58 | {320, 568} 59 | {568, 320} 60 | 61 | 62 | IBCocoaTouchFramework 63 | Retina 4 Full Screen 64 | 2 65 | 66 | IBCocoaTouchFramework 67 | 2 68 | 0.0 69 | 0.0 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | view 81 | 82 | 83 | 84 | 14 85 | 86 | 87 | 88 | photoAlbumLayout 89 | 90 | 91 | 92 | 16 93 | 94 | 95 | 96 | dataSource 97 | 98 | 99 | 100 | 11 101 | 102 | 103 | 104 | delegate 105 | 106 | 107 | 108 | 12 109 | 110 | 111 | 112 | 113 | 114 | 0 115 | 116 | 117 | 118 | 119 | 120 | -1 121 | 122 | 123 | File's Owner 124 | 125 | 126 | -2 127 | 128 | 129 | 130 | 131 | 8 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 13 140 | 141 | 142 | 143 | 144 | 145 | 146 | BHCollectionViewController 147 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 148 | UIResponder 149 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 150 | BHPhotoAlbumLayout 151 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 152 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 153 | 154 | 155 | 156 | 157 | 158 | 16 159 | 160 | 161 | 162 | 163 | BHCollectionViewController 164 | UICollectionViewController 165 | 166 | photoAlbumLayout 167 | BHPhotoAlbumLayout 168 | 169 | 170 | photoAlbumLayout 171 | 172 | photoAlbumLayout 173 | BHPhotoAlbumLayout 174 | 175 | 176 | 177 | IBProjectSource 178 | ./Classes/BHCollectionViewController.h 179 | 180 | 181 | 182 | BHPhotoAlbumLayout 183 | UICollectionViewLayout 184 | 185 | IBProjectSource 186 | ./Classes/BHPhotoAlbumLayout.h 187 | 188 | 189 | 190 | UICollectionViewController 191 | UIViewController 192 | 193 | IBProjectSource 194 | ./Classes/UICollectionViewController.h 195 | 196 | 197 | 198 | UICollectionViewLayout 199 | NSObject 200 | 201 | IBProjectSource 202 | ./Classes/UICollectionViewLayout.h 203 | 204 | 205 | 206 | 207 | 0 208 | IBCocoaTouchFramework 209 | YES 210 | 3 211 | YES 212 | 1930 213 | 214 | 215 | -------------------------------------------------------------------------------- /CollectionViewTutorial/en.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* Localized versions of Info.plist keys */ 2 | 3 | -------------------------------------------------------------------------------- /CollectionViewTutorial/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // CollectionViewTutorial 4 | // 5 | // Created by Bryan Hansen on 11/3/12. 6 | // Copyright (c) 2012 Bryan Hansen. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | #import "BHAppDelegate.h" 12 | 13 | int main(int argc, char *argv[]) 14 | { 15 | @autoreleasepool { 16 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([BHAppDelegate class])); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Images/concrete_wall.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ShadoFlameX/PhotoCollectionView/426944fe41dfb5e26b3302b425b3e52e6d41c8d6/Images/concrete_wall.png -------------------------------------------------------------------------------- /Images/concrete_wall@2X.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ShadoFlameX/PhotoCollectionView/426944fe41dfb5e26b3302b425b3e52e6d41c8d6/Images/concrete_wall@2X.png -------------------------------------------------------------------------------- /Images/emblem.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ShadoFlameX/PhotoCollectionView/426944fe41dfb5e26b3302b425b3e52e6d41c8d6/Images/emblem.png -------------------------------------------------------------------------------- /Images/emblem@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ShadoFlameX/PhotoCollectionView/426944fe41dfb5e26b3302b425b3e52e6d41c8d6/Images/emblem@2x.png -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright (c) 2014 Bryan Hansen 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 | -------------------------------------------------------------------------------- /Photos/thumbnail0.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ShadoFlameX/PhotoCollectionView/426944fe41dfb5e26b3302b425b3e52e6d41c8d6/Photos/thumbnail0.jpg -------------------------------------------------------------------------------- /Photos/thumbnail1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ShadoFlameX/PhotoCollectionView/426944fe41dfb5e26b3302b425b3e52e6d41c8d6/Photos/thumbnail1.jpg -------------------------------------------------------------------------------- /Photos/thumbnail10.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ShadoFlameX/PhotoCollectionView/426944fe41dfb5e26b3302b425b3e52e6d41c8d6/Photos/thumbnail10.jpg -------------------------------------------------------------------------------- /Photos/thumbnail11.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ShadoFlameX/PhotoCollectionView/426944fe41dfb5e26b3302b425b3e52e6d41c8d6/Photos/thumbnail11.jpg -------------------------------------------------------------------------------- /Photos/thumbnail12.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ShadoFlameX/PhotoCollectionView/426944fe41dfb5e26b3302b425b3e52e6d41c8d6/Photos/thumbnail12.jpg -------------------------------------------------------------------------------- /Photos/thumbnail13.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ShadoFlameX/PhotoCollectionView/426944fe41dfb5e26b3302b425b3e52e6d41c8d6/Photos/thumbnail13.jpg -------------------------------------------------------------------------------- /Photos/thumbnail14.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ShadoFlameX/PhotoCollectionView/426944fe41dfb5e26b3302b425b3e52e6d41c8d6/Photos/thumbnail14.jpg -------------------------------------------------------------------------------- /Photos/thumbnail15.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ShadoFlameX/PhotoCollectionView/426944fe41dfb5e26b3302b425b3e52e6d41c8d6/Photos/thumbnail15.jpg -------------------------------------------------------------------------------- /Photos/thumbnail16.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ShadoFlameX/PhotoCollectionView/426944fe41dfb5e26b3302b425b3e52e6d41c8d6/Photos/thumbnail16.jpg -------------------------------------------------------------------------------- /Photos/thumbnail17.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ShadoFlameX/PhotoCollectionView/426944fe41dfb5e26b3302b425b3e52e6d41c8d6/Photos/thumbnail17.jpg -------------------------------------------------------------------------------- /Photos/thumbnail18.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ShadoFlameX/PhotoCollectionView/426944fe41dfb5e26b3302b425b3e52e6d41c8d6/Photos/thumbnail18.jpg -------------------------------------------------------------------------------- /Photos/thumbnail19.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ShadoFlameX/PhotoCollectionView/426944fe41dfb5e26b3302b425b3e52e6d41c8d6/Photos/thumbnail19.jpg -------------------------------------------------------------------------------- /Photos/thumbnail2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ShadoFlameX/PhotoCollectionView/426944fe41dfb5e26b3302b425b3e52e6d41c8d6/Photos/thumbnail2.jpg -------------------------------------------------------------------------------- /Photos/thumbnail20.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ShadoFlameX/PhotoCollectionView/426944fe41dfb5e26b3302b425b3e52e6d41c8d6/Photos/thumbnail20.jpg -------------------------------------------------------------------------------- /Photos/thumbnail21.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ShadoFlameX/PhotoCollectionView/426944fe41dfb5e26b3302b425b3e52e6d41c8d6/Photos/thumbnail21.jpg -------------------------------------------------------------------------------- /Photos/thumbnail22.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ShadoFlameX/PhotoCollectionView/426944fe41dfb5e26b3302b425b3e52e6d41c8d6/Photos/thumbnail22.jpg -------------------------------------------------------------------------------- /Photos/thumbnail23.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ShadoFlameX/PhotoCollectionView/426944fe41dfb5e26b3302b425b3e52e6d41c8d6/Photos/thumbnail23.jpg -------------------------------------------------------------------------------- /Photos/thumbnail24.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ShadoFlameX/PhotoCollectionView/426944fe41dfb5e26b3302b425b3e52e6d41c8d6/Photos/thumbnail24.jpg -------------------------------------------------------------------------------- /Photos/thumbnail3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ShadoFlameX/PhotoCollectionView/426944fe41dfb5e26b3302b425b3e52e6d41c8d6/Photos/thumbnail3.jpg -------------------------------------------------------------------------------- /Photos/thumbnail4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ShadoFlameX/PhotoCollectionView/426944fe41dfb5e26b3302b425b3e52e6d41c8d6/Photos/thumbnail4.jpg -------------------------------------------------------------------------------- /Photos/thumbnail5.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ShadoFlameX/PhotoCollectionView/426944fe41dfb5e26b3302b425b3e52e6d41c8d6/Photos/thumbnail5.jpg -------------------------------------------------------------------------------- /Photos/thumbnail6.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ShadoFlameX/PhotoCollectionView/426944fe41dfb5e26b3302b425b3e52e6d41c8d6/Photos/thumbnail6.jpg -------------------------------------------------------------------------------- /Photos/thumbnail7.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ShadoFlameX/PhotoCollectionView/426944fe41dfb5e26b3302b425b3e52e6d41c8d6/Photos/thumbnail7.jpg -------------------------------------------------------------------------------- /Photos/thumbnail8.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ShadoFlameX/PhotoCollectionView/426944fe41dfb5e26b3302b425b3e52e6d41c8d6/Photos/thumbnail8.jpg -------------------------------------------------------------------------------- /Photos/thumbnail9.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ShadoFlameX/PhotoCollectionView/426944fe41dfb5e26b3302b425b3e52e6d41c8d6/Photos/thumbnail9.jpg -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PhotoCollectionView 2 | ## a step-by-step tutorial for UICollectionViews with custom layouts 3 | 4 | Instructions for this tutorial can be found at [http://www.skeuo.com/uicollectionview-custom-layout-tutorial](http://www.skeuo.com/uicollectionview-custom-layout-tutorial) 5 | 6 | Feedback can be sent to [bryanehansen@gmail.com](mailto:bryanehansen@gmail.com) --------------------------------------------------------------------------------