├── .gitignore ├── .travis.yml ├── Assets ├── reordering.gif └── screenshot.png ├── HPReorderTableView.podspec ├── HPReorderTableView.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ └── contents.xcworkspacedata └── xcshareddata │ └── xcschemes │ ├── HPReorderTableView.xcscheme │ └── HPReorderTableViewDemo.xcscheme ├── HPReorderTableView ├── HPReorderTableView.h └── HPReorderTableView.m ├── HPReorderTableViewDemo ├── HPAppDelegate.h ├── HPAppDelegate.m ├── HPReorderTableViewDemo-Info.plist ├── HPReorderTableViewDemo-Prefix.pch ├── HPViewController.h ├── HPViewController.m ├── Images.xcassets │ ├── AppIcon.appiconset │ │ └── Contents.json │ └── LaunchImage.launchimage │ │ └── Contents.json ├── en.lproj │ └── InfoPlist.strings └── main.m ├── HPReorderTableViewTests ├── HPReorderTableViewTests-Info.plist ├── HPReorderTableViewTests.m └── en.lproj │ └── InfoPlist.strings ├── 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 | xcuserdata 13 | profile 14 | *.moved-aside 15 | DerivedData 16 | .idea/ 17 | *.hmap 18 | *.xccheckout 19 | 20 | #CocoaPods 21 | Pods 22 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: objective-c 2 | before_script: 3 | - brew update 4 | - brew upgrade xctool 5 | script: 6 | - xctool -project HPReorderTableView.xcodeproj -scheme 'HPReorderTableView' -configuration Release -sdk iphonesimulator test 7 | - xctool -project HPReorderTableView.xcodeproj -scheme 'HPReorderTableViewDemo' -configuration Release -sdk iphonesimulator 8 | -------------------------------------------------------------------------------- /Assets/reordering.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hpique/HPReorderTableView/2590155d07b1274c13f054a35e260779ffa83871/Assets/reordering.gif -------------------------------------------------------------------------------- /Assets/screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hpique/HPReorderTableView/2590155d07b1274c13f054a35e260779ffa83871/Assets/screenshot.png -------------------------------------------------------------------------------- /HPReorderTableView.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |s| 2 | s.name = "HPReorderTableView" 3 | s.version = "0.2" 4 | s.summary = "Drop-in UITableView replacement to reorder cells with long press on any part of the cell." 5 | s.screenshot = "https://raw.githubusercontent.com/hpique/HPReorderTableView/master/Assets/screenshot.png" 6 | s.homepage = "https://github.com/hpique/HPReorderTableView" 7 | s.license = { :type => 'Apache 2.0', :file => 'LICENSE' } 8 | s.author = { "Hermes Pique" => "@hpique" } 9 | s.social_media_url = 'https://twitter.com/hpique' 10 | s.ios.deployment_target = '6.1' 11 | s.source = { :git => "https://github.com/hpique/HPReorderTableView.git", :tag => "v#{s.version}" } 12 | s.source_files = 'HPReorderTableView/*.{h,m}' 13 | s.requires_arc = true; 14 | end -------------------------------------------------------------------------------- /HPReorderTableView.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 873E091618E843A9007E9675 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 873E091518E843A9007E9675 /* Images.xcassets */; }; 11 | 87D02E72188F7ABE00273DEC /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 87D02E71188F7ABE00273DEC /* Foundation.framework */; }; 12 | 87D02E77188F7ABE00273DEC /* HPReorderTableView.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = 87D02E76188F7ABE00273DEC /* HPReorderTableView.h */; }; 13 | 87D02E79188F7ABE00273DEC /* HPReorderTableView.m in Sources */ = {isa = PBXBuildFile; fileRef = 87D02E78188F7ABE00273DEC /* HPReorderTableView.m */; }; 14 | 87D02E80188F7ABE00273DEC /* XCTest.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 87D02E7F188F7ABE00273DEC /* XCTest.framework */; }; 15 | 87D02E81188F7ABE00273DEC /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 87D02E71188F7ABE00273DEC /* Foundation.framework */; }; 16 | 87D02E83188F7ABE00273DEC /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 87D02E82188F7ABE00273DEC /* UIKit.framework */; }; 17 | 87D02E86188F7ABE00273DEC /* libHPReorderTableView.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 87D02E6E188F7ABE00273DEC /* libHPReorderTableView.a */; }; 18 | 87D02E8C188F7ABE00273DEC /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 87D02E8A188F7ABE00273DEC /* InfoPlist.strings */; }; 19 | 87D02E8E188F7ABE00273DEC /* HPReorderTableViewTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 87D02E8D188F7ABE00273DEC /* HPReorderTableViewTests.m */; }; 20 | 87D02E9C188F7B0D00273DEC /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 87D02E71188F7ABE00273DEC /* Foundation.framework */; }; 21 | 87D02E9E188F7B0D00273DEC /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 87D02E9D188F7B0D00273DEC /* CoreGraphics.framework */; }; 22 | 87D02E9F188F7B0D00273DEC /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 87D02E82188F7ABE00273DEC /* UIKit.framework */; }; 23 | 87D02EA5188F7B0D00273DEC /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 87D02EA3188F7B0D00273DEC /* InfoPlist.strings */; }; 24 | 87D02EA7188F7B0D00273DEC /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 87D02EA6188F7B0D00273DEC /* main.m */; }; 25 | 87D02EAB188F7B0D00273DEC /* HPAppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 87D02EAA188F7B0D00273DEC /* HPAppDelegate.m */; }; 26 | 87D02EB4188F7B0E00273DEC /* HPViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 87D02EB3188F7B0E00273DEC /* HPViewController.m */; }; 27 | 87D02ECF188F7BC100273DEC /* HPReorderTableView.m in Sources */ = {isa = PBXBuildFile; fileRef = 87D02E78188F7ABE00273DEC /* HPReorderTableView.m */; }; 28 | /* End PBXBuildFile section */ 29 | 30 | /* Begin PBXContainerItemProxy section */ 31 | 87D02E84188F7ABE00273DEC /* PBXContainerItemProxy */ = { 32 | isa = PBXContainerItemProxy; 33 | containerPortal = 87D02E66188F7ABE00273DEC /* Project object */; 34 | proxyType = 1; 35 | remoteGlobalIDString = 87D02E6D188F7ABE00273DEC; 36 | remoteInfo = HPReorderTableView; 37 | }; 38 | /* End PBXContainerItemProxy section */ 39 | 40 | /* Begin PBXCopyFilesBuildPhase section */ 41 | 87D02E6C188F7ABE00273DEC /* CopyFiles */ = { 42 | isa = PBXCopyFilesBuildPhase; 43 | buildActionMask = 2147483647; 44 | dstPath = "include/$(PRODUCT_NAME)"; 45 | dstSubfolderSpec = 16; 46 | files = ( 47 | 87D02E77188F7ABE00273DEC /* HPReorderTableView.h in CopyFiles */, 48 | ); 49 | runOnlyForDeploymentPostprocessing = 0; 50 | }; 51 | /* End PBXCopyFilesBuildPhase section */ 52 | 53 | /* Begin PBXFileReference section */ 54 | 873E091518E843A9007E9675 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; 55 | 87D02E6E188F7ABE00273DEC /* libHPReorderTableView.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libHPReorderTableView.a; sourceTree = BUILT_PRODUCTS_DIR; }; 56 | 87D02E71188F7ABE00273DEC /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; 57 | 87D02E76188F7ABE00273DEC /* HPReorderTableView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = HPReorderTableView.h; sourceTree = ""; }; 58 | 87D02E78188F7ABE00273DEC /* HPReorderTableView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = HPReorderTableView.m; sourceTree = ""; }; 59 | 87D02E7E188F7ABE00273DEC /* HPReorderTableViewTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = HPReorderTableViewTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 60 | 87D02E7F188F7ABE00273DEC /* XCTest.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = XCTest.framework; path = Library/Frameworks/XCTest.framework; sourceTree = DEVELOPER_DIR; }; 61 | 87D02E82188F7ABE00273DEC /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = Library/Frameworks/UIKit.framework; sourceTree = DEVELOPER_DIR; }; 62 | 87D02E89188F7ABE00273DEC /* HPReorderTableViewTests-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "HPReorderTableViewTests-Info.plist"; sourceTree = ""; }; 63 | 87D02E8B188F7ABE00273DEC /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; 64 | 87D02E8D188F7ABE00273DEC /* HPReorderTableViewTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = HPReorderTableViewTests.m; sourceTree = ""; }; 65 | 87D02E9B188F7B0D00273DEC /* HPReorderTableViewDemo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = HPReorderTableViewDemo.app; sourceTree = BUILT_PRODUCTS_DIR; }; 66 | 87D02E9D188F7B0D00273DEC /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; }; 67 | 87D02EA2188F7B0D00273DEC /* HPReorderTableViewDemo-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "HPReorderTableViewDemo-Info.plist"; sourceTree = ""; }; 68 | 87D02EA4188F7B0D00273DEC /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; 69 | 87D02EA6188F7B0D00273DEC /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 70 | 87D02EA8188F7B0D00273DEC /* HPReorderTableViewDemo-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "HPReorderTableViewDemo-Prefix.pch"; sourceTree = ""; }; 71 | 87D02EA9188F7B0D00273DEC /* HPAppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = HPAppDelegate.h; sourceTree = ""; }; 72 | 87D02EAA188F7B0D00273DEC /* HPAppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = HPAppDelegate.m; sourceTree = ""; }; 73 | 87D02EB2188F7B0E00273DEC /* HPViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = HPViewController.h; sourceTree = ""; }; 74 | 87D02EB3188F7B0E00273DEC /* HPViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = HPViewController.m; sourceTree = ""; }; 75 | /* End PBXFileReference section */ 76 | 77 | /* Begin PBXFrameworksBuildPhase section */ 78 | 87D02E6B188F7ABE00273DEC /* Frameworks */ = { 79 | isa = PBXFrameworksBuildPhase; 80 | buildActionMask = 2147483647; 81 | files = ( 82 | 87D02E72188F7ABE00273DEC /* Foundation.framework in Frameworks */, 83 | ); 84 | runOnlyForDeploymentPostprocessing = 0; 85 | }; 86 | 87D02E7B188F7ABE00273DEC /* Frameworks */ = { 87 | isa = PBXFrameworksBuildPhase; 88 | buildActionMask = 2147483647; 89 | files = ( 90 | 87D02E80188F7ABE00273DEC /* XCTest.framework in Frameworks */, 91 | 87D02E83188F7ABE00273DEC /* UIKit.framework in Frameworks */, 92 | 87D02E81188F7ABE00273DEC /* Foundation.framework in Frameworks */, 93 | 87D02E86188F7ABE00273DEC /* libHPReorderTableView.a in Frameworks */, 94 | ); 95 | runOnlyForDeploymentPostprocessing = 0; 96 | }; 97 | 87D02E98188F7B0D00273DEC /* Frameworks */ = { 98 | isa = PBXFrameworksBuildPhase; 99 | buildActionMask = 2147483647; 100 | files = ( 101 | 87D02E9E188F7B0D00273DEC /* CoreGraphics.framework in Frameworks */, 102 | 87D02E9F188F7B0D00273DEC /* UIKit.framework in Frameworks */, 103 | 87D02E9C188F7B0D00273DEC /* Foundation.framework in Frameworks */, 104 | ); 105 | runOnlyForDeploymentPostprocessing = 0; 106 | }; 107 | /* End PBXFrameworksBuildPhase section */ 108 | 109 | /* Begin PBXGroup section */ 110 | 87D02E65188F7ABE00273DEC = { 111 | isa = PBXGroup; 112 | children = ( 113 | 87D02E73188F7ABE00273DEC /* HPReorderTableView */, 114 | 87D02E87188F7ABE00273DEC /* HPReorderTableViewTests */, 115 | 87D02EA0188F7B0D00273DEC /* HPReorderTableViewDemo */, 116 | 87D02E70188F7ABE00273DEC /* Frameworks */, 117 | 87D02E6F188F7ABE00273DEC /* Products */, 118 | ); 119 | sourceTree = ""; 120 | }; 121 | 87D02E6F188F7ABE00273DEC /* Products */ = { 122 | isa = PBXGroup; 123 | children = ( 124 | 87D02E6E188F7ABE00273DEC /* libHPReorderTableView.a */, 125 | 87D02E7E188F7ABE00273DEC /* HPReorderTableViewTests.xctest */, 126 | 87D02E9B188F7B0D00273DEC /* HPReorderTableViewDemo.app */, 127 | ); 128 | name = Products; 129 | sourceTree = ""; 130 | }; 131 | 87D02E70188F7ABE00273DEC /* Frameworks */ = { 132 | isa = PBXGroup; 133 | children = ( 134 | 87D02E71188F7ABE00273DEC /* Foundation.framework */, 135 | 87D02E7F188F7ABE00273DEC /* XCTest.framework */, 136 | 87D02E82188F7ABE00273DEC /* UIKit.framework */, 137 | 87D02E9D188F7B0D00273DEC /* CoreGraphics.framework */, 138 | ); 139 | name = Frameworks; 140 | sourceTree = ""; 141 | }; 142 | 87D02E73188F7ABE00273DEC /* HPReorderTableView */ = { 143 | isa = PBXGroup; 144 | children = ( 145 | 87D02E76188F7ABE00273DEC /* HPReorderTableView.h */, 146 | 87D02E78188F7ABE00273DEC /* HPReorderTableView.m */, 147 | ); 148 | path = HPReorderTableView; 149 | sourceTree = ""; 150 | }; 151 | 87D02E87188F7ABE00273DEC /* HPReorderTableViewTests */ = { 152 | isa = PBXGroup; 153 | children = ( 154 | 87D02E8D188F7ABE00273DEC /* HPReorderTableViewTests.m */, 155 | 87D02E88188F7ABE00273DEC /* Supporting Files */, 156 | ); 157 | path = HPReorderTableViewTests; 158 | sourceTree = ""; 159 | }; 160 | 87D02E88188F7ABE00273DEC /* Supporting Files */ = { 161 | isa = PBXGroup; 162 | children = ( 163 | 87D02E89188F7ABE00273DEC /* HPReorderTableViewTests-Info.plist */, 164 | 87D02E8A188F7ABE00273DEC /* InfoPlist.strings */, 165 | ); 166 | name = "Supporting Files"; 167 | sourceTree = ""; 168 | }; 169 | 87D02EA0188F7B0D00273DEC /* HPReorderTableViewDemo */ = { 170 | isa = PBXGroup; 171 | children = ( 172 | 873E091518E843A9007E9675 /* Images.xcassets */, 173 | 87D02EA9188F7B0D00273DEC /* HPAppDelegate.h */, 174 | 87D02EAA188F7B0D00273DEC /* HPAppDelegate.m */, 175 | 87D02EB2188F7B0E00273DEC /* HPViewController.h */, 176 | 87D02EB3188F7B0E00273DEC /* HPViewController.m */, 177 | 87D02EA1188F7B0D00273DEC /* Supporting Files */, 178 | ); 179 | path = HPReorderTableViewDemo; 180 | sourceTree = ""; 181 | }; 182 | 87D02EA1188F7B0D00273DEC /* Supporting Files */ = { 183 | isa = PBXGroup; 184 | children = ( 185 | 87D02EA2188F7B0D00273DEC /* HPReorderTableViewDemo-Info.plist */, 186 | 87D02EA3188F7B0D00273DEC /* InfoPlist.strings */, 187 | 87D02EA6188F7B0D00273DEC /* main.m */, 188 | 87D02EA8188F7B0D00273DEC /* HPReorderTableViewDemo-Prefix.pch */, 189 | ); 190 | name = "Supporting Files"; 191 | sourceTree = ""; 192 | }; 193 | /* End PBXGroup section */ 194 | 195 | /* Begin PBXNativeTarget section */ 196 | 87D02E6D188F7ABE00273DEC /* HPReorderTableView */ = { 197 | isa = PBXNativeTarget; 198 | buildConfigurationList = 87D02E91188F7ABE00273DEC /* Build configuration list for PBXNativeTarget "HPReorderTableView" */; 199 | buildPhases = ( 200 | 87D02E6A188F7ABE00273DEC /* Sources */, 201 | 87D02E6B188F7ABE00273DEC /* Frameworks */, 202 | 87D02E6C188F7ABE00273DEC /* CopyFiles */, 203 | ); 204 | buildRules = ( 205 | ); 206 | dependencies = ( 207 | ); 208 | name = HPReorderTableView; 209 | productName = HPReorderTableView; 210 | productReference = 87D02E6E188F7ABE00273DEC /* libHPReorderTableView.a */; 211 | productType = "com.apple.product-type.library.static"; 212 | }; 213 | 87D02E7D188F7ABE00273DEC /* HPReorderTableViewTests */ = { 214 | isa = PBXNativeTarget; 215 | buildConfigurationList = 87D02E94188F7ABE00273DEC /* Build configuration list for PBXNativeTarget "HPReorderTableViewTests" */; 216 | buildPhases = ( 217 | 87D02E7A188F7ABE00273DEC /* Sources */, 218 | 87D02E7B188F7ABE00273DEC /* Frameworks */, 219 | 87D02E7C188F7ABE00273DEC /* Resources */, 220 | ); 221 | buildRules = ( 222 | ); 223 | dependencies = ( 224 | 87D02E85188F7ABE00273DEC /* PBXTargetDependency */, 225 | ); 226 | name = HPReorderTableViewTests; 227 | productName = HPReorderTableViewTests; 228 | productReference = 87D02E7E188F7ABE00273DEC /* HPReorderTableViewTests.xctest */; 229 | productType = "com.apple.product-type.bundle.unit-test"; 230 | }; 231 | 87D02E9A188F7B0D00273DEC /* HPReorderTableViewDemo */ = { 232 | isa = PBXNativeTarget; 233 | buildConfigurationList = 87D02ECD188F7B0E00273DEC /* Build configuration list for PBXNativeTarget "HPReorderTableViewDemo" */; 234 | buildPhases = ( 235 | 87D02E97188F7B0D00273DEC /* Sources */, 236 | 87D02E98188F7B0D00273DEC /* Frameworks */, 237 | 87D02E99188F7B0D00273DEC /* Resources */, 238 | ); 239 | buildRules = ( 240 | ); 241 | dependencies = ( 242 | ); 243 | name = HPReorderTableViewDemo; 244 | productName = HPReorderTableViewDemo; 245 | productReference = 87D02E9B188F7B0D00273DEC /* HPReorderTableViewDemo.app */; 246 | productType = "com.apple.product-type.application"; 247 | }; 248 | /* End PBXNativeTarget section */ 249 | 250 | /* Begin PBXProject section */ 251 | 87D02E66188F7ABE00273DEC /* Project object */ = { 252 | isa = PBXProject; 253 | attributes = { 254 | LastUpgradeCheck = 0510; 255 | ORGANIZATIONNAME = "Hermes Pique"; 256 | TargetAttributes = { 257 | 87D02E7D188F7ABE00273DEC = { 258 | TestTargetID = 87D02E9A188F7B0D00273DEC; 259 | }; 260 | }; 261 | }; 262 | buildConfigurationList = 87D02E69188F7ABE00273DEC /* Build configuration list for PBXProject "HPReorderTableView" */; 263 | compatibilityVersion = "Xcode 3.2"; 264 | developmentRegion = English; 265 | hasScannedForEncodings = 0; 266 | knownRegions = ( 267 | en, 268 | Base, 269 | ); 270 | mainGroup = 87D02E65188F7ABE00273DEC; 271 | productRefGroup = 87D02E6F188F7ABE00273DEC /* Products */; 272 | projectDirPath = ""; 273 | projectRoot = ""; 274 | targets = ( 275 | 87D02E6D188F7ABE00273DEC /* HPReorderTableView */, 276 | 87D02E7D188F7ABE00273DEC /* HPReorderTableViewTests */, 277 | 87D02E9A188F7B0D00273DEC /* HPReorderTableViewDemo */, 278 | ); 279 | }; 280 | /* End PBXProject section */ 281 | 282 | /* Begin PBXResourcesBuildPhase section */ 283 | 87D02E7C188F7ABE00273DEC /* Resources */ = { 284 | isa = PBXResourcesBuildPhase; 285 | buildActionMask = 2147483647; 286 | files = ( 287 | 87D02E8C188F7ABE00273DEC /* InfoPlist.strings in Resources */, 288 | ); 289 | runOnlyForDeploymentPostprocessing = 0; 290 | }; 291 | 87D02E99188F7B0D00273DEC /* Resources */ = { 292 | isa = PBXResourcesBuildPhase; 293 | buildActionMask = 2147483647; 294 | files = ( 295 | 87D02EA5188F7B0D00273DEC /* InfoPlist.strings in Resources */, 296 | 873E091618E843A9007E9675 /* Images.xcassets in Resources */, 297 | ); 298 | runOnlyForDeploymentPostprocessing = 0; 299 | }; 300 | /* End PBXResourcesBuildPhase section */ 301 | 302 | /* Begin PBXSourcesBuildPhase section */ 303 | 87D02E6A188F7ABE00273DEC /* Sources */ = { 304 | isa = PBXSourcesBuildPhase; 305 | buildActionMask = 2147483647; 306 | files = ( 307 | 87D02E79188F7ABE00273DEC /* HPReorderTableView.m in Sources */, 308 | ); 309 | runOnlyForDeploymentPostprocessing = 0; 310 | }; 311 | 87D02E7A188F7ABE00273DEC /* Sources */ = { 312 | isa = PBXSourcesBuildPhase; 313 | buildActionMask = 2147483647; 314 | files = ( 315 | 87D02E8E188F7ABE00273DEC /* HPReorderTableViewTests.m in Sources */, 316 | ); 317 | runOnlyForDeploymentPostprocessing = 0; 318 | }; 319 | 87D02E97188F7B0D00273DEC /* Sources */ = { 320 | isa = PBXSourcesBuildPhase; 321 | buildActionMask = 2147483647; 322 | files = ( 323 | 87D02EB4188F7B0E00273DEC /* HPViewController.m in Sources */, 324 | 87D02ECF188F7BC100273DEC /* HPReorderTableView.m in Sources */, 325 | 87D02EAB188F7B0D00273DEC /* HPAppDelegate.m in Sources */, 326 | 87D02EA7188F7B0D00273DEC /* main.m in Sources */, 327 | ); 328 | runOnlyForDeploymentPostprocessing = 0; 329 | }; 330 | /* End PBXSourcesBuildPhase section */ 331 | 332 | /* Begin PBXTargetDependency section */ 333 | 87D02E85188F7ABE00273DEC /* PBXTargetDependency */ = { 334 | isa = PBXTargetDependency; 335 | target = 87D02E6D188F7ABE00273DEC /* HPReorderTableView */; 336 | targetProxy = 87D02E84188F7ABE00273DEC /* PBXContainerItemProxy */; 337 | }; 338 | /* End PBXTargetDependency section */ 339 | 340 | /* Begin PBXVariantGroup section */ 341 | 87D02E8A188F7ABE00273DEC /* InfoPlist.strings */ = { 342 | isa = PBXVariantGroup; 343 | children = ( 344 | 87D02E8B188F7ABE00273DEC /* en */, 345 | ); 346 | name = InfoPlist.strings; 347 | sourceTree = ""; 348 | }; 349 | 87D02EA3188F7B0D00273DEC /* InfoPlist.strings */ = { 350 | isa = PBXVariantGroup; 351 | children = ( 352 | 87D02EA4188F7B0D00273DEC /* en */, 353 | ); 354 | name = InfoPlist.strings; 355 | sourceTree = ""; 356 | }; 357 | /* End PBXVariantGroup section */ 358 | 359 | /* Begin XCBuildConfiguration section */ 360 | 87D02E8F188F7ABE00273DEC /* Debug */ = { 361 | isa = XCBuildConfiguration; 362 | buildSettings = { 363 | ALWAYS_SEARCH_USER_PATHS = NO; 364 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 365 | CLANG_CXX_LIBRARY = "libc++"; 366 | CLANG_ENABLE_MODULES = YES; 367 | CLANG_ENABLE_OBJC_ARC = YES; 368 | CLANG_WARN_BOOL_CONVERSION = YES; 369 | CLANG_WARN_CONSTANT_CONVERSION = YES; 370 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 371 | CLANG_WARN_EMPTY_BODY = YES; 372 | CLANG_WARN_ENUM_CONVERSION = YES; 373 | CLANG_WARN_INT_CONVERSION = YES; 374 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 375 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 376 | COPY_PHASE_STRIP = NO; 377 | GCC_C_LANGUAGE_STANDARD = gnu99; 378 | GCC_DYNAMIC_NO_PIC = NO; 379 | GCC_OPTIMIZATION_LEVEL = 0; 380 | GCC_PREPROCESSOR_DEFINITIONS = ( 381 | "DEBUG=1", 382 | "$(inherited)", 383 | ); 384 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 385 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 386 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 387 | GCC_WARN_UNDECLARED_SELECTOR = YES; 388 | GCC_WARN_UNINITIALIZED_AUTOS = YES; 389 | GCC_WARN_UNUSED_FUNCTION = YES; 390 | GCC_WARN_UNUSED_VARIABLE = YES; 391 | IPHONEOS_DEPLOYMENT_TARGET = 7.0; 392 | ONLY_ACTIVE_ARCH = YES; 393 | SDKROOT = iphoneos; 394 | }; 395 | name = Debug; 396 | }; 397 | 87D02E90188F7ABE00273DEC /* Release */ = { 398 | isa = XCBuildConfiguration; 399 | buildSettings = { 400 | ALWAYS_SEARCH_USER_PATHS = NO; 401 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 402 | CLANG_CXX_LIBRARY = "libc++"; 403 | CLANG_ENABLE_MODULES = YES; 404 | CLANG_ENABLE_OBJC_ARC = YES; 405 | CLANG_WARN_BOOL_CONVERSION = YES; 406 | CLANG_WARN_CONSTANT_CONVERSION = YES; 407 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 408 | CLANG_WARN_EMPTY_BODY = YES; 409 | CLANG_WARN_ENUM_CONVERSION = YES; 410 | CLANG_WARN_INT_CONVERSION = YES; 411 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 412 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 413 | COPY_PHASE_STRIP = YES; 414 | ENABLE_NS_ASSERTIONS = NO; 415 | GCC_C_LANGUAGE_STANDARD = gnu99; 416 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 417 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 418 | GCC_WARN_UNDECLARED_SELECTOR = YES; 419 | GCC_WARN_UNINITIALIZED_AUTOS = YES; 420 | GCC_WARN_UNUSED_FUNCTION = YES; 421 | GCC_WARN_UNUSED_VARIABLE = YES; 422 | IPHONEOS_DEPLOYMENT_TARGET = 7.0; 423 | SDKROOT = iphoneos; 424 | VALIDATE_PRODUCT = YES; 425 | }; 426 | name = Release; 427 | }; 428 | 87D02E92188F7ABE00273DEC /* Debug */ = { 429 | isa = XCBuildConfiguration; 430 | buildSettings = { 431 | DSTROOT = /tmp/HPReorderTableView.dst; 432 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 433 | OTHER_LDFLAGS = "-ObjC"; 434 | PRODUCT_NAME = "$(TARGET_NAME)"; 435 | SKIP_INSTALL = YES; 436 | }; 437 | name = Debug; 438 | }; 439 | 87D02E93188F7ABE00273DEC /* Release */ = { 440 | isa = XCBuildConfiguration; 441 | buildSettings = { 442 | DSTROOT = /tmp/HPReorderTableView.dst; 443 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 444 | OTHER_LDFLAGS = "-ObjC"; 445 | PRODUCT_NAME = "$(TARGET_NAME)"; 446 | SKIP_INSTALL = YES; 447 | }; 448 | name = Release; 449 | }; 450 | 87D02E95188F7ABE00273DEC /* Debug */ = { 451 | isa = XCBuildConfiguration; 452 | buildSettings = { 453 | FRAMEWORK_SEARCH_PATHS = ( 454 | "$(SDKROOT)/Developer/Library/Frameworks", 455 | "$(inherited)", 456 | "$(DEVELOPER_FRAMEWORKS_DIR)", 457 | ); 458 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 459 | GCC_PREPROCESSOR_DEFINITIONS = ( 460 | "DEBUG=1", 461 | "$(inherited)", 462 | ); 463 | INFOPLIST_FILE = "HPReorderTableViewTests/HPReorderTableViewTests-Info.plist"; 464 | PRODUCT_NAME = "$(TARGET_NAME)"; 465 | TEST_HOST = "$(BUNDLE_LOADER)"; 466 | WRAPPER_EXTENSION = xctest; 467 | }; 468 | name = Debug; 469 | }; 470 | 87D02E96188F7ABE00273DEC /* Release */ = { 471 | isa = XCBuildConfiguration; 472 | buildSettings = { 473 | FRAMEWORK_SEARCH_PATHS = ( 474 | "$(SDKROOT)/Developer/Library/Frameworks", 475 | "$(inherited)", 476 | "$(DEVELOPER_FRAMEWORKS_DIR)", 477 | ); 478 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 479 | INFOPLIST_FILE = "HPReorderTableViewTests/HPReorderTableViewTests-Info.plist"; 480 | PRODUCT_NAME = "$(TARGET_NAME)"; 481 | TEST_HOST = "$(BUNDLE_LOADER)"; 482 | WRAPPER_EXTENSION = xctest; 483 | }; 484 | name = Release; 485 | }; 486 | 87D02EC9188F7B0E00273DEC /* Debug */ = { 487 | isa = XCBuildConfiguration; 488 | buildSettings = { 489 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 490 | ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; 491 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 492 | FRAMEWORK_SEARCH_PATHS = ( 493 | "$(inherited)", 494 | "$(DEVELOPER_FRAMEWORKS_DIR)", 495 | ); 496 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 497 | GCC_PREFIX_HEADER = "HPReorderTableViewDemo/HPReorderTableViewDemo-Prefix.pch"; 498 | GCC_PREPROCESSOR_DEFINITIONS = ( 499 | "DEBUG=1", 500 | "$(inherited)", 501 | ); 502 | INFOPLIST_FILE = "HPReorderTableViewDemo/HPReorderTableViewDemo-Info.plist"; 503 | IPHONEOS_DEPLOYMENT_TARGET = 6.0; 504 | PRODUCT_NAME = "$(TARGET_NAME)"; 505 | TARGETED_DEVICE_FAMILY = "1,2"; 506 | WRAPPER_EXTENSION = app; 507 | }; 508 | name = Debug; 509 | }; 510 | 87D02ECA188F7B0E00273DEC /* Release */ = { 511 | isa = XCBuildConfiguration; 512 | buildSettings = { 513 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 514 | ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; 515 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 516 | FRAMEWORK_SEARCH_PATHS = ( 517 | "$(inherited)", 518 | "$(DEVELOPER_FRAMEWORKS_DIR)", 519 | ); 520 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 521 | GCC_PREFIX_HEADER = "HPReorderTableViewDemo/HPReorderTableViewDemo-Prefix.pch"; 522 | INFOPLIST_FILE = "HPReorderTableViewDemo/HPReorderTableViewDemo-Info.plist"; 523 | IPHONEOS_DEPLOYMENT_TARGET = 6.0; 524 | PRODUCT_NAME = "$(TARGET_NAME)"; 525 | TARGETED_DEVICE_FAMILY = "1,2"; 526 | WRAPPER_EXTENSION = app; 527 | }; 528 | name = Release; 529 | }; 530 | /* End XCBuildConfiguration section */ 531 | 532 | /* Begin XCConfigurationList section */ 533 | 87D02E69188F7ABE00273DEC /* Build configuration list for PBXProject "HPReorderTableView" */ = { 534 | isa = XCConfigurationList; 535 | buildConfigurations = ( 536 | 87D02E8F188F7ABE00273DEC /* Debug */, 537 | 87D02E90188F7ABE00273DEC /* Release */, 538 | ); 539 | defaultConfigurationIsVisible = 0; 540 | defaultConfigurationName = Release; 541 | }; 542 | 87D02E91188F7ABE00273DEC /* Build configuration list for PBXNativeTarget "HPReorderTableView" */ = { 543 | isa = XCConfigurationList; 544 | buildConfigurations = ( 545 | 87D02E92188F7ABE00273DEC /* Debug */, 546 | 87D02E93188F7ABE00273DEC /* Release */, 547 | ); 548 | defaultConfigurationIsVisible = 0; 549 | defaultConfigurationName = Release; 550 | }; 551 | 87D02E94188F7ABE00273DEC /* Build configuration list for PBXNativeTarget "HPReorderTableViewTests" */ = { 552 | isa = XCConfigurationList; 553 | buildConfigurations = ( 554 | 87D02E95188F7ABE00273DEC /* Debug */, 555 | 87D02E96188F7ABE00273DEC /* Release */, 556 | ); 557 | defaultConfigurationIsVisible = 0; 558 | defaultConfigurationName = Release; 559 | }; 560 | 87D02ECD188F7B0E00273DEC /* Build configuration list for PBXNativeTarget "HPReorderTableViewDemo" */ = { 561 | isa = XCConfigurationList; 562 | buildConfigurations = ( 563 | 87D02EC9188F7B0E00273DEC /* Debug */, 564 | 87D02ECA188F7B0E00273DEC /* Release */, 565 | ); 566 | defaultConfigurationIsVisible = 0; 567 | defaultConfigurationName = Release; 568 | }; 569 | /* End XCConfigurationList section */ 570 | }; 571 | rootObject = 87D02E66188F7ABE00273DEC /* Project object */; 572 | } 573 | -------------------------------------------------------------------------------- /HPReorderTableView.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /HPReorderTableView.xcodeproj/xcshareddata/xcschemes/HPReorderTableView.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 33 | 39 | 40 | 41 | 42 | 43 | 52 | 53 | 54 | 55 | 61 | 62 | 64 | 65 | 68 | 69 | 70 | -------------------------------------------------------------------------------- /HPReorderTableView.xcodeproj/xcshareddata/xcschemes/HPReorderTableViewDemo.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 33 | 39 | 40 | 41 | 42 | 43 | 49 | 50 | 51 | 52 | 61 | 62 | 68 | 69 | 70 | 71 | 72 | 73 | 79 | 80 | 86 | 87 | 88 | 89 | 91 | 92 | 95 | 96 | 97 | -------------------------------------------------------------------------------- /HPReorderTableView/HPReorderTableView.h: -------------------------------------------------------------------------------- 1 | // 2 | // HPReorderTableView.h 3 | // 4 | // Created by Hermes Pique on 22/01/14. 5 | // Copyright (c) 2014 Hermes Pique 6 | // 7 | // Licensed under the Apache License, Version 2.0 (the "License"); 8 | // you may not use this file except in compliance with the License. 9 | // You may obtain a copy of the License at 10 | // 11 | // http://www.apache.org/licenses/LICENSE-2.0 12 | // 13 | // Unless required by applicable law or agreed to in writing, software 14 | // distributed under the License is distributed on an "AS IS" BASIS, 15 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | // See the License for the specific language governing permissions and 17 | // limitations under the License. 18 | // 19 | 20 | #import 21 | 22 | @protocol HPReorderTableViewDelegate 23 | @optional 24 | - (void)tableView:(UITableView *)tableView didEndReorderingRowAtIndexPath:(NSIndexPath *)indexPath; 25 | @end 26 | 27 | /** 28 | Table view with drag-and-drop reordering of cells. 29 | @discussion If you need the default swipe-to-delete functionality, use @c HPReorderAndSwipeToDeleteTableView instead. 30 | */ 31 | @interface HPReorderTableView : UITableView 32 | 33 | @property (nonatomic, assign) id delegate; 34 | 35 | /** Set enabled to NO to disable reordering functionality. You can also provide a UIGestureRecognizerDelegate and implement gestureRecognizerShouldBegin:. 36 | If you need to limit the area in which touches can trigger reordering, implement gestureRecognizer:shouldReceiveTouch:. 37 | */ 38 | @property (nonatomic, readonly) UILongPressGestureRecognizer *reorderGestureRecognizer; 39 | 40 | /** 41 | To configure the appearence of the drag view (e.g., its shadow properties). 42 | */ 43 | @property (nonatomic, readonly) UIView *reorderDragView; 44 | 45 | /** 46 | Provide your UITableViewCell subclass to set the appeareance of the temporary empty cell during dragging. An empty UITableViewCell is used by default, which leaves the empty space white. 47 | */ 48 | - (void)registerTemporaryEmptyCellClass:(Class)cellClass; 49 | 50 | @end 51 | 52 | /** 53 | Table view with drag-and-drop reordering of cells and swipe-to-delete enabled. 54 | @discussion Due to an unfortunate UITableView design decision the only way for @c HPReorderTableView to support the default swipe-to-delete functionality is to enable it by default. This is different from the standard UITableView behavior, thus the need for a separate class to use the swipe-to-delete feature. See https://github.com/hpique/HPReorderTableView/issues/4 for more information. 55 | **/ 56 | @interface HPReorderAndSwipeToDeleteTableView : HPReorderTableView 57 | 58 | @end 59 | -------------------------------------------------------------------------------- /HPReorderTableView/HPReorderTableView.m: -------------------------------------------------------------------------------- 1 | 2 | // 3 | // HPReorderTableView.m 4 | // 5 | // Created by Hermes Pique on 22/01/14. 6 | // Copyright (c) 2014 Hermes Pique 7 | // 8 | // Licensed under the Apache License, Version 2.0 (the "License"); 9 | // you may not use this file except in compliance with the License. 10 | // You may obtain a copy of the License at 11 | // 12 | // http://www.apache.org/licenses/LICENSE-2.0 13 | // 14 | // Unless required by applicable law or agreed to in writing, software 15 | // distributed under the License is distributed on an "AS IS" BASIS, 16 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | // See the License for the specific language governing permissions and 18 | // limitations under the License. 19 | // 20 | 21 | #import "HPReorderTableView.h" 22 | 23 | @interface HPReorderTableView(Subclassing) 24 | 25 | @property (nonatomic, readonly) id hp_realDataSource; 26 | 27 | @end 28 | 29 | @interface HPReorderTableView() 30 | 31 | @end 32 | 33 | @implementation HPReorderTableView { 34 | UIImageView *_reorderDragView; 35 | __weak id _realDataSource; 36 | NSIndexPath *_reorderInitialIndexPath; 37 | NSIndexPath *_reorderCurrentIndexPath; 38 | CADisplayLink *_scrollDisplayLink; 39 | CGFloat _scrollRate; 40 | CGFloat _reorderDragViewShadowOpacity; 41 | } 42 | 43 | @dynamic delegate; 44 | 45 | static NSTimeInterval HPReorderTableViewAnimationDuration = 0.2; 46 | 47 | static NSString *HPReorderTableViewCellReuseIdentifier = @"HPReorderTableViewCellReuseIdentifier"; 48 | 49 | @synthesize reorderDragView = _reorderDragView; 50 | 51 | - (id)initWithCoder:(NSCoder *)aDecoder 52 | { 53 | if (self = [super initWithCoder:aDecoder]) 54 | { 55 | [self initHelper]; 56 | } 57 | return self; 58 | } 59 | 60 | - (id)initWithFrame:(CGRect)frame style:(UITableViewStyle)style 61 | { 62 | if (self = [super initWithFrame:frame style:style]) 63 | { 64 | [self initHelper]; 65 | } 66 | return self; 67 | } 68 | 69 | - (void)initHelper 70 | { 71 | _reorderGestureRecognizer = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(recognizeLongPressGestureRecognizer:)]; 72 | [self addGestureRecognizer:_reorderGestureRecognizer]; 73 | 74 | _reorderDragView = [[UIImageView alloc] init]; 75 | _reorderDragView.layer.shadowColor = [UIColor blackColor].CGColor; 76 | _reorderDragView.layer.shadowRadius = 2; 77 | _reorderDragView.layer.shadowOpacity = 0.5; 78 | _reorderDragView.layer.shadowOffset = CGSizeMake(0, 0); 79 | _reorderDragView.layer.masksToBounds = NO; 80 | 81 | // Data Source forwarding 82 | [super setDataSource:self]; 83 | [self registerTemporaryEmptyCellClass:[UITableViewCell class]]; 84 | } 85 | 86 | #pragma mark - Public 87 | 88 | - (void)registerTemporaryEmptyCellClass:(Class)cellClass 89 | { 90 | [self registerClass:cellClass forCellReuseIdentifier:HPReorderTableViewCellReuseIdentifier]; 91 | } 92 | 93 | #pragma mark - Actions 94 | 95 | - (void)recognizeLongPressGestureRecognizer:(UILongPressGestureRecognizer*)gestureRecognizer 96 | { 97 | if (![self hasRows]) 98 | { 99 | HPGestureRecognizerCancel(gestureRecognizer); 100 | return; 101 | } 102 | 103 | switch (gestureRecognizer.state) 104 | { 105 | case UIGestureRecognizerStateBegan: 106 | [self didBeginLongPressGestureRecognizer:gestureRecognizer]; 107 | break; 108 | case UIGestureRecognizerStateChanged: 109 | [self didChangeLongPressGestureRecognizer:gestureRecognizer]; 110 | break; 111 | case UIGestureRecognizerStateEnded: 112 | [self didEndLongPressGestureRecognizer:gestureRecognizer]; 113 | default: 114 | break; 115 | } 116 | } 117 | 118 | #pragma mark - UITableViewDataSource 119 | 120 | - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath 121 | { 122 | if (_reorderCurrentIndexPath && [_reorderCurrentIndexPath compare:indexPath] == NSOrderedSame) 123 | { 124 | UITableViewCell *cell = [self dequeueReusableCellWithIdentifier:HPReorderTableViewCellReuseIdentifier]; 125 | cell.accessoryType = UITableViewCellAccessoryNone; 126 | return cell; 127 | } 128 | else 129 | { 130 | return [_realDataSource tableView:tableView cellForRowAtIndexPath:indexPath]; 131 | } 132 | } 133 | 134 | - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section 135 | { 136 | return [_realDataSource tableView:self numberOfRowsInSection:section]; 137 | } 138 | 139 | #pragma mark - Data Source Forwarding 140 | 141 | - (void)dealloc 142 | { // Data Source forwarding 143 | self.delegate = nil; 144 | } 145 | 146 | - (void)forwardInvocation:(NSInvocation *)invocation 147 | { 148 | if ([_realDataSource respondsToSelector:invocation.selector]) 149 | { 150 | [invocation invokeWithTarget:_realDataSource]; 151 | } 152 | else 153 | { 154 | [super forwardInvocation:invocation]; 155 | } 156 | } 157 | 158 | - (NSMethodSignature *)methodSignatureForSelector:(SEL)s 159 | { 160 | return [super methodSignatureForSelector:s] ?: [(id)_realDataSource methodSignatureForSelector:s]; 161 | } 162 | 163 | - (BOOL)respondsToSelector:(SEL)s 164 | { 165 | return [super respondsToSelector:s] || [_realDataSource respondsToSelector:s]; 166 | } 167 | 168 | - (void)setDataSource:(id)dataSource 169 | { // Data Source forwarding 170 | [super setDataSource:dataSource ? self : nil]; 171 | _realDataSource = dataSource != self ? dataSource : nil; 172 | } 173 | 174 | #pragma mark - Utils 175 | 176 | - (BOOL)canMoveRowAtIndexPath:(NSIndexPath*)indexPath 177 | { 178 | return ![self.dataSource respondsToSelector:@selector(tableView:canMoveRowAtIndexPath:)] || [self.dataSource tableView:self canMoveRowAtIndexPath:indexPath]; 179 | } 180 | 181 | - (BOOL)hasRows 182 | { 183 | NSInteger sectionCount = [self numberOfSections]; 184 | for (NSInteger i = 0; i < sectionCount; i++) 185 | { 186 | if ([self numberOfRowsInSection:i] > 0) return YES; 187 | } 188 | return NO; 189 | } 190 | 191 | static UIImage* HPImageFromView(UIView *view) 192 | { 193 | UIGraphicsBeginImageContextWithOptions(view.bounds.size, YES, 0); 194 | [view.layer renderInContext:UIGraphicsGetCurrentContext()]; 195 | UIImage *image = UIGraphicsGetImageFromCurrentImageContext(); 196 | UIGraphicsEndImageContext(); 197 | return image; 198 | } 199 | 200 | static void HPGestureRecognizerCancel(UIGestureRecognizer *gestureRecognizer) 201 | { // See: http://stackoverflow.com/a/4167471/143378 202 | gestureRecognizer.enabled = NO; 203 | gestureRecognizer.enabled = YES; 204 | } 205 | 206 | #pragma mark - Private 207 | 208 | - (void)animateShadowOpacityFromValue:(CGFloat)fromValue toValue:(CGFloat)toValue 209 | { 210 | CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:NSStringFromSelector(@selector(shadowOpacity))]; 211 | animation.fromValue = [NSNumber numberWithFloat:fromValue]; 212 | animation.toValue = [NSNumber numberWithFloat:toValue]; 213 | animation.duration = HPReorderTableViewAnimationDuration; 214 | [_reorderDragView.layer addAnimation:animation forKey:NSStringFromSelector(@selector(shadowOpacity))]; 215 | _reorderDragViewShadowOpacity = _reorderDragView.layer.shadowOpacity; 216 | _reorderDragView.layer.shadowOpacity = toValue; 217 | } 218 | 219 | - (void)didBeginLongPressGestureRecognizer:(UILongPressGestureRecognizer*)gestureRecognizer 220 | { 221 | const CGPoint location = [gestureRecognizer locationInView:self]; 222 | NSIndexPath *indexPath = [self indexPathForRowAtPoint:location]; 223 | if (indexPath == nil || ![self canMoveRowAtIndexPath:indexPath]) 224 | { 225 | HPGestureRecognizerCancel(gestureRecognizer); 226 | return; 227 | } 228 | 229 | UITableViewCell *cell = [self cellForRowAtIndexPath:indexPath]; 230 | [cell setSelected:NO animated:NO]; 231 | [cell setHighlighted:NO animated:NO]; 232 | 233 | UIImage *image = HPImageFromView(cell); 234 | _reorderDragView.image = image; 235 | 236 | CGRect cellRect = [self rectForRowAtIndexPath:indexPath]; 237 | _reorderDragView.frame = CGRectOffset(CGRectMake(0, 0, image.size.width, image.size.height), cellRect.origin.x, cellRect.origin.y); 238 | [self addSubview:_reorderDragView]; 239 | if (_reorderDragView.layer.shadowOpacity == 0) 240 | { 241 | _reorderDragView.layer.shadowOpacity = _reorderDragViewShadowOpacity; 242 | } 243 | 244 | _reorderInitialIndexPath = indexPath; 245 | _reorderCurrentIndexPath = indexPath; 246 | 247 | [self animateShadowOpacityFromValue:0 toValue:_reorderDragView.layer.shadowOpacity]; 248 | [UIView animateWithDuration:HPReorderTableViewAnimationDuration animations:^{ 249 | _reorderDragView.center = CGPointMake(self.center.x, location.y); 250 | }]; 251 | 252 | [self reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationNone]; 253 | 254 | _scrollDisplayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(scrollTableWithCell:)]; 255 | [_scrollDisplayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode]; 256 | } 257 | 258 | - (void)didEndLongPressGestureRecognizer:(UILongPressGestureRecognizer*)gestureRecognizer 259 | { 260 | if (!_reorderCurrentIndexPath) 261 | { 262 | HPGestureRecognizerCancel(gestureRecognizer); 263 | return; 264 | } 265 | 266 | NSIndexPath *indexPath = _reorderCurrentIndexPath; 267 | 268 | { // Reset 269 | [_scrollDisplayLink invalidate]; 270 | _scrollDisplayLink = nil; 271 | _scrollRate = 0; 272 | _reorderCurrentIndexPath = nil; 273 | _reorderInitialIndexPath = nil; 274 | } 275 | 276 | [self animateShadowOpacityFromValue:_reorderDragView.layer.shadowOpacity toValue:0]; 277 | 278 | [UIView animateWithDuration:HPReorderTableViewAnimationDuration 279 | animations:^{ 280 | CGRect rect = [self rectForRowAtIndexPath:indexPath]; 281 | _reorderDragView.frame = CGRectOffset(_reorderDragView.bounds, rect.origin.x, rect.origin.y); 282 | } completion:^(BOOL finished) { 283 | [self reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationNone]; 284 | [self performSelector:@selector(removeReorderDragView) withObject:nil afterDelay:0]; // Prevent flicker 285 | if ([self.delegate respondsToSelector:@selector(tableView: didEndReorderingRowAtIndexPath:)]) { 286 | [self.delegate tableView:self didEndReorderingRowAtIndexPath:indexPath]; 287 | } 288 | }]; 289 | } 290 | 291 | - (void)removeReorderDragView 292 | { 293 | [_reorderDragView removeFromSuperview]; 294 | } 295 | 296 | - (void)reorderCurrentRowToIndexPath:(NSIndexPath*)toIndexPath 297 | { 298 | [self beginUpdates]; 299 | [self moveRowAtIndexPath:toIndexPath toIndexPath:_reorderCurrentIndexPath]; // Order is important to keep the empty cell behind 300 | if ([self.dataSource respondsToSelector:@selector(tableView:moveRowAtIndexPath:toIndexPath:)]) 301 | { 302 | [self.dataSource tableView:self moveRowAtIndexPath:_reorderCurrentIndexPath toIndexPath:toIndexPath]; 303 | } 304 | _reorderCurrentIndexPath = toIndexPath; 305 | [self endUpdates]; 306 | } 307 | 308 | #pragma mark Subclassing 309 | 310 | - (id)hp_realDataSource 311 | { 312 | return _realDataSource; 313 | } 314 | 315 | #pragma mark After BVReorderTableView 316 | // Taken from https://github.com/bvogelzang/BVReorderTableView/blob/master/BVReorderTableView.m with minor modifications 317 | // 318 | // BVReorderTableView.m 319 | // 320 | // Copyright (c) 2013 Ben Vogelzang. 321 | // 322 | // Permission is hereby granted, free of charge, to any person obtaining a copy 323 | // of this software and associated documentation files (the "Software"), to deal 324 | // in the Software without restriction, including without limitation the rights 325 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 326 | // copies of the Software, and to permit persons to whom the Software is 327 | // furnished to do so, subject to the following conditions: 328 | // 329 | // The above copyright notice and this permission notice shall be included in 330 | // all copies or substantial portions of the Software. 331 | // 332 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 333 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 334 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 335 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 336 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 337 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 338 | // THE SOFTWARE. 339 | 340 | - (void)didChangeLongPressGestureRecognizer:(UILongPressGestureRecognizer*)gestureRecognizer 341 | { 342 | const CGPoint location = [gestureRecognizer locationInView:self]; 343 | 344 | // update position of the drag view 345 | // don't let it go past the top or the bottom too far 346 | if (location.y >= 0 && location.y <= self.contentSize.height + 50) 347 | { 348 | _reorderDragView.center = CGPointMake(self.center.x, location.y); 349 | } 350 | 351 | CGRect rect = self.bounds; 352 | // adjust rect for content inset as we will use it below for calculating scroll zones 353 | rect.size.height -= self.contentInset.top; 354 | 355 | [self updateCurrentLocation:gestureRecognizer]; 356 | 357 | // tell us if we should scroll and which direction 358 | CGFloat scrollZoneHeight = rect.size.height / 6; 359 | CGFloat bottomScrollBeginning = self.contentOffset.y + self.contentInset.top + rect.size.height - scrollZoneHeight; 360 | CGFloat topScrollBeginning = self.contentOffset.y + self.contentInset.top + scrollZoneHeight; 361 | 362 | // we're in the bottom zone 363 | if (location.y >= bottomScrollBeginning) 364 | { 365 | _scrollRate = (location.y - bottomScrollBeginning) / scrollZoneHeight; 366 | } 367 | // we're in the top zone 368 | else if (location.y <= topScrollBeginning) 369 | { 370 | _scrollRate = (location.y - topScrollBeginning) / scrollZoneHeight; 371 | } 372 | else 373 | { 374 | _scrollRate = 0; 375 | } 376 | } 377 | 378 | - (void)scrollTableWithCell:(NSTimer *)timer 379 | { 380 | UILongPressGestureRecognizer *gesture = self.reorderGestureRecognizer; 381 | const CGPoint location = [gesture locationInView:self]; 382 | 383 | CGPoint currentOffset = self.contentOffset; 384 | CGPoint newOffset = CGPointMake(currentOffset.x, currentOffset.y + _scrollRate * 10); 385 | 386 | if (newOffset.y < -self.contentInset.top) 387 | { 388 | newOffset.y = -self.contentInset.top; 389 | } 390 | else if (self.contentSize.height + self.contentInset.bottom < self.frame.size.height) 391 | { 392 | newOffset = currentOffset; 393 | } 394 | else if (newOffset.y > (self.contentSize.height + self.contentInset.bottom) - self.frame.size.height) 395 | { 396 | newOffset.y = (self.contentSize.height + self.contentInset.bottom) - self.frame.size.height; 397 | } 398 | 399 | [self setContentOffset:newOffset]; 400 | 401 | if (location.y >= 0 && location.y <= self.contentSize.height + 50) 402 | { 403 | _reorderDragView.center = CGPointMake(self.center.x, location.y); 404 | } 405 | 406 | [self updateCurrentLocation:gesture]; 407 | } 408 | 409 | - (void)updateCurrentLocation:(UILongPressGestureRecognizer *)gesture 410 | { 411 | const CGPoint location = [gesture locationInView:self]; 412 | NSIndexPath *toIndexPath = [self indexPathForRowAtPoint:location]; 413 | 414 | if ([self.delegate respondsToSelector:@selector(tableView:targetIndexPathForMoveFromRowAtIndexPath:toProposedIndexPath:)]) 415 | { 416 | toIndexPath = [self.delegate tableView:self targetIndexPathForMoveFromRowAtIndexPath:_reorderInitialIndexPath toProposedIndexPath:toIndexPath]; 417 | } 418 | 419 | if ([toIndexPath compare:_reorderCurrentIndexPath] == NSOrderedSame) return; 420 | 421 | NSInteger originalHeight = _reorderDragView.frame.size.height; 422 | NSInteger toHeight = [self rectForRowAtIndexPath:toIndexPath].size.height; 423 | UITableViewCell *toCell = [self cellForRowAtIndexPath:toIndexPath]; 424 | const CGPoint toCellLocation = [gesture locationInView:toCell]; 425 | 426 | if (toCellLocation.y <= toHeight - originalHeight) return; 427 | 428 | [self reorderCurrentRowToIndexPath:toIndexPath]; 429 | } 430 | 431 | @end 432 | 433 | @implementation HPReorderAndSwipeToDeleteTableView 434 | 435 | -(void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath 436 | { 437 | if ([self.hp_realDataSource respondsToSelector:@selector(tableView:commitEditingStyle:forRowAtIndexPath:)]) 438 | { 439 | return [self.hp_realDataSource tableView:tableView commitEditingStyle:editingStyle forRowAtIndexPath:indexPath]; 440 | } 441 | } 442 | 443 | @end 444 | -------------------------------------------------------------------------------- /HPReorderTableViewDemo/HPAppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // HPAppDelegate.h 3 | // HPReorderTableViewDemo 4 | // 5 | // Created by Hermes on 22/01/14. 6 | // Copyright (c) 2014 Hermes Pique. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface HPAppDelegate : UIResponder 12 | 13 | @property (strong, nonatomic) UIWindow *window; 14 | 15 | @end 16 | -------------------------------------------------------------------------------- /HPReorderTableViewDemo/HPAppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // HPAppDelegate.m 3 | // HPReorderTableViewDemo 4 | // 5 | // Created by Hermes on 22/01/14. 6 | // Copyright (c) 2014 Hermes Pique. All rights reserved. 7 | // 8 | 9 | #import "HPAppDelegate.h" 10 | #import "HPViewController.h" 11 | 12 | @implementation HPAppDelegate 13 | 14 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions 15 | { 16 | self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; 17 | self.window.rootViewController = [HPViewController new]; 18 | [self.window makeKeyAndVisible]; 19 | return YES; 20 | } 21 | 22 | @end 23 | -------------------------------------------------------------------------------- /HPReorderTableViewDemo/HPReorderTableViewDemo-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleDisplayName 8 | ${PRODUCT_NAME} 9 | CFBundleExecutable 10 | ${EXECUTABLE_NAME} 11 | CFBundleIdentifier 12 | com.hique.${PRODUCT_NAME:rfc1034identifier} 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | ${PRODUCT_NAME} 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | 1.0 25 | LSRequiresIPhoneOS 26 | 27 | UIRequiredDeviceCapabilities 28 | 29 | armv7 30 | 31 | UIStatusBarHidden 32 | 33 | UISupportedInterfaceOrientations 34 | 35 | UIInterfaceOrientationPortrait 36 | UIInterfaceOrientationLandscapeLeft 37 | UIInterfaceOrientationLandscapeRight 38 | 39 | UISupportedInterfaceOrientations~ipad 40 | 41 | UIInterfaceOrientationPortrait 42 | UIInterfaceOrientationPortraitUpsideDown 43 | UIInterfaceOrientationLandscapeLeft 44 | UIInterfaceOrientationLandscapeRight 45 | 46 | UIViewControllerBasedStatusBarAppearance 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /HPReorderTableViewDemo/HPReorderTableViewDemo-Prefix.pch: -------------------------------------------------------------------------------- 1 | // 2 | // Prefix header 3 | // 4 | // The contents of this file are implicitly included at the beginning of every source file. 5 | // 6 | 7 | #import 8 | 9 | #ifndef __IPHONE_5_0 10 | #warning "This project uses features only available in iOS SDK 5.0 and later." 11 | #endif 12 | 13 | #ifdef __OBJC__ 14 | #import 15 | #import 16 | #endif 17 | -------------------------------------------------------------------------------- /HPReorderTableViewDemo/HPViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // HPViewController.h 3 | // HPReorderTableViewDemo 4 | // 5 | // Created by Hermes on 22/01/14. 6 | // Copyright (c) 2014 Hermes Pique. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface HPViewController : UIViewController 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /HPReorderTableViewDemo/HPViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // HPViewController.m 3 | // HPReorderTableViewDemo 4 | // 5 | // Created by Hermes on 22/01/14. 6 | // Copyright (c) 2014 Hermes Pique. All rights reserved. 7 | // 8 | 9 | #import "HPViewController.h" 10 | #import "HPReorderTableView.h" 11 | 12 | @interface HPViewController () 13 | 14 | @end 15 | 16 | @implementation HPViewController 17 | { 18 | NSMutableArray *_objects; 19 | UITableView *_tableView; 20 | } 21 | 22 | - (void)viewDidLoad 23 | { 24 | [super viewDidLoad]; 25 | [self loadData]; 26 | 27 | _tableView = [[HPReorderTableView alloc] initWithFrame:self.view.bounds]; // Here is where the magic happens 28 | _tableView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; 29 | _tableView.separatorStyle = UITableViewCellSeparatorStyleNone; 30 | _tableView.dataSource = self; 31 | [_tableView registerClass:UITableViewCell.class forCellReuseIdentifier:@"Cell"]; 32 | [self.view addSubview:_tableView]; 33 | } 34 | 35 | #pragma mark UITableViewDataSource 36 | 37 | - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section 38 | { 39 | return _objects.count; 40 | } 41 | 42 | - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath 43 | { 44 | UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"Cell"]; 45 | NSArray *object = _objects[indexPath.row]; 46 | cell.textLabel.text = object[0]; 47 | cell.contentView.backgroundColor = object[1]; 48 | return cell; 49 | } 50 | 51 | - (void)tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)sourceIndexPath toIndexPath:(NSIndexPath *)destinationIndexPath 52 | { 53 | [_objects exchangeObjectAtIndex:sourceIndexPath.row withObjectAtIndex:destinationIndexPath.row]; 54 | } 55 | 56 | #pragma mark Utils 57 | 58 | - (void)loadData 59 | { 60 | _objects = [NSMutableArray array]; 61 | for (int i = 0; i < 100; i++) 62 | { 63 | NSString *text = [NSString stringWithFormat:NSLocalizedString(@"Cell %d", @""), i + 1]; 64 | UIColor *color = [self.class cuasiRandomColor]; 65 | [_objects addObject:@[text, color]]; 66 | } 67 | } 68 | 69 | + (UIColor*)cuasiRandomColor 70 | { 71 | float r = arc4random() % 72; 72 | return [UIColor colorWithHue:(5*r)/360 saturation:0.2f brightness:1.0f alpha:1.0f]; 73 | } 74 | 75 | @end 76 | -------------------------------------------------------------------------------- /HPReorderTableViewDemo/Images.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "29x29", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "40x40", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "60x60", 16 | "scale" : "2x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /HPReorderTableViewDemo/Images.xcassets/LaunchImage.launchimage/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "orientation" : "portrait", 5 | "idiom" : "iphone", 6 | "extent" : "full-screen", 7 | "minimum-system-version" : "7.0", 8 | "scale" : "2x" 9 | }, 10 | { 11 | "orientation" : "portrait", 12 | "idiom" : "iphone", 13 | "subtype" : "retina4", 14 | "extent" : "full-screen", 15 | "minimum-system-version" : "7.0", 16 | "scale" : "2x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /HPReorderTableViewDemo/en.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* Localized versions of Info.plist keys */ 2 | 3 | -------------------------------------------------------------------------------- /HPReorderTableViewDemo/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // HPReorderTableViewDemo 4 | // 5 | // Created by Hermes on 22/01/14. 6 | // Copyright (c) 2014 Hermes Pique. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | #import "HPAppDelegate.h" 12 | 13 | int main(int argc, char * argv[]) 14 | { 15 | @autoreleasepool { 16 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([HPAppDelegate class])); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /HPReorderTableViewTests/HPReorderTableViewTests-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIdentifier 10 | com.hique.${PRODUCT_NAME:rfc1034identifier} 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundlePackageType 14 | BNDL 15 | CFBundleShortVersionString 16 | 1.0 17 | CFBundleSignature 18 | ???? 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /HPReorderTableViewTests/HPReorderTableViewTests.m: -------------------------------------------------------------------------------- 1 | // 2 | // HPReorderTableViewTests.m 3 | // HPReorderTableViewTests 4 | // 5 | // Created by Hermes on 22/01/14. 6 | // Copyright (c) 2014 Hermes Pique. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "HPReorderTableView.h" 11 | 12 | @interface HPReorderTableViewTests : XCTestCase 13 | 14 | @end 15 | 16 | @implementation HPReorderTableViewTests { 17 | HPReorderTableView *_tableView; 18 | } 19 | 20 | - (void)setUp 21 | { 22 | [super setUp]; 23 | _tableView = [HPReorderTableView new]; 24 | } 25 | 26 | 27 | - (void)testInitWithFrameStyle 28 | { 29 | HPReorderTableView *tableView = [[HPReorderTableView alloc] initWithFrame:CGRectZero style:UITableViewStylePlain]; 30 | XCTAssertNotNil(tableView.reorderGestureRecognizer, @""); 31 | XCTAssertNotNil(tableView.reorderDragView, @""); 32 | } 33 | 34 | - (void)testInitWithFrame 35 | { 36 | HPReorderTableView *tableView = [[HPReorderTableView alloc] initWithFrame:CGRectZero]; 37 | XCTAssertNotNil(tableView.reorderGestureRecognizer, @""); 38 | XCTAssertNotNil(tableView.reorderDragView, @""); 39 | } 40 | 41 | - (void)testInitWithCoder 42 | { 43 | NSCoder *coder = [[NSKeyedUnarchiver alloc] initForReadingWithData:[NSData new]]; 44 | HPReorderTableView *tableView = [[HPReorderTableView alloc] initWithCoder:coder]; 45 | XCTAssertNotNil(tableView.reorderGestureRecognizer, @""); 46 | XCTAssertNotNil(tableView.reorderDragView, @""); 47 | } 48 | 49 | - (void)testRegisterTemporaryEmptyCellClass 50 | { 51 | [_tableView registerTemporaryEmptyCellClass:UITableViewCell.class]; 52 | } 53 | 54 | 55 | @end 56 | -------------------------------------------------------------------------------- /HPReorderTableViewTests/en.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* Localized versions of Info.plist keys */ 2 | 3 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright {yyyy} {name of copyright owner} 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | 203 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | HPReorderTableView 2 | ================== 3 | 4 | [![Version](https://cocoapod-badges.herokuapp.com/v/HPReorderTableView/badge.png)](http://cocoadocs.org/docsets/HPReorderTableView) [![Platform](https://cocoapod-badges.herokuapp.com/p/HPReorderTableView/badge.png)](http://cocoadocs.org/docsets/HPReorderTableView) [![Build Status](https://travis-ci.org/hpique/HPReorderTableView.png)](https://travis-ci.org/hpique/HPReorderTableView) 5 | 6 | Drop-in UITableView replacement to reorder cells with long press on any part of the cell. 7 | 8 | ![Reordering animation](Assets/reordering.gif) 9 | 10 | ##Installation 11 | 12 | Using [CocoaPods](http://cocoapods.org/): 13 | 14 | ```ruby 15 | pod 'HPReorderTableView', '~> 0.2' 16 | ``` 17 | 18 | Or add the files from the [HPReorderTableView](https://github.com/hpique/HPReorderTableView/tree/master/HPReorderTableView) directory if you're doing it manually. 19 | 20 | ##Usage 21 | 22 | Simply instance `HPReorderTableView` instead of `UITableView`: 23 | 24 | ```objective-c 25 | UITableView *tableView = [[HPReorderTableView alloc] init]; 26 | ``` 27 | 28 | Or if you're using interface builder, change the class of the table view to `HPReorderTableView`. 29 | 30 | Then implement the reordering methods of your table view data source as you would normally do if you were using the default reorder behavior. For example: 31 | 32 | ```objective-c 33 | - (void)tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)sourceIndexPath toIndexPath:(NSIndexPath *)destinationIndexPath 34 | { 35 | [_objects exchangeObjectAtIndex:sourceIndexPath.row withObjectAtIndex:destinationIndexPath.row]; 36 | } 37 | ``` 38 | 39 | ##Customization 40 | 41 | See the [documentation](http://cocoadocs.org/docsets/HPReorderTableView) for customization options. 42 | 43 | ##Requirements 44 | 45 | HPReorderTableView requires iOS 6.1 or above and ARC. 46 | 47 | ##License 48 | 49 | Copyright 2014 Hermes Pique ([@hpique](https://twitter.com/hpique)) 50 | 51 | Licensed under the Apache License, Version 2.0 (the "License"); 52 | you may not use this file except in compliance with the License. 53 | You may obtain a copy of the License at 54 | 55 | http://www.apache.org/licenses/LICENSE-2.0 56 | 57 | Unless required by applicable law or agreed to in writing, software 58 | distributed under the License is distributed on an "AS IS" BASIS, 59 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 60 | See the License for the specific language governing permissions and 61 | limitations under the License. 62 | 63 | Uses code from [BVReorderTableView](https://github.com/bvogelzang/BVReorderTableView) by [bvogelzang](https://github.com/bvogelzang). 64 | --------------------------------------------------------------------------------