├── LICENSE ├── README.md ├── ReorderableGridView-Swift.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ ├── xcshareddata │ │ ├── ReordableGridView-Swift.xccheckout │ │ └── ReorderableGridView-Swift.xccheckout │ └── xcuserdata │ │ └── cem.xcuserdatad │ │ └── UserInterfaceState.xcuserstate └── xcuserdata │ └── cem.xcuserdatad │ ├── xcdebugger │ └── Breakpoints_v2.xcbkptlist │ └── xcschemes │ ├── ReorderableGridView-Swift.xcscheme │ └── xcschememanagement.plist ├── ReorderableGridView-Swift ├── AppDelegate.swift ├── Base.lproj │ ├── LaunchScreen.xib │ └── Main.storyboard ├── Images.xcassets │ └── AppIcon.appiconset │ │ └── Contents.json ├── Info.plist ├── MultipleGridViewController.swift ├── ReorderableGridView.swift └── ViewController.swift ├── ReorderableGridView-SwiftTests ├── Info.plist └── ReorderableGridView_SwiftTests.swift └── demo.gif /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (C) 2014, Cem Olcay 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a 4 | copy of this software and associated documentation files (the "Software"), 5 | to deal in the Software without restriction, including without limitation 6 | the rights to use, copy, modify, merge, publish, distribute, sublicense, 7 | and/or sell copies of the Software, and to permit persons to whom the 8 | Software is furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 16 | THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 18 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 19 | DEALINGS IN THE SOFTWARE. 20 | 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ReorderableGridView-Swift 2 | ======================= 3 | 4 | reorderable grid view solution implemented with swift.
5 | its UIScrollView subclass, its not a collection view layout.
6 | automatically sets horizontal item spacing by item widths. so items must be fixed-width.
7 | also sets automatically its content size.
8 | if you call `gridView?.invalidateLayout()` after orientation changed, it will lays out the grid by new orientation. 9 | 10 | 11 | Demo 12 | ---- 13 | 14 | ![alt tag](https://raw.githubusercontent.com/cemolcay/ReordableGridView-Swift/master/demo.gif) 15 | 16 | Usage 17 | ----- 18 | 19 | copy & paste the `ReorderableGridView.swift` into your project.
20 | 21 | gridView = ReorderableGridView(frame: self.view.frame, itemWidth: 180, verticalPadding: 20) 22 | self.view.addSubview(gridView!) 23 | 24 | Grid view ready ! 25 | 26 | now you can add it `ReorderableView` instances 27 | 28 | let itemView = ReorderableView (x: 0, y: 0, w: 180, h: 250) 29 | ... 30 | gridView?.addReorderableView(itemView) 31 | 32 | // or 33 | let pos = GridPosition (x: 0, y: 1) 34 | gridView?addReorderableView (itemView, gridPosition: pos) 35 | 36 | 37 | 38 | or remove them 39 | 40 | gridView?.removeReorderableViewAtGridPosition(GridPosition (x: 0, y: 0)) 41 | 42 | // or 43 | gridView?.removeReorderableView (itemView) 44 | 45 | 46 | > **Design Tip** 47 | > View itself don't have any margin padding. 48 | > It uses all frame width to calculate how many `ReorderableView`s can fit and 49 | > what should be their horizontal padding in a row. 50 | > Padding between columns (vertical padding) can be set in init method, 51 | > which is 10 by default. 52 | > You can have a container view and use something like 53 | > `CGRectInset (containerView.frame, marginX, marginY)` 54 | > when init grid with margin 55 | 56 | 57 | 58 | Optional Values 59 | --------------- 60 | 61 | var reorderable : Bool = true 62 | var draggable : Bool = true 63 | var draggableDelegate: Draggable? 64 | 65 | set them if you want your grid editable or not 66 | 67 | **Draggable Protocol** 68 | 69 | func didDragStartedForView (reorderableGridView: ReordableGridView, view: ReordableView) 70 | func didDraggedView (reorderableGridView: ReordableGridView, view: ReordableView) 71 | func didDragEndForView (reorderableGridView: ReordableGridView, view: ReordableView) 72 | 73 | 74 | set `gridView.draggableDelegate = self` and implement `Draggable` protocol functions if you want to access info about dragging actions in grid. 75 | This can be useful for multiple grid layouts. 76 | Example included in second tab of demo. 77 | -------------------------------------------------------------------------------- /ReorderableGridView-Swift.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | B25078711A1F36E300651D6A /* MultipleGridViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B25078701A1F36E200651D6A /* MultipleGridViewController.swift */; }; 11 | B2C47F671A1C9078002FDA24 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = B2C47F661A1C9078002FDA24 /* AppDelegate.swift */; }; 12 | B2C47F691A1C9078002FDA24 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B2C47F681A1C9078002FDA24 /* ViewController.swift */; }; 13 | B2C47F6C1A1C9078002FDA24 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = B2C47F6A1A1C9078002FDA24 /* Main.storyboard */; }; 14 | B2C47F6E1A1C9078002FDA24 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = B2C47F6D1A1C9078002FDA24 /* Images.xcassets */; }; 15 | B2C47F711A1C9078002FDA24 /* LaunchScreen.xib in Resources */ = {isa = PBXBuildFile; fileRef = B2C47F6F1A1C9078002FDA24 /* LaunchScreen.xib */; }; 16 | B2C47F7D1A1C9078002FDA24 /* ReorderableGridView_SwiftTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B2C47F7C1A1C9078002FDA24 /* ReorderableGridView_SwiftTests.swift */; }; 17 | B2C47F871A1C909B002FDA24 /* ReorderableGridView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B2C47F861A1C909B002FDA24 /* ReorderableGridView.swift */; }; 18 | /* End PBXBuildFile section */ 19 | 20 | /* Begin PBXContainerItemProxy section */ 21 | B2C47F771A1C9078002FDA24 /* PBXContainerItemProxy */ = { 22 | isa = PBXContainerItemProxy; 23 | containerPortal = B2C47F591A1C9078002FDA24 /* Project object */; 24 | proxyType = 1; 25 | remoteGlobalIDString = B2C47F601A1C9078002FDA24; 26 | remoteInfo = "ReorderableGridView-Swift"; 27 | }; 28 | /* End PBXContainerItemProxy section */ 29 | 30 | /* Begin PBXFileReference section */ 31 | B25078701A1F36E200651D6A /* MultipleGridViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MultipleGridViewController.swift; sourceTree = ""; }; 32 | B2C47F611A1C9078002FDA24 /* ReorderableGridView-Swift.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "ReorderableGridView-Swift.app"; sourceTree = BUILT_PRODUCTS_DIR; }; 33 | B2C47F651A1C9078002FDA24 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 34 | B2C47F661A1C9078002FDA24 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 35 | B2C47F681A1C9078002FDA24 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; 36 | B2C47F6B1A1C9078002FDA24 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 37 | B2C47F6D1A1C9078002FDA24 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; 38 | B2C47F701A1C9078002FDA24 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/LaunchScreen.xib; sourceTree = ""; }; 39 | B2C47F761A1C9078002FDA24 /* ReorderableGridView-SwiftTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "ReorderableGridView-SwiftTests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; 40 | B2C47F7B1A1C9078002FDA24 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 41 | B2C47F7C1A1C9078002FDA24 /* ReorderableGridView_SwiftTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReorderableGridView_SwiftTests.swift; sourceTree = ""; }; 42 | B2C47F861A1C909B002FDA24 /* ReorderableGridView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReorderableGridView.swift; sourceTree = ""; }; 43 | /* End PBXFileReference section */ 44 | 45 | /* Begin PBXFrameworksBuildPhase section */ 46 | B2C47F5E1A1C9078002FDA24 /* Frameworks */ = { 47 | isa = PBXFrameworksBuildPhase; 48 | buildActionMask = 2147483647; 49 | files = ( 50 | ); 51 | runOnlyForDeploymentPostprocessing = 0; 52 | }; 53 | B2C47F731A1C9078002FDA24 /* Frameworks */ = { 54 | isa = PBXFrameworksBuildPhase; 55 | buildActionMask = 2147483647; 56 | files = ( 57 | ); 58 | runOnlyForDeploymentPostprocessing = 0; 59 | }; 60 | /* End PBXFrameworksBuildPhase section */ 61 | 62 | /* Begin PBXGroup section */ 63 | B2C47F581A1C9078002FDA24 = { 64 | isa = PBXGroup; 65 | children = ( 66 | B2C47F631A1C9078002FDA24 /* ReorderableGridView-Swift */, 67 | B2C47F791A1C9078002FDA24 /* ReorderableGridView-SwiftTests */, 68 | B2C47F621A1C9078002FDA24 /* Products */, 69 | ); 70 | sourceTree = ""; 71 | }; 72 | B2C47F621A1C9078002FDA24 /* Products */ = { 73 | isa = PBXGroup; 74 | children = ( 75 | B2C47F611A1C9078002FDA24 /* ReorderableGridView-Swift.app */, 76 | B2C47F761A1C9078002FDA24 /* ReorderableGridView-SwiftTests.xctest */, 77 | ); 78 | name = Products; 79 | sourceTree = ""; 80 | }; 81 | B2C47F631A1C9078002FDA24 /* ReorderableGridView-Swift */ = { 82 | isa = PBXGroup; 83 | children = ( 84 | B2C47F661A1C9078002FDA24 /* AppDelegate.swift */, 85 | B2C47F681A1C9078002FDA24 /* ViewController.swift */, 86 | B25078701A1F36E200651D6A /* MultipleGridViewController.swift */, 87 | B2C47F861A1C909B002FDA24 /* ReorderableGridView.swift */, 88 | B2C47F641A1C9078002FDA24 /* Supporting Files */, 89 | ); 90 | path = "ReorderableGridView-Swift"; 91 | sourceTree = ""; 92 | }; 93 | B2C47F641A1C9078002FDA24 /* Supporting Files */ = { 94 | isa = PBXGroup; 95 | children = ( 96 | B2C47F6A1A1C9078002FDA24 /* Main.storyboard */, 97 | B2C47F6D1A1C9078002FDA24 /* Images.xcassets */, 98 | B2C47F6F1A1C9078002FDA24 /* LaunchScreen.xib */, 99 | B2C47F651A1C9078002FDA24 /* Info.plist */, 100 | ); 101 | name = "Supporting Files"; 102 | sourceTree = ""; 103 | }; 104 | B2C47F791A1C9078002FDA24 /* ReorderableGridView-SwiftTests */ = { 105 | isa = PBXGroup; 106 | children = ( 107 | B2C47F7C1A1C9078002FDA24 /* ReorderableGridView_SwiftTests.swift */, 108 | B2C47F7A1A1C9078002FDA24 /* Supporting Files */, 109 | ); 110 | path = "ReorderableGridView-SwiftTests"; 111 | sourceTree = ""; 112 | }; 113 | B2C47F7A1A1C9078002FDA24 /* Supporting Files */ = { 114 | isa = PBXGroup; 115 | children = ( 116 | B2C47F7B1A1C9078002FDA24 /* Info.plist */, 117 | ); 118 | name = "Supporting Files"; 119 | sourceTree = ""; 120 | }; 121 | /* End PBXGroup section */ 122 | 123 | /* Begin PBXNativeTarget section */ 124 | B2C47F601A1C9078002FDA24 /* ReorderableGridView-Swift */ = { 125 | isa = PBXNativeTarget; 126 | buildConfigurationList = B2C47F801A1C9078002FDA24 /* Build configuration list for PBXNativeTarget "ReorderableGridView-Swift" */; 127 | buildPhases = ( 128 | B2C47F5D1A1C9078002FDA24 /* Sources */, 129 | B2C47F5E1A1C9078002FDA24 /* Frameworks */, 130 | B2C47F5F1A1C9078002FDA24 /* Resources */, 131 | ); 132 | buildRules = ( 133 | ); 134 | dependencies = ( 135 | ); 136 | name = "ReorderableGridView-Swift"; 137 | productName = "ReorderableGridView-Swift"; 138 | productReference = B2C47F611A1C9078002FDA24 /* ReorderableGridView-Swift.app */; 139 | productType = "com.apple.product-type.application"; 140 | }; 141 | B2C47F751A1C9078002FDA24 /* ReorderableGridView-SwiftTests */ = { 142 | isa = PBXNativeTarget; 143 | buildConfigurationList = B2C47F831A1C9078002FDA24 /* Build configuration list for PBXNativeTarget "ReorderableGridView-SwiftTests" */; 144 | buildPhases = ( 145 | B2C47F721A1C9078002FDA24 /* Sources */, 146 | B2C47F731A1C9078002FDA24 /* Frameworks */, 147 | B2C47F741A1C9078002FDA24 /* Resources */, 148 | ); 149 | buildRules = ( 150 | ); 151 | dependencies = ( 152 | B2C47F781A1C9078002FDA24 /* PBXTargetDependency */, 153 | ); 154 | name = "ReorderableGridView-SwiftTests"; 155 | productName = "ReorderableGridView-SwiftTests"; 156 | productReference = B2C47F761A1C9078002FDA24 /* ReorderableGridView-SwiftTests.xctest */; 157 | productType = "com.apple.product-type.bundle.unit-test"; 158 | }; 159 | /* End PBXNativeTarget section */ 160 | 161 | /* Begin PBXProject section */ 162 | B2C47F591A1C9078002FDA24 /* Project object */ = { 163 | isa = PBXProject; 164 | attributes = { 165 | LastUpgradeCheck = 0610; 166 | ORGANIZATIONNAME = "Cem Olcay"; 167 | TargetAttributes = { 168 | B2C47F601A1C9078002FDA24 = { 169 | CreatedOnToolsVersion = 6.1; 170 | }; 171 | B2C47F751A1C9078002FDA24 = { 172 | CreatedOnToolsVersion = 6.1; 173 | TestTargetID = B2C47F601A1C9078002FDA24; 174 | }; 175 | }; 176 | }; 177 | buildConfigurationList = B2C47F5C1A1C9078002FDA24 /* Build configuration list for PBXProject "ReorderableGridView-Swift" */; 178 | compatibilityVersion = "Xcode 3.2"; 179 | developmentRegion = English; 180 | hasScannedForEncodings = 0; 181 | knownRegions = ( 182 | en, 183 | Base, 184 | ); 185 | mainGroup = B2C47F581A1C9078002FDA24; 186 | productRefGroup = B2C47F621A1C9078002FDA24 /* Products */; 187 | projectDirPath = ""; 188 | projectRoot = ""; 189 | targets = ( 190 | B2C47F601A1C9078002FDA24 /* ReorderableGridView-Swift */, 191 | B2C47F751A1C9078002FDA24 /* ReorderableGridView-SwiftTests */, 192 | ); 193 | }; 194 | /* End PBXProject section */ 195 | 196 | /* Begin PBXResourcesBuildPhase section */ 197 | B2C47F5F1A1C9078002FDA24 /* Resources */ = { 198 | isa = PBXResourcesBuildPhase; 199 | buildActionMask = 2147483647; 200 | files = ( 201 | B2C47F6C1A1C9078002FDA24 /* Main.storyboard in Resources */, 202 | B2C47F711A1C9078002FDA24 /* LaunchScreen.xib in Resources */, 203 | B2C47F6E1A1C9078002FDA24 /* Images.xcassets in Resources */, 204 | ); 205 | runOnlyForDeploymentPostprocessing = 0; 206 | }; 207 | B2C47F741A1C9078002FDA24 /* Resources */ = { 208 | isa = PBXResourcesBuildPhase; 209 | buildActionMask = 2147483647; 210 | files = ( 211 | ); 212 | runOnlyForDeploymentPostprocessing = 0; 213 | }; 214 | /* End PBXResourcesBuildPhase section */ 215 | 216 | /* Begin PBXSourcesBuildPhase section */ 217 | B2C47F5D1A1C9078002FDA24 /* Sources */ = { 218 | isa = PBXSourcesBuildPhase; 219 | buildActionMask = 2147483647; 220 | files = ( 221 | B25078711A1F36E300651D6A /* MultipleGridViewController.swift in Sources */, 222 | B2C47F691A1C9078002FDA24 /* ViewController.swift in Sources */, 223 | B2C47F871A1C909B002FDA24 /* ReorderableGridView.swift in Sources */, 224 | B2C47F671A1C9078002FDA24 /* AppDelegate.swift in Sources */, 225 | ); 226 | runOnlyForDeploymentPostprocessing = 0; 227 | }; 228 | B2C47F721A1C9078002FDA24 /* Sources */ = { 229 | isa = PBXSourcesBuildPhase; 230 | buildActionMask = 2147483647; 231 | files = ( 232 | B2C47F7D1A1C9078002FDA24 /* ReorderableGridView_SwiftTests.swift in Sources */, 233 | ); 234 | runOnlyForDeploymentPostprocessing = 0; 235 | }; 236 | /* End PBXSourcesBuildPhase section */ 237 | 238 | /* Begin PBXTargetDependency section */ 239 | B2C47F781A1C9078002FDA24 /* PBXTargetDependency */ = { 240 | isa = PBXTargetDependency; 241 | target = B2C47F601A1C9078002FDA24 /* ReorderableGridView-Swift */; 242 | targetProxy = B2C47F771A1C9078002FDA24 /* PBXContainerItemProxy */; 243 | }; 244 | /* End PBXTargetDependency section */ 245 | 246 | /* Begin PBXVariantGroup section */ 247 | B2C47F6A1A1C9078002FDA24 /* Main.storyboard */ = { 248 | isa = PBXVariantGroup; 249 | children = ( 250 | B2C47F6B1A1C9078002FDA24 /* Base */, 251 | ); 252 | name = Main.storyboard; 253 | sourceTree = ""; 254 | }; 255 | B2C47F6F1A1C9078002FDA24 /* LaunchScreen.xib */ = { 256 | isa = PBXVariantGroup; 257 | children = ( 258 | B2C47F701A1C9078002FDA24 /* Base */, 259 | ); 260 | name = LaunchScreen.xib; 261 | sourceTree = ""; 262 | }; 263 | /* End PBXVariantGroup section */ 264 | 265 | /* Begin XCBuildConfiguration section */ 266 | B2C47F7E1A1C9078002FDA24 /* Debug */ = { 267 | isa = XCBuildConfiguration; 268 | buildSettings = { 269 | ALWAYS_SEARCH_USER_PATHS = NO; 270 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 271 | CLANG_CXX_LIBRARY = "libc++"; 272 | CLANG_ENABLE_MODULES = YES; 273 | CLANG_ENABLE_OBJC_ARC = YES; 274 | CLANG_WARN_BOOL_CONVERSION = YES; 275 | CLANG_WARN_CONSTANT_CONVERSION = YES; 276 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 277 | CLANG_WARN_EMPTY_BODY = YES; 278 | CLANG_WARN_ENUM_CONVERSION = YES; 279 | CLANG_WARN_INT_CONVERSION = YES; 280 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 281 | CLANG_WARN_UNREACHABLE_CODE = YES; 282 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 283 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 284 | COPY_PHASE_STRIP = NO; 285 | ENABLE_STRICT_OBJC_MSGSEND = YES; 286 | GCC_C_LANGUAGE_STANDARD = gnu99; 287 | GCC_DYNAMIC_NO_PIC = NO; 288 | GCC_OPTIMIZATION_LEVEL = 0; 289 | GCC_PREPROCESSOR_DEFINITIONS = ( 290 | "DEBUG=1", 291 | "$(inherited)", 292 | ); 293 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 294 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 295 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 296 | GCC_WARN_UNDECLARED_SELECTOR = YES; 297 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 298 | GCC_WARN_UNUSED_FUNCTION = YES; 299 | GCC_WARN_UNUSED_VARIABLE = YES; 300 | IPHONEOS_DEPLOYMENT_TARGET = 8.1; 301 | MTL_ENABLE_DEBUG_INFO = YES; 302 | ONLY_ACTIVE_ARCH = YES; 303 | SDKROOT = iphoneos; 304 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 305 | }; 306 | name = Debug; 307 | }; 308 | B2C47F7F1A1C9078002FDA24 /* Release */ = { 309 | isa = XCBuildConfiguration; 310 | buildSettings = { 311 | ALWAYS_SEARCH_USER_PATHS = NO; 312 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 313 | CLANG_CXX_LIBRARY = "libc++"; 314 | CLANG_ENABLE_MODULES = YES; 315 | CLANG_ENABLE_OBJC_ARC = YES; 316 | CLANG_WARN_BOOL_CONVERSION = YES; 317 | CLANG_WARN_CONSTANT_CONVERSION = YES; 318 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 319 | CLANG_WARN_EMPTY_BODY = YES; 320 | CLANG_WARN_ENUM_CONVERSION = YES; 321 | CLANG_WARN_INT_CONVERSION = YES; 322 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 323 | CLANG_WARN_UNREACHABLE_CODE = YES; 324 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 325 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 326 | COPY_PHASE_STRIP = YES; 327 | ENABLE_NS_ASSERTIONS = NO; 328 | ENABLE_STRICT_OBJC_MSGSEND = YES; 329 | GCC_C_LANGUAGE_STANDARD = gnu99; 330 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 331 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 332 | GCC_WARN_UNDECLARED_SELECTOR = YES; 333 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 334 | GCC_WARN_UNUSED_FUNCTION = YES; 335 | GCC_WARN_UNUSED_VARIABLE = YES; 336 | IPHONEOS_DEPLOYMENT_TARGET = 8.1; 337 | MTL_ENABLE_DEBUG_INFO = NO; 338 | SDKROOT = iphoneos; 339 | VALIDATE_PRODUCT = YES; 340 | }; 341 | name = Release; 342 | }; 343 | B2C47F811A1C9078002FDA24 /* Debug */ = { 344 | isa = XCBuildConfiguration; 345 | buildSettings = { 346 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 347 | INFOPLIST_FILE = "ReorderableGridView-Swift/Info.plist"; 348 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 349 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 350 | PRODUCT_NAME = "ReorderableGridView-Swift"; 351 | TARGETED_DEVICE_FAMILY = "1,2"; 352 | }; 353 | name = Debug; 354 | }; 355 | B2C47F821A1C9078002FDA24 /* Release */ = { 356 | isa = XCBuildConfiguration; 357 | buildSettings = { 358 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 359 | INFOPLIST_FILE = "ReorderableGridView-Swift/Info.plist"; 360 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 361 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 362 | PRODUCT_NAME = "ReorderableGridView-Swift"; 363 | TARGETED_DEVICE_FAMILY = "1,2"; 364 | }; 365 | name = Release; 366 | }; 367 | B2C47F841A1C9078002FDA24 /* Debug */ = { 368 | isa = XCBuildConfiguration; 369 | buildSettings = { 370 | BUNDLE_LOADER = "$(TEST_HOST)"; 371 | FRAMEWORK_SEARCH_PATHS = ( 372 | "$(SDKROOT)/Developer/Library/Frameworks", 373 | "$(inherited)", 374 | ); 375 | GCC_PREPROCESSOR_DEFINITIONS = ( 376 | "DEBUG=1", 377 | "$(inherited)", 378 | ); 379 | INFOPLIST_FILE = "ReorderableGridView-SwiftTests/Info.plist"; 380 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 381 | PRODUCT_NAME = "ReorderableGridView-SwiftTests"; 382 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/ReorderableGridView-Swift.app/ReorderableGridView-Swift"; 383 | }; 384 | name = Debug; 385 | }; 386 | B2C47F851A1C9078002FDA24 /* Release */ = { 387 | isa = XCBuildConfiguration; 388 | buildSettings = { 389 | BUNDLE_LOADER = "$(TEST_HOST)"; 390 | FRAMEWORK_SEARCH_PATHS = ( 391 | "$(SDKROOT)/Developer/Library/Frameworks", 392 | "$(inherited)", 393 | ); 394 | INFOPLIST_FILE = "ReorderableGridView-SwiftTests/Info.plist"; 395 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 396 | PRODUCT_NAME = "ReorderableGridView-SwiftTests"; 397 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/ReorderableGridView-Swift.app/ReorderableGridView-Swift"; 398 | }; 399 | name = Release; 400 | }; 401 | /* End XCBuildConfiguration section */ 402 | 403 | /* Begin XCConfigurationList section */ 404 | B2C47F5C1A1C9078002FDA24 /* Build configuration list for PBXProject "ReorderableGridView-Swift" */ = { 405 | isa = XCConfigurationList; 406 | buildConfigurations = ( 407 | B2C47F7E1A1C9078002FDA24 /* Debug */, 408 | B2C47F7F1A1C9078002FDA24 /* Release */, 409 | ); 410 | defaultConfigurationIsVisible = 0; 411 | defaultConfigurationName = Release; 412 | }; 413 | B2C47F801A1C9078002FDA24 /* Build configuration list for PBXNativeTarget "ReorderableGridView-Swift" */ = { 414 | isa = XCConfigurationList; 415 | buildConfigurations = ( 416 | B2C47F811A1C9078002FDA24 /* Debug */, 417 | B2C47F821A1C9078002FDA24 /* Release */, 418 | ); 419 | defaultConfigurationIsVisible = 0; 420 | defaultConfigurationName = Release; 421 | }; 422 | B2C47F831A1C9078002FDA24 /* Build configuration list for PBXNativeTarget "ReorderableGridView-SwiftTests" */ = { 423 | isa = XCConfigurationList; 424 | buildConfigurations = ( 425 | B2C47F841A1C9078002FDA24 /* Debug */, 426 | B2C47F851A1C9078002FDA24 /* Release */, 427 | ); 428 | defaultConfigurationIsVisible = 0; 429 | defaultConfigurationName = Release; 430 | }; 431 | /* End XCConfigurationList section */ 432 | }; 433 | rootObject = B2C47F591A1C9078002FDA24 /* Project object */; 434 | } 435 | -------------------------------------------------------------------------------- /ReorderableGridView-Swift.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /ReorderableGridView-Swift.xcodeproj/project.xcworkspace/xcshareddata/ReordableGridView-Swift.xccheckout: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDESourceControlProjectFavoriteDictionaryKey 6 | 7 | IDESourceControlProjectIdentifier 8 | 0FC2F698-B9C1-4634-BAFC-F2FA48F24EDE 9 | IDESourceControlProjectName 10 | ReordableGridView-Swift 11 | IDESourceControlProjectOriginsDictionary 12 | 13 | 2A6249D544D1E1349BAC88A3DF3143C5072ED4CE 14 | https://github.com/cemolcay/ReordableGridView-Swift.git 15 | 16 | IDESourceControlProjectPath 17 | ReordableGridView-Swift.xcodeproj 18 | IDESourceControlProjectRelativeInstallPathDictionary 19 | 20 | 2A6249D544D1E1349BAC88A3DF3143C5072ED4CE 21 | ../.. 22 | 23 | IDESourceControlProjectURL 24 | https://github.com/cemolcay/ReordableGridView-Swift.git 25 | IDESourceControlProjectVersion 26 | 111 27 | IDESourceControlProjectWCCIdentifier 28 | 2A6249D544D1E1349BAC88A3DF3143C5072ED4CE 29 | IDESourceControlProjectWCConfigurations 30 | 31 | 32 | IDESourceControlRepositoryExtensionIdentifierKey 33 | public.vcs.git 34 | IDESourceControlWCCIdentifierKey 35 | 2A6249D544D1E1349BAC88A3DF3143C5072ED4CE 36 | IDESourceControlWCCName 37 | ReordableGridView-Swift 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /ReorderableGridView-Swift.xcodeproj/project.xcworkspace/xcshareddata/ReorderableGridView-Swift.xccheckout: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDESourceControlProjectFavoriteDictionaryKey 6 | 7 | IDESourceControlProjectIdentifier 8 | 3E32D046-C0A8-4159-983D-E3D279557D00 9 | IDESourceControlProjectName 10 | ReorderableGridView-Swift 11 | IDESourceControlProjectOriginsDictionary 12 | 13 | 2A6249D544D1E1349BAC88A3DF3143C5072ED4CE 14 | https://github.com/cemolcay/ReorderableGridView-Swift.git 15 | 16 | IDESourceControlProjectPath 17 | ReorderableGridView-Swift.xcodeproj 18 | IDESourceControlProjectRelativeInstallPathDictionary 19 | 20 | 2A6249D544D1E1349BAC88A3DF3143C5072ED4CE 21 | ../.. 22 | 23 | IDESourceControlProjectURL 24 | https://github.com/cemolcay/ReorderableGridView-Swift.git 25 | IDESourceControlProjectVersion 26 | 111 27 | IDESourceControlProjectWCCIdentifier 28 | 2A6249D544D1E1349BAC88A3DF3143C5072ED4CE 29 | IDESourceControlProjectWCConfigurations 30 | 31 | 32 | IDESourceControlRepositoryExtensionIdentifierKey 33 | public.vcs.git 34 | IDESourceControlWCCIdentifierKey 35 | 2A6249D544D1E1349BAC88A3DF3143C5072ED4CE 36 | IDESourceControlWCCName 37 | ReorderableGridView-Swift 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /ReorderableGridView-Swift.xcodeproj/project.xcworkspace/xcuserdata/cem.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cemolcay/ReorderableGridView-Swift/176ab79d6a4513444758b2591d27c670ca31101b/ReorderableGridView-Swift.xcodeproj/project.xcworkspace/xcuserdata/cem.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /ReorderableGridView-Swift.xcodeproj/xcuserdata/cem.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | -------------------------------------------------------------------------------- /ReorderableGridView-Swift.xcodeproj/xcuserdata/cem.xcuserdatad/xcschemes/ReorderableGridView-Swift.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 29 | 35 | 36 | 37 | 38 | 39 | 44 | 45 | 47 | 53 | 54 | 55 | 56 | 57 | 63 | 64 | 65 | 66 | 75 | 76 | 82 | 83 | 84 | 85 | 86 | 87 | 93 | 94 | 100 | 101 | 102 | 103 | 105 | 106 | 109 | 110 | 111 | -------------------------------------------------------------------------------- /ReorderableGridView-Swift.xcodeproj/xcuserdata/cem.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | ReordableGridView-Swift.xcscheme 8 | 9 | orderHint 10 | 0 11 | 12 | ReorderableGridView-Swift.xcscheme 13 | 14 | orderHint 15 | 0 16 | 17 | 18 | SuppressBuildableAutocreation 19 | 20 | B2C47F601A1C9078002FDA24 21 | 22 | primary 23 | 24 | 25 | B2C47F751A1C9078002FDA24 26 | 27 | primary 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /ReorderableGridView-Swift/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // ReorderableGridView-Swift 4 | // 5 | // Created by Cem Olcay on 19/11/14. 6 | // Copyright (c) 2014 Cem Olcay. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | @UIApplicationMain 12 | class AppDelegate: UIResponder, UIApplicationDelegate { 13 | 14 | var window: UIWindow? 15 | 16 | 17 | func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool { 18 | // Override point for customization after application launch. 19 | 20 | UINavigationBar.appearance().titleTextAttributes = [NSFontAttributeName: UIFont.HelveticaNeue(.Thin, size: 20)] 21 | 22 | return true 23 | } 24 | 25 | func applicationWillResignActive(application: UIApplication) { 26 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 27 | // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game. 28 | } 29 | 30 | func applicationDidEnterBackground(application: UIApplication) { 31 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 32 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 33 | } 34 | 35 | func applicationWillEnterForeground(application: UIApplication) { 36 | // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background. 37 | } 38 | 39 | func applicationDidBecomeActive(application: UIApplication) { 40 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 41 | } 42 | 43 | func applicationWillTerminate(application: UIApplication) { 44 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 45 | } 46 | 47 | 48 | } 49 | 50 | -------------------------------------------------------------------------------- /ReorderableGridView-Swift/Base.lproj/LaunchScreen.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 20 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /ReorderableGridView-Swift/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | -------------------------------------------------------------------------------- /ReorderableGridView-Swift/Images.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "29x29", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "29x29", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "40x40", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "40x40", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "60x60", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "60x60", 31 | "scale" : "3x" 32 | } 33 | ], 34 | "info" : { 35 | "version" : 1, 36 | "author" : "xcode" 37 | } 38 | } -------------------------------------------------------------------------------- /ReorderableGridView-Swift/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | com.questa.$(PRODUCT_NAME:rfc1034identifier) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIMainStoryboardFile 28 | Main 29 | UIRequiredDeviceCapabilities 30 | 31 | armv7 32 | 33 | UISupportedInterfaceOrientations 34 | 35 | UIInterfaceOrientationPortrait 36 | UIInterfaceOrientationLandscapeLeft 37 | UIInterfaceOrientationLandscapeRight 38 | UIInterfaceOrientationPortraitUpsideDown 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /ReorderableGridView-Swift/MultipleGridViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MultipleGridViewController.swift 3 | // ReorderableGridView-Swift 4 | // 5 | // Created by Cem Olcay on 21/11/14. 6 | // Copyright (c) 2014 Cem Olcay. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class MultipleGridViewController: UIViewController, Draggable { 12 | 13 | // MARK: Properties 14 | @IBOutlet var selectedItemsGridContainerView: UIView! 15 | @IBOutlet var itemsGridContainerView: UIView! 16 | 17 | var selectedItemsGrid: ReorderableGridView? 18 | var itemsGrid: ReorderableGridView? 19 | var itemCount: Int = 0 20 | 21 | var borderColor: UIColor? 22 | var bgColor: UIColor? 23 | var bottomColor: UIColor? 24 | 25 | 26 | 27 | // MARK: Lifecycle 28 | 29 | override func viewDidLoad() { 30 | super.viewDidLoad() 31 | 32 | borderColor = RGBColor(233, g: 233, b: 233) 33 | bgColor = RGBColor(242, g: 242, b: 242) 34 | bottomColor = RGBColor(65, g: 65, b: 65) 35 | 36 | self.title = "Multiple Grid" 37 | self.navigationItem.title = "Multiple Grid View Demo" 38 | self.view.backgroundColor = bgColor 39 | 40 | selectedItemsGrid = ReorderableGridView (frame: selectedItemsGridContainerView.bounds, itemWidth: 180) 41 | selectedItemsGrid?.draggableDelegate = self 42 | selectedItemsGrid?.reorderable = false 43 | selectedItemsGrid?.clipsToBounds = false 44 | selectedItemsGridContainerView.addSubview(selectedItemsGrid!) 45 | 46 | itemsGrid = ReorderableGridView (frame: itemsGridContainerView.bounds, itemWidth: 180) 47 | itemsGrid?.draggableDelegate = self 48 | itemsGrid?.clipsToBounds = false 49 | itemsGrid?.reorderable = false 50 | itemsGridContainerView.addSubview(itemsGrid!) 51 | 52 | for _ in 0..<20 { 53 | itemsGrid?.addReorderableView(itemView()) 54 | } 55 | } 56 | 57 | override func viewDidAppear(animated: Bool) { 58 | super.viewDidAppear(animated) 59 | 60 | if let grid = itemsGrid { 61 | grid.frame = itemsGridContainerView.bounds 62 | grid.invalidateLayout() 63 | } 64 | 65 | if let grid = selectedItemsGrid { 66 | grid.frame = selectedItemsGridContainerView.bounds 67 | grid.invalidateLayout() 68 | } 69 | } 70 | 71 | func itemView () -> ReorderableView { 72 | let w : CGFloat = 180 73 | let h : CGFloat = 100 + CGFloat(arc4random()%100) 74 | 75 | let view = ReorderableView (x: 0, y: 0, w: w, h: h) 76 | view.tag = itemCount++ 77 | view.layer.borderColor = borderColor?.CGColor 78 | view.layer.backgroundColor = UIColor.whiteColor().CGColor 79 | view.layer.borderWidth = 1 80 | view.layer.cornerRadius = 5 81 | view.layer.masksToBounds = true 82 | 83 | let topView = UIView(x: 0, y: 0, w: view.w, h: 50) 84 | view.addSubview(topView) 85 | 86 | let itemLabel = UILabel (frame: topView.frame) 87 | itemLabel.center = topView.center 88 | itemLabel.font = UIFont.HelveticaNeue(.Thin, size: 20) 89 | itemLabel.textAlignment = NSTextAlignment.Center 90 | itemLabel.textColor = bottomColor? 91 | itemLabel.text = "\(view.tag)" 92 | itemLabel.layer.masksToBounds = true 93 | topView.addSubview(itemLabel) 94 | 95 | let sepLayer = CALayer () 96 | sepLayer.frame = CGRect (x: 0, y: topView.bottom, width: topView.w, height: 1) 97 | sepLayer.backgroundColor = borderColor?.CGColor 98 | topView.layer.addSublayer(sepLayer) 99 | 100 | let bottomView = UIView(frame: CGRect(x: 0, y: topView.bottom, width: view.w, height: view.h-topView.h)) 101 | let bottomLayer = CALayer () 102 | bottomLayer.frame = CGRect (x: 0, y: bottomView.h-5, width: bottomView.w, height: 5) 103 | bottomLayer.backgroundColor = bottomColor?.CGColor 104 | bottomView.layer.addSublayer(bottomLayer) 105 | bottomView.layer.masksToBounds = true 106 | view.addSubview(bottomView) 107 | 108 | return view 109 | } 110 | 111 | 112 | 113 | // MARK: Draggable 114 | 115 | func didDragStartedForView(reorderableGridView: ReorderableGridView, view: ReorderableView) { 116 | 117 | } 118 | 119 | func didDraggedView(reorderableGridView: ReorderableGridView, view: ReorderableView) { 120 | 121 | } 122 | 123 | func didDragEndForView(reorderableGridView: ReorderableGridView, view: ReorderableView) { 124 | 125 | // items grid to selected items grid 126 | 127 | if (reorderableGridView == itemsGrid!) { 128 | let convertedPos : CGPoint = self.view.convertPoint(view.center, fromView: itemsGridContainerView) 129 | if (CGRectContainsPoint(selectedItemsGridContainerView.frame, convertedPos)) { 130 | itemsGrid?.removeReorderableView(view) 131 | selectedItemsGrid?.addReorderableView(view) 132 | } else { 133 | reorderableGridView.invalidateLayout() 134 | } 135 | } 136 | 137 | 138 | // selected items grid to items grid 139 | 140 | else if (reorderableGridView == selectedItemsGrid!) { 141 | let convertedPos : CGPoint = self.view.convertPoint(view.center, fromView: selectedItemsGridContainerView) 142 | if (CGRectContainsPoint(itemsGridContainerView.frame, convertedPos)) { 143 | selectedItemsGrid?.removeReorderableView(view) 144 | itemsGrid?.addReorderableView(view) 145 | } else { 146 | reorderableGridView.invalidateLayout() 147 | } 148 | } 149 | } 150 | 151 | 152 | 153 | // MARK: Interface Rotation 154 | 155 | override func didRotateFromInterfaceOrientation(fromInterfaceOrientation: UIInterfaceOrientation) { 156 | selectedItemsGrid?.frame = selectedItemsGridContainerView.bounds 157 | itemsGrid?.frame = itemsGridContainerView.bounds 158 | 159 | selectedItemsGrid?.invalidateLayout() 160 | itemsGrid?.invalidateLayout() 161 | } 162 | 163 | 164 | 165 | // MARK: Utils 166 | 167 | func randomColor () -> UIColor { 168 | var randomRed:CGFloat = CGFloat(drand48()) 169 | var randomGreen:CGFloat = CGFloat(drand48()) 170 | var randomBlue:CGFloat = CGFloat(drand48()) 171 | 172 | return UIColor(red: randomRed, green: randomGreen, blue: randomBlue, alpha: 1.0) 173 | } 174 | 175 | func RGBColor(r: CGFloat, g: CGFloat, b: CGFloat) -> UIColor { 176 | return UIColor (red: r / 255.0, green: g / 255.0, blue: b / 255.0, alpha: 1) 177 | } 178 | 179 | func RGBAColor (r: CGFloat, g: CGFloat, b: CGFloat, a: CGFloat) -> UIColor { 180 | return UIColor (red: r / 255.0, green: g / 255.0, blue: b / 255.0, alpha: a) 181 | } 182 | } 183 | -------------------------------------------------------------------------------- /ReorderableGridView-Swift/ReorderableGridView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ReorderableGridView.swift 3 | // ReorderableGridView-Swift 4 | // 5 | // Created by Cem Olcay on 19/11/14. 6 | // Copyright (c) 2014 Cem Olcay. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | 12 | extension UIView { 13 | 14 | // MARK: Frame Extensions 15 | 16 | var x: CGFloat { 17 | return self.frame.origin.x 18 | } 19 | 20 | var y: CGFloat { 21 | return self.frame.origin.y 22 | } 23 | 24 | var w: CGFloat { 25 | return self.frame.size.width 26 | } 27 | 28 | var h: CGFloat { 29 | return self.frame.size.height 30 | } 31 | 32 | 33 | var left: CGFloat { 34 | return self.x 35 | } 36 | 37 | var right: CGFloat { 38 | return self.x + self.w 39 | } 40 | 41 | var top: CGFloat { 42 | return self.y 43 | } 44 | 45 | var bottom: CGFloat { 46 | return self.y + self.h 47 | } 48 | 49 | 50 | func setX (x: CGFloat) { 51 | self.frame = CGRect (x: x, y: self.y, width: self.w, height: self.h) 52 | } 53 | 54 | func setY (y: CGFloat) { 55 | UIView.animateWithDuration(0.2, animations: { () -> Void in 56 | self.frame = CGRect (x: self.x, y: y, width: self.w, height: self.h) 57 | }) 58 | } 59 | 60 | func setX (x: CGFloat, y: CGFloat) { 61 | self.frame = CGRect (x: x, y: y, width: self.w, height: self.h) 62 | } 63 | 64 | 65 | func setW (w: CGFloat) { 66 | self.frame = CGRect (x: self.x, y: self.y, width: w, height: self.h) 67 | } 68 | 69 | func setH (h: CGFloat) { 70 | self.frame = CGRect (x: self.x, y: self.y, width: self.w, height: h) 71 | } 72 | 73 | func setW (w: CGFloat, h: CGFloat) { 74 | self.frame = CGRect (x: self.x, y: self.y, width: w, height: h) 75 | } 76 | 77 | func setSize (size: CGSize) { 78 | self.frame = CGRect (x: self.x, y: self.y, width: size.width, height: size.height) 79 | } 80 | 81 | func setPosition (position: CGPoint) { 82 | if (frame.origin == position) { 83 | return 84 | } 85 | 86 | UIView.animateWithDuration(0.2, animations: { () -> Void in 87 | self.frame = CGRect (x: position.x, y: position.y, width: self.w, height: self.h) 88 | }) 89 | } 90 | 91 | 92 | func leftWithOffset (offset: CGFloat) -> CGFloat { 93 | return self.left - offset 94 | } 95 | 96 | func rightWithOffset (offset: CGFloat) -> CGFloat { 97 | return self.right + offset 98 | } 99 | 100 | func topWithOffset (offset: CGFloat) -> CGFloat { 101 | return self.top - offset 102 | } 103 | 104 | func botttomWithOffset (offset: CGFloat) -> CGFloat { 105 | return self.bottom + offset 106 | } 107 | 108 | 109 | func setScale (x: CGFloat, y: CGFloat, z: CGFloat) { 110 | self.transform = CGAffineTransformMakeScale(x, y) 111 | } 112 | 113 | convenience init (x: CGFloat, y: CGFloat, w: CGFloat, h: CGFloat) { 114 | self.init (frame: CGRect (x: x, y: y, width: w, height: h)) 115 | } 116 | } 117 | 118 | 119 | protocol Reorderable { 120 | func didReorderStartedForView (view: ReorderableView) 121 | func didReorderedView (view: ReorderableView, pan: UIPanGestureRecognizer) 122 | func didReorderEndForView (view: ReorderableView, pan: UIPanGestureRecognizer) 123 | } 124 | 125 | 126 | protocol Draggable { 127 | func didDragStartedForView (ReorderableGridView: ReorderableGridView, view: ReorderableView) 128 | func didDraggedView (ReorderableGridView: ReorderableGridView, view: ReorderableView) 129 | func didDragEndForView (ReorderableGridView: ReorderableGridView, view: ReorderableView) 130 | } 131 | 132 | 133 | func == (lhs: GridPosition, rhs: GridPosition) -> Bool { 134 | return (lhs.x == rhs.x && lhs.y == rhs.y) 135 | } 136 | 137 | struct GridPosition: Equatable { 138 | var x: Int? 139 | var y: Int? 140 | 141 | 142 | func col () -> Int { 143 | return x! 144 | } 145 | 146 | func row () -> Int { 147 | return y! 148 | } 149 | 150 | 151 | func arrayIndex (colsInRow: Int) -> Int { 152 | let index = row()*colsInRow + col() 153 | return index 154 | } 155 | 156 | 157 | func up () -> GridPosition? { 158 | if y <= 0 { 159 | return nil 160 | } else { 161 | return GridPosition(x: x!, y: y!-1) 162 | } 163 | } 164 | 165 | func down () -> GridPosition { 166 | return GridPosition(x: x!, y: y!+1) 167 | } 168 | 169 | func left () -> GridPosition? { 170 | if x <= 0 { 171 | return nil 172 | } else { 173 | return GridPosition (x: x!-1, y: y!) 174 | } 175 | } 176 | 177 | func right () -> GridPosition { 178 | return GridPosition (x: x!+1, y: y!) 179 | } 180 | 181 | 182 | func string () -> String { 183 | return "\(x!), \(y!)" 184 | } 185 | 186 | func detailedString () -> String { 187 | return "x: \(x!), y: \(y!)" 188 | } 189 | } 190 | 191 | 192 | class ReorderableView: UIView, UIGestureRecognizerDelegate { 193 | 194 | // MARK: Properties 195 | 196 | var delegate: Reorderable! = nil 197 | var pan: UIPanGestureRecognizer? 198 | var gridPosition: GridPosition? 199 | 200 | let animationDuration: NSTimeInterval = 0.2 201 | let reorderModeScale: CGFloat = 1.1 202 | let reorderModeAlpha: CGFloat = 0.6 203 | 204 | var isReordering : Bool = false { 205 | didSet { 206 | if isReordering { 207 | startReorderMode() 208 | } else { 209 | endReorderMode() 210 | } 211 | } 212 | } 213 | 214 | 215 | 216 | // MARK: Lifecycle 217 | 218 | convenience init (x: CGFloat, y: CGFloat, w: CGFloat, h: CGFloat) { 219 | self.init (frame: CGRect (x: x, y: y, width: w, height: h)) 220 | } 221 | 222 | override init(frame: CGRect) { 223 | super.init(frame: frame) 224 | 225 | var doubleTap = UITapGestureRecognizer (target: self, action: "doubleTapped:") 226 | doubleTap.numberOfTapsRequired = 1 227 | doubleTap.numberOfTouchesRequired = 1 228 | doubleTap.delegate = self 229 | 230 | self.addGestureRecognizer(doubleTap) 231 | 232 | 233 | var longPress = UILongPressGestureRecognizer (target: self, action: "longPressed:") 234 | longPress.numberOfTouchesRequired = 1 235 | longPress.delegate = self 236 | 237 | self.addGestureRecognizer(longPress) 238 | } 239 | 240 | required init(coder aDecoder: NSCoder) { 241 | fatalError("init(coder:) has not been implemented") 242 | } 243 | 244 | 245 | 246 | // MARK: Animations 247 | 248 | func startReorderMode () { 249 | addPan() 250 | 251 | superview?.bringSubviewToFront(self) 252 | 253 | UIView.animateWithDuration(animationDuration, animations: { () -> Void in 254 | self.alpha = self.reorderModeAlpha 255 | self.setScale(self.reorderModeScale, y: self.reorderModeScale, z: self.reorderModeScale) 256 | }) 257 | } 258 | 259 | func endReorderMode () { 260 | UIView.animateWithDuration(animationDuration, animations: { () -> Void in 261 | self.alpha = 1 262 | self.setScale(1, y: 1, z: 1) 263 | }) { finished -> Void in 264 | self.removePan() 265 | } 266 | } 267 | 268 | 269 | 270 | // MARK: Gestures 271 | 272 | func addPan () { 273 | pan = UIPanGestureRecognizer (target: self, action: "pan:") 274 | self.addGestureRecognizer(pan!) 275 | } 276 | 277 | func removePan () { 278 | self.removeGestureRecognizer(pan!) 279 | } 280 | 281 | 282 | func longPressed (gesture: UITapGestureRecognizer) { 283 | if isReordering { return } else { isReordering = true } 284 | delegate?.didReorderStartedForView(self) 285 | } 286 | 287 | func doubleTapped (gesture: UITapGestureRecognizer) { 288 | isReordering = !isReordering 289 | 290 | if isReordering { 291 | delegate?.didReorderStartedForView(self) 292 | } 293 | } 294 | 295 | func pan (gesture: UIPanGestureRecognizer) { 296 | switch gesture.state { 297 | case .Ended: 298 | isReordering = false 299 | delegate.didReorderEndForView(self, pan: pan!) 300 | 301 | case .Changed: 302 | delegate.didReorderedView(self, pan: pan!) 303 | 304 | default: 305 | return 306 | } 307 | } 308 | 309 | 310 | func gestureRecognizer(gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWithGestureRecognizer otherGestureRecognizer: UIGestureRecognizer) -> Bool { 311 | return false 312 | } 313 | 314 | } 315 | 316 | 317 | class ReorderableGridView: UIScrollView, Reorderable { 318 | 319 | 320 | // MARK: Properties 321 | 322 | var itemWidth: CGFloat? 323 | var verticalPadding: CGFloat? 324 | var horizontalPadding: CGFloat? 325 | var colsInRow: Int? 326 | 327 | var visibleRect: CGRect? 328 | 329 | var reorderable : Bool = true 330 | var draggable : Bool = true 331 | 332 | var draggableDelegate: Draggable? 333 | 334 | var reorderableViews : [ReorderableView] = [] 335 | 336 | 337 | 338 | // MARK: Observers 339 | 340 | var currentCol: Int = 0 { 341 | didSet { 342 | if currentCol > colsInRow!-1 { 343 | currentCol = 0 344 | currentRow++ 345 | } else if currentCol < 0 { 346 | currentCol = colsInRow!-1 347 | currentRow-- 348 | } 349 | } 350 | } 351 | 352 | var currentRow: Int = 0 { 353 | didSet { 354 | if currentRow < 0 { 355 | currentRow = 0 356 | } 357 | } 358 | } 359 | 360 | override var contentOffset: CGPoint { 361 | didSet { 362 | visibleRect? = CGRect (origin: contentOffset, size: frame.size) 363 | checkReusableViews() 364 | } 365 | } 366 | 367 | 368 | 369 | // MARK: Lifecycle 370 | 371 | init (frame: CGRect, itemWidth: CGFloat, verticalPadding: CGFloat = 10) { 372 | super.init(frame: frame) 373 | self.itemWidth = itemWidth 374 | self.verticalPadding = verticalPadding 375 | self.visibleRect = frame 376 | 377 | invalidateLayout() 378 | } 379 | 380 | required init(coder aDecoder: NSCoder) { 381 | fatalError("init(coder:) has not been implemented") 382 | } 383 | 384 | 385 | 386 | // MARK: ScrollView 387 | 388 | func setContentHeight (height: CGFloat) { 389 | contentSize = CGSize (width: w, height: height) 390 | } 391 | 392 | func addContentHeight (height: CGFloat) { 393 | setContentHeight(contentSize.height + height) 394 | } 395 | 396 | func isViewInVisibleRect (view: ReorderableView) -> Bool { 397 | if let rect = visibleRect { 398 | return CGRectIntersectsRect(view.frame, rect) 399 | } else { 400 | return false 401 | } 402 | } 403 | 404 | func checkReusableViews () { 405 | for view in reorderableViews { 406 | if isViewInVisibleRect(view) { 407 | // is already added to view stack 408 | if let superView = view.superview { 409 | if superView == self { 410 | // already added 411 | continue 412 | } else { 413 | // FIXME: another view is super ! 414 | continue 415 | } 416 | } else { 417 | if let gridPos = view.gridPosition { 418 | // add 419 | addSubview(view) 420 | placeView(view, toGridPosition: gridPos) 421 | } else { 422 | // not initilized yet 423 | continue 424 | } 425 | } 426 | } else { 427 | // should removed 428 | if let superView = view.superview { 429 | if superView == self { 430 | // remove 431 | view.removeFromSuperview() 432 | } else { 433 | // FIXME: another view is super ! 434 | continue 435 | } 436 | } else { 437 | // already removed 438 | continue 439 | } 440 | } 441 | } 442 | } 443 | 444 | 445 | 446 | // MARK: Layout 447 | 448 | func invalidateLayout () { 449 | colsInRow = Int (self.w / itemWidth!) 450 | horizontalPadding = (self.w - (CGFloat(colsInRow!) * itemWidth!)) / (CGFloat(colsInRow!) - 1) 451 | contentSize.height = contentOffset.y + h 452 | 453 | currentCol = 0 454 | currentRow = 0 455 | 456 | if reorderableViews.isEmpty { 457 | return 458 | } 459 | 460 | for i in 0...reorderableViews.count-1 { 461 | let y = currentRow 462 | let x = currentCol++ 463 | let gridPosition = GridPosition (x: x, y: y) 464 | 465 | placeView(reorderableViews[i], toGridPosition: gridPosition) 466 | } 467 | } 468 | 469 | func placeView (view: ReorderableView, toGridPosition: GridPosition) { 470 | view.gridPosition = toGridPosition 471 | view.delegate = self 472 | 473 | let pos = gridPositionToViewPosition(toGridPosition) 474 | view.setPosition(pos) 475 | 476 | let height = view.botttomWithOffset(verticalPadding!) 477 | if height > contentSize.height { 478 | setContentHeight(height) 479 | } 480 | } 481 | 482 | func insertViewAtPosition (view: ReorderableView, position: GridPosition) { 483 | if (view.gridPosition == position) { 484 | return 485 | } 486 | 487 | reorderableViews.removeAtIndex(view.gridPosition!.arrayIndex(colsInRow!)) 488 | reorderableViews.insert(view, atIndex: position.arrayIndex(colsInRow!)) 489 | invalidateLayout() 490 | } 491 | 492 | 493 | 494 | // MARK: Grid 495 | 496 | func gridPositionToViewPosition (gridPosition: GridPosition) -> CGPoint { 497 | var x : CGFloat = (CGFloat(gridPosition.x! - 1) * (itemWidth! + verticalPadding!)) + itemWidth! 498 | var y : CGFloat = 0 499 | 500 | if let up = gridPosition.up() { 501 | y = viewAtGridPosition(up)!.bottom + verticalPadding! 502 | } 503 | 504 | return CGPoint (x: x, y: y) 505 | } 506 | 507 | func viewAtGridPosition (gridPosition: GridPosition) -> ReorderableView? { 508 | let index = gridPosition.arrayIndex(colsInRow!) 509 | if (index < reorderableViews.count) { 510 | return reorderableViews[index] 511 | } else { 512 | return nil 513 | } 514 | } 515 | 516 | 517 | 518 | // MARK: Adding 519 | 520 | func addReorderableView (view: ReorderableView) { 521 | super.addSubview(view) 522 | reorderableViews.append(view) 523 | invalidateLayout() 524 | } 525 | 526 | func addReorderableView (view: ReorderableView, gridPosition: GridPosition) { 527 | super.addSubview(view) 528 | 529 | var addingIndex = gridPosition.arrayIndex(colsInRow!) 530 | if addingIndex >= reorderableViews.count { 531 | addingIndex = reorderableViews.count 532 | } 533 | 534 | reorderableViews.insert(view, atIndex: addingIndex) 535 | invalidateLayout() 536 | } 537 | 538 | 539 | 540 | // MARK: Removing 541 | 542 | func removeReorderableViewAtGridPosition (gridPosition: GridPosition) { 543 | if let view = viewAtGridPosition(gridPosition) { 544 | reorderableViews.removeAtIndex(gridPosition.arrayIndex(colsInRow!)) 545 | 546 | view.removeFromSuperview() 547 | invalidateLayout() 548 | } 549 | } 550 | 551 | func removeReorderableView (view: ReorderableView) { 552 | if let pos = view.gridPosition { 553 | removeReorderableViewAtGridPosition(pos) 554 | } else { 555 | println ("view is not in grid hierarchy") 556 | } 557 | } 558 | 559 | 560 | 561 | // MARK: Reorderable 562 | 563 | func didReorderStartedForView (view: ReorderableView) { 564 | draggableDelegate?.didDragStartedForView(self, view: view) 565 | } 566 | 567 | func didReorderedView (view: ReorderableView, pan: UIPanGestureRecognizer) { 568 | 569 | if !draggable { 570 | return 571 | } else { 572 | draggableDelegate?.didDraggedView(self, view: view) 573 | } 574 | 575 | let location = pan.locationInView(self) 576 | view.center = location 577 | 578 | 579 | if !reorderable { 580 | return 581 | } 582 | 583 | let col : Int = min(Int(location.x) / Int(itemWidth! + horizontalPadding!), colsInRow!-1) 584 | let rowCount : Int = reorderableViews.count/colsInRow! 585 | 586 | var gridPos = GridPosition (x: col, y: 0) 587 | for (var row = 0; row < reorderableViews.count/colsInRow!; row++) { 588 | gridPos.y = row 589 | 590 | if let otherView = viewAtGridPosition(gridPos) { 591 | if otherView == view { 592 | continue 593 | } 594 | 595 | if CGRectContainsPoint(otherView.frame, location) { 596 | //println("im at \(col), \(row), im " + view.gridPosition!.string()) 597 | insertViewAtPosition(view, position: gridPos) 598 | } 599 | } 600 | } 601 | } 602 | 603 | func didReorderEndForView (view: ReorderableView, pan: UIPanGestureRecognizer) { 604 | if reorderable { 605 | invalidateLayout() 606 | } 607 | 608 | if draggable { 609 | draggableDelegate?.didDragEndForView(self, view: view) 610 | } 611 | } 612 | } 613 | -------------------------------------------------------------------------------- /ReorderableGridView-Swift/ViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.swift 3 | // ReorderableGridView-Swift 4 | // 5 | // Created by Cem Olcay on 19/11/14. 6 | // Copyright (c) 2014 Cem Olcay. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | extension UIFont { 12 | 13 | enum FontType: String { 14 | case Regular = "Regular" 15 | case Bold = "Bold" 16 | case Light = "Light" 17 | case UltraLight = "UltraLight" 18 | case Italic = "Italic" 19 | case Thin = "Thin" 20 | } 21 | 22 | enum FontName: String { 23 | case HelveticaNeue = "HelveticaNeue" 24 | case Helvetica = "Helvetica" 25 | case Futura = "Futura" 26 | case Menlo = "Menlo" 27 | } 28 | 29 | class func Font (name: FontName, type: FontType, size: CGFloat) -> UIFont { 30 | return UIFont (name: name.rawValue + "-" + type.rawValue, size: size)! 31 | } 32 | 33 | class func HelveticaNeue (type: FontType, size: CGFloat) -> UIFont { 34 | return Font(.HelveticaNeue, type: type, size: size) 35 | } 36 | } 37 | 38 | class ViewController: UIViewController { 39 | 40 | // MARK: Properties 41 | 42 | var borderColor: UIColor? 43 | var bgColor: UIColor? 44 | var bottomColor: UIColor? 45 | 46 | var gridView : ReorderableGridView? 47 | var itemCount: Int = 0 48 | 49 | 50 | 51 | // MARK: Lifecycle 52 | 53 | override func viewDidLoad() { 54 | super.viewDidLoad() 55 | 56 | borderColor = RGBColor(233, g: 233, b: 233) 57 | bgColor = RGBColor(242, g: 242, b: 242) 58 | bottomColor = RGBColor(65, g: 65, b: 65) 59 | 60 | self.title = "Reorderable Grid" 61 | self.navigationItem.title = "Reorderable Grid View Demo" 62 | self.view.backgroundColor = bgColor 63 | 64 | setupGridView() 65 | } 66 | 67 | override func viewDidAppear(animated: Bool) { 68 | if let grid = gridView { 69 | grid.invalidateLayout() 70 | } 71 | } 72 | 73 | 74 | 75 | // MARK: Grid View 76 | 77 | func setupGridView () { 78 | let add = UIBarButtonItem (barButtonSystemItem: UIBarButtonSystemItem.Add, target: self, action: "addPressed:") 79 | let remove = UIBarButtonItem (barButtonSystemItem: UIBarButtonSystemItem.Trash, target: self, action: "removePressed:") 80 | 81 | self.navigationItem.leftBarButtonItem = add 82 | self.navigationItem.rightBarButtonItem = remove 83 | 84 | gridView = ReorderableGridView(frame: view.frame, itemWidth: 180, verticalPadding: 20) 85 | view.addSubview(gridView!) 86 | 87 | for _ in 0..<100 { 88 | gridView!.addReorderableView(itemView()) 89 | } 90 | } 91 | 92 | func itemView () -> ReorderableView { 93 | let w : CGFloat = 180 94 | let h : CGFloat = 100 + CGFloat(arc4random()%100) 95 | 96 | let view = ReorderableView (x: 0, y: 0, w: w, h: h) 97 | view.tag = itemCount++ 98 | view.layer.borderColor = borderColor?.CGColor 99 | view.layer.backgroundColor = UIColor.whiteColor().CGColor 100 | view.layer.borderWidth = 1 101 | view.layer.cornerRadius = 5 102 | view.layer.masksToBounds = true 103 | 104 | let topView = UIView(x: 0, y: 0, w: view.w, h: 50) 105 | view.addSubview(topView) 106 | 107 | let itemLabel = UILabel (frame: topView.frame) 108 | itemLabel.center = topView.center 109 | itemLabel.font = UIFont.HelveticaNeue(.Thin, size: 20) 110 | itemLabel.textAlignment = NSTextAlignment.Center 111 | itemLabel.textColor = bottomColor? 112 | itemLabel.text = "\(view.tag)" 113 | itemLabel.layer.masksToBounds = true 114 | topView.addSubview(itemLabel) 115 | 116 | let sepLayer = CALayer () 117 | sepLayer.frame = CGRect (x: 0, y: topView.bottom, width: topView.w, height: 1) 118 | sepLayer.backgroundColor = borderColor?.CGColor 119 | topView.layer.addSublayer(sepLayer) 120 | 121 | let bottomView = UIView(frame: CGRect(x: 0, y: topView.bottom, width: view.w, height: view.h-topView.h)) 122 | let bottomLayer = CALayer () 123 | bottomLayer.frame = CGRect (x: 0, y: bottomView.h-5, width: bottomView.w, height: 5) 124 | bottomLayer.backgroundColor = bottomColor?.CGColor 125 | bottomView.layer.addSublayer(bottomLayer) 126 | bottomView.layer.masksToBounds = true 127 | view.addSubview(bottomView) 128 | 129 | return view 130 | } 131 | 132 | 133 | 134 | // MARK: Add/Remove 135 | 136 | func addPressed (sender: AnyObject) { 137 | //gridView?.addReorderableView(itemView()) 138 | gridView?.addReorderableView(itemView(), gridPosition: GridPosition (x: 0, y: 1)) 139 | } 140 | 141 | func removePressed (sender: AnyObject) { 142 | gridView?.removeReorderableViewAtGridPosition(GridPosition (x: 0, y: 0)) 143 | //gridView?.removeReorderableView(gridView!.ReorderableViews[2]) 144 | } 145 | 146 | 147 | 148 | // MARK: Interface Rotation 149 | 150 | override func willRotateToInterfaceOrientation(toInterfaceOrientation: UIInterfaceOrientation, duration: NSTimeInterval) { 151 | let w = UIScreen.mainScreen().bounds.size.width 152 | let h = UIScreen.mainScreen().bounds.size.height 153 | 154 | gridView?.setW(h, h: w) 155 | gridView?.invalidateLayout() 156 | } 157 | 158 | 159 | 160 | // MARK: Utils 161 | 162 | func randomColor () -> UIColor { 163 | var randomRed:CGFloat = CGFloat(drand48()) 164 | var randomGreen:CGFloat = CGFloat(drand48()) 165 | var randomBlue:CGFloat = CGFloat(drand48()) 166 | 167 | return UIColor(red: randomRed, green: randomGreen, blue: randomBlue, alpha: 1.0) 168 | } 169 | 170 | func RGBColor(r: CGFloat, g: CGFloat, b: CGFloat) -> UIColor { 171 | return UIColor (red: r / 255.0, green: g / 255.0, blue: b / 255.0, alpha: 1) 172 | } 173 | 174 | func RGBAColor (r: CGFloat, g: CGFloat, b: CGFloat, a: CGFloat) -> UIColor { 175 | return UIColor (red: r / 255.0, green: g / 255.0, blue: b / 255.0, alpha: a) 176 | } 177 | } 178 | 179 | -------------------------------------------------------------------------------- /ReorderableGridView-SwiftTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | com.questa.$(PRODUCT_NAME:rfc1034identifier) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /ReorderableGridView-SwiftTests/ReorderableGridView_SwiftTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ReorderableGridView_SwiftTests.swift 3 | // ReorderableGridView-SwiftTests 4 | // 5 | // Created by Cem Olcay on 19/11/14. 6 | // Copyright (c) 2014 Cem Olcay. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import XCTest 11 | 12 | class ReorderableGridView_SwiftTests: XCTestCase { 13 | 14 | override func setUp() { 15 | super.setUp() 16 | // Put setup code here. This method is called before the invocation of each test method in the class. 17 | } 18 | 19 | override func tearDown() { 20 | // Put teardown code here. This method is called after the invocation of each test method in the class. 21 | super.tearDown() 22 | } 23 | 24 | func testExample() { 25 | // This is an example of a functional test case. 26 | XCTAssert(true, "Pass") 27 | } 28 | 29 | func testPerformanceExample() { 30 | // This is an example of a performance test case. 31 | self.measureBlock() { 32 | // Put the code you want to measure the time of here. 33 | } 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /demo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cemolcay/ReorderableGridView-Swift/176ab79d6a4513444758b2591d27c670ca31101b/demo.gif --------------------------------------------------------------------------------