├── .gitignore ├── CircleLayoutDemo.xcodeproj └── project.pbxproj ├── CircleLayoutDemo ├── AppDelegate.h ├── AppDelegate.m ├── Cell.h ├── Cell.m ├── CircleLayout.h ├── CircleLayout.m ├── CircleLayoutDemo-Info.plist ├── CircleLayoutDemo-Prefix.pch ├── Default-568h@2x.png ├── Default.png ├── Default@2x.png ├── DraggableCircleLayout.h ├── DraggableCircleLayout.m ├── ViewController.h ├── ViewController.m ├── en.lproj │ ├── InfoPlist.strings │ ├── MainStoryboard_iPad.storyboard │ └── MainStoryboard_iPhone.storyboard └── main.m ├── DraggableCollectionView ├── Helpers │ ├── LSCollectionViewHelper.h │ ├── LSCollectionViewHelper.m │ ├── LSCollectionViewLayoutHelper.h │ └── LSCollectionViewLayoutHelper.m ├── Layout │ ├── DraggableCollectionViewFlowLayout.h │ └── DraggableCollectionViewFlowLayout.m ├── UICollectionView+Draggable.h ├── UICollectionView+Draggable.m ├── UICollectionViewDataSource_Draggable.h └── UICollectionViewLayout_Warpable.h ├── FlowLayoutDemo.xcodeproj └── project.pbxproj ├── FlowLayoutDemo ├── AppDelegate.h ├── AppDelegate.m ├── Cell.h ├── Cell.m ├── Default-568h@2x.png ├── Default.png ├── Default@2x.png ├── FlowLayoutDemo-Info.plist ├── FlowLayoutDemo-Prefix.pch ├── ViewController.h ├── ViewController.m ├── en.lproj │ ├── InfoPlist.strings │ ├── MainStoryboard_iPad.storyboard │ └── MainStoryboard_iPhone.storyboard └── main.m ├── LICENSE └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | .DS_Store 3 | build/ 4 | *.pbxuser 5 | !default.pbxuser 6 | *.mode1v3 7 | !default.mode1v3 8 | *.mode2v3 9 | !default.mode2v3 10 | *.perspectivev3 11 | !default.perspectivev3 12 | *.xcworkspace 13 | !default.xcworkspace 14 | xcuserdata 15 | profile 16 | *.moved-aside 17 | DerivedData 18 | .idea/ 19 | -------------------------------------------------------------------------------- /CircleLayoutDemo.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 8E947CA3175458980077D957 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8E947CA2175458980077D957 /* UIKit.framework */; }; 11 | 8E947CA5175458980077D957 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8E947CA4175458980077D957 /* Foundation.framework */; }; 12 | 8E947CA7175458980077D957 /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8E947CA6175458980077D957 /* CoreGraphics.framework */; }; 13 | 8E947CD717545A2C0077D957 /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8E947CD617545A2C0077D957 /* QuartzCore.framework */; }; 14 | 8E947CF717545E190077D957 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 8E947CE617545E190077D957 /* AppDelegate.m */; }; 15 | 8E947CF817545E190077D957 /* Cell.m in Sources */ = {isa = PBXBuildFile; fileRef = 8E947CE817545E190077D957 /* Cell.m */; }; 16 | 8E947CFA17545E190077D957 /* Default-568h@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 8E947CEB17545E190077D957 /* Default-568h@2x.png */; }; 17 | 8E947CFB17545E190077D957 /* Default.png in Resources */ = {isa = PBXBuildFile; fileRef = 8E947CEC17545E190077D957 /* Default.png */; }; 18 | 8E947CFC17545E190077D957 /* Default@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 8E947CED17545E190077D957 /* Default@2x.png */; }; 19 | 8E947CFD17545E190077D957 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 8E947CEE17545E190077D957 /* InfoPlist.strings */; }; 20 | 8E947CFE17545E190077D957 /* MainStoryboard_iPad.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 8E947CF017545E190077D957 /* MainStoryboard_iPad.storyboard */; }; 21 | 8E947CFF17545E190077D957 /* MainStoryboard_iPhone.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 8E947CF217545E190077D957 /* MainStoryboard_iPhone.storyboard */; }; 22 | 8E947D0017545E190077D957 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 8E947CF417545E190077D957 /* main.m */; }; 23 | 8E947D0117545E190077D957 /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 8E947CF617545E190077D957 /* ViewController.m */; }; 24 | 8E947D0F17545E740077D957 /* LSCollectionViewHelper.m in Sources */ = {isa = PBXBuildFile; fileRef = 8E947D0517545E740077D957 /* LSCollectionViewHelper.m */; }; 25 | 8E947D1017545E740077D957 /* LSCollectionViewLayoutHelper.m in Sources */ = {isa = PBXBuildFile; fileRef = 8E947D0717545E740077D957 /* LSCollectionViewLayoutHelper.m */; }; 26 | 8E947D1117545E740077D957 /* DraggableCollectionViewFlowLayout.m in Sources */ = {isa = PBXBuildFile; fileRef = 8E947D0A17545E740077D957 /* DraggableCollectionViewFlowLayout.m */; }; 27 | 8E947D1217545E740077D957 /* UICollectionView+Draggable.m in Sources */ = {isa = PBXBuildFile; fileRef = 8E947D0C17545E740077D957 /* UICollectionView+Draggable.m */; }; 28 | 8E947D1817545F870077D957 /* CircleLayout.m in Sources */ = {isa = PBXBuildFile; fileRef = 8E947D1517545F190077D957 /* CircleLayout.m */; }; 29 | 8E947D1917545F8A0077D957 /* DraggableCircleLayout.m in Sources */ = {isa = PBXBuildFile; fileRef = 8E947D1717545F190077D957 /* DraggableCircleLayout.m */; }; 30 | /* End PBXBuildFile section */ 31 | 32 | /* Begin PBXFileReference section */ 33 | 8E947C9F175458980077D957 /* CircleLayoutDemo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = CircleLayoutDemo.app; sourceTree = BUILT_PRODUCTS_DIR; }; 34 | 8E947CA2175458980077D957 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; }; 35 | 8E947CA4175458980077D957 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; 36 | 8E947CA6175458980077D957 /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; }; 37 | 8E947CD617545A2C0077D957 /* QuartzCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuartzCore.framework; path = System/Library/Frameworks/QuartzCore.framework; sourceTree = SDKROOT; }; 38 | 8E947CE517545E190077D957 /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 39 | 8E947CE617545E190077D957 /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; 40 | 8E947CE717545E190077D957 /* Cell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Cell.h; sourceTree = ""; }; 41 | 8E947CE817545E190077D957 /* Cell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Cell.m; sourceTree = ""; }; 42 | 8E947CE917545E190077D957 /* CircleLayoutDemo-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "CircleLayoutDemo-Info.plist"; sourceTree = ""; }; 43 | 8E947CEA17545E190077D957 /* CircleLayoutDemo-Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "CircleLayoutDemo-Prefix.pch"; sourceTree = ""; }; 44 | 8E947CEB17545E190077D957 /* Default-568h@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Default-568h@2x.png"; sourceTree = ""; }; 45 | 8E947CEC17545E190077D957 /* Default.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = Default.png; sourceTree = ""; }; 46 | 8E947CED17545E190077D957 /* Default@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Default@2x.png"; sourceTree = ""; }; 47 | 8E947CEF17545E190077D957 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; 48 | 8E947CF117545E190077D957 /* en */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = en; path = en.lproj/MainStoryboard_iPad.storyboard; sourceTree = ""; }; 49 | 8E947CF317545E190077D957 /* en */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = en; path = en.lproj/MainStoryboard_iPhone.storyboard; sourceTree = ""; }; 50 | 8E947CF417545E190077D957 /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 51 | 8E947CF517545E190077D957 /* ViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = ""; }; 52 | 8E947CF617545E190077D957 /* ViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = ""; }; 53 | 8E947D0417545E740077D957 /* LSCollectionViewHelper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LSCollectionViewHelper.h; sourceTree = ""; }; 54 | 8E947D0517545E740077D957 /* LSCollectionViewHelper.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LSCollectionViewHelper.m; sourceTree = ""; }; 55 | 8E947D0617545E740077D957 /* LSCollectionViewLayoutHelper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LSCollectionViewLayoutHelper.h; sourceTree = ""; }; 56 | 8E947D0717545E740077D957 /* LSCollectionViewLayoutHelper.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LSCollectionViewLayoutHelper.m; sourceTree = ""; }; 57 | 8E947D0917545E740077D957 /* DraggableCollectionViewFlowLayout.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DraggableCollectionViewFlowLayout.h; sourceTree = ""; }; 58 | 8E947D0A17545E740077D957 /* DraggableCollectionViewFlowLayout.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DraggableCollectionViewFlowLayout.m; sourceTree = ""; }; 59 | 8E947D0B17545E740077D957 /* UICollectionView+Draggable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UICollectionView+Draggable.h"; sourceTree = ""; }; 60 | 8E947D0C17545E740077D957 /* UICollectionView+Draggable.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UICollectionView+Draggable.m"; sourceTree = ""; }; 61 | 8E947D0D17545E740077D957 /* UICollectionViewDataSource_Draggable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = UICollectionViewDataSource_Draggable.h; sourceTree = ""; }; 62 | 8E947D0E17545E740077D957 /* UICollectionViewLayout_Warpable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = UICollectionViewLayout_Warpable.h; sourceTree = ""; }; 63 | 8E947D1417545F190077D957 /* CircleLayout.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CircleLayout.h; sourceTree = ""; }; 64 | 8E947D1517545F190077D957 /* CircleLayout.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CircleLayout.m; sourceTree = ""; }; 65 | 8E947D1617545F190077D957 /* DraggableCircleLayout.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = DraggableCircleLayout.h; sourceTree = ""; }; 66 | 8E947D1717545F190077D957 /* DraggableCircleLayout.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = DraggableCircleLayout.m; sourceTree = ""; }; 67 | /* End PBXFileReference section */ 68 | 69 | /* Begin PBXFrameworksBuildPhase section */ 70 | 8E947C9C175458980077D957 /* Frameworks */ = { 71 | isa = PBXFrameworksBuildPhase; 72 | buildActionMask = 2147483647; 73 | files = ( 74 | 8E947CA3175458980077D957 /* UIKit.framework in Frameworks */, 75 | 8E947CA5175458980077D957 /* Foundation.framework in Frameworks */, 76 | 8E947CA7175458980077D957 /* CoreGraphics.framework in Frameworks */, 77 | 8E947CD717545A2C0077D957 /* QuartzCore.framework in Frameworks */, 78 | ); 79 | runOnlyForDeploymentPostprocessing = 0; 80 | }; 81 | /* End PBXFrameworksBuildPhase section */ 82 | 83 | /* Begin PBXGroup section */ 84 | 8E947C96175458980077D957 = { 85 | isa = PBXGroup; 86 | children = ( 87 | 8E947CE417545E190077D957 /* CircleLayoutDemo */, 88 | 8E947CA1175458980077D957 /* Frameworks */, 89 | 8E947CA0175458980077D957 /* Products */, 90 | ); 91 | sourceTree = ""; 92 | }; 93 | 8E947CA0175458980077D957 /* Products */ = { 94 | isa = PBXGroup; 95 | children = ( 96 | 8E947C9F175458980077D957 /* CircleLayoutDemo.app */, 97 | ); 98 | name = Products; 99 | sourceTree = ""; 100 | }; 101 | 8E947CA1175458980077D957 /* Frameworks */ = { 102 | isa = PBXGroup; 103 | children = ( 104 | 8E947CA2175458980077D957 /* UIKit.framework */, 105 | 8E947CA4175458980077D957 /* Foundation.framework */, 106 | 8E947CA6175458980077D957 /* CoreGraphics.framework */, 107 | 8E947CD617545A2C0077D957 /* QuartzCore.framework */, 108 | ); 109 | name = Frameworks; 110 | sourceTree = ""; 111 | }; 112 | 8E947CE417545E190077D957 /* CircleLayoutDemo */ = { 113 | isa = PBXGroup; 114 | children = ( 115 | 8E947CE517545E190077D957 /* AppDelegate.h */, 116 | 8E947CE617545E190077D957 /* AppDelegate.m */, 117 | 8E947CF017545E190077D957 /* MainStoryboard_iPad.storyboard */, 118 | 8E947CF217545E190077D957 /* MainStoryboard_iPhone.storyboard */, 119 | 8E947CF517545E190077D957 /* ViewController.h */, 120 | 8E947CF617545E190077D957 /* ViewController.m */, 121 | 8E947CE717545E190077D957 /* Cell.h */, 122 | 8E947CE817545E190077D957 /* Cell.m */, 123 | 8E947D1617545F190077D957 /* DraggableCircleLayout.h */, 124 | 8E947D1717545F190077D957 /* DraggableCircleLayout.m */, 125 | 8E947D1417545F190077D957 /* CircleLayout.h */, 126 | 8E947D1517545F190077D957 /* CircleLayout.m */, 127 | 8E947D0217545E730077D957 /* DraggableCollectionView */, 128 | 8E947D1317545EB60077D957 /* Supporting Files */, 129 | ); 130 | path = CircleLayoutDemo; 131 | sourceTree = ""; 132 | }; 133 | 8E947D0217545E730077D957 /* DraggableCollectionView */ = { 134 | isa = PBXGroup; 135 | children = ( 136 | 8E947D0317545E740077D957 /* Helpers */, 137 | 8E947D0817545E740077D957 /* Layout */, 138 | 8E947D0B17545E740077D957 /* UICollectionView+Draggable.h */, 139 | 8E947D0C17545E740077D957 /* UICollectionView+Draggable.m */, 140 | 8E947D0D17545E740077D957 /* UICollectionViewDataSource_Draggable.h */, 141 | 8E947D0E17545E740077D957 /* UICollectionViewLayout_Warpable.h */, 142 | ); 143 | path = DraggableCollectionView; 144 | sourceTree = SOURCE_ROOT; 145 | }; 146 | 8E947D0317545E740077D957 /* Helpers */ = { 147 | isa = PBXGroup; 148 | children = ( 149 | 8E947D0417545E740077D957 /* LSCollectionViewHelper.h */, 150 | 8E947D0517545E740077D957 /* LSCollectionViewHelper.m */, 151 | 8E947D0617545E740077D957 /* LSCollectionViewLayoutHelper.h */, 152 | 8E947D0717545E740077D957 /* LSCollectionViewLayoutHelper.m */, 153 | ); 154 | path = Helpers; 155 | sourceTree = ""; 156 | }; 157 | 8E947D0817545E740077D957 /* Layout */ = { 158 | isa = PBXGroup; 159 | children = ( 160 | 8E947D0917545E740077D957 /* DraggableCollectionViewFlowLayout.h */, 161 | 8E947D0A17545E740077D957 /* DraggableCollectionViewFlowLayout.m */, 162 | ); 163 | path = Layout; 164 | sourceTree = ""; 165 | }; 166 | 8E947D1317545EB60077D957 /* Supporting Files */ = { 167 | isa = PBXGroup; 168 | children = ( 169 | 8E947CE917545E190077D957 /* CircleLayoutDemo-Info.plist */, 170 | 8E947CEE17545E190077D957 /* InfoPlist.strings */, 171 | 8E947CF417545E190077D957 /* main.m */, 172 | 8E947CEA17545E190077D957 /* CircleLayoutDemo-Prefix.pch */, 173 | 8E947CEC17545E190077D957 /* Default.png */, 174 | 8E947CED17545E190077D957 /* Default@2x.png */, 175 | 8E947CEB17545E190077D957 /* Default-568h@2x.png */, 176 | ); 177 | name = "Supporting Files"; 178 | sourceTree = ""; 179 | }; 180 | /* End PBXGroup section */ 181 | 182 | /* Begin PBXNativeTarget section */ 183 | 8E947C9E175458980077D957 /* CircleLayoutDemo */ = { 184 | isa = PBXNativeTarget; 185 | buildConfigurationList = 8E947CC5175458980077D957 /* Build configuration list for PBXNativeTarget "CircleLayoutDemo" */; 186 | buildPhases = ( 187 | 8E947C9B175458980077D957 /* Sources */, 188 | 8E947C9C175458980077D957 /* Frameworks */, 189 | 8E947C9D175458980077D957 /* Resources */, 190 | ); 191 | buildRules = ( 192 | ); 193 | dependencies = ( 194 | ); 195 | name = CircleLayoutDemo; 196 | productName = FlowLayoutDemo; 197 | productReference = 8E947C9F175458980077D957 /* CircleLayoutDemo.app */; 198 | productType = "com.apple.product-type.application"; 199 | }; 200 | /* End PBXNativeTarget section */ 201 | 202 | /* Begin PBXProject section */ 203 | 8E947C97175458980077D957 /* Project object */ = { 204 | isa = PBXProject; 205 | attributes = { 206 | LastUpgradeCheck = 0460; 207 | ORGANIZATIONNAME = "Luke Scott"; 208 | }; 209 | buildConfigurationList = 8E947C9A175458980077D957 /* Build configuration list for PBXProject "CircleLayoutDemo" */; 210 | compatibilityVersion = "Xcode 3.2"; 211 | developmentRegion = English; 212 | hasScannedForEncodings = 0; 213 | knownRegions = ( 214 | en, 215 | ); 216 | mainGroup = 8E947C96175458980077D957; 217 | productRefGroup = 8E947CA0175458980077D957 /* Products */; 218 | projectDirPath = ""; 219 | projectRoot = ""; 220 | targets = ( 221 | 8E947C9E175458980077D957 /* CircleLayoutDemo */, 222 | ); 223 | }; 224 | /* End PBXProject section */ 225 | 226 | /* Begin PBXResourcesBuildPhase section */ 227 | 8E947C9D175458980077D957 /* Resources */ = { 228 | isa = PBXResourcesBuildPhase; 229 | buildActionMask = 2147483647; 230 | files = ( 231 | 8E947CFA17545E190077D957 /* Default-568h@2x.png in Resources */, 232 | 8E947CFB17545E190077D957 /* Default.png in Resources */, 233 | 8E947CFC17545E190077D957 /* Default@2x.png in Resources */, 234 | 8E947CFD17545E190077D957 /* InfoPlist.strings in Resources */, 235 | 8E947CFE17545E190077D957 /* MainStoryboard_iPad.storyboard in Resources */, 236 | 8E947CFF17545E190077D957 /* MainStoryboard_iPhone.storyboard in Resources */, 237 | ); 238 | runOnlyForDeploymentPostprocessing = 0; 239 | }; 240 | /* End PBXResourcesBuildPhase section */ 241 | 242 | /* Begin PBXSourcesBuildPhase section */ 243 | 8E947C9B175458980077D957 /* Sources */ = { 244 | isa = PBXSourcesBuildPhase; 245 | buildActionMask = 2147483647; 246 | files = ( 247 | 8E947CF717545E190077D957 /* AppDelegate.m in Sources */, 248 | 8E947CF817545E190077D957 /* Cell.m in Sources */, 249 | 8E947D0017545E190077D957 /* main.m in Sources */, 250 | 8E947D0117545E190077D957 /* ViewController.m in Sources */, 251 | 8E947D0F17545E740077D957 /* LSCollectionViewHelper.m in Sources */, 252 | 8E947D1017545E740077D957 /* LSCollectionViewLayoutHelper.m in Sources */, 253 | 8E947D1117545E740077D957 /* DraggableCollectionViewFlowLayout.m in Sources */, 254 | 8E947D1217545E740077D957 /* UICollectionView+Draggable.m in Sources */, 255 | 8E947D1817545F870077D957 /* CircleLayout.m in Sources */, 256 | 8E947D1917545F8A0077D957 /* DraggableCircleLayout.m in Sources */, 257 | ); 258 | runOnlyForDeploymentPostprocessing = 0; 259 | }; 260 | /* End PBXSourcesBuildPhase section */ 261 | 262 | /* Begin PBXVariantGroup section */ 263 | 8E947CEE17545E190077D957 /* InfoPlist.strings */ = { 264 | isa = PBXVariantGroup; 265 | children = ( 266 | 8E947CEF17545E190077D957 /* en */, 267 | ); 268 | name = InfoPlist.strings; 269 | sourceTree = ""; 270 | }; 271 | 8E947CF017545E190077D957 /* MainStoryboard_iPad.storyboard */ = { 272 | isa = PBXVariantGroup; 273 | children = ( 274 | 8E947CF117545E190077D957 /* en */, 275 | ); 276 | name = MainStoryboard_iPad.storyboard; 277 | sourceTree = ""; 278 | }; 279 | 8E947CF217545E190077D957 /* MainStoryboard_iPhone.storyboard */ = { 280 | isa = PBXVariantGroup; 281 | children = ( 282 | 8E947CF317545E190077D957 /* en */, 283 | ); 284 | name = MainStoryboard_iPhone.storyboard; 285 | sourceTree = ""; 286 | }; 287 | /* End PBXVariantGroup section */ 288 | 289 | /* Begin XCBuildConfiguration section */ 290 | 8E947CC3175458980077D957 /* Debug */ = { 291 | isa = XCBuildConfiguration; 292 | buildSettings = { 293 | ALWAYS_SEARCH_USER_PATHS = NO; 294 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 295 | CLANG_CXX_LIBRARY = "libc++"; 296 | CLANG_ENABLE_OBJC_ARC = YES; 297 | CLANG_WARN_CONSTANT_CONVERSION = YES; 298 | CLANG_WARN_EMPTY_BODY = YES; 299 | CLANG_WARN_ENUM_CONVERSION = YES; 300 | CLANG_WARN_INT_CONVERSION = YES; 301 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 302 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 303 | COPY_PHASE_STRIP = NO; 304 | GCC_C_LANGUAGE_STANDARD = gnu99; 305 | GCC_DYNAMIC_NO_PIC = NO; 306 | GCC_OPTIMIZATION_LEVEL = 0; 307 | GCC_PREPROCESSOR_DEFINITIONS = ( 308 | "DEBUG=1", 309 | "$(inherited)", 310 | ); 311 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 312 | GCC_WARN_ABOUT_RETURN_TYPE = YES; 313 | GCC_WARN_UNINITIALIZED_AUTOS = YES; 314 | GCC_WARN_UNUSED_VARIABLE = YES; 315 | IPHONEOS_DEPLOYMENT_TARGET = 6.1; 316 | ONLY_ACTIVE_ARCH = YES; 317 | SDKROOT = iphoneos; 318 | TARGETED_DEVICE_FAMILY = "1,2"; 319 | }; 320 | name = Debug; 321 | }; 322 | 8E947CC4175458980077D957 /* Release */ = { 323 | isa = XCBuildConfiguration; 324 | buildSettings = { 325 | ALWAYS_SEARCH_USER_PATHS = NO; 326 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 327 | CLANG_CXX_LIBRARY = "libc++"; 328 | CLANG_ENABLE_OBJC_ARC = YES; 329 | CLANG_WARN_CONSTANT_CONVERSION = YES; 330 | CLANG_WARN_EMPTY_BODY = YES; 331 | CLANG_WARN_ENUM_CONVERSION = YES; 332 | CLANG_WARN_INT_CONVERSION = YES; 333 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 334 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 335 | COPY_PHASE_STRIP = YES; 336 | GCC_C_LANGUAGE_STANDARD = gnu99; 337 | GCC_WARN_ABOUT_RETURN_TYPE = YES; 338 | GCC_WARN_UNINITIALIZED_AUTOS = YES; 339 | GCC_WARN_UNUSED_VARIABLE = YES; 340 | IPHONEOS_DEPLOYMENT_TARGET = 6.1; 341 | OTHER_CFLAGS = "-DNS_BLOCK_ASSERTIONS=1"; 342 | SDKROOT = iphoneos; 343 | TARGETED_DEVICE_FAMILY = "1,2"; 344 | VALIDATE_PRODUCT = YES; 345 | }; 346 | name = Release; 347 | }; 348 | 8E947CC6175458980077D957 /* Debug */ = { 349 | isa = XCBuildConfiguration; 350 | buildSettings = { 351 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 352 | GCC_PREFIX_HEADER = "CircleLayoutDemo/CircleLayoutDemo-Prefix.pch"; 353 | INFOPLIST_FILE = "CircleLayoutDemo/CircleLayoutDemo-Info.plist"; 354 | IPHONEOS_DEPLOYMENT_TARGET = 6.0; 355 | PRODUCT_NAME = CircleLayoutDemo; 356 | WRAPPER_EXTENSION = app; 357 | }; 358 | name = Debug; 359 | }; 360 | 8E947CC7175458980077D957 /* Release */ = { 361 | isa = XCBuildConfiguration; 362 | buildSettings = { 363 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 364 | GCC_PREFIX_HEADER = "CircleLayoutDemo/CircleLayoutDemo-Prefix.pch"; 365 | INFOPLIST_FILE = "CircleLayoutDemo/CircleLayoutDemo-Info.plist"; 366 | IPHONEOS_DEPLOYMENT_TARGET = 6.0; 367 | PRODUCT_NAME = CircleLayoutDemo; 368 | WRAPPER_EXTENSION = app; 369 | }; 370 | name = Release; 371 | }; 372 | /* End XCBuildConfiguration section */ 373 | 374 | /* Begin XCConfigurationList section */ 375 | 8E947C9A175458980077D957 /* Build configuration list for PBXProject "CircleLayoutDemo" */ = { 376 | isa = XCConfigurationList; 377 | buildConfigurations = ( 378 | 8E947CC3175458980077D957 /* Debug */, 379 | 8E947CC4175458980077D957 /* Release */, 380 | ); 381 | defaultConfigurationIsVisible = 0; 382 | defaultConfigurationName = Release; 383 | }; 384 | 8E947CC5175458980077D957 /* Build configuration list for PBXNativeTarget "CircleLayoutDemo" */ = { 385 | isa = XCConfigurationList; 386 | buildConfigurations = ( 387 | 8E947CC6175458980077D957 /* Debug */, 388 | 8E947CC7175458980077D957 /* Release */, 389 | ); 390 | defaultConfigurationIsVisible = 0; 391 | defaultConfigurationName = Release; 392 | }; 393 | /* End XCConfigurationList section */ 394 | }; 395 | rootObject = 8E947C97175458980077D957 /* Project object */; 396 | } 397 | -------------------------------------------------------------------------------- /CircleLayoutDemo/AppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2013 Luke Scott 3 | // https://github.com/lukescott/DraggableCollectionView 4 | // Distributed under MIT license 5 | // 6 | 7 | #import 8 | 9 | @interface AppDelegate : UIResponder 10 | 11 | @property (strong, nonatomic) UIWindow *window; 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /CircleLayoutDemo/AppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2013 Luke Scott 3 | // https://github.com/lukescott/DraggableCollectionView 4 | // Distributed under MIT license 5 | // 6 | 7 | #import "AppDelegate.h" 8 | 9 | @implementation AppDelegate 10 | 11 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions 12 | { 13 | // Override point for customization after application launch. 14 | return YES; 15 | } 16 | 17 | @end 18 | -------------------------------------------------------------------------------- /CircleLayoutDemo/Cell.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2013 Luke Scott 3 | // https://github.com/lukescott/DraggableCollectionView 4 | // Distributed under MIT license 5 | // 6 | 7 | #import 8 | 9 | @interface Cell : UICollectionViewCell 10 | @property (weak, nonatomic) IBOutlet UILabel *label; 11 | @end 12 | -------------------------------------------------------------------------------- /CircleLayoutDemo/Cell.m: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2013 Luke Scott 3 | // https://github.com/lukescott/DraggableCollectionView 4 | // Distributed under MIT license 5 | // 6 | 7 | #import "Cell.h" 8 | 9 | @implementation Cell 10 | 11 | - (void)setHighlighted:(BOOL)highlighted 12 | { 13 | [super setHighlighted:highlighted]; 14 | if (highlighted) { 15 | self.alpha = 0.5; 16 | } 17 | else { 18 | self.alpha = 1.f; 19 | } 20 | } 21 | 22 | @end 23 | -------------------------------------------------------------------------------- /CircleLayoutDemo/CircleLayout.h: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | File: CircleLayout.h 4 | Abstract: 5 | 6 | Version: 1.0 7 | 8 | Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple 9 | Inc. ("Apple") in consideration of your agreement to the following 10 | terms, and your use, installation, modification or redistribution of 11 | this Apple software constitutes acceptance of these terms. If you do 12 | not agree with these terms, please do not use, install, modify or 13 | redistribute this Apple software. 14 | 15 | In consideration of your agreement to abide by the following terms, and 16 | subject to these terms, Apple grants you a personal, non-exclusive 17 | license, under Apple's copyrights in this original Apple software (the 18 | "Apple Software"), to use, reproduce, modify and redistribute the Apple 19 | Software, with or without modifications, in source and/or binary forms; 20 | provided that if you redistribute the Apple Software in its entirety and 21 | without modifications, you must retain this notice and the following 22 | text and disclaimers in all such redistributions of the Apple Software. 23 | Neither the name, trademarks, service marks or logos of Apple Inc. may 24 | be used to endorse or promote products derived from the Apple Software 25 | without specific prior written permission from Apple. Except as 26 | expressly stated in this notice, no other rights or licenses, express or 27 | implied, are granted by Apple herein, including but not limited to any 28 | patent rights that may be infringed by your derivative works or by other 29 | works in which the Apple Software may be incorporated. 30 | 31 | The Apple Software is provided by Apple on an "AS IS" basis. APPLE 32 | MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION 33 | THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS 34 | FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND 35 | OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS. 36 | 37 | IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL 38 | OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 39 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 40 | INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, 41 | MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED 42 | AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE), 43 | STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE 44 | POSSIBILITY OF SUCH DAMAGE. 45 | 46 | Copyright (C) 2012 Apple Inc. All Rights Reserved. 47 | 48 | 49 | WWDC 2012 License 50 | 51 | NOTE: This Apple Software was supplied by Apple as part of a WWDC 2012 52 | Session. Please refer to the applicable WWDC 2012 Session for further 53 | information. 54 | 55 | IMPORTANT: This Apple software is supplied to you by Apple 56 | Inc. ("Apple") in consideration of your agreement to the following 57 | terms, and your use, installation, modification or redistribution of 58 | this Apple software constitutes acceptance of these terms. If you do 59 | not agree with these terms, please do not use, install, modify or 60 | redistribute this Apple software. 61 | 62 | In consideration of your agreement to abide by the following terms, and 63 | subject to these terms, Apple grants you a non-exclusive license, under 64 | Apple's copyrights in this original Apple software (the "Apple 65 | Software"), to use, reproduce, modify and redistribute the Apple 66 | Software, with or without modifications, in source and/or binary forms; 67 | provided that if you redistribute the Apple Software in its entirety and 68 | without modifications, you must retain this notice and the following 69 | text and disclaimers in all such redistributions of the Apple Software. 70 | Neither the name, trademarks, service marks or logos of Apple Inc. may 71 | be used to endorse or promote products derived from the Apple Software 72 | without specific prior written permission from Apple. Except as 73 | expressly stated in this notice, no other rights or licenses, express or 74 | implied, are granted by Apple herein, including but not limited to any 75 | patent rights that may be infringed by your derivative works or by other 76 | works in which the Apple Software may be incorporated. 77 | 78 | The Apple Software is provided by Apple on an "AS IS" basis. APPLE 79 | MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION 80 | THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS 81 | FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND 82 | OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS. 83 | 84 | IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL 85 | OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 86 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 87 | INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, 88 | MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED 89 | AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE), 90 | STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE 91 | POSSIBILITY OF SUCH DAMAGE. 92 | 93 | */ 94 | 95 | #import 96 | 97 | @interface CircleLayout : UICollectionViewLayout 98 | 99 | @property (nonatomic, assign) CGPoint center; 100 | @property (nonatomic, assign) CGFloat radius; 101 | @property (nonatomic, assign) NSInteger cellCount; 102 | 103 | @end 104 | -------------------------------------------------------------------------------- /CircleLayoutDemo/CircleLayout.m: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | File: CircleLayout.m 4 | Abstract: 5 | 6 | Version: 1.0 7 | 8 | Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple 9 | Inc. ("Apple") in consideration of your agreement to the following 10 | terms, and your use, installation, modification or redistribution of 11 | this Apple software constitutes acceptance of these terms. If you do 12 | not agree with these terms, please do not use, install, modify or 13 | redistribute this Apple software. 14 | 15 | In consideration of your agreement to abide by the following terms, and 16 | subject to these terms, Apple grants you a personal, non-exclusive 17 | license, under Apple's copyrights in this original Apple software (the 18 | "Apple Software"), to use, reproduce, modify and redistribute the Apple 19 | Software, with or without modifications, in source and/or binary forms; 20 | provided that if you redistribute the Apple Software in its entirety and 21 | without modifications, you must retain this notice and the following 22 | text and disclaimers in all such redistributions of the Apple Software. 23 | Neither the name, trademarks, service marks or logos of Apple Inc. may 24 | be used to endorse or promote products derived from the Apple Software 25 | without specific prior written permission from Apple. Except as 26 | expressly stated in this notice, no other rights or licenses, express or 27 | implied, are granted by Apple herein, including but not limited to any 28 | patent rights that may be infringed by your derivative works or by other 29 | works in which the Apple Software may be incorporated. 30 | 31 | The Apple Software is provided by Apple on an "AS IS" basis. APPLE 32 | MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION 33 | THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS 34 | FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND 35 | OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS. 36 | 37 | IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL 38 | OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 39 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 40 | INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, 41 | MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED 42 | AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE), 43 | STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE 44 | POSSIBILITY OF SUCH DAMAGE. 45 | 46 | Copyright (C) 2012 Apple Inc. All Rights Reserved. 47 | 48 | 49 | WWDC 2012 License 50 | 51 | NOTE: This Apple Software was supplied by Apple as part of a WWDC 2012 52 | Session. Please refer to the applicable WWDC 2012 Session for further 53 | information. 54 | 55 | IMPORTANT: This Apple software is supplied to you by Apple 56 | Inc. ("Apple") in consideration of your agreement to the following 57 | terms, and your use, installation, modification or redistribution of 58 | this Apple software constitutes acceptance of these terms. If you do 59 | not agree with these terms, please do not use, install, modify or 60 | redistribute this Apple software. 61 | 62 | In consideration of your agreement to abide by the following terms, and 63 | subject to these terms, Apple grants you a non-exclusive license, under 64 | Apple's copyrights in this original Apple software (the "Apple 65 | Software"), to use, reproduce, modify and redistribute the Apple 66 | Software, with or without modifications, in source and/or binary forms; 67 | provided that if you redistribute the Apple Software in its entirety and 68 | without modifications, you must retain this notice and the following 69 | text and disclaimers in all such redistributions of the Apple Software. 70 | Neither the name, trademarks, service marks or logos of Apple Inc. may 71 | be used to endorse or promote products derived from the Apple Software 72 | without specific prior written permission from Apple. Except as 73 | expressly stated in this notice, no other rights or licenses, express or 74 | implied, are granted by Apple herein, including but not limited to any 75 | patent rights that may be infringed by your derivative works or by other 76 | works in which the Apple Software may be incorporated. 77 | 78 | The Apple Software is provided by Apple on an "AS IS" basis. APPLE 79 | MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION 80 | THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS 81 | FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND 82 | OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS. 83 | 84 | IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL 85 | OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 86 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 87 | INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, 88 | MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED 89 | AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE), 90 | STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE 91 | POSSIBILITY OF SUCH DAMAGE. 92 | 93 | */ 94 | 95 | #import "CircleLayout.h" 96 | 97 | #define ITEM_SIZE 70 98 | 99 | @implementation CircleLayout 100 | 101 | -(void)prepareLayout 102 | { 103 | [super prepareLayout]; 104 | 105 | CGSize size = self.collectionView.frame.size; 106 | _cellCount = [[self collectionView] numberOfItemsInSection:0]; 107 | _center = CGPointMake(size.width / 2.0, size.height / 2.0); 108 | _radius = MIN(size.width, size.height) / 2.5; 109 | } 110 | 111 | -(CGSize)collectionViewContentSize 112 | { 113 | return [self collectionView].frame.size; 114 | } 115 | 116 | - (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)path 117 | { 118 | UICollectionViewLayoutAttributes* attributes = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:path]; 119 | attributes.size = CGSizeMake(ITEM_SIZE, ITEM_SIZE); 120 | attributes.center = CGPointMake(_center.x + _radius * cosf(2 * path.item * M_PI / _cellCount), 121 | _center.y + _radius * sinf(2 * path.item * M_PI / _cellCount)); 122 | return attributes; 123 | } 124 | 125 | -(NSArray*)layoutAttributesForElementsInRect:(CGRect)rect 126 | { 127 | NSMutableArray* attributes = [NSMutableArray array]; 128 | for (NSInteger i=0 ; i < self.cellCount; i++) { 129 | NSIndexPath* indexPath = [NSIndexPath indexPathForItem:i inSection:0]; 130 | [attributes addObject:[self layoutAttributesForItemAtIndexPath:indexPath]]; 131 | } 132 | return attributes; 133 | } 134 | 135 | - (UICollectionViewLayoutAttributes *)initialLayoutAttributesForInsertedItemAtIndexPath:(NSIndexPath *)itemIndexPath 136 | { 137 | UICollectionViewLayoutAttributes* attributes = [self layoutAttributesForItemAtIndexPath:itemIndexPath]; 138 | attributes.alpha = 0.0; 139 | attributes.center = CGPointMake(_center.x, _center.y); 140 | return attributes; 141 | } 142 | 143 | - (UICollectionViewLayoutAttributes *)finalLayoutAttributesForDeletedItemAtIndexPath:(NSIndexPath *)itemIndexPath 144 | { 145 | UICollectionViewLayoutAttributes* attributes = [self layoutAttributesForItemAtIndexPath:itemIndexPath]; 146 | attributes.alpha = 0.0; 147 | attributes.center = CGPointMake(_center.x, _center.y); 148 | attributes.transform3D = CATransform3DMakeScale(0.1, 0.1, 1.0); 149 | return attributes; 150 | } 151 | 152 | @end 153 | -------------------------------------------------------------------------------- /CircleLayoutDemo/CircleLayoutDemo-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleDisplayName 8 | ${PRODUCT_NAME} 9 | CFBundleExecutable 10 | ${EXECUTABLE_NAME} 11 | CFBundleIdentifier 12 | lukescott.${PRODUCT_NAME:rfc1034identifier} 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | ${PRODUCT_NAME} 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | 1.0 25 | LSRequiresIPhoneOS 26 | 27 | UIMainStoryboardFile 28 | MainStoryboard_iPhone 29 | UIMainStoryboardFile~ipad 30 | MainStoryboard_iPad 31 | UIRequiredDeviceCapabilities 32 | 33 | armv7 34 | 35 | UISupportedInterfaceOrientations 36 | 37 | UIInterfaceOrientationPortrait 38 | UIInterfaceOrientationLandscapeLeft 39 | UIInterfaceOrientationLandscapeRight 40 | 41 | UISupportedInterfaceOrientations~ipad 42 | 43 | UIInterfaceOrientationPortrait 44 | UIInterfaceOrientationPortraitUpsideDown 45 | UIInterfaceOrientationLandscapeLeft 46 | UIInterfaceOrientationLandscapeRight 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /CircleLayoutDemo/CircleLayoutDemo-Prefix.pch: -------------------------------------------------------------------------------- 1 | // 2 | // Prefix header for all source files of the 'FlowLayoutDemo' target in the 'FlowLayoutDemo' project 3 | // 4 | 5 | #import 6 | 7 | #ifndef __IPHONE_5_0 8 | #warning "This project uses features only available in iOS SDK 5.0 and later." 9 | #endif 10 | 11 | #ifdef __OBJC__ 12 | #import 13 | #import 14 | #endif 15 | -------------------------------------------------------------------------------- /CircleLayoutDemo/Default-568h@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lukescott/DraggableCollectionView/b78539845dd290be036b3d6977ff64709a7acde9/CircleLayoutDemo/Default-568h@2x.png -------------------------------------------------------------------------------- /CircleLayoutDemo/Default.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lukescott/DraggableCollectionView/b78539845dd290be036b3d6977ff64709a7acde9/CircleLayoutDemo/Default.png -------------------------------------------------------------------------------- /CircleLayoutDemo/Default@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lukescott/DraggableCollectionView/b78539845dd290be036b3d6977ff64709a7acde9/CircleLayoutDemo/Default@2x.png -------------------------------------------------------------------------------- /CircleLayoutDemo/DraggableCircleLayout.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2013 Luke Scott 3 | // https://github.com/lukescott/DraggableCollectionView 4 | // Distributed under MIT license 5 | // 6 | 7 | #import "CircleLayout.h" 8 | #import "UICollectionViewLayout_Warpable.h" 9 | 10 | @interface DraggableCircleLayout : CircleLayout 11 | 12 | @property (readonly, nonatomic) LSCollectionViewLayoutHelper *layoutHelper; 13 | @end 14 | -------------------------------------------------------------------------------- /CircleLayoutDemo/DraggableCircleLayout.m: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2013 Luke Scott 3 | // https://github.com/lukescott/DraggableCollectionView 4 | // Distributed under MIT license 5 | // 6 | 7 | #import "DraggableCircleLayout.h" 8 | #import "LSCollectionViewLayoutHelper.h" 9 | 10 | @interface DraggableCircleLayout () 11 | { 12 | LSCollectionViewLayoutHelper *_layoutHelper; 13 | } 14 | @end 15 | 16 | @implementation DraggableCircleLayout 17 | 18 | - (LSCollectionViewLayoutHelper *)layoutHelper 19 | { 20 | if(_layoutHelper == nil) { 21 | _layoutHelper = [[LSCollectionViewLayoutHelper alloc] initWithCollectionViewLayout:self]; 22 | } 23 | return _layoutHelper; 24 | } 25 | 26 | - (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect 27 | { 28 | return [self.layoutHelper modifiedLayoutAttributesForElements:[super layoutAttributesForElementsInRect:rect]]; 29 | } 30 | 31 | @end 32 | -------------------------------------------------------------------------------- /CircleLayoutDemo/ViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2013 Luke Scott 3 | // https://github.com/lukescott/DraggableCollectionView 4 | // Distributed under MIT license 5 | // 6 | 7 | #import 8 | #import "UICollectionView+Draggable.h" 9 | 10 | @interface ViewController : UIViewController 11 | 12 | @property (weak, nonatomic) IBOutlet UICollectionView *collectionView; 13 | @end 14 | -------------------------------------------------------------------------------- /CircleLayoutDemo/ViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2013 Luke Scott 3 | // https://github.com/lukescott/DraggableCollectionView 4 | // Distributed under MIT license 5 | // 6 | 7 | #import "ViewController.h" 8 | #import "Cell.h" 9 | #import "DraggableCircleLayout.h" 10 | 11 | @interface ViewController () 12 | { 13 | NSMutableArray *data; 14 | } 15 | @end 16 | 17 | @implementation ViewController 18 | 19 | - (void)viewDidLoad 20 | { 21 | [super viewDidLoad]; 22 | 23 | data = [[NSMutableArray alloc] initWithCapacity:20]; 24 | for(int i = 0; i < 20; i++) { 25 | [data addObject:@(i)]; 26 | } 27 | 28 | self.collectionView.collectionViewLayout = [[DraggableCircleLayout alloc] init]; 29 | } 30 | 31 | - (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section 32 | { 33 | return 20; 34 | } 35 | 36 | - (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath 37 | { 38 | Cell *cell = (Cell*)[collectionView dequeueReusableCellWithReuseIdentifier:@"Cell" forIndexPath:indexPath]; 39 | NSNumber *index = [data objectAtIndex:indexPath.item]; 40 | cell.label.text = [NSString stringWithFormat:@"%d", index.integerValue]; 41 | 42 | return cell; 43 | } 44 | 45 | - (BOOL)collectionView:(LSCollectionViewHelper *)collectionView canMoveItemAtIndexPath:(NSIndexPath *)indexPath 46 | { 47 | return YES; 48 | } 49 | 50 | - (void)collectionView:(LSCollectionViewHelper *)collectionView moveItemAtIndexPath:(NSIndexPath *)fromIndexPath toIndexPath:(NSIndexPath *)toIndexPath 51 | { 52 | NSNumber *index = [data objectAtIndex:fromIndexPath.item]; 53 | [data removeObjectAtIndex:fromIndexPath.item]; 54 | [data insertObject:index atIndex:toIndexPath.item]; 55 | } 56 | 57 | @end 58 | -------------------------------------------------------------------------------- /CircleLayoutDemo/en.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* Localized versions of Info.plist keys */ 2 | 3 | -------------------------------------------------------------------------------- /CircleLayoutDemo/en.lproj/MainStoryboard_iPad.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | -------------------------------------------------------------------------------- /CircleLayoutDemo/en.lproj/MainStoryboard_iPhone.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | -------------------------------------------------------------------------------- /CircleLayoutDemo/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // FlowLayoutDemo 4 | // 5 | // Created by Luke Scott on 5/27/13. 6 | // Copyright (c) 2013 Luke Scott. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | #import "AppDelegate.h" 12 | 13 | int main(int argc, char *argv[]) 14 | { 15 | @autoreleasepool { 16 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /DraggableCollectionView/Helpers/LSCollectionViewHelper.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2013 Luke Scott 3 | // https://github.com/lukescott/DraggableCollectionView 4 | // Distributed under MIT license 5 | // 6 | 7 | #import 8 | 9 | @interface LSCollectionViewHelper : NSObject 10 | 11 | - (id)initWithCollectionView:(UICollectionView *)collectionView; 12 | 13 | @property (nonatomic, readonly) UICollectionView *collectionView; 14 | @property (nonatomic, readonly) UIGestureRecognizer *longPressGestureRecognizer; 15 | @property (nonatomic, readonly) UIGestureRecognizer *panPressGestureRecognizer; 16 | @property (nonatomic, assign) UIEdgeInsets scrollingEdgeInsets; 17 | @property (nonatomic, assign) CGFloat scrollingSpeed; 18 | @property (nonatomic, assign) BOOL enabled; 19 | @end 20 | -------------------------------------------------------------------------------- /DraggableCollectionView/Helpers/LSCollectionViewHelper.m: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2013 Luke Scott 3 | // https://github.com/lukescott/DraggableCollectionView 4 | // Distributed under MIT license 5 | // 6 | 7 | #import "LSCollectionViewHelper.h" 8 | #import "UICollectionViewLayout_Warpable.h" 9 | #import "UICollectionViewDataSource_Draggable.h" 10 | #import "LSCollectionViewLayoutHelper.h" 11 | #import 12 | 13 | static int kObservingCollectionViewLayoutContext; 14 | 15 | #ifndef CGGEOMETRY__SUPPORT_H_ 16 | CG_INLINE CGPoint 17 | _CGPointAdd(CGPoint point1, CGPoint point2) { 18 | return CGPointMake(point1.x + point2.x, point1.y + point2.y); 19 | } 20 | #endif 21 | 22 | typedef NS_ENUM(NSInteger, _ScrollingDirection) { 23 | _ScrollingDirectionUnknown = 0, 24 | _ScrollingDirectionUp, 25 | _ScrollingDirectionDown, 26 | _ScrollingDirectionLeft, 27 | _ScrollingDirectionRight 28 | }; 29 | 30 | @interface LSCollectionViewHelper () 31 | { 32 | NSIndexPath *lastIndexPath; 33 | UIImageView *mockCell; 34 | CGPoint mockCenter; 35 | CGPoint fingerTranslation; 36 | CADisplayLink *timer; 37 | _ScrollingDirection scrollingDirection; 38 | BOOL canWarp; 39 | BOOL canScroll; 40 | } 41 | @property (readonly, nonatomic) LSCollectionViewLayoutHelper *layoutHelper; 42 | @end 43 | 44 | @implementation LSCollectionViewHelper 45 | 46 | - (id)initWithCollectionView:(UICollectionView *)collectionView 47 | { 48 | self = [super init]; 49 | if (self) { 50 | _collectionView = collectionView; 51 | [_collectionView addObserver:self 52 | forKeyPath:@"collectionViewLayout" 53 | options:0 54 | context:&kObservingCollectionViewLayoutContext]; 55 | _scrollingEdgeInsets = UIEdgeInsetsMake(50.0f, 50.0f, 50.0f, 50.0f); 56 | _scrollingSpeed = 300.f; 57 | 58 | _longPressGestureRecognizer = [[UILongPressGestureRecognizer alloc] 59 | initWithTarget:self 60 | action:@selector(handleLongPressGesture:)]; 61 | [_collectionView addGestureRecognizer:_longPressGestureRecognizer]; 62 | 63 | _panPressGestureRecognizer = [[UIPanGestureRecognizer alloc] 64 | initWithTarget:self action:@selector(handlePanGesture:)]; 65 | _panPressGestureRecognizer.delegate = self; 66 | 67 | [_collectionView addGestureRecognizer:_panPressGestureRecognizer]; 68 | 69 | for (UIGestureRecognizer *gestureRecognizer in _collectionView.gestureRecognizers) { 70 | if ([gestureRecognizer isKindOfClass:[UILongPressGestureRecognizer class]]) { 71 | [gestureRecognizer requireGestureRecognizerToFail:_longPressGestureRecognizer]; 72 | break; 73 | } 74 | } 75 | 76 | [self layoutChanged]; 77 | } 78 | return self; 79 | } 80 | 81 | - (LSCollectionViewLayoutHelper *)layoutHelper 82 | { 83 | return [(id )self.collectionView.collectionViewLayout layoutHelper]; 84 | } 85 | 86 | - (void)layoutChanged 87 | { 88 | canWarp = [self.collectionView.collectionViewLayout conformsToProtocol:@protocol(UICollectionViewLayout_Warpable)]; 89 | canScroll = [self.collectionView.collectionViewLayout respondsToSelector:@selector(scrollDirection)]; 90 | _longPressGestureRecognizer.enabled = _panPressGestureRecognizer.enabled = canWarp && self.enabled; 91 | } 92 | 93 | - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context 94 | { 95 | if (context == &kObservingCollectionViewLayoutContext) { 96 | [self layoutChanged]; 97 | } 98 | else { 99 | [super observeValueForKeyPath:keyPath ofObject:object change:change context:context]; 100 | } 101 | } 102 | 103 | - (void)setEnabled:(BOOL)enabled 104 | { 105 | _enabled = enabled; 106 | _longPressGestureRecognizer.enabled = canWarp && enabled; 107 | _panPressGestureRecognizer.enabled = canWarp && enabled; 108 | } 109 | 110 | - (UIImage *)imageFromCell:(UICollectionViewCell *)cell { 111 | UIGraphicsBeginImageContextWithOptions(cell.bounds.size, cell.isOpaque, 0.0f); 112 | [cell.layer renderInContext:UIGraphicsGetCurrentContext()]; 113 | UIImage *image = UIGraphicsGetImageFromCurrentImageContext(); 114 | UIGraphicsEndImageContext(); 115 | return image; 116 | } 117 | 118 | - (void)invalidatesScrollTimer { 119 | if (timer != nil) { 120 | [timer invalidate]; 121 | timer = nil; 122 | } 123 | scrollingDirection = _ScrollingDirectionUnknown; 124 | } 125 | 126 | - (void)setupScrollTimerInDirection:(_ScrollingDirection)direction { 127 | scrollingDirection = direction; 128 | if (timer == nil) { 129 | timer = [CADisplayLink displayLinkWithTarget:self selector:@selector(handleScroll:)]; 130 | [timer addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode]; 131 | } 132 | } 133 | 134 | - (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer 135 | { 136 | if([gestureRecognizer isEqual:_panPressGestureRecognizer]) { 137 | return self.layoutHelper.fromIndexPath != nil; 138 | } 139 | return YES; 140 | } 141 | 142 | - (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer 143 | { 144 | if ([gestureRecognizer isEqual:_longPressGestureRecognizer]) { 145 | return [otherGestureRecognizer isEqual:_panPressGestureRecognizer]; 146 | } 147 | 148 | if ([gestureRecognizer isEqual:_panPressGestureRecognizer]) { 149 | return [otherGestureRecognizer isEqual:_longPressGestureRecognizer]; 150 | } 151 | 152 | return NO; 153 | } 154 | 155 | - (NSIndexPath *)indexPathForItemClosestToPoint:(CGPoint)point 156 | { 157 | NSArray *layoutAttrsInRect; 158 | NSInteger closestDist = NSIntegerMax; 159 | NSIndexPath *indexPath; 160 | NSIndexPath *toIndexPath = self.layoutHelper.toIndexPath; 161 | 162 | // We need original positions of cells 163 | self.layoutHelper.toIndexPath = nil; 164 | layoutAttrsInRect = [self.collectionView.collectionViewLayout layoutAttributesForElementsInRect:self.collectionView.bounds]; 165 | self.layoutHelper.toIndexPath = toIndexPath; 166 | 167 | // What cell are we closest to? 168 | for (UICollectionViewLayoutAttributes *layoutAttr in layoutAttrsInRect) { 169 | CGFloat xd = layoutAttr.center.x - point.x; 170 | CGFloat yd = layoutAttr.center.y - point.y; 171 | NSInteger dist = sqrtf(xd*xd + yd*yd); 172 | if (dist < closestDist) { 173 | closestDist = dist; 174 | indexPath = layoutAttr.indexPath; 175 | } 176 | } 177 | 178 | // Are we closer to being the last cell in a different section? 179 | NSInteger sections = [self.collectionView numberOfSections]; 180 | for (NSInteger i = 0; i < sections; ++i) { 181 | if (i == self.layoutHelper.fromIndexPath.section) { 182 | continue; 183 | } 184 | NSInteger items = [self.collectionView numberOfItemsInSection:i]; 185 | NSIndexPath *nextIndexPath = [NSIndexPath indexPathForItem:items inSection:i]; 186 | UICollectionViewLayoutAttributes *layoutAttr; 187 | CGFloat xd, yd; 188 | 189 | if (items > 0) { 190 | layoutAttr = [self.collectionView.collectionViewLayout layoutAttributesForItemAtIndexPath:nextIndexPath]; 191 | xd = layoutAttr.center.x - point.x; 192 | yd = layoutAttr.center.y - point.y; 193 | } else { 194 | // Trying to use layoutAttributesForItemAtIndexPath while section is empty causes EXC_ARITHMETIC (division by zero items) 195 | // So we're going to ask for the header instead. It doesn't have to exist. 196 | layoutAttr = [self.collectionView.collectionViewLayout layoutAttributesForSupplementaryViewOfKind:UICollectionElementKindSectionHeader 197 | atIndexPath:nextIndexPath]; 198 | xd = layoutAttr.frame.origin.x - point.x; 199 | yd = layoutAttr.frame.origin.y - point.y; 200 | } 201 | 202 | NSInteger dist = sqrtf(xd*xd + yd*yd); 203 | if (dist < closestDist) { 204 | closestDist = dist; 205 | indexPath = layoutAttr.indexPath; 206 | } 207 | } 208 | 209 | return indexPath; 210 | } 211 | 212 | - (void)handleLongPressGesture:(UILongPressGestureRecognizer *)sender 213 | { 214 | if (sender.state == UIGestureRecognizerStateChanged) { 215 | return; 216 | } 217 | if (![self.collectionView.dataSource conformsToProtocol:@protocol(UICollectionViewDataSource_Draggable)]) { 218 | return; 219 | } 220 | 221 | NSIndexPath *indexPath = [self indexPathForItemClosestToPoint:[sender locationInView:self.collectionView]]; 222 | 223 | switch (sender.state) { 224 | case UIGestureRecognizerStateBegan: { 225 | if (indexPath == nil) { 226 | return; 227 | } 228 | if (![(id)self.collectionView.dataSource 229 | collectionView:self.collectionView 230 | canMoveItemAtIndexPath:indexPath]) { 231 | return; 232 | } 233 | // Create mock cell to drag around 234 | UICollectionViewCell *cell = [self.collectionView cellForItemAtIndexPath:indexPath]; 235 | cell.highlighted = NO; 236 | [mockCell removeFromSuperview]; 237 | mockCell = [[UIImageView alloc] initWithFrame:cell.frame]; 238 | mockCell.image = [self imageFromCell:cell]; 239 | mockCenter = mockCell.center; 240 | [self.collectionView addSubview:mockCell]; 241 | [UIView 242 | animateWithDuration:0.3 243 | animations:^{ 244 | mockCell.transform = CGAffineTransformMakeScale(1.1f, 1.1f); 245 | } 246 | completion:nil]; 247 | 248 | // Start warping 249 | lastIndexPath = indexPath; 250 | self.layoutHelper.fromIndexPath = indexPath; 251 | self.layoutHelper.hideIndexPath = indexPath; 252 | self.layoutHelper.toIndexPath = indexPath; 253 | [self.collectionView.collectionViewLayout invalidateLayout]; 254 | } break; 255 | case UIGestureRecognizerStateEnded: 256 | case UIGestureRecognizerStateCancelled: { 257 | if(self.layoutHelper.fromIndexPath == nil) { 258 | return; 259 | } 260 | // Need these for later, but need to nil out layoutHelper's references sooner 261 | NSIndexPath *fromIndexPath = self.layoutHelper.fromIndexPath; 262 | NSIndexPath *toIndexPath = self.layoutHelper.toIndexPath; 263 | // Tell the data source to move the item 264 | id dataSource = (id)self.collectionView.dataSource; 265 | [dataSource collectionView:self.collectionView moveItemAtIndexPath:fromIndexPath toIndexPath:toIndexPath]; 266 | 267 | // Move the item 268 | [self.collectionView performBatchUpdates:^{ 269 | [self.collectionView moveItemAtIndexPath:fromIndexPath toIndexPath:toIndexPath]; 270 | self.layoutHelper.fromIndexPath = nil; 271 | self.layoutHelper.toIndexPath = nil; 272 | } completion:^(BOOL finished) { 273 | if (finished) { 274 | if ([dataSource respondsToSelector:@selector(collectionView:didMoveItemAtIndexPath:toIndexPath:)]) { 275 | [dataSource collectionView:self.collectionView didMoveItemAtIndexPath:fromIndexPath toIndexPath:toIndexPath]; 276 | } 277 | } 278 | }]; 279 | 280 | // Switch mock for cell 281 | UICollectionViewLayoutAttributes *layoutAttributes = [self.collectionView layoutAttributesForItemAtIndexPath:self.layoutHelper.hideIndexPath]; 282 | [UIView 283 | animateWithDuration:0.3 284 | animations:^{ 285 | mockCell.center = layoutAttributes.center; 286 | mockCell.transform = CGAffineTransformMakeScale(1.f, 1.f); 287 | } 288 | completion:^(BOOL finished) { 289 | [mockCell removeFromSuperview]; 290 | mockCell = nil; 291 | self.layoutHelper.hideIndexPath = nil; 292 | [self.collectionView.collectionViewLayout invalidateLayout]; 293 | }]; 294 | 295 | // Reset 296 | [self invalidatesScrollTimer]; 297 | lastIndexPath = nil; 298 | } break; 299 | default: break; 300 | } 301 | } 302 | 303 | - (void)warpToIndexPath:(NSIndexPath *)indexPath 304 | { 305 | if(indexPath == nil || [lastIndexPath isEqual:indexPath]) { 306 | return; 307 | } 308 | lastIndexPath = indexPath; 309 | 310 | if ([self.collectionView.dataSource respondsToSelector:@selector(collectionView:canMoveItemAtIndexPath:toIndexPath:)] == YES 311 | && [(id)self.collectionView.dataSource 312 | collectionView:self.collectionView 313 | canMoveItemAtIndexPath:self.layoutHelper.fromIndexPath 314 | toIndexPath:indexPath] == NO) { 315 | return; 316 | } 317 | [self.collectionView performBatchUpdates:^{ 318 | self.layoutHelper.hideIndexPath = indexPath; 319 | self.layoutHelper.toIndexPath = indexPath; 320 | } completion:nil]; 321 | } 322 | 323 | - (void)handlePanGesture:(UIPanGestureRecognizer *)sender 324 | { 325 | if(sender.state == UIGestureRecognizerStateChanged) { 326 | // Move mock to match finger 327 | fingerTranslation = [sender translationInView:self.collectionView]; 328 | mockCell.center = _CGPointAdd(mockCenter, fingerTranslation); 329 | 330 | // Scroll when necessary 331 | if (canScroll) { 332 | UICollectionViewFlowLayout *scrollLayout = (UICollectionViewFlowLayout*)self.collectionView.collectionViewLayout; 333 | if([scrollLayout scrollDirection] == UICollectionViewScrollDirectionVertical) { 334 | if (mockCell.center.y < (CGRectGetMinY(self.collectionView.bounds) + self.scrollingEdgeInsets.top)) { 335 | [self setupScrollTimerInDirection:_ScrollingDirectionUp]; 336 | } 337 | else { 338 | if (mockCell.center.y > (CGRectGetMaxY(self.collectionView.bounds) - self.scrollingEdgeInsets.bottom)) { 339 | [self setupScrollTimerInDirection:_ScrollingDirectionDown]; 340 | } 341 | else { 342 | [self invalidatesScrollTimer]; 343 | } 344 | } 345 | } 346 | else { 347 | if (mockCell.center.x < (CGRectGetMinX(self.collectionView.bounds) + self.scrollingEdgeInsets.left)) { 348 | [self setupScrollTimerInDirection:_ScrollingDirectionLeft]; 349 | } else { 350 | if (mockCell.center.x > (CGRectGetMaxX(self.collectionView.bounds) - self.scrollingEdgeInsets.right)) { 351 | [self setupScrollTimerInDirection:_ScrollingDirectionRight]; 352 | } else { 353 | [self invalidatesScrollTimer]; 354 | } 355 | } 356 | } 357 | } 358 | 359 | // Avoid warping a second time while scrolling 360 | if (scrollingDirection > _ScrollingDirectionUnknown) { 361 | return; 362 | } 363 | 364 | // Warp item to finger location 365 | CGPoint point = [sender locationInView:self.collectionView]; 366 | NSIndexPath *indexPath = [self indexPathForItemClosestToPoint:point]; 367 | [self warpToIndexPath:indexPath]; 368 | } 369 | } 370 | 371 | - (void)handleScroll:(NSTimer *)timer { 372 | if (scrollingDirection == _ScrollingDirectionUnknown) { 373 | return; 374 | } 375 | 376 | CGSize frameSize = self.collectionView.bounds.size; 377 | CGSize contentSize = self.collectionView.contentSize; 378 | CGPoint contentOffset = self.collectionView.contentOffset; 379 | CGFloat distance = self.scrollingSpeed / 60.f; 380 | CGPoint translation = CGPointZero; 381 | 382 | switch(scrollingDirection) { 383 | case _ScrollingDirectionUp: { 384 | distance = -distance; 385 | if ((contentOffset.y + distance) <= 0.f) { 386 | distance = -contentOffset.y; 387 | } 388 | translation = CGPointMake(0.f, distance); 389 | } break; 390 | case _ScrollingDirectionDown: { 391 | CGFloat maxY = MAX(contentSize.height, frameSize.height) - frameSize.height; 392 | if ((contentOffset.y + distance) >= maxY) { 393 | distance = maxY - contentOffset.y; 394 | } 395 | translation = CGPointMake(0.f, distance); 396 | } break; 397 | case _ScrollingDirectionLeft: { 398 | distance = -distance; 399 | if ((contentOffset.x + distance) <= 0.f) { 400 | distance = -contentOffset.x; 401 | } 402 | translation = CGPointMake(distance, 0.f); 403 | } break; 404 | case _ScrollingDirectionRight: { 405 | CGFloat maxX = MAX(contentSize.width, frameSize.width) - frameSize.width; 406 | if ((contentOffset.x + distance) >= maxX) { 407 | distance = maxX - contentOffset.x; 408 | } 409 | translation = CGPointMake(distance, 0.f); 410 | } break; 411 | default: break; 412 | } 413 | 414 | mockCenter = _CGPointAdd(mockCenter, translation); 415 | mockCell.center = _CGPointAdd(mockCenter, fingerTranslation); 416 | self.collectionView.contentOffset = _CGPointAdd(contentOffset, translation); 417 | 418 | // Warp items while scrolling 419 | NSIndexPath *indexPath = [self indexPathForItemClosestToPoint:mockCell.center]; 420 | [self warpToIndexPath:indexPath]; 421 | } 422 | 423 | @end 424 | -------------------------------------------------------------------------------- /DraggableCollectionView/Helpers/LSCollectionViewLayoutHelper.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2013 Luke Scott 3 | // https://github.com/lukescott/DraggableCollectionView 4 | // Distributed under MIT license 5 | // 6 | 7 | #import 8 | #import "UICollectionViewLayout_Warpable.h" 9 | 10 | @interface LSCollectionViewLayoutHelper : NSObject 11 | 12 | - (id)initWithCollectionViewLayout:(UICollectionViewLayout*)collectionViewLayout; 13 | 14 | - (NSArray *)modifiedLayoutAttributesForElements:(NSArray *)elements; 15 | 16 | @property (nonatomic, weak, readonly) UICollectionViewLayout *collectionViewLayout; 17 | @property (strong, nonatomic) NSIndexPath *fromIndexPath; 18 | @property (strong, nonatomic) NSIndexPath *toIndexPath; 19 | @property (strong, nonatomic) NSIndexPath *hideIndexPath; 20 | @end 21 | -------------------------------------------------------------------------------- /DraggableCollectionView/Helpers/LSCollectionViewLayoutHelper.m: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2013 Luke Scott 3 | // https://github.com/lukescott/DraggableCollectionView 4 | // Distributed under MIT license 5 | // 6 | 7 | #import "LSCollectionViewLayoutHelper.h" 8 | 9 | @interface LSCollectionViewLayoutHelper () 10 | 11 | @end 12 | 13 | @implementation LSCollectionViewLayoutHelper 14 | 15 | - (id)initWithCollectionViewLayout:(UICollectionViewLayout*)collectionViewLayout 16 | { 17 | self = [super init]; 18 | if (self) { 19 | _collectionViewLayout = collectionViewLayout; 20 | } 21 | return self; 22 | } 23 | 24 | - (NSArray *)modifiedLayoutAttributesForElements:(NSArray *)elements 25 | { 26 | UICollectionView *collectionView = self.collectionViewLayout.collectionView; 27 | NSIndexPath *fromIndexPath = self.fromIndexPath; 28 | NSIndexPath *toIndexPath = self.toIndexPath; 29 | NSIndexPath *hideIndexPath = self.hideIndexPath; 30 | NSIndexPath *indexPathToRemove; 31 | 32 | if (toIndexPath == nil) { 33 | if (hideIndexPath == nil) { 34 | return elements; 35 | } 36 | for (UICollectionViewLayoutAttributes *layoutAttributes in elements) { 37 | if(layoutAttributes.representedElementCategory != UICollectionElementCategoryCell) { 38 | continue; 39 | } 40 | if ([layoutAttributes.indexPath isEqual:hideIndexPath]) { 41 | layoutAttributes.hidden = YES; 42 | } 43 | } 44 | return elements; 45 | } 46 | 47 | if (fromIndexPath.section != toIndexPath.section) { 48 | indexPathToRemove = [NSIndexPath indexPathForItem:[collectionView numberOfItemsInSection:fromIndexPath.section] - 1 49 | inSection:fromIndexPath.section]; 50 | } 51 | 52 | for (UICollectionViewLayoutAttributes *layoutAttributes in elements) { 53 | if(layoutAttributes.representedElementCategory != UICollectionElementCategoryCell) { 54 | continue; 55 | } 56 | if([layoutAttributes.indexPath isEqual:indexPathToRemove]) { 57 | // Remove item in source section and insert item in target section 58 | layoutAttributes.indexPath = [NSIndexPath indexPathForItem:[collectionView numberOfItemsInSection:toIndexPath.section] 59 | inSection:toIndexPath.section]; 60 | if (layoutAttributes.indexPath.item != 0) { 61 | layoutAttributes.center = [self.collectionViewLayout layoutAttributesForItemAtIndexPath:layoutAttributes.indexPath].center; 62 | } 63 | } 64 | NSIndexPath *indexPath = layoutAttributes.indexPath; 65 | if ([indexPath isEqual:hideIndexPath]) { 66 | layoutAttributes.hidden = YES; 67 | } 68 | if([indexPath isEqual:toIndexPath]) { 69 | // Item's new location 70 | layoutAttributes.indexPath = fromIndexPath; 71 | } 72 | else if(fromIndexPath.section != toIndexPath.section) { 73 | if(indexPath.section == fromIndexPath.section && indexPath.item >= fromIndexPath.item) { 74 | // Change indexes in source section 75 | layoutAttributes.indexPath = [NSIndexPath indexPathForItem:indexPath.item + 1 inSection:indexPath.section]; 76 | } 77 | else if(indexPath.section == toIndexPath.section && indexPath.item >= toIndexPath.item) { 78 | // Change indexes in destination section 79 | layoutAttributes.indexPath = [NSIndexPath indexPathForItem:indexPath.item - 1 inSection:indexPath.section]; 80 | } 81 | } 82 | else if(indexPath.section == fromIndexPath.section) { 83 | if(indexPath.item <= fromIndexPath.item && indexPath.item > toIndexPath.item) { 84 | // Item moved back 85 | layoutAttributes.indexPath = [NSIndexPath indexPathForItem:indexPath.item - 1 inSection:indexPath.section]; 86 | } 87 | else if(indexPath.item >= fromIndexPath.item && indexPath.item < toIndexPath.item) { 88 | // Item moved forward 89 | layoutAttributes.indexPath = [NSIndexPath indexPathForItem:indexPath.item + 1 inSection:indexPath.section]; 90 | } 91 | } 92 | } 93 | 94 | return elements; 95 | } 96 | 97 | @end 98 | -------------------------------------------------------------------------------- /DraggableCollectionView/Layout/DraggableCollectionViewFlowLayout.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2013 Luke Scott 3 | // https://github.com/lukescott/DraggableCollectionView 4 | // Distributed under MIT license 5 | // 6 | 7 | #import 8 | #import "UICollectionViewLayout_Warpable.h" 9 | #import "LSCollectionViewLayoutHelper.h" 10 | 11 | @interface DraggableCollectionViewFlowLayout : UICollectionViewFlowLayout 12 | 13 | @property (readonly, nonatomic) LSCollectionViewLayoutHelper *layoutHelper; 14 | @end 15 | -------------------------------------------------------------------------------- /DraggableCollectionView/Layout/DraggableCollectionViewFlowLayout.m: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2013 Luke Scott 3 | // https://github.com/lukescott/DraggableCollectionView 4 | // Distributed under MIT license 5 | // 6 | 7 | #import "DraggableCollectionViewFlowLayout.h" 8 | #import "LSCollectionViewLayoutHelper.h" 9 | 10 | @interface DraggableCollectionViewFlowLayout () 11 | { 12 | LSCollectionViewLayoutHelper *_layoutHelper; 13 | } 14 | @end 15 | 16 | @implementation DraggableCollectionViewFlowLayout 17 | 18 | - (LSCollectionViewLayoutHelper *)layoutHelper 19 | { 20 | if(_layoutHelper == nil) { 21 | _layoutHelper = [[LSCollectionViewLayoutHelper alloc] initWithCollectionViewLayout:self]; 22 | } 23 | return _layoutHelper; 24 | } 25 | 26 | - (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect 27 | { 28 | return [self.layoutHelper modifiedLayoutAttributesForElements:[super layoutAttributesForElementsInRect:rect]]; 29 | } 30 | 31 | @end 32 | -------------------------------------------------------------------------------- /DraggableCollectionView/UICollectionView+Draggable.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2013 Luke Scott 3 | // https://github.com/lukescott/DraggableCollectionView 4 | // Distributed under MIT license 5 | // 6 | 7 | #import 8 | #import "UICollectionViewDataSource_Draggable.h" 9 | 10 | @interface UICollectionView (Draggable) 11 | 12 | @property (nonatomic, assign) BOOL draggable; 13 | @property (nonatomic, assign) UIEdgeInsets scrollingEdgeInsets; 14 | @property (nonatomic, assign) CGFloat scrollingSpeed; 15 | @end 16 | -------------------------------------------------------------------------------- /DraggableCollectionView/UICollectionView+Draggable.m: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2013 Luke Scott 3 | // https://github.com/lukescott/DraggableCollectionView 4 | // Distributed under MIT license 5 | // 6 | 7 | #import "UICollectionView+Draggable.h" 8 | #import "LSCollectionViewHelper.h" 9 | #import 10 | 11 | @implementation UICollectionView (Draggable) 12 | 13 | - (LSCollectionViewHelper *)getHelper 14 | { 15 | LSCollectionViewHelper *helper = objc_getAssociatedObject(self, "LSCollectionViewHelper"); 16 | if(helper == nil) { 17 | helper = [[LSCollectionViewHelper alloc] initWithCollectionView:self]; 18 | objc_setAssociatedObject(self, "LSCollectionViewHelper", helper, OBJC_ASSOCIATION_RETAIN_NONATOMIC); 19 | } 20 | return helper; 21 | } 22 | 23 | - (BOOL)draggable 24 | { 25 | return [self getHelper].enabled; 26 | } 27 | 28 | - (void)setDraggable:(BOOL)draggable 29 | { 30 | [self getHelper].enabled = draggable; 31 | } 32 | 33 | - (UIEdgeInsets)scrollingEdgeInsets 34 | { 35 | return [self getHelper].scrollingEdgeInsets; 36 | } 37 | 38 | - (void)setScrollingEdgeInsets:(UIEdgeInsets)scrollingEdgeInsets 39 | { 40 | [self getHelper].scrollingEdgeInsets = scrollingEdgeInsets; 41 | } 42 | 43 | - (CGFloat)scrollingSpeed 44 | { 45 | return [self getHelper].scrollingSpeed; 46 | } 47 | 48 | - (void)setScrollingSpeed:(CGFloat)scrollingSpeed 49 | { 50 | [self getHelper].scrollingSpeed = scrollingSpeed; 51 | } 52 | 53 | @end 54 | -------------------------------------------------------------------------------- /DraggableCollectionView/UICollectionViewDataSource_Draggable.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2013 Luke Scott 3 | // https://github.com/lukescott/DraggableCollectionView 4 | // Distributed under MIT license 5 | // 6 | 7 | #import 8 | 9 | @class LSCollectionViewHelper; 10 | 11 | @protocol UICollectionViewDataSource_Draggable 12 | @required 13 | 14 | - (void)collectionView:(UICollectionView *)collectionView moveItemAtIndexPath:(NSIndexPath *)fromIndexPath toIndexPath:(NSIndexPath *)toIndexPath; 15 | - (BOOL)collectionView:(UICollectionView *)collectionView canMoveItemAtIndexPath:(NSIndexPath *)indexPath; 16 | 17 | @optional 18 | 19 | - (BOOL)collectionView:(UICollectionView *)collectionView canMoveItemAtIndexPath:(NSIndexPath *)indexPath toIndexPath:(NSIndexPath *)toIndexPath; 20 | - (void)collectionView:(UICollectionView *)collectionView didMoveItemAtIndexPath:(NSIndexPath *)indexPath toIndexPath:(NSIndexPath *)toIndexPath; 21 | 22 | @end 23 | -------------------------------------------------------------------------------- /DraggableCollectionView/UICollectionViewLayout_Warpable.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2013 Luke Scott 3 | // https://github.com/lukescott/DraggableCollectionView 4 | // Distributed under MIT license 5 | // 6 | 7 | #import 8 | 9 | @class LSCollectionViewLayoutHelper; 10 | 11 | @protocol UICollectionViewLayout_Warpable 12 | @required 13 | 14 | @property (readonly, nonatomic) LSCollectionViewLayoutHelper *layoutHelper; 15 | @end -------------------------------------------------------------------------------- /FlowLayoutDemo.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 8E11CC4D1759BB55004D2284 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 8E11CC4B1759BB55004D2284 /* InfoPlist.strings */; }; 11 | 8E11CC521759BB5E004D2284 /* MainStoryboard_iPad.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 8E11CC4E1759BB5E004D2284 /* MainStoryboard_iPad.storyboard */; }; 12 | 8E11CC531759BB5E004D2284 /* MainStoryboard_iPhone.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 8E11CC501759BB5E004D2284 /* MainStoryboard_iPhone.storyboard */; }; 13 | 8E947CA3175458980077D957 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8E947CA2175458980077D957 /* UIKit.framework */; }; 14 | 8E947CA5175458980077D957 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8E947CA4175458980077D957 /* Foundation.framework */; }; 15 | 8E947CA7175458980077D957 /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8E947CA6175458980077D957 /* CoreGraphics.framework */; }; 16 | 8E947CAF175458980077D957 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 8E947CAE175458980077D957 /* main.m */; }; 17 | 8E947CB3175458980077D957 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 8E947CB2175458980077D957 /* AppDelegate.m */; }; 18 | 8E947CB5175458980077D957 /* Default.png in Resources */ = {isa = PBXBuildFile; fileRef = 8E947CB4175458980077D957 /* Default.png */; }; 19 | 8E947CB7175458980077D957 /* Default@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 8E947CB6175458980077D957 /* Default@2x.png */; }; 20 | 8E947CB9175458980077D957 /* Default-568h@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 8E947CB8175458980077D957 /* Default-568h@2x.png */; }; 21 | 8E947CC2175458980077D957 /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 8E947CC1175458980077D957 /* ViewController.m */; }; 22 | 8E947CD31754593B0077D957 /* UICollectionView+Draggable.m in Sources */ = {isa = PBXBuildFile; fileRef = 8E947CCE1754593B0077D957 /* UICollectionView+Draggable.m */; }; 23 | 8E947CD817545AF10077D957 /* Cell.m in Sources */ = {isa = PBXBuildFile; fileRef = 8E947CD51754598E0077D957 /* Cell.m */; }; 24 | 8E947CDC17545C100077D957 /* DraggableCollectionViewFlowLayout.m in Sources */ = {isa = PBXBuildFile; fileRef = 8E947CDB17545C100077D957 /* DraggableCollectionViewFlowLayout.m */; }; 25 | 8E947CE217545C370077D957 /* LSCollectionViewHelper.m in Sources */ = {isa = PBXBuildFile; fileRef = 8E947CDF17545C370077D957 /* LSCollectionViewHelper.m */; }; 26 | 8E947CE317545C370077D957 /* LSCollectionViewLayoutHelper.m in Sources */ = {isa = PBXBuildFile; fileRef = 8E947CE117545C370077D957 /* LSCollectionViewLayoutHelper.m */; }; 27 | 8E955E881793ADF5008F72D9 /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8E955E871793ADF5008F72D9 /* QuartzCore.framework */; }; 28 | /* End PBXBuildFile section */ 29 | 30 | /* Begin PBXFileReference section */ 31 | 8E11CC4C1759BB55004D2284 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; 32 | 8E11CC4F1759BB5E004D2284 /* en */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = en; path = en.lproj/MainStoryboard_iPad.storyboard; sourceTree = ""; }; 33 | 8E11CC511759BB5E004D2284 /* en */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = en; path = en.lproj/MainStoryboard_iPhone.storyboard; sourceTree = ""; }; 34 | 8E947C9F175458980077D957 /* FlowLayoutDemo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = FlowLayoutDemo.app; sourceTree = BUILT_PRODUCTS_DIR; }; 35 | 8E947CA2175458980077D957 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; }; 36 | 8E947CA4175458980077D957 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; 37 | 8E947CA6175458980077D957 /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; }; 38 | 8E947CAA175458980077D957 /* FlowLayoutDemo-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "FlowLayoutDemo-Info.plist"; sourceTree = ""; }; 39 | 8E947CAE175458980077D957 /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 40 | 8E947CB0175458980077D957 /* FlowLayoutDemo-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "FlowLayoutDemo-Prefix.pch"; sourceTree = ""; }; 41 | 8E947CB1175458980077D957 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 42 | 8E947CB2175458980077D957 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; 43 | 8E947CB4175458980077D957 /* Default.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = Default.png; sourceTree = ""; }; 44 | 8E947CB6175458980077D957 /* Default@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Default@2x.png"; sourceTree = ""; }; 45 | 8E947CB8175458980077D957 /* Default-568h@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Default-568h@2x.png"; sourceTree = ""; }; 46 | 8E947CC0175458980077D957 /* ViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = ""; }; 47 | 8E947CC1175458980077D957 /* ViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = ""; }; 48 | 8E947CCD1754593B0077D957 /* UICollectionView+Draggable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UICollectionView+Draggable.h"; sourceTree = ""; }; 49 | 8E947CCE1754593B0077D957 /* UICollectionView+Draggable.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UICollectionView+Draggable.m"; sourceTree = ""; }; 50 | 8E947CCF1754593B0077D957 /* UICollectionViewDataSource_Draggable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = UICollectionViewDataSource_Draggable.h; sourceTree = ""; }; 51 | 8E947CD01754593B0077D957 /* UICollectionViewLayout_Warpable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = UICollectionViewLayout_Warpable.h; sourceTree = ""; }; 52 | 8E947CD41754598E0077D957 /* Cell.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Cell.h; sourceTree = ""; }; 53 | 8E947CD51754598E0077D957 /* Cell.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = Cell.m; sourceTree = ""; }; 54 | 8E947CDA17545C100077D957 /* DraggableCollectionViewFlowLayout.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DraggableCollectionViewFlowLayout.h; sourceTree = ""; }; 55 | 8E947CDB17545C100077D957 /* DraggableCollectionViewFlowLayout.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DraggableCollectionViewFlowLayout.m; sourceTree = ""; }; 56 | 8E947CDE17545C370077D957 /* LSCollectionViewHelper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LSCollectionViewHelper.h; sourceTree = ""; }; 57 | 8E947CDF17545C370077D957 /* LSCollectionViewHelper.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LSCollectionViewHelper.m; sourceTree = ""; }; 58 | 8E947CE017545C370077D957 /* LSCollectionViewLayoutHelper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LSCollectionViewLayoutHelper.h; sourceTree = ""; }; 59 | 8E947CE117545C370077D957 /* LSCollectionViewLayoutHelper.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LSCollectionViewLayoutHelper.m; sourceTree = ""; }; 60 | 8E955E871793ADF5008F72D9 /* QuartzCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuartzCore.framework; path = System/Library/Frameworks/QuartzCore.framework; sourceTree = SDKROOT; }; 61 | /* End PBXFileReference section */ 62 | 63 | /* Begin PBXFrameworksBuildPhase section */ 64 | 8E947C9C175458980077D957 /* Frameworks */ = { 65 | isa = PBXFrameworksBuildPhase; 66 | buildActionMask = 2147483647; 67 | files = ( 68 | 8E947CA3175458980077D957 /* UIKit.framework in Frameworks */, 69 | 8E947CA5175458980077D957 /* Foundation.framework in Frameworks */, 70 | 8E947CA7175458980077D957 /* CoreGraphics.framework in Frameworks */, 71 | 8E955E881793ADF5008F72D9 /* QuartzCore.framework in Frameworks */, 72 | ); 73 | runOnlyForDeploymentPostprocessing = 0; 74 | }; 75 | /* End PBXFrameworksBuildPhase section */ 76 | 77 | /* Begin PBXGroup section */ 78 | 8E947C96175458980077D957 = { 79 | isa = PBXGroup; 80 | children = ( 81 | 8E947CA8175458980077D957 /* FlowLayoutDemo */, 82 | 8E947CA1175458980077D957 /* Frameworks */, 83 | 8E947CA0175458980077D957 /* Products */, 84 | ); 85 | sourceTree = ""; 86 | }; 87 | 8E947CA0175458980077D957 /* Products */ = { 88 | isa = PBXGroup; 89 | children = ( 90 | 8E947C9F175458980077D957 /* FlowLayoutDemo.app */, 91 | ); 92 | name = Products; 93 | sourceTree = ""; 94 | }; 95 | 8E947CA1175458980077D957 /* Frameworks */ = { 96 | isa = PBXGroup; 97 | children = ( 98 | 8E947CA2175458980077D957 /* UIKit.framework */, 99 | 8E947CA4175458980077D957 /* Foundation.framework */, 100 | 8E947CA6175458980077D957 /* CoreGraphics.framework */, 101 | 8E955E871793ADF5008F72D9 /* QuartzCore.framework */, 102 | ); 103 | name = Frameworks; 104 | sourceTree = ""; 105 | }; 106 | 8E947CA8175458980077D957 /* FlowLayoutDemo */ = { 107 | isa = PBXGroup; 108 | children = ( 109 | 8E947CB1175458980077D957 /* AppDelegate.h */, 110 | 8E947CB2175458980077D957 /* AppDelegate.m */, 111 | 8E11CC4E1759BB5E004D2284 /* MainStoryboard_iPad.storyboard */, 112 | 8E11CC501759BB5E004D2284 /* MainStoryboard_iPhone.storyboard */, 113 | 8E947CC0175458980077D957 /* ViewController.h */, 114 | 8E947CC1175458980077D957 /* ViewController.m */, 115 | 8E947CD41754598E0077D957 /* Cell.h */, 116 | 8E947CD51754598E0077D957 /* Cell.m */, 117 | 8E947CC81754593B0077D957 /* DraggableCollectionView */, 118 | 8E947CA9175458980077D957 /* Supporting Files */, 119 | ); 120 | path = FlowLayoutDemo; 121 | sourceTree = ""; 122 | }; 123 | 8E947CA9175458980077D957 /* Supporting Files */ = { 124 | isa = PBXGroup; 125 | children = ( 126 | 8E947CAA175458980077D957 /* FlowLayoutDemo-Info.plist */, 127 | 8E11CC4B1759BB55004D2284 /* InfoPlist.strings */, 128 | 8E947CAE175458980077D957 /* main.m */, 129 | 8E947CB0175458980077D957 /* FlowLayoutDemo-Prefix.pch */, 130 | 8E947CB4175458980077D957 /* Default.png */, 131 | 8E947CB6175458980077D957 /* Default@2x.png */, 132 | 8E947CB8175458980077D957 /* Default-568h@2x.png */, 133 | ); 134 | name = "Supporting Files"; 135 | sourceTree = ""; 136 | }; 137 | 8E947CC81754593B0077D957 /* DraggableCollectionView */ = { 138 | isa = PBXGroup; 139 | children = ( 140 | 8E947CDD17545C370077D957 /* Helpers */, 141 | 8E947CD917545C100077D957 /* Layout */, 142 | 8E947CCD1754593B0077D957 /* UICollectionView+Draggable.h */, 143 | 8E947CCE1754593B0077D957 /* UICollectionView+Draggable.m */, 144 | 8E947CCF1754593B0077D957 /* UICollectionViewDataSource_Draggable.h */, 145 | 8E947CD01754593B0077D957 /* UICollectionViewLayout_Warpable.h */, 146 | ); 147 | path = DraggableCollectionView; 148 | sourceTree = SOURCE_ROOT; 149 | }; 150 | 8E947CD917545C100077D957 /* Layout */ = { 151 | isa = PBXGroup; 152 | children = ( 153 | 8E947CDA17545C100077D957 /* DraggableCollectionViewFlowLayout.h */, 154 | 8E947CDB17545C100077D957 /* DraggableCollectionViewFlowLayout.m */, 155 | ); 156 | path = Layout; 157 | sourceTree = ""; 158 | }; 159 | 8E947CDD17545C370077D957 /* Helpers */ = { 160 | isa = PBXGroup; 161 | children = ( 162 | 8E947CDE17545C370077D957 /* LSCollectionViewHelper.h */, 163 | 8E947CDF17545C370077D957 /* LSCollectionViewHelper.m */, 164 | 8E947CE017545C370077D957 /* LSCollectionViewLayoutHelper.h */, 165 | 8E947CE117545C370077D957 /* LSCollectionViewLayoutHelper.m */, 166 | ); 167 | path = Helpers; 168 | sourceTree = ""; 169 | }; 170 | /* End PBXGroup section */ 171 | 172 | /* Begin PBXNativeTarget section */ 173 | 8E947C9E175458980077D957 /* FlowLayoutDemo */ = { 174 | isa = PBXNativeTarget; 175 | buildConfigurationList = 8E947CC5175458980077D957 /* Build configuration list for PBXNativeTarget "FlowLayoutDemo" */; 176 | buildPhases = ( 177 | 8E947C9B175458980077D957 /* Sources */, 178 | 8E947C9C175458980077D957 /* Frameworks */, 179 | 8E947C9D175458980077D957 /* Resources */, 180 | ); 181 | buildRules = ( 182 | ); 183 | dependencies = ( 184 | ); 185 | name = FlowLayoutDemo; 186 | productName = FlowLayoutDemo; 187 | productReference = 8E947C9F175458980077D957 /* FlowLayoutDemo.app */; 188 | productType = "com.apple.product-type.application"; 189 | }; 190 | /* End PBXNativeTarget section */ 191 | 192 | /* Begin PBXProject section */ 193 | 8E947C97175458980077D957 /* Project object */ = { 194 | isa = PBXProject; 195 | attributes = { 196 | LastUpgradeCheck = 0460; 197 | ORGANIZATIONNAME = "Luke Scott"; 198 | }; 199 | buildConfigurationList = 8E947C9A175458980077D957 /* Build configuration list for PBXProject "FlowLayoutDemo" */; 200 | compatibilityVersion = "Xcode 3.2"; 201 | developmentRegion = English; 202 | hasScannedForEncodings = 0; 203 | knownRegions = ( 204 | en, 205 | ); 206 | mainGroup = 8E947C96175458980077D957; 207 | productRefGroup = 8E947CA0175458980077D957 /* Products */; 208 | projectDirPath = ""; 209 | projectRoot = ""; 210 | targets = ( 211 | 8E947C9E175458980077D957 /* FlowLayoutDemo */, 212 | ); 213 | }; 214 | /* End PBXProject section */ 215 | 216 | /* Begin PBXResourcesBuildPhase section */ 217 | 8E947C9D175458980077D957 /* Resources */ = { 218 | isa = PBXResourcesBuildPhase; 219 | buildActionMask = 2147483647; 220 | files = ( 221 | 8E947CB5175458980077D957 /* Default.png in Resources */, 222 | 8E947CB7175458980077D957 /* Default@2x.png in Resources */, 223 | 8E947CB9175458980077D957 /* Default-568h@2x.png in Resources */, 224 | 8E11CC4D1759BB55004D2284 /* InfoPlist.strings in Resources */, 225 | 8E11CC521759BB5E004D2284 /* MainStoryboard_iPad.storyboard in Resources */, 226 | 8E11CC531759BB5E004D2284 /* MainStoryboard_iPhone.storyboard in Resources */, 227 | ); 228 | runOnlyForDeploymentPostprocessing = 0; 229 | }; 230 | /* End PBXResourcesBuildPhase section */ 231 | 232 | /* Begin PBXSourcesBuildPhase section */ 233 | 8E947C9B175458980077D957 /* Sources */ = { 234 | isa = PBXSourcesBuildPhase; 235 | buildActionMask = 2147483647; 236 | files = ( 237 | 8E947CAF175458980077D957 /* main.m in Sources */, 238 | 8E947CB3175458980077D957 /* AppDelegate.m in Sources */, 239 | 8E947CC2175458980077D957 /* ViewController.m in Sources */, 240 | 8E947CD817545AF10077D957 /* Cell.m in Sources */, 241 | 8E947CD31754593B0077D957 /* UICollectionView+Draggable.m in Sources */, 242 | 8E947CDC17545C100077D957 /* DraggableCollectionViewFlowLayout.m in Sources */, 243 | 8E947CE217545C370077D957 /* LSCollectionViewHelper.m in Sources */, 244 | 8E947CE317545C370077D957 /* LSCollectionViewLayoutHelper.m in Sources */, 245 | ); 246 | runOnlyForDeploymentPostprocessing = 0; 247 | }; 248 | /* End PBXSourcesBuildPhase section */ 249 | 250 | /* Begin PBXVariantGroup section */ 251 | 8E11CC4B1759BB55004D2284 /* InfoPlist.strings */ = { 252 | isa = PBXVariantGroup; 253 | children = ( 254 | 8E11CC4C1759BB55004D2284 /* en */, 255 | ); 256 | name = InfoPlist.strings; 257 | sourceTree = ""; 258 | }; 259 | 8E11CC4E1759BB5E004D2284 /* MainStoryboard_iPad.storyboard */ = { 260 | isa = PBXVariantGroup; 261 | children = ( 262 | 8E11CC4F1759BB5E004D2284 /* en */, 263 | ); 264 | name = MainStoryboard_iPad.storyboard; 265 | sourceTree = ""; 266 | }; 267 | 8E11CC501759BB5E004D2284 /* MainStoryboard_iPhone.storyboard */ = { 268 | isa = PBXVariantGroup; 269 | children = ( 270 | 8E11CC511759BB5E004D2284 /* en */, 271 | ); 272 | name = MainStoryboard_iPhone.storyboard; 273 | sourceTree = ""; 274 | }; 275 | /* End PBXVariantGroup section */ 276 | 277 | /* Begin XCBuildConfiguration section */ 278 | 8E947CC3175458980077D957 /* Debug */ = { 279 | isa = XCBuildConfiguration; 280 | buildSettings = { 281 | ALWAYS_SEARCH_USER_PATHS = NO; 282 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 283 | CLANG_CXX_LIBRARY = "libc++"; 284 | CLANG_ENABLE_OBJC_ARC = YES; 285 | CLANG_WARN_CONSTANT_CONVERSION = YES; 286 | CLANG_WARN_EMPTY_BODY = YES; 287 | CLANG_WARN_ENUM_CONVERSION = YES; 288 | CLANG_WARN_INT_CONVERSION = YES; 289 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 290 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 291 | COPY_PHASE_STRIP = NO; 292 | GCC_C_LANGUAGE_STANDARD = gnu99; 293 | GCC_DYNAMIC_NO_PIC = NO; 294 | GCC_OPTIMIZATION_LEVEL = 0; 295 | GCC_PREPROCESSOR_DEFINITIONS = ( 296 | "DEBUG=1", 297 | "$(inherited)", 298 | ); 299 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 300 | GCC_WARN_ABOUT_RETURN_TYPE = YES; 301 | GCC_WARN_UNINITIALIZED_AUTOS = YES; 302 | GCC_WARN_UNUSED_VARIABLE = YES; 303 | IPHONEOS_DEPLOYMENT_TARGET = 6.1; 304 | ONLY_ACTIVE_ARCH = YES; 305 | SDKROOT = iphoneos; 306 | TARGETED_DEVICE_FAMILY = "1,2"; 307 | }; 308 | name = Debug; 309 | }; 310 | 8E947CC4175458980077D957 /* Release */ = { 311 | isa = XCBuildConfiguration; 312 | buildSettings = { 313 | ALWAYS_SEARCH_USER_PATHS = NO; 314 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 315 | CLANG_CXX_LIBRARY = "libc++"; 316 | CLANG_ENABLE_OBJC_ARC = YES; 317 | CLANG_WARN_CONSTANT_CONVERSION = YES; 318 | CLANG_WARN_EMPTY_BODY = YES; 319 | CLANG_WARN_ENUM_CONVERSION = YES; 320 | CLANG_WARN_INT_CONVERSION = YES; 321 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 322 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 323 | COPY_PHASE_STRIP = YES; 324 | GCC_C_LANGUAGE_STANDARD = gnu99; 325 | GCC_WARN_ABOUT_RETURN_TYPE = YES; 326 | GCC_WARN_UNINITIALIZED_AUTOS = YES; 327 | GCC_WARN_UNUSED_VARIABLE = YES; 328 | IPHONEOS_DEPLOYMENT_TARGET = 6.1; 329 | OTHER_CFLAGS = "-DNS_BLOCK_ASSERTIONS=1"; 330 | SDKROOT = iphoneos; 331 | TARGETED_DEVICE_FAMILY = "1,2"; 332 | VALIDATE_PRODUCT = YES; 333 | }; 334 | name = Release; 335 | }; 336 | 8E947CC6175458980077D957 /* Debug */ = { 337 | isa = XCBuildConfiguration; 338 | buildSettings = { 339 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 340 | GCC_PREFIX_HEADER = "FlowLayoutDemo/FlowLayoutDemo-Prefix.pch"; 341 | INFOPLIST_FILE = "FlowLayoutDemo/FlowLayoutDemo-Info.plist"; 342 | IPHONEOS_DEPLOYMENT_TARGET = 6.0; 343 | PRODUCT_NAME = "$(TARGET_NAME)"; 344 | WRAPPER_EXTENSION = app; 345 | }; 346 | name = Debug; 347 | }; 348 | 8E947CC7175458980077D957 /* Release */ = { 349 | isa = XCBuildConfiguration; 350 | buildSettings = { 351 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 352 | GCC_PREFIX_HEADER = "FlowLayoutDemo/FlowLayoutDemo-Prefix.pch"; 353 | INFOPLIST_FILE = "FlowLayoutDemo/FlowLayoutDemo-Info.plist"; 354 | IPHONEOS_DEPLOYMENT_TARGET = 6.0; 355 | PRODUCT_NAME = "$(TARGET_NAME)"; 356 | WRAPPER_EXTENSION = app; 357 | }; 358 | name = Release; 359 | }; 360 | /* End XCBuildConfiguration section */ 361 | 362 | /* Begin XCConfigurationList section */ 363 | 8E947C9A175458980077D957 /* Build configuration list for PBXProject "FlowLayoutDemo" */ = { 364 | isa = XCConfigurationList; 365 | buildConfigurations = ( 366 | 8E947CC3175458980077D957 /* Debug */, 367 | 8E947CC4175458980077D957 /* Release */, 368 | ); 369 | defaultConfigurationIsVisible = 0; 370 | defaultConfigurationName = Release; 371 | }; 372 | 8E947CC5175458980077D957 /* Build configuration list for PBXNativeTarget "FlowLayoutDemo" */ = { 373 | isa = XCConfigurationList; 374 | buildConfigurations = ( 375 | 8E947CC6175458980077D957 /* Debug */, 376 | 8E947CC7175458980077D957 /* Release */, 377 | ); 378 | defaultConfigurationIsVisible = 0; 379 | defaultConfigurationName = Release; 380 | }; 381 | /* End XCConfigurationList section */ 382 | }; 383 | rootObject = 8E947C97175458980077D957 /* Project object */; 384 | } 385 | -------------------------------------------------------------------------------- /FlowLayoutDemo/AppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2013 Luke Scott 3 | // https://github.com/lukescott/DraggableCollectionView 4 | // Distributed under MIT license 5 | // 6 | 7 | #import 8 | 9 | @interface AppDelegate : UIResponder 10 | 11 | @property (strong, nonatomic) UIWindow *window; 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /FlowLayoutDemo/AppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2013 Luke Scott 3 | // https://github.com/lukescott/DraggableCollectionView 4 | // Distributed under MIT license 5 | // 6 | 7 | #import "AppDelegate.h" 8 | 9 | @implementation AppDelegate 10 | 11 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions 12 | { 13 | // Override point for customization after application launch. 14 | return YES; 15 | } 16 | 17 | @end 18 | -------------------------------------------------------------------------------- /FlowLayoutDemo/Cell.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2013 Luke Scott 3 | // https://github.com/lukescott/DraggableCollectionView 4 | // Distributed under MIT license 5 | // 6 | 7 | #import 8 | 9 | @interface Cell : UICollectionViewCell 10 | @property (weak, nonatomic) IBOutlet UILabel *label; 11 | @end 12 | -------------------------------------------------------------------------------- /FlowLayoutDemo/Cell.m: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2013 Luke Scott 3 | // https://github.com/lukescott/DraggableCollectionView 4 | // Distributed under MIT license 5 | // 6 | 7 | #import "Cell.h" 8 | 9 | @implementation Cell 10 | 11 | - (void)setHighlighted:(BOOL)highlighted 12 | { 13 | [super setHighlighted:highlighted]; 14 | if (highlighted) { 15 | self.alpha = 0.5; 16 | } 17 | else { 18 | self.alpha = 1.f; 19 | } 20 | } 21 | 22 | @end 23 | -------------------------------------------------------------------------------- /FlowLayoutDemo/Default-568h@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lukescott/DraggableCollectionView/b78539845dd290be036b3d6977ff64709a7acde9/FlowLayoutDemo/Default-568h@2x.png -------------------------------------------------------------------------------- /FlowLayoutDemo/Default.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lukescott/DraggableCollectionView/b78539845dd290be036b3d6977ff64709a7acde9/FlowLayoutDemo/Default.png -------------------------------------------------------------------------------- /FlowLayoutDemo/Default@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lukescott/DraggableCollectionView/b78539845dd290be036b3d6977ff64709a7acde9/FlowLayoutDemo/Default@2x.png -------------------------------------------------------------------------------- /FlowLayoutDemo/FlowLayoutDemo-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleDisplayName 8 | ${PRODUCT_NAME} 9 | CFBundleExecutable 10 | ${EXECUTABLE_NAME} 11 | CFBundleIdentifier 12 | lukescott.${PRODUCT_NAME:rfc1034identifier} 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | ${PRODUCT_NAME} 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | 1.0 25 | LSRequiresIPhoneOS 26 | 27 | UIMainStoryboardFile 28 | MainStoryboard_iPhone 29 | UIMainStoryboardFile~ipad 30 | MainStoryboard_iPad 31 | UIRequiredDeviceCapabilities 32 | 33 | armv7 34 | 35 | UISupportedInterfaceOrientations 36 | 37 | UIInterfaceOrientationPortrait 38 | UIInterfaceOrientationLandscapeLeft 39 | UIInterfaceOrientationLandscapeRight 40 | 41 | UISupportedInterfaceOrientations~ipad 42 | 43 | UIInterfaceOrientationPortrait 44 | UIInterfaceOrientationPortraitUpsideDown 45 | UIInterfaceOrientationLandscapeLeft 46 | UIInterfaceOrientationLandscapeRight 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /FlowLayoutDemo/FlowLayoutDemo-Prefix.pch: -------------------------------------------------------------------------------- 1 | // 2 | // Prefix header for all source files of the 'FlowLayoutDemo' target in the 'FlowLayoutDemo' project 3 | // 4 | 5 | #import 6 | 7 | #ifndef __IPHONE_5_0 8 | #warning "This project uses features only available in iOS SDK 5.0 and later." 9 | #endif 10 | 11 | #ifdef __OBJC__ 12 | #import 13 | #import 14 | #endif 15 | -------------------------------------------------------------------------------- /FlowLayoutDemo/ViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2013 Luke Scott 3 | // https://github.com/lukescott/DraggableCollectionView 4 | // Distributed under MIT license 5 | // 6 | 7 | #import 8 | #import "UICollectionView+Draggable.h" 9 | 10 | @interface ViewController : UIViewController 11 | 12 | @property (weak, nonatomic) IBOutlet UICollectionView *collectionView; 13 | @end 14 | -------------------------------------------------------------------------------- /FlowLayoutDemo/ViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2013 Luke Scott 3 | // https://github.com/lukescott/DraggableCollectionView 4 | // Distributed under MIT license 5 | // 6 | 7 | #import "ViewController.h" 8 | #import "Cell.h" 9 | 10 | #define SECTION_COUNT 5 11 | #define ITEM_COUNT 20 12 | 13 | @interface ViewController () 14 | { 15 | NSMutableArray *sections; 16 | } 17 | @end 18 | 19 | @implementation ViewController 20 | 21 | - (void)viewDidLoad 22 | { 23 | [super viewDidLoad]; 24 | 25 | sections = [[NSMutableArray alloc] initWithCapacity:ITEM_COUNT]; 26 | for(int s = 0; s < SECTION_COUNT; s++) { 27 | NSMutableArray *data = [[NSMutableArray alloc] initWithCapacity:ITEM_COUNT]; 28 | for(int i = 0; i < ITEM_COUNT; i++) { 29 | [data addObject:[NSString stringWithFormat:@"%c %@", 65+s, @(i)]]; 30 | } 31 | [sections addObject:data]; 32 | } 33 | } 34 | 35 | - (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView 36 | { 37 | return sections.count; 38 | } 39 | 40 | - (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section 41 | { 42 | return [[sections objectAtIndex:section] count]; 43 | } 44 | 45 | - (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath 46 | { 47 | Cell *cell = (Cell*)[collectionView dequeueReusableCellWithReuseIdentifier:@"Cell" forIndexPath:indexPath]; 48 | NSMutableArray *data = [sections objectAtIndex:indexPath.section]; 49 | 50 | cell.label.text = [data objectAtIndex:indexPath.item]; 51 | 52 | return cell; 53 | } 54 | 55 | - (BOOL)collectionView:(LSCollectionViewHelper *)collectionView canMoveItemAtIndexPath:(NSIndexPath *)indexPath 56 | { 57 | return YES; 58 | } 59 | 60 | - (BOOL)collectionView:(UICollectionView *)collectionView canMoveItemAtIndexPath:(NSIndexPath *)indexPath toIndexPath:(NSIndexPath *)toIndexPath 61 | { 62 | // Prevent item from being moved to index 0 63 | // if (toIndexPath.item == 0) { 64 | // return NO; 65 | // } 66 | return YES; 67 | } 68 | 69 | - (void)collectionView:(LSCollectionViewHelper *)collectionView moveItemAtIndexPath:(NSIndexPath *)fromIndexPath toIndexPath:(NSIndexPath *)toIndexPath 70 | { 71 | NSMutableArray *data1 = [sections objectAtIndex:fromIndexPath.section]; 72 | NSMutableArray *data2 = [sections objectAtIndex:toIndexPath.section]; 73 | NSString *index = [data1 objectAtIndex:fromIndexPath.item]; 74 | 75 | [data1 removeObjectAtIndex:fromIndexPath.item]; 76 | [data2 insertObject:index atIndex:toIndexPath.item]; 77 | } 78 | 79 | @end 80 | -------------------------------------------------------------------------------- /FlowLayoutDemo/en.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* Localized versions of Info.plist keys */ 2 | 3 | -------------------------------------------------------------------------------- /FlowLayoutDemo/en.lproj/MainStoryboard_iPad.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | -------------------------------------------------------------------------------- /FlowLayoutDemo/en.lproj/MainStoryboard_iPhone.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | -------------------------------------------------------------------------------- /FlowLayoutDemo/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2013 Luke Scott 3 | // https://github.com/lukescott/DraggableCollectionView 4 | // Distributed under MIT license 5 | // 6 | 7 | #import 8 | 9 | #import "AppDelegate.h" 10 | 11 | int main(int argc, char *argv[]) 12 | { 13 | @autoreleasepool { 14 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013 Luke Scott 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is furnished 8 | to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | DraggableCollectionView 2 | ===================================== 3 | 4 | Extension for the `UICollectionView` and `UICollectionViewLayout` that allows a user to move items with drag and drop. 5 | 6 | ## Getting Started 7 | 8 | - Include UICollectionView+Draggable catagory (and related files). 9 | - Include QuartzCoreFramework. 10 | - Set `draggable` to true on collection view. 11 | - Set `collectionViewLayout` to a layout that implements the `UICollectionViewLayout_Warpable` protocol (will fallback if layout does not). For the default Flow layout (grid) use `DraggableCollectionViewFlowLayout` - you can set this in Interface Builder. 12 | - Implement the `UICollectionViewDataSource_Draggable` extended protocol. 13 | 14 | ## How it Works 15 | 16 | It works just like `UITableView`. The extended protocol contains similarly named methods related to drag and drop found in the `UITableViewDataSource` protocol. The `moveItemAtIndexPath:toIndexPath` method is only called once - when the user lifts their finger. This is acomplished by "warping" the cells by modifying the output from the `layoutAttributesForElementsInRect` method. This allows you to physically move the cells around without touching the data source. 17 | 18 | ## Custom Layouts 19 | 20 | This extension can work with most custom layouts - just implement the `UICollectionViewLayout_Warpable` protocol. An easy way to do this is subclass your layout and feed the output from `layoutAttributesForElementsInRect` through `LSCollectionViewLayoutHelper`. For an example how to do this see `DraggableCollectionViewFlowLayout` - you can pretty much copy and paste from there. `DraggableCollectionViewFlowLayout` is included for Apple's default `UICollectionViewFlowLayout` (see FlowLayoutDemo). There is also CircleLayoutDemo for Apple's CircleLayout example from WWDC 2012. 21 | 22 | ## License 23 | 24 | DraggableCollectionView is available under the [MIT license](LICENSE). 25 | --------------------------------------------------------------------------------