├── .gitignore ├── .travis.yml ├── Example ├── FSCSwipeCell.xcodeproj │ ├── project.pbxproj │ ├── project.xcworkspace │ │ └── contents.xcworkspacedata │ └── xcshareddata │ │ └── xcschemes │ │ └── FSCSwipeCell-Example.xcscheme ├── FSCSwipeCell.xcworkspace │ └── contents.xcworkspacedata ├── FSCSwipeCell │ ├── FSCAppDelegate.h │ ├── FSCAppDelegate.m │ ├── FSCSwipeCell-Info.plist │ ├── FSCSwipeCell-Prefix.pch │ ├── FSCTableViewController.h │ ├── FSCTableViewController.m │ ├── Images.xcassets │ │ ├── AppIcon.appiconset │ │ │ └── Contents.json │ │ ├── LaunchImage.launchimage │ │ │ └── Contents.json │ │ └── Snooze.imageset │ │ │ ├── Contents.json │ │ │ ├── Snooze.png │ │ │ ├── Snooze@2x.png │ │ │ └── Snooze@3x.png │ ├── Main.storyboard │ └── main.m ├── Podfile ├── Podfile.lock └── Tests │ ├── Tests-Info.plist │ ├── Tests-Prefix.pch │ └── Tests.m ├── FSCSwipeCell.podspec ├── LICENSE ├── Pod ├── Assets │ └── .gitkeep └── Classes │ ├── .gitkeep │ ├── FSCSwipeCell.h │ └── FSCSwipeCell.m └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | # OS X 2 | .DS_Store 3 | 4 | # Xcode 5 | build/ 6 | *.pbxuser 7 | !default.pbxuser 8 | *.mode1v3 9 | !default.mode1v3 10 | *.mode2v3 11 | !default.mode2v3 12 | *.perspectivev3 13 | !default.perspectivev3 14 | xcuserdata 15 | *.xccheckout 16 | profile 17 | *.moved-aside 18 | DerivedData 19 | *.hmap 20 | *.ipa 21 | 22 | # Bundler 23 | .bundle 24 | 25 | # CocoaPods 26 | Pods/ 27 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: objective-c 2 | cache: cocoapods 3 | podfile: Example/Podfile 4 | before_install: 5 | - gem install cocoapods 6 | - pod install --project-directory=Example 7 | install: 8 | - gem install xcpretty --no-rdoc --no-ri --no-document --quiet 9 | script: 10 | - set -o pipefail && xcodebuild test -workspace Example/FSCSwipeCell.xcworkspace -scheme FSCSwipeCell-Example -sdk iphonesimulator ONLY_ACTIVE_ARCH=NO | xcpretty -c 11 | - pod lib lint --quick 12 | -------------------------------------------------------------------------------- /Example/FSCSwipeCell.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 04DC41BBD7A7FEE6EE81026D /* libPods-Tests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 17FE96E9B266129784C1B028 /* libPods-Tests.a */; }; 11 | 6003F58E195388D20070C39A /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6003F58D195388D20070C39A /* Foundation.framework */; }; 12 | 6003F590195388D20070C39A /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6003F58F195388D20070C39A /* CoreGraphics.framework */; }; 13 | 6003F592195388D20070C39A /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6003F591195388D20070C39A /* UIKit.framework */; }; 14 | 6003F59A195388D20070C39A /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 6003F599195388D20070C39A /* main.m */; }; 15 | 6003F59E195388D20070C39A /* FSCAppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 6003F59D195388D20070C39A /* FSCAppDelegate.m */; }; 16 | 6003F5A7195388D20070C39A /* FSCTableViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 6003F5A6195388D20070C39A /* FSCTableViewController.m */; }; 17 | 6003F5A9195388D20070C39A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 6003F5A8195388D20070C39A /* Images.xcassets */; }; 18 | 6003F5B0195388D20070C39A /* XCTest.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6003F5AF195388D20070C39A /* XCTest.framework */; }; 19 | 6003F5B1195388D20070C39A /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6003F58D195388D20070C39A /* Foundation.framework */; }; 20 | 6003F5B2195388D20070C39A /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6003F591195388D20070C39A /* UIKit.framework */; }; 21 | 6003F5BC195388D20070C39A /* Tests.m in Sources */ = {isa = PBXBuildFile; fileRef = 6003F5BB195388D20070C39A /* Tests.m */; }; 22 | 7EE3E0C4218DF12BF5DA9F27 /* libPods-FSCSwipeCell.a in Frameworks */ = {isa = PBXBuildFile; fileRef = CF8B8DD6F4C9D32B45A8F820 /* libPods-FSCSwipeCell.a */; }; 23 | FAD2C2411A8EB53B00D44B12 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = FAD2C2401A8EB53B00D44B12 /* Main.storyboard */; }; 24 | /* End PBXBuildFile section */ 25 | 26 | /* Begin PBXContainerItemProxy section */ 27 | 6003F5B3195388D20070C39A /* PBXContainerItemProxy */ = { 28 | isa = PBXContainerItemProxy; 29 | containerPortal = 6003F582195388D10070C39A /* Project object */; 30 | proxyType = 1; 31 | remoteGlobalIDString = 6003F589195388D20070C39A; 32 | remoteInfo = FSCSwipeCell; 33 | }; 34 | /* End PBXContainerItemProxy section */ 35 | 36 | /* Begin PBXFileReference section */ 37 | 0EB3EE23834E75C20D8F10F4 /* Pods-FSCSwipeCell.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-FSCSwipeCell.release.xcconfig"; path = "Pods/Target Support Files/Pods-FSCSwipeCell/Pods-FSCSwipeCell.release.xcconfig"; sourceTree = ""; }; 38 | 17FE96E9B266129784C1B028 /* libPods-Tests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Tests.a"; sourceTree = BUILT_PRODUCTS_DIR; }; 39 | 3E735B2E52ACA216F03E5810 /* FSCSwipeCell.podspec */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; name = FSCSwipeCell.podspec; path = ../FSCSwipeCell.podspec; sourceTree = ""; }; 40 | 40FEC2A3C1003DD8CA73B566 /* README.md */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = net.daringfireball.markdown; name = README.md; path = ../README.md; sourceTree = ""; }; 41 | 4816D7DEB648B9AF1C417DD7 /* Pods-FSCSwipeCell.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-FSCSwipeCell.debug.xcconfig"; path = "Pods/Target Support Files/Pods-FSCSwipeCell/Pods-FSCSwipeCell.debug.xcconfig"; sourceTree = ""; }; 42 | 5A99C04A014D55F31F481814 /* Pods-Tests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Tests.debug.xcconfig"; path = "Pods/Target Support Files/Pods-Tests/Pods-Tests.debug.xcconfig"; sourceTree = ""; }; 43 | 6003F58A195388D20070C39A /* FSCSwipeCell.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = FSCSwipeCell.app; sourceTree = BUILT_PRODUCTS_DIR; }; 44 | 6003F58D195388D20070C39A /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; 45 | 6003F58F195388D20070C39A /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; }; 46 | 6003F591195388D20070C39A /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; }; 47 | 6003F595195388D20070C39A /* FSCSwipeCell-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "FSCSwipeCell-Info.plist"; sourceTree = ""; }; 48 | 6003F599195388D20070C39A /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 49 | 6003F59B195388D20070C39A /* FSCSwipeCell-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "FSCSwipeCell-Prefix.pch"; sourceTree = ""; }; 50 | 6003F59C195388D20070C39A /* FSCAppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FSCAppDelegate.h; sourceTree = ""; }; 51 | 6003F59D195388D20070C39A /* FSCAppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FSCAppDelegate.m; sourceTree = ""; }; 52 | 6003F5A5195388D20070C39A /* FSCTableViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FSCTableViewController.h; sourceTree = ""; }; 53 | 6003F5A6195388D20070C39A /* FSCTableViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FSCTableViewController.m; sourceTree = ""; }; 54 | 6003F5A8195388D20070C39A /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; 55 | 6003F5AE195388D20070C39A /* Tests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = Tests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 56 | 6003F5AF195388D20070C39A /* XCTest.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = XCTest.framework; path = Library/Frameworks/XCTest.framework; sourceTree = DEVELOPER_DIR; }; 57 | 6003F5B7195388D20070C39A /* Tests-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "Tests-Info.plist"; sourceTree = ""; }; 58 | 6003F5BB195388D20070C39A /* Tests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = Tests.m; sourceTree = ""; }; 59 | 606FC2411953D9B200FFA9A0 /* Tests-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Tests-Prefix.pch"; sourceTree = ""; }; 60 | B0742DCF4CB3F9B23E546504 /* Pods-Tests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Tests.release.xcconfig"; path = "Pods/Target Support Files/Pods-Tests/Pods-Tests.release.xcconfig"; sourceTree = ""; }; 61 | C60406F2C036B4D9C77BC83D /* LICENSE */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; name = LICENSE; path = ../LICENSE; sourceTree = ""; }; 62 | CF8B8DD6F4C9D32B45A8F820 /* libPods-FSCSwipeCell.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-FSCSwipeCell.a"; sourceTree = BUILT_PRODUCTS_DIR; }; 63 | FAD2C2401A8EB53B00D44B12 /* Main.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = Main.storyboard; sourceTree = ""; }; 64 | /* End PBXFileReference section */ 65 | 66 | /* Begin PBXFrameworksBuildPhase section */ 67 | 6003F587195388D20070C39A /* Frameworks */ = { 68 | isa = PBXFrameworksBuildPhase; 69 | buildActionMask = 2147483647; 70 | files = ( 71 | 6003F590195388D20070C39A /* CoreGraphics.framework in Frameworks */, 72 | 6003F592195388D20070C39A /* UIKit.framework in Frameworks */, 73 | 6003F58E195388D20070C39A /* Foundation.framework in Frameworks */, 74 | 7EE3E0C4218DF12BF5DA9F27 /* libPods-FSCSwipeCell.a in Frameworks */, 75 | ); 76 | runOnlyForDeploymentPostprocessing = 0; 77 | }; 78 | 6003F5AB195388D20070C39A /* Frameworks */ = { 79 | isa = PBXFrameworksBuildPhase; 80 | buildActionMask = 2147483647; 81 | files = ( 82 | 6003F5B0195388D20070C39A /* XCTest.framework in Frameworks */, 83 | 6003F5B2195388D20070C39A /* UIKit.framework in Frameworks */, 84 | 6003F5B1195388D20070C39A /* Foundation.framework in Frameworks */, 85 | 04DC41BBD7A7FEE6EE81026D /* libPods-Tests.a in Frameworks */, 86 | ); 87 | runOnlyForDeploymentPostprocessing = 0; 88 | }; 89 | /* End PBXFrameworksBuildPhase section */ 90 | 91 | /* Begin PBXGroup section */ 92 | 33396856D448867F83828C7D /* Pods */ = { 93 | isa = PBXGroup; 94 | children = ( 95 | 4816D7DEB648B9AF1C417DD7 /* Pods-FSCSwipeCell.debug.xcconfig */, 96 | 0EB3EE23834E75C20D8F10F4 /* Pods-FSCSwipeCell.release.xcconfig */, 97 | 5A99C04A014D55F31F481814 /* Pods-Tests.debug.xcconfig */, 98 | B0742DCF4CB3F9B23E546504 /* Pods-Tests.release.xcconfig */, 99 | ); 100 | name = Pods; 101 | sourceTree = ""; 102 | }; 103 | 6003F581195388D10070C39A = { 104 | isa = PBXGroup; 105 | children = ( 106 | 60FF7A9C1954A5C5007DD14C /* Podspec Metadata */, 107 | 6003F593195388D20070C39A /* FSCSwipeCell */, 108 | 6003F5B5195388D20070C39A /* Tests */, 109 | 6003F58C195388D20070C39A /* Frameworks */, 110 | 6003F58B195388D20070C39A /* Products */, 111 | 33396856D448867F83828C7D /* Pods */, 112 | ); 113 | sourceTree = ""; 114 | }; 115 | 6003F58B195388D20070C39A /* Products */ = { 116 | isa = PBXGroup; 117 | children = ( 118 | 6003F58A195388D20070C39A /* FSCSwipeCell.app */, 119 | 6003F5AE195388D20070C39A /* Tests.xctest */, 120 | ); 121 | name = Products; 122 | sourceTree = ""; 123 | }; 124 | 6003F58C195388D20070C39A /* Frameworks */ = { 125 | isa = PBXGroup; 126 | children = ( 127 | 6003F58D195388D20070C39A /* Foundation.framework */, 128 | 6003F58F195388D20070C39A /* CoreGraphics.framework */, 129 | 6003F591195388D20070C39A /* UIKit.framework */, 130 | 6003F5AF195388D20070C39A /* XCTest.framework */, 131 | CF8B8DD6F4C9D32B45A8F820 /* libPods-FSCSwipeCell.a */, 132 | 17FE96E9B266129784C1B028 /* libPods-Tests.a */, 133 | ); 134 | name = Frameworks; 135 | sourceTree = ""; 136 | }; 137 | 6003F593195388D20070C39A /* FSCSwipeCell */ = { 138 | isa = PBXGroup; 139 | children = ( 140 | 6003F59C195388D20070C39A /* FSCAppDelegate.h */, 141 | 6003F59D195388D20070C39A /* FSCAppDelegate.m */, 142 | 6003F5A5195388D20070C39A /* FSCTableViewController.h */, 143 | 6003F5A6195388D20070C39A /* FSCTableViewController.m */, 144 | FAD2C2401A8EB53B00D44B12 /* Main.storyboard */, 145 | 6003F5A8195388D20070C39A /* Images.xcassets */, 146 | 6003F594195388D20070C39A /* Supporting Files */, 147 | ); 148 | path = FSCSwipeCell; 149 | sourceTree = ""; 150 | }; 151 | 6003F594195388D20070C39A /* Supporting Files */ = { 152 | isa = PBXGroup; 153 | children = ( 154 | 6003F595195388D20070C39A /* FSCSwipeCell-Info.plist */, 155 | 6003F59B195388D20070C39A /* FSCSwipeCell-Prefix.pch */, 156 | 6003F599195388D20070C39A /* main.m */, 157 | ); 158 | name = "Supporting Files"; 159 | sourceTree = ""; 160 | }; 161 | 6003F5B5195388D20070C39A /* Tests */ = { 162 | isa = PBXGroup; 163 | children = ( 164 | 6003F5BB195388D20070C39A /* Tests.m */, 165 | 6003F5B6195388D20070C39A /* Supporting Files */, 166 | ); 167 | path = Tests; 168 | sourceTree = ""; 169 | }; 170 | 6003F5B6195388D20070C39A /* Supporting Files */ = { 171 | isa = PBXGroup; 172 | children = ( 173 | 6003F5B7195388D20070C39A /* Tests-Info.plist */, 174 | 606FC2411953D9B200FFA9A0 /* Tests-Prefix.pch */, 175 | ); 176 | name = "Supporting Files"; 177 | sourceTree = ""; 178 | }; 179 | 60FF7A9C1954A5C5007DD14C /* Podspec Metadata */ = { 180 | isa = PBXGroup; 181 | children = ( 182 | 3E735B2E52ACA216F03E5810 /* FSCSwipeCell.podspec */, 183 | 40FEC2A3C1003DD8CA73B566 /* README.md */, 184 | C60406F2C036B4D9C77BC83D /* LICENSE */, 185 | ); 186 | name = "Podspec Metadata"; 187 | sourceTree = ""; 188 | }; 189 | /* End PBXGroup section */ 190 | 191 | /* Begin PBXNativeTarget section */ 192 | 6003F589195388D20070C39A /* FSCSwipeCell */ = { 193 | isa = PBXNativeTarget; 194 | buildConfigurationList = 6003F5BF195388D20070C39A /* Build configuration list for PBXNativeTarget "FSCSwipeCell" */; 195 | buildPhases = ( 196 | EFBA91B01B4839C37612865D /* Check Pods Manifest.lock */, 197 | 6003F586195388D20070C39A /* Sources */, 198 | 6003F587195388D20070C39A /* Frameworks */, 199 | 6003F588195388D20070C39A /* Resources */, 200 | 957ECC4FCD17B7D2D17F4D7B /* Copy Pods Resources */, 201 | ); 202 | buildRules = ( 203 | ); 204 | dependencies = ( 205 | ); 206 | name = FSCSwipeCell; 207 | productName = FSCSwipeCell; 208 | productReference = 6003F58A195388D20070C39A /* FSCSwipeCell.app */; 209 | productType = "com.apple.product-type.application"; 210 | }; 211 | 6003F5AD195388D20070C39A /* Tests */ = { 212 | isa = PBXNativeTarget; 213 | buildConfigurationList = 6003F5C2195388D20070C39A /* Build configuration list for PBXNativeTarget "Tests" */; 214 | buildPhases = ( 215 | 9A4BDCF77795E653EDBFE8BB /* Check Pods Manifest.lock */, 216 | 6003F5AA195388D20070C39A /* Sources */, 217 | 6003F5AB195388D20070C39A /* Frameworks */, 218 | 6003F5AC195388D20070C39A /* Resources */, 219 | 24CEADDEF8AF11276AE1FFAF /* Copy Pods Resources */, 220 | ); 221 | buildRules = ( 222 | ); 223 | dependencies = ( 224 | 6003F5B4195388D20070C39A /* PBXTargetDependency */, 225 | ); 226 | name = Tests; 227 | productName = FSCSwipeCellTests; 228 | productReference = 6003F5AE195388D20070C39A /* Tests.xctest */; 229 | productType = "com.apple.product-type.bundle.unit-test"; 230 | }; 231 | /* End PBXNativeTarget section */ 232 | 233 | /* Begin PBXProject section */ 234 | 6003F582195388D10070C39A /* Project object */ = { 235 | isa = PBXProject; 236 | attributes = { 237 | CLASSPREFIX = FSC; 238 | LastUpgradeCheck = 0510; 239 | ORGANIZATIONNAME = Blixt; 240 | TargetAttributes = { 241 | 6003F5AD195388D20070C39A = { 242 | TestTargetID = 6003F589195388D20070C39A; 243 | }; 244 | }; 245 | }; 246 | buildConfigurationList = 6003F585195388D10070C39A /* Build configuration list for PBXProject "FSCSwipeCell" */; 247 | compatibilityVersion = "Xcode 3.2"; 248 | developmentRegion = English; 249 | hasScannedForEncodings = 0; 250 | knownRegions = ( 251 | en, 252 | Base, 253 | ); 254 | mainGroup = 6003F581195388D10070C39A; 255 | productRefGroup = 6003F58B195388D20070C39A /* Products */; 256 | projectDirPath = ""; 257 | projectRoot = ""; 258 | targets = ( 259 | 6003F589195388D20070C39A /* FSCSwipeCell */, 260 | 6003F5AD195388D20070C39A /* Tests */, 261 | ); 262 | }; 263 | /* End PBXProject section */ 264 | 265 | /* Begin PBXResourcesBuildPhase section */ 266 | 6003F588195388D20070C39A /* Resources */ = { 267 | isa = PBXResourcesBuildPhase; 268 | buildActionMask = 2147483647; 269 | files = ( 270 | FAD2C2411A8EB53B00D44B12 /* Main.storyboard in Resources */, 271 | 6003F5A9195388D20070C39A /* Images.xcassets in Resources */, 272 | ); 273 | runOnlyForDeploymentPostprocessing = 0; 274 | }; 275 | 6003F5AC195388D20070C39A /* Resources */ = { 276 | isa = PBXResourcesBuildPhase; 277 | buildActionMask = 2147483647; 278 | files = ( 279 | ); 280 | runOnlyForDeploymentPostprocessing = 0; 281 | }; 282 | /* End PBXResourcesBuildPhase section */ 283 | 284 | /* Begin PBXShellScriptBuildPhase section */ 285 | 24CEADDEF8AF11276AE1FFAF /* Copy Pods Resources */ = { 286 | isa = PBXShellScriptBuildPhase; 287 | buildActionMask = 2147483647; 288 | files = ( 289 | ); 290 | inputPaths = ( 291 | ); 292 | name = "Copy Pods Resources"; 293 | outputPaths = ( 294 | ); 295 | runOnlyForDeploymentPostprocessing = 0; 296 | shellPath = /bin/sh; 297 | shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-Tests/Pods-Tests-resources.sh\"\n"; 298 | showEnvVarsInLog = 0; 299 | }; 300 | 957ECC4FCD17B7D2D17F4D7B /* Copy Pods Resources */ = { 301 | isa = PBXShellScriptBuildPhase; 302 | buildActionMask = 2147483647; 303 | files = ( 304 | ); 305 | inputPaths = ( 306 | ); 307 | name = "Copy Pods Resources"; 308 | outputPaths = ( 309 | ); 310 | runOnlyForDeploymentPostprocessing = 0; 311 | shellPath = /bin/sh; 312 | shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-FSCSwipeCell/Pods-FSCSwipeCell-resources.sh\"\n"; 313 | showEnvVarsInLog = 0; 314 | }; 315 | 9A4BDCF77795E653EDBFE8BB /* Check Pods Manifest.lock */ = { 316 | isa = PBXShellScriptBuildPhase; 317 | buildActionMask = 2147483647; 318 | files = ( 319 | ); 320 | inputPaths = ( 321 | ); 322 | name = "Check Pods Manifest.lock"; 323 | outputPaths = ( 324 | ); 325 | runOnlyForDeploymentPostprocessing = 0; 326 | shellPath = /bin/sh; 327 | shellScript = "diff \"${PODS_ROOT}/../Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [[ $? != 0 ]] ; then\n cat << EOM\nerror: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\nEOM\n exit 1\nfi\n"; 328 | showEnvVarsInLog = 0; 329 | }; 330 | EFBA91B01B4839C37612865D /* Check Pods Manifest.lock */ = { 331 | isa = PBXShellScriptBuildPhase; 332 | buildActionMask = 2147483647; 333 | files = ( 334 | ); 335 | inputPaths = ( 336 | ); 337 | name = "Check Pods Manifest.lock"; 338 | outputPaths = ( 339 | ); 340 | runOnlyForDeploymentPostprocessing = 0; 341 | shellPath = /bin/sh; 342 | shellScript = "diff \"${PODS_ROOT}/../Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [[ $? != 0 ]] ; then\n cat << EOM\nerror: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\nEOM\n exit 1\nfi\n"; 343 | showEnvVarsInLog = 0; 344 | }; 345 | /* End PBXShellScriptBuildPhase section */ 346 | 347 | /* Begin PBXSourcesBuildPhase section */ 348 | 6003F586195388D20070C39A /* Sources */ = { 349 | isa = PBXSourcesBuildPhase; 350 | buildActionMask = 2147483647; 351 | files = ( 352 | 6003F59E195388D20070C39A /* FSCAppDelegate.m in Sources */, 353 | 6003F5A7195388D20070C39A /* FSCTableViewController.m in Sources */, 354 | 6003F59A195388D20070C39A /* main.m in Sources */, 355 | ); 356 | runOnlyForDeploymentPostprocessing = 0; 357 | }; 358 | 6003F5AA195388D20070C39A /* Sources */ = { 359 | isa = PBXSourcesBuildPhase; 360 | buildActionMask = 2147483647; 361 | files = ( 362 | 6003F5BC195388D20070C39A /* Tests.m in Sources */, 363 | ); 364 | runOnlyForDeploymentPostprocessing = 0; 365 | }; 366 | /* End PBXSourcesBuildPhase section */ 367 | 368 | /* Begin PBXTargetDependency section */ 369 | 6003F5B4195388D20070C39A /* PBXTargetDependency */ = { 370 | isa = PBXTargetDependency; 371 | target = 6003F589195388D20070C39A /* FSCSwipeCell */; 372 | targetProxy = 6003F5B3195388D20070C39A /* PBXContainerItemProxy */; 373 | }; 374 | /* End PBXTargetDependency section */ 375 | 376 | /* Begin XCBuildConfiguration section */ 377 | 6003F5BD195388D20070C39A /* Debug */ = { 378 | isa = XCBuildConfiguration; 379 | buildSettings = { 380 | ALWAYS_SEARCH_USER_PATHS = NO; 381 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 382 | CLANG_CXX_LIBRARY = "libc++"; 383 | CLANG_ENABLE_MODULES = YES; 384 | CLANG_ENABLE_OBJC_ARC = YES; 385 | CLANG_WARN_BOOL_CONVERSION = YES; 386 | CLANG_WARN_CONSTANT_CONVERSION = YES; 387 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 388 | CLANG_WARN_EMPTY_BODY = YES; 389 | CLANG_WARN_ENUM_CONVERSION = YES; 390 | CLANG_WARN_INT_CONVERSION = YES; 391 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 392 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 393 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 394 | COPY_PHASE_STRIP = NO; 395 | GCC_C_LANGUAGE_STANDARD = gnu99; 396 | GCC_DYNAMIC_NO_PIC = NO; 397 | GCC_OPTIMIZATION_LEVEL = 0; 398 | GCC_PREPROCESSOR_DEFINITIONS = ( 399 | "DEBUG=1", 400 | "$(inherited)", 401 | ); 402 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 403 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 404 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 405 | GCC_WARN_UNDECLARED_SELECTOR = YES; 406 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 407 | GCC_WARN_UNUSED_FUNCTION = YES; 408 | GCC_WARN_UNUSED_VARIABLE = YES; 409 | IPHONEOS_DEPLOYMENT_TARGET = 7.1; 410 | ONLY_ACTIVE_ARCH = YES; 411 | SDKROOT = iphoneos; 412 | TARGETED_DEVICE_FAMILY = "1,2"; 413 | }; 414 | name = Debug; 415 | }; 416 | 6003F5BE195388D20070C39A /* Release */ = { 417 | isa = XCBuildConfiguration; 418 | buildSettings = { 419 | ALWAYS_SEARCH_USER_PATHS = NO; 420 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 421 | CLANG_CXX_LIBRARY = "libc++"; 422 | CLANG_ENABLE_MODULES = YES; 423 | CLANG_ENABLE_OBJC_ARC = YES; 424 | CLANG_WARN_BOOL_CONVERSION = YES; 425 | CLANG_WARN_CONSTANT_CONVERSION = YES; 426 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 427 | CLANG_WARN_EMPTY_BODY = YES; 428 | CLANG_WARN_ENUM_CONVERSION = YES; 429 | CLANG_WARN_INT_CONVERSION = YES; 430 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 431 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 432 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 433 | COPY_PHASE_STRIP = YES; 434 | ENABLE_NS_ASSERTIONS = NO; 435 | GCC_C_LANGUAGE_STANDARD = gnu99; 436 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 437 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 438 | GCC_WARN_UNDECLARED_SELECTOR = YES; 439 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 440 | GCC_WARN_UNUSED_FUNCTION = YES; 441 | GCC_WARN_UNUSED_VARIABLE = YES; 442 | IPHONEOS_DEPLOYMENT_TARGET = 7.1; 443 | SDKROOT = iphoneos; 444 | TARGETED_DEVICE_FAMILY = "1,2"; 445 | VALIDATE_PRODUCT = YES; 446 | }; 447 | name = Release; 448 | }; 449 | 6003F5C0195388D20070C39A /* Debug */ = { 450 | isa = XCBuildConfiguration; 451 | baseConfigurationReference = 4816D7DEB648B9AF1C417DD7 /* Pods-FSCSwipeCell.debug.xcconfig */; 452 | buildSettings = { 453 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 454 | ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; 455 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 456 | GCC_PREFIX_HEADER = "FSCSwipeCell/FSCSwipeCell-Prefix.pch"; 457 | INFOPLIST_FILE = "FSCSwipeCell/FSCSwipeCell-Info.plist"; 458 | PRODUCT_NAME = "$(TARGET_NAME)"; 459 | WRAPPER_EXTENSION = app; 460 | }; 461 | name = Debug; 462 | }; 463 | 6003F5C1195388D20070C39A /* Release */ = { 464 | isa = XCBuildConfiguration; 465 | baseConfigurationReference = 0EB3EE23834E75C20D8F10F4 /* Pods-FSCSwipeCell.release.xcconfig */; 466 | buildSettings = { 467 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 468 | ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; 469 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 470 | GCC_PREFIX_HEADER = "FSCSwipeCell/FSCSwipeCell-Prefix.pch"; 471 | INFOPLIST_FILE = "FSCSwipeCell/FSCSwipeCell-Info.plist"; 472 | PRODUCT_NAME = "$(TARGET_NAME)"; 473 | WRAPPER_EXTENSION = app; 474 | }; 475 | name = Release; 476 | }; 477 | 6003F5C3195388D20070C39A /* Debug */ = { 478 | isa = XCBuildConfiguration; 479 | baseConfigurationReference = 5A99C04A014D55F31F481814 /* Pods-Tests.debug.xcconfig */; 480 | buildSettings = { 481 | BUNDLE_LOADER = "$(BUILT_PRODUCTS_DIR)/FSCSwipeCell.app/FSCSwipeCell"; 482 | FRAMEWORK_SEARCH_PATHS = ( 483 | "$(SDKROOT)/Developer/Library/Frameworks", 484 | "$(inherited)", 485 | "$(DEVELOPER_FRAMEWORKS_DIR)", 486 | ); 487 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 488 | GCC_PREFIX_HEADER = "Tests/Tests-Prefix.pch"; 489 | GCC_PREPROCESSOR_DEFINITIONS = ( 490 | "DEBUG=1", 491 | "$(inherited)", 492 | ); 493 | INFOPLIST_FILE = "Tests/Tests-Info.plist"; 494 | PRODUCT_NAME = "$(TARGET_NAME)"; 495 | TEST_HOST = "$(BUNDLE_LOADER)"; 496 | WRAPPER_EXTENSION = xctest; 497 | }; 498 | name = Debug; 499 | }; 500 | 6003F5C4195388D20070C39A /* Release */ = { 501 | isa = XCBuildConfiguration; 502 | baseConfigurationReference = B0742DCF4CB3F9B23E546504 /* Pods-Tests.release.xcconfig */; 503 | buildSettings = { 504 | BUNDLE_LOADER = "$(BUILT_PRODUCTS_DIR)/FSCSwipeCell.app/FSCSwipeCell"; 505 | FRAMEWORK_SEARCH_PATHS = ( 506 | "$(SDKROOT)/Developer/Library/Frameworks", 507 | "$(inherited)", 508 | "$(DEVELOPER_FRAMEWORKS_DIR)", 509 | ); 510 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 511 | GCC_PREFIX_HEADER = "Tests/Tests-Prefix.pch"; 512 | INFOPLIST_FILE = "Tests/Tests-Info.plist"; 513 | PRODUCT_NAME = "$(TARGET_NAME)"; 514 | TEST_HOST = "$(BUNDLE_LOADER)"; 515 | WRAPPER_EXTENSION = xctest; 516 | }; 517 | name = Release; 518 | }; 519 | /* End XCBuildConfiguration section */ 520 | 521 | /* Begin XCConfigurationList section */ 522 | 6003F585195388D10070C39A /* Build configuration list for PBXProject "FSCSwipeCell" */ = { 523 | isa = XCConfigurationList; 524 | buildConfigurations = ( 525 | 6003F5BD195388D20070C39A /* Debug */, 526 | 6003F5BE195388D20070C39A /* Release */, 527 | ); 528 | defaultConfigurationIsVisible = 0; 529 | defaultConfigurationName = Release; 530 | }; 531 | 6003F5BF195388D20070C39A /* Build configuration list for PBXNativeTarget "FSCSwipeCell" */ = { 532 | isa = XCConfigurationList; 533 | buildConfigurations = ( 534 | 6003F5C0195388D20070C39A /* Debug */, 535 | 6003F5C1195388D20070C39A /* Release */, 536 | ); 537 | defaultConfigurationIsVisible = 0; 538 | defaultConfigurationName = Release; 539 | }; 540 | 6003F5C2195388D20070C39A /* Build configuration list for PBXNativeTarget "Tests" */ = { 541 | isa = XCConfigurationList; 542 | buildConfigurations = ( 543 | 6003F5C3195388D20070C39A /* Debug */, 544 | 6003F5C4195388D20070C39A /* Release */, 545 | ); 546 | defaultConfigurationIsVisible = 0; 547 | defaultConfigurationName = Release; 548 | }; 549 | /* End XCConfigurationList section */ 550 | }; 551 | rootObject = 6003F582195388D10070C39A /* Project object */; 552 | } 553 | -------------------------------------------------------------------------------- /Example/FSCSwipeCell.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Example/FSCSwipeCell.xcodeproj/xcshareddata/xcschemes/FSCSwipeCell-Example.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 | -------------------------------------------------------------------------------- /Example/FSCSwipeCell.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /Example/FSCSwipeCell/FSCAppDelegate.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | @interface FSCAppDelegate : UIResponder 4 | 5 | @property (strong, nonatomic) UIWindow *window; 6 | 7 | @end -------------------------------------------------------------------------------- /Example/FSCSwipeCell/FSCAppDelegate.m: -------------------------------------------------------------------------------- 1 | #import "FSCAppDelegate.h" 2 | 3 | @implementation FSCAppDelegate 4 | 5 | @end -------------------------------------------------------------------------------- /Example/FSCSwipeCell/FSCSwipeCell-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleDisplayName 8 | ${PRODUCT_NAME} 9 | CFBundleExecutable 10 | ${EXECUTABLE_NAME} 11 | CFBundleIdentifier 12 | com.47center.${PRODUCT_NAME:rfc1034identifier} 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | ${PRODUCT_NAME} 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | 0.1.8 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | 0.1.8 25 | LSRequiresIPhoneOS 26 | 27 | UIMainStoryboardFile 28 | Main 29 | UIRequiredDeviceCapabilities 30 | 31 | armv7 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 | 47 | 48 | -------------------------------------------------------------------------------- /Example/FSCSwipeCell/FSCSwipeCell-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 | -------------------------------------------------------------------------------- /Example/FSCSwipeCell/FSCTableViewController.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | #import "FSCSwipeCell/FSCSwipeCell.h" 4 | 5 | @interface FSCTableViewController : UITableViewController 6 | 7 | @end -------------------------------------------------------------------------------- /Example/FSCSwipeCell/FSCTableViewController.m: -------------------------------------------------------------------------------- 1 | #import "FSCTableViewController.h" 2 | 3 | @interface FSCTableViewController () 4 | 5 | @property (nonatomic, strong) NSMutableArray *labels; 6 | @property (nonatomic, weak) FSCSwipeCell *optionsCell; 7 | @property (nonatomic, strong) UIView *optionsView; 8 | 9 | @end 10 | 11 | /** 12 | * This example aims to show the versatility of FSCSwipeView. While FSCSwipeView does very little on its own, 13 | * it's pretty easy to implement your own logic on top of it to get swipable table view cells that behave as 14 | * you'd expect. 15 | * 16 | * The main ideas shown in this example are: 17 | * - Cells that can be swiped left and right without interfering with normal scrolling; 18 | * - Swipe-to-perform-action cells (a la Google's Inbox); 19 | * - Swipe to reveal option buttons; 20 | * - Reuse a single option buttons view for all cells; 21 | * - Dynamic background and iconography to indicate to the user how far to swipe 22 | */ 23 | @implementation FSCTableViewController 24 | 25 | - (void)viewDidLoad { 26 | // Create the options view which will be shared by all cells (instead of recreating it for every cell). 27 | self.optionsView = [[UIView alloc] init]; 28 | UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(20, 0, 250, 60)]; 29 | label.font = [UIFont fontWithName:@"Helvetica Neue" size:13]; 30 | label.textColor = [UIColor whiteColor]; 31 | label.text = @"Imagine there are actually options here. :)"; 32 | [self.optionsView addSubview:label]; 33 | 34 | // Create an array which contains a bunch of names that we'll use as example data. 35 | self.labels = [NSMutableArray arrayWithObjects:@"Pippi Longstocking", @"Austin Powers", @"Spider-Man", @"James Bond", 36 | @"Lisbeth Salander", @"Donald Duck", @"Luke Skywalker", @"Lara Croft", 37 | @"Frodo Baggins", @"Hermione Granger", @"Dexter Morgan", @"Ted Mosby", 38 | @"Homer Simpson", @"John Connor", @"Arya Stark", @"Captain Kirk", nil]; 39 | } 40 | 41 | #pragma mark Private methods 42 | 43 | - (void)displaySnoozeMenuForCell:(FSCSwipeCell *)cell { 44 | if (![UIAlertController class]) { 45 | UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Sorry!" 46 | message:@"This part of the demo doesn't work in iOS 7." 47 | delegate:nil 48 | cancelButtonTitle:@"No problem" 49 | otherButtonTitles:nil]; 50 | [alert show]; 51 | dispatch_after(dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_SEC), dispatch_get_main_queue(), ^{ 52 | cell.currentSide = FSCSwipeCellSideNone; 53 | }); 54 | return; 55 | } 56 | 57 | UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"Snooze" 58 | message:@"How long do you want to snooze?" 59 | preferredStyle:UIAlertControllerStyleActionSheet]; 60 | 61 | // This is only used by iPad. 62 | UIPopoverPresentationController *popover = alert.popoverPresentationController; 63 | if (popover) { 64 | popover.sourceView = cell; 65 | popover.sourceRect = cell.bounds; 66 | popover.permittedArrowDirections = UIPopoverArrowDirectionAny; 67 | } 68 | 69 | // This is the block that we will run if the user picks an option. 70 | void (^remove)(UIAlertAction *) = ^void(UIAlertAction *action) { 71 | // Don't do this at home, kids. 72 | NSUInteger index = [self.labels indexOfObject:cell.textLabel.text]; 73 | // Remove the row from view. 74 | [self.labels removeObjectAtIndex:index]; 75 | [self.tableView deleteRowsAtIndexPaths:@[[NSIndexPath indexPathForRow:index inSection:0]] 76 | withRowAnimation:UITableViewRowAnimationAutomatic]; 77 | }; 78 | 79 | [alert addAction:[UIAlertAction actionWithTitle:@"5 minutes" style:UIAlertActionStyleDefault handler:remove]]; 80 | [alert addAction:[UIAlertAction actionWithTitle:@"One hour" style:UIAlertActionStyleDefault handler:remove]]; 81 | [alert addAction:[UIAlertAction actionWithTitle:@"Until tomorrow" style:UIAlertActionStyleDefault handler:remove]]; 82 | 83 | // If the user cancels, simply reset the cell. 84 | [alert addAction:[UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleCancel handler:^(UIAlertAction *action) { 85 | // This line is the only FSCSwipeCell-related code for snoozing. It will "close" the open cell. 86 | cell.currentSide = FSCSwipeCellSideNone; 87 | }]]; 88 | 89 | [self presentViewController:alert animated:YES completion:nil]; 90 | } 91 | 92 | #pragma mark FSCSwipeCellDelegate 93 | 94 | - (void)swipeCell:(FSCSwipeCell *)cell didSwipe:(CGFloat)distance side:(FSCSwipeCellSide)side { 95 | // This method is called when a cell is being swiped by the user. The distance is always a positive number. 96 | 97 | // Calculate the progress (0.0-1.0) until releasing the cell will perform the action. 98 | CGFloat progressToAction = MIN(distance / kFSCSwipeCellOpenDistanceThreshold, 1); 99 | 100 | // Calculate a brightness based on how far the cell has been swiped. 101 | CGFloat brightness = 0.5 + progressToAction / 2; 102 | if (side == FSCSwipeCellSideLeft) { 103 | cell.leftView.backgroundColor = [UIColor colorWithHue:0.6 saturation:0.4 brightness:brightness alpha:1]; 104 | } else if (side == FSCSwipeCellSideRight) { 105 | cell.rightView.backgroundColor = [UIColor colorWithHue:0.12 saturation:1 brightness:brightness alpha:1]; 106 | 107 | // Place the snooze icon with the correct size. 108 | UIImageView *snooze = (UIImageView *)[cell.rightView viewWithTag:1]; 109 | if (progressToAction > 0.5) { 110 | CGFloat size = 16 * (progressToAction * 2 - 1); 111 | snooze.frame = CGRectMake(0, 0, size, size); 112 | snooze.center = CGPointMake(cell.bounds.size.width - 30, cell.bounds.size.height / 2); 113 | snooze.hidden = NO; 114 | } else { 115 | snooze.hidden = YES; 116 | } 117 | } 118 | } 119 | 120 | - (void)swipeCellDidChangeCurrentSide:(FSCSwipeCell *)cell { 121 | // This method is called whenever the open side changed. Note that this happens before animations finish. 122 | switch (cell.currentSide) { 123 | case FSCSwipeCellSideLeft: 124 | // This is now the cell showing options. 125 | self.optionsCell = cell; 126 | break; 127 | case FSCSwipeCellSideNone: 128 | break; 129 | case FSCSwipeCellSideRight: 130 | // Show the snooze menu. 131 | [self displaySnoozeMenuForCell:cell]; 132 | break; 133 | } 134 | } 135 | 136 | - (void)swipeCell:(FSCSwipeCell *)cell didHideSide:(FSCSwipeCellSide)side { 137 | // This method is called after a side is no longer visible (after animations). 138 | if (side == FSCSwipeCellSideLeft && cell == self.optionsCell) { 139 | // The cell is no longer open. 140 | self.optionsCell = nil; 141 | } 142 | } 143 | 144 | - (BOOL)swipeCell:(FSCSwipeCell *)cell shouldShowSide:(FSCSwipeCellSide)side { 145 | // This method is called before a side starts showing, giving you the opportunity to cancel the swipe. 146 | if (self.optionsCell && cell != self.optionsCell) { 147 | // We only want to display one cell with options at the time. Close the other one first. 148 | self.optionsCell.currentSide = FSCSwipeCellSideNone; 149 | // Keep the left side closed until the other cell has finished closing. 150 | if (side == FSCSwipeCellSideLeft) { 151 | return NO; 152 | } 153 | } 154 | 155 | // We're about to show the left side - move the options view to this cell. 156 | if (side == FSCSwipeCellSideLeft) { 157 | cell.leftView = self.optionsView; 158 | } 159 | 160 | return YES; 161 | } 162 | 163 | #pragma mark UITableViewDataSource 164 | 165 | - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { 166 | static NSString *cellId = @"randomCell"; 167 | 168 | // Either reuse an existing cell or set up a new one if necessary. 169 | FSCSwipeCell *cell = [tableView dequeueReusableCellWithIdentifier:cellId]; 170 | if (!cell) { 171 | // Note: We won't create the left view here because it'll be shared by all cells. 172 | cell = [[FSCSwipeCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:cellId]; 173 | cell.delegate = self; 174 | 175 | // Create the right view which will be our "Snooze" action. 176 | cell.rightView = [[UIView alloc] init]; 177 | 178 | // Create an image view for the Snooze icon. 179 | UIImageView *snooze = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"Snooze"]]; 180 | snooze.tag = 1; 181 | [cell.rightView addSubview:snooze]; 182 | } 183 | 184 | // Set up the labels. 185 | cell.detailTextLabel.text = @"Swipe right for options, left to snooze."; 186 | cell.detailTextLabel.textColor = [UIColor grayColor]; 187 | cell.textLabel.text = self.labels[indexPath.row]; 188 | 189 | return cell; 190 | } 191 | 192 | - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { 193 | return [self.labels count]; 194 | } 195 | 196 | #pragma mark UIScrollViewDelegate 197 | 198 | - (void)scrollViewDidScroll:(UIScrollView *)scrollView { 199 | for (FSCSwipeCell *cell in [self.tableView visibleCells]) { 200 | cell.currentSide = FSCSwipeCellSideNone; 201 | } 202 | } 203 | 204 | #pragma mark UITableViewDelegate 205 | 206 | - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath { 207 | return 60; 208 | } 209 | 210 | - (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath { 211 | // Remove insets and margins from cells. 212 | if ([cell respondsToSelector:@selector(setSeparatorInset:)]) { 213 | [cell setSeparatorInset:UIEdgeInsetsZero]; 214 | } 215 | 216 | if ([cell respondsToSelector:@selector(setPreservesSuperviewLayoutMargins:)]) { 217 | [cell setPreservesSuperviewLayoutMargins:NO]; 218 | } 219 | 220 | if ([cell respondsToSelector:@selector(setLayoutMargins:)]) { 221 | [cell setLayoutMargins:UIEdgeInsetsZero]; 222 | } 223 | } 224 | 225 | @end -------------------------------------------------------------------------------- /Example/FSCSwipeCell/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 | "idiom" : "iphone", 20 | "size" : "60x60", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "ipad", 25 | "size" : "29x29", 26 | "scale" : "1x" 27 | }, 28 | { 29 | "idiom" : "ipad", 30 | "size" : "29x29", 31 | "scale" : "2x" 32 | }, 33 | { 34 | "idiom" : "ipad", 35 | "size" : "40x40", 36 | "scale" : "1x" 37 | }, 38 | { 39 | "idiom" : "ipad", 40 | "size" : "40x40", 41 | "scale" : "2x" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "size" : "76x76", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "size" : "76x76", 51 | "scale" : "2x" 52 | } 53 | ], 54 | "info" : { 55 | "version" : 1, 56 | "author" : "xcode" 57 | } 58 | } -------------------------------------------------------------------------------- /Example/FSCSwipeCell/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 | "orientation" : "portrait", 20 | "idiom" : "ipad", 21 | "extent" : "full-screen", 22 | "minimum-system-version" : "7.0", 23 | "scale" : "1x" 24 | }, 25 | { 26 | "orientation" : "landscape", 27 | "idiom" : "ipad", 28 | "extent" : "full-screen", 29 | "minimum-system-version" : "7.0", 30 | "scale" : "1x" 31 | }, 32 | { 33 | "orientation" : "portrait", 34 | "idiom" : "ipad", 35 | "extent" : "full-screen", 36 | "minimum-system-version" : "7.0", 37 | "scale" : "2x" 38 | }, 39 | { 40 | "orientation" : "landscape", 41 | "idiom" : "ipad", 42 | "extent" : "full-screen", 43 | "minimum-system-version" : "7.0", 44 | "scale" : "2x" 45 | } 46 | ], 47 | "info" : { 48 | "version" : 1, 49 | "author" : "xcode" 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /Example/FSCSwipeCell/Images.xcassets/Snooze.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x", 6 | "filename" : "Snooze.png" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x", 11 | "filename" : "Snooze@2x.png" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "scale" : "3x", 16 | "filename" : "Snooze@3x.png" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /Example/FSCSwipeCell/Images.xcassets/Snooze.imageset/Snooze.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blixt/FSCSwipeCell/9655fb820f431a5d2d26a3648e5312c331c15401/Example/FSCSwipeCell/Images.xcassets/Snooze.imageset/Snooze.png -------------------------------------------------------------------------------- /Example/FSCSwipeCell/Images.xcassets/Snooze.imageset/Snooze@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blixt/FSCSwipeCell/9655fb820f431a5d2d26a3648e5312c331c15401/Example/FSCSwipeCell/Images.xcassets/Snooze.imageset/Snooze@2x.png -------------------------------------------------------------------------------- /Example/FSCSwipeCell/Images.xcassets/Snooze.imageset/Snooze@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blixt/FSCSwipeCell/9655fb820f431a5d2d26a3648e5312c331c15401/Example/FSCSwipeCell/Images.xcassets/Snooze.imageset/Snooze@3x.png -------------------------------------------------------------------------------- /Example/FSCSwipeCell/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 | -------------------------------------------------------------------------------- /Example/FSCSwipeCell/main.m: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | #import "FSCAppDelegate.h" 4 | 5 | int main(int argc, char * argv[]) 6 | { 7 | @autoreleasepool { 8 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([FSCAppDelegate class])); 9 | } 10 | } -------------------------------------------------------------------------------- /Example/Podfile: -------------------------------------------------------------------------------- 1 | source 'https://github.com/CocoaPods/Specs.git' 2 | 3 | target 'FSCSwipeCell', :exclusive => true do 4 | pod "FSCSwipeCell", :path => "../" 5 | end 6 | 7 | target 'Tests', :exclusive => true do 8 | pod "FSCSwipeCell", :path => "../" 9 | 10 | pod 'Specta', :git => 'https://github.com/specta/specta.git', :tag => 'v0.3.0.beta1' 11 | pod 'Expecta' 12 | pod 'FBSnapshotTestCase' 13 | pod 'Expecta+Snapshots' 14 | end 15 | -------------------------------------------------------------------------------- /Example/Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - Expecta (0.3.2) 3 | - Expecta+Snapshots (1.3.1): 4 | - Expecta 5 | - FBSnapshotTestCase 6 | - FBSnapshotTestCase (1.5) 7 | - FSCSwipeCell (0.1.8) 8 | - Specta (0.3.0.beta1) 9 | 10 | DEPENDENCIES: 11 | - Expecta 12 | - Expecta+Snapshots 13 | - FBSnapshotTestCase 14 | - FSCSwipeCell (from `../`) 15 | - Specta (from `https://github.com/specta/specta.git`, tag `v0.3.0.beta1`) 16 | 17 | EXTERNAL SOURCES: 18 | FSCSwipeCell: 19 | :path: ../ 20 | Specta: 21 | :git: https://github.com/specta/specta.git 22 | :tag: v0.3.0.beta1 23 | 24 | CHECKOUT OPTIONS: 25 | Specta: 26 | :git: https://github.com/specta/specta.git 27 | :tag: v0.3.0.beta1 28 | 29 | SPEC CHECKSUMS: 30 | Expecta: ee641011fe10aa1855d487b40e4976dac50ec342 31 | Expecta+Snapshots: 4a56b9411c6ed156987072e52c39de67d864015a 32 | FBSnapshotTestCase: e2914fbaabccea1dcc773d6a16b1c24540642488 33 | FSCSwipeCell: 27000940cf0f196cbf13647e966dd1b7ca031789 34 | Specta: d2158a483ad179e5f8d3a29f75ff4fc0ff6fab16 35 | 36 | COCOAPODS: 0.36.0.rc.1 37 | -------------------------------------------------------------------------------- /Example/Tests/Tests-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIdentifier 10 | com.47center.${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 | -------------------------------------------------------------------------------- /Example/Tests/Tests-Prefix.pch: -------------------------------------------------------------------------------- 1 | // 2 | // Prefix header 3 | // 4 | // The contents of this file are implicitly included at the beginning of every test case source file. 5 | // 6 | 7 | #ifdef __OBJC__ 8 | 9 | #define EXP_SHORTHAND 10 | #import 11 | #import 12 | #import 13 | #import 14 | 15 | #endif 16 | -------------------------------------------------------------------------------- /Example/Tests/Tests.m: -------------------------------------------------------------------------------- 1 | SpecBegin(InitialSpecs) 2 | 3 | describe(@"FSCSwipeCell", ^{ 4 | it(@"currently has no tests", ^{ 5 | expect(YES).beTruthy; 6 | }); 7 | }); 8 | 9 | SpecEnd -------------------------------------------------------------------------------- /FSCSwipeCell.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |s| 2 | s.name = 'FSCSwipeCell' 3 | s.version = '0.1.8' 4 | s.summary = 'A table view cell that can be swiped left and/or right to perform an action.' 5 | s.description = <<-DESC 6 | Table view cells of this class will reveal a colored area that represents an action when 7 | the user swipes left or right on the cell. If the user passes over a certain threshold, 8 | the action will be triggered; otherwise, the cell will just bounce back to its default 9 | state. 10 | DESC 11 | s.homepage = 'https://github.com/47center/FSCSwipeCell' 12 | s.screenshots = 'http://fat.gfycat.com/CarefreeCreativeAdder.gif' 13 | s.license = 'MIT' 14 | s.author = { 'Blixt' => 'blixt@47center.com' } 15 | s.source = { :git => 'https://github.com/47center/FSCSwipeCell.git', :tag => s.version.to_s } 16 | s.social_media_url = 'https://twitter.com/blixt' 17 | 18 | s.platform = :ios, '7.0' 19 | s.requires_arc = true 20 | 21 | s.source_files = 'Pod/Classes/**/*' 22 | s.resource_bundles = { 23 | 'FSCSwipeCell' => ['Pod/Assets/*.png'] 24 | } 25 | 26 | s.frameworks = 'QuartzCore', 'UIKit' 27 | end 28 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015 47 Center, Inc. 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. -------------------------------------------------------------------------------- /Pod/Assets/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blixt/FSCSwipeCell/9655fb820f431a5d2d26a3648e5312c331c15401/Pod/Assets/.gitkeep -------------------------------------------------------------------------------- /Pod/Classes/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blixt/FSCSwipeCell/9655fb820f431a5d2d26a3648e5312c331c15401/Pod/Classes/.gitkeep -------------------------------------------------------------------------------- /Pod/Classes/FSCSwipeCell.h: -------------------------------------------------------------------------------- 1 | #import 2 | #import 3 | 4 | @class FSCSwipeCell; 5 | 6 | /** 7 | * Refers to a particular side of a cell. 8 | */ 9 | typedef NS_ENUM(NSInteger, FSCSwipeCellSide) { 10 | /** Neither side of the cell, often referring to the cell being in its default state. */ 11 | FSCSwipeCellSideNone = 0, 12 | /** The left side of the cell. */ 13 | FSCSwipeCellSideLeft = -1, 14 | /** The right side of the cell. */ 15 | FSCSwipeCellSideRight = 1, 16 | }; 17 | 18 | /** 19 | * The default duration of the cell's open/close animations, in seconds. 20 | */ 21 | extern NSTimeInterval const kFSCSwipeCellAnimationDuration; 22 | 23 | /** 24 | * The magnitude with which to reduce the swipe distance when there is no view to show under 25 | * the cell. 26 | */ 27 | extern CGFloat const kFSCSwipeCellBounceElasticity; 28 | 29 | /** 30 | * How many points the user has to swipe the cell in a direction to open when the user lets 31 | * go of the cell. 32 | */ 33 | extern CGFloat const kFSCSwipeCellOpenDistanceThreshold; 34 | 35 | /** 36 | * The minimum velocity required to perform an action if released before the threshold has 37 | * been passed. 38 | */ 39 | extern CGFloat const kFSCSwipeCellOpenVelocityThreshold; 40 | 41 | #pragma mark - FSCSwipeCellDelegate 42 | 43 | /** 44 | * Allows monitoring and some control of the user's interaction with the cell. 45 | */ 46 | @protocol FSCSwipeCellDelegate 47 | 48 | @optional 49 | 50 | /** 51 | * Called when the left/right side view of the cell is no longer visible. 52 | */ 53 | - (void)swipeCell:(FSCSwipeCell *)cell didHideSide:(FSCSwipeCellSide)side; 54 | 55 | /** 56 | * Called whenever the offset changes (either by swiping or programmatically). 57 | */ 58 | - (void)swipeCell:(FSCSwipeCell *)cell didSwipe:(CGFloat)distance side:(FSCSwipeCellSide)side; 59 | 60 | /** 61 | * Called before a left/right side view is shown, allowing the swipe to be ignored. 62 | */ 63 | - (BOOL)swipeCell:(FSCSwipeCell *)cell shouldShowSide:(FSCSwipeCellSide)side; 64 | 65 | /** 66 | * Called when the current side of the cell changes. This will be called before animations 67 | * complete. 68 | */ 69 | - (void)swipeCellDidChangeCurrentSide:(FSCSwipeCell *)cell; 70 | 71 | /** 72 | * Called when the user has stopped swiping the cell. 73 | */ 74 | - (void)swipeCellDidEndSwiping:(FSCSwipeCell *)cell; 75 | 76 | /** 77 | * Called when the user began swiping the cell. 78 | */ 79 | - (void)swipeCellWillBeginSwiping:(FSCSwipeCell *)cell; 80 | 81 | @end 82 | 83 | #pragma mark - FSCSwipeCell 84 | 85 | /** 86 | * Table view cells of this class will reveal a colored area that represents an action when 87 | * the user swipes left or right on the cell. If the user passes over a certain threshold, 88 | * the action will be triggered; otherwise, the cell will just bounce back to its default 89 | * state. 90 | */ 91 | @interface FSCSwipeCell : UITableViewCell 92 | 93 | /** 94 | * The currently shown side of the cell. Note that this value will change before the animation 95 | * finishes. 96 | */ 97 | @property (nonatomic) FSCSwipeCellSide currentSide; 98 | 99 | /** 100 | * An optional delegate which will be notified whenever the user interacts with the cell. 101 | */ 102 | @property (nonatomic, weak) id delegate; 103 | 104 | /** 105 | * The view to display when the cell is swiped from left to right. Setting this value will put 106 | * the specified view as a subview of this cell. Removing that view from this cell will reset 107 | * this value to nil. 108 | */ 109 | @property (nonatomic, strong) UIView *leftView; 110 | 111 | /** 112 | * The current offset of the main content view in the cell. 113 | */ 114 | @property (nonatomic) CGFloat offset; 115 | 116 | /** 117 | * The gesture recognizer that handles swiping the cell left and right. 118 | */ 119 | @property (nonatomic, readonly, strong) UIPanGestureRecognizer *panGestureRecognizer; 120 | 121 | /** 122 | * The view to display when the cell is swiped from right to left. Setting this value will put 123 | * the specified view as a subview of this cell. Removing that view from this cell will reset 124 | * this value to nil. 125 | */ 126 | @property (nonatomic, strong) UIView *rightView; 127 | 128 | /** 129 | * Whether the cell is currently being swiped. 130 | */ 131 | @property (nonatomic, readonly) BOOL swiping; 132 | 133 | /** 134 | * Sets the current side of the cell, with control over animation. By default, the side change 135 | * is animated, but passing in 0 will make the change instant. 136 | */ 137 | - (void)setCurrentSide:(FSCSwipeCellSide)side duration:(NSTimeInterval)duration; 138 | 139 | /** 140 | * Sets the current offset of the content cell, with control over animation. By default, 141 | * the change is animated, but passing in 0 will make the change instant. 142 | */ 143 | - (void)setOffset:(CGFloat)x duration:(NSTimeInterval)duration; 144 | 145 | /** 146 | * Sets the current offset of the content cell, with a block to call when the animation 147 | * has finished. 148 | */ 149 | - (void)setOffset:(CGFloat)x completion:(void (^)(BOOL finished))completion; 150 | 151 | /** 152 | * Sets the current offset of the content cell, with control over animation and a block 153 | * to call when it finishes. By default, the change is animated, but passing in 0 will 154 | * make the change instant. 155 | */ 156 | - (void)setOffset:(CGFloat)x duration:(NSTimeInterval)duration completion:(void (^)(BOOL finished))completion; 157 | 158 | @end 159 | -------------------------------------------------------------------------------- /Pod/Classes/FSCSwipeCell.m: -------------------------------------------------------------------------------- 1 | #import "FSCSwipeCell.h" 2 | 3 | NSTimeInterval const kFSCSwipeCellAnimationDuration = 0.1; 4 | CGFloat const kFSCSwipeCellBounceElasticity = 0.2; 5 | CGFloat const kFSCSwipeCellOpenDistanceThreshold = 75; 6 | CGFloat const kFSCSwipeCellOpenVelocityThreshold = 500; 7 | 8 | FSCSwipeCell *FSCSwipeCellCurrentSwipingCell; 9 | 10 | #pragma mark - FSCSwipeCell 11 | 12 | @interface FSCSwipeCell () 13 | 14 | @property (nonatomic) NSUInteger animationCount; 15 | @property (nonatomic, strong) CADisplayLink *displayLink; 16 | @property (nonatomic) CFAbsoluteTime lastPanEventTime; 17 | @property (nonatomic, strong) UIPanGestureRecognizer *panGestureRecognizer; 18 | @property (nonatomic) BOOL swiping; 19 | @property (nonatomic, strong) UIView *wrapper; 20 | 21 | @end 22 | 23 | @implementation FSCSwipeCell 24 | 25 | #pragma mark Lifecycle methods 26 | 27 | - (void)awakeFromNib { 28 | [super awakeFromNib]; 29 | [self setUp]; 30 | } 31 | 32 | - (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier { 33 | self = [super initWithStyle:style reuseIdentifier:reuseIdentifier]; 34 | if (self) { 35 | [self setUp]; 36 | } 37 | return self; 38 | } 39 | 40 | - (void)setUp { 41 | _panGestureRecognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(cellWasSwiped:)]; 42 | _panGestureRecognizer.delaysTouchesBegan = YES; 43 | _panGestureRecognizer.delegate = self; 44 | [self addGestureRecognizer:_panGestureRecognizer]; 45 | 46 | // Create a wrapper view which will change its bounds to move the content view left/right. 47 | _wrapper = [[UIView alloc] initWithFrame:self.bounds]; 48 | _wrapper.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; 49 | _wrapper.userInteractionEnabled = NO; 50 | [self addSubview:_wrapper]; 51 | 52 | // Move the content view into the wrapper. 53 | UIView *contentView = self.contentView; 54 | [contentView removeFromSuperview]; 55 | [_wrapper addSubview:contentView]; 56 | 57 | // Remove the white background color from the cell itself. 58 | self.backgroundColor = [UIColor clearColor]; 59 | 60 | // Make the content view white since it can now uncover things behind it. 61 | contentView.backgroundColor = [UIColor whiteColor]; 62 | } 63 | 64 | #pragma mark Properties 65 | 66 | - (void)setCurrentSide:(FSCSwipeCellSide)side { 67 | [self setCurrentSide:side duration:kFSCSwipeCellAnimationDuration]; 68 | } 69 | 70 | - (void)setCurrentSide:(FSCSwipeCellSide)side duration:(NSTimeInterval)duration { 71 | if (duration > 0 && side == _currentSide) return; 72 | [self setOffset:(self.bounds.size.width * side) duration:duration]; 73 | } 74 | 75 | - (void)setLeftView:(UIView *)view { 76 | if (view == _leftView) return; 77 | if (_leftView) [_leftView removeFromSuperview]; 78 | _leftView = view; 79 | 80 | if (view) { 81 | view.hidden = (self.offset >= 0); 82 | view.frame = CGRectMake(0, 0, self.bounds.size.width, self.bounds.size.height); 83 | [self.wrapper.superview insertSubview:view belowSubview:self.wrapper]; 84 | } 85 | } 86 | 87 | - (void)setOffset:(CGFloat)x { 88 | [self setOffset:x duration:kFSCSwipeCellAnimationDuration completion:nil]; 89 | } 90 | 91 | - (void)setOffset:(CGFloat)x duration:(NSTimeInterval)duration { 92 | [self setOffset:x duration:duration completion:nil]; 93 | } 94 | 95 | - (void)setOffset:(CGFloat)x completion:(void (^)(BOOL finished))completion { 96 | [self setOffset:x duration:kFSCSwipeCellAnimationDuration completion:completion]; 97 | } 98 | 99 | - (void)setOffset:(CGFloat)x duration:(NSTimeInterval)duration completion:(void (^)(BOOL finished))completion { 100 | // Determine which side is showing, and what side will show. 101 | FSCSwipeCellSide previousSide = (_offset < 0 ? FSCSwipeCellSideLeft : (_offset > 0 ? FSCSwipeCellSideRight : FSCSwipeCellSideNone)); 102 | FSCSwipeCellSide side = (x < 0 ? FSCSwipeCellSideLeft : (x > 0 ? FSCSwipeCellSideRight : FSCSwipeCellSideNone)); 103 | 104 | // Handle sides changing. 105 | if (side != previousSide && side != FSCSwipeCellSideNone && [self.delegate respondsToSelector:@selector(swipeCell:shouldShowSide:)]) { 106 | // Ask the delegate if the side should show. 107 | if (![self.delegate swipeCell:self shouldShowSide:side]) { 108 | // Instantly reset the offset to 0. 109 | x = 0; 110 | side = FSCSwipeCellSideNone; 111 | duration = 0; 112 | } 113 | } 114 | 115 | // Update the underlying variable holding the offset. 116 | _offset = x; 117 | 118 | // Update the current side if it's fully exposed and the cell is not being swiped. 119 | if (!self.swiping && side != self.currentSide && x == side * self.bounds.size.width) { 120 | _currentSide = side; 121 | // Let the delegate know of side changes. 122 | if ([self.delegate respondsToSelector:@selector(swipeCellDidChangeCurrentSide:)]) { 123 | [self.delegate swipeCellDidChangeCurrentSide:self]; 124 | } 125 | } 126 | 127 | // Apply bounce. 128 | if ((x < 0 && !self.leftView) || (x > 0 && !self.rightView)) { 129 | x *= kFSCSwipeCellBounceElasticity; 130 | } 131 | 132 | // Convenience block for calling delegates and callbacks. 133 | void (^done)(BOOL) = ^(BOOL finished) { 134 | if (finished && side != previousSide && previousSide != FSCSwipeCellSideNone) { 135 | if ([self.delegate respondsToSelector:@selector(swipeCell:didHideSide:)]) { 136 | [self.delegate swipeCell:self didHideSide:previousSide]; 137 | } 138 | } 139 | if (completion) completion(finished); 140 | }; 141 | 142 | // Calculate the destination bounds. 143 | CGRect bounds = CGRectMake(x, 0, self.bounds.size.width, self.bounds.size.height); 144 | 145 | dispatch_async(dispatch_get_main_queue(), ^{ 146 | // Perform the change instantly if no duration was specified. 147 | if (duration <= 0) { 148 | [self.wrapper.layer removeAllAnimations]; 149 | self.wrapper.bounds = bounds; 150 | [self reportOffset:x]; 151 | done(YES); 152 | return; 153 | } 154 | 155 | if (self.animationCount == 0) { 156 | // Create a timer which will update the delegate every frame. 157 | if (self.displayLink) { 158 | [self.displayLink invalidate]; 159 | } 160 | self.displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(frameTick:)]; 161 | [self.displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes]; 162 | } 163 | 164 | // Animate the movement of the cell. 165 | self.animationCount++; 166 | [UIView animateWithDuration:duration 167 | delay:0 168 | options:UIViewAnimationOptionBeginFromCurrentState | UIViewAnimationOptionCurveEaseOut 169 | animations:^{ 170 | self.wrapper.bounds = bounds; 171 | } 172 | completion:^(BOOL finished) { 173 | self.animationCount--; 174 | if (self.animationCount == 0) { 175 | if (finished) { 176 | [self.displayLink invalidate]; 177 | self.displayLink = nil; 178 | } 179 | done(finished); 180 | } 181 | }]; 182 | }); 183 | } 184 | 185 | - (void)setRightView:(UIView *)view { 186 | if (view == _rightView) return; 187 | if (_rightView) [_rightView removeFromSuperview]; 188 | _rightView = view; 189 | 190 | if (view) { 191 | view.hidden = (self.offset <= 0); 192 | view.frame = CGRectMake(0, 0, self.bounds.size.width, self.bounds.size.height); 193 | [self.wrapper.superview insertSubview:view belowSubview:self.wrapper]; 194 | } 195 | } 196 | 197 | #pragma mark Private methods 198 | 199 | - (void)cellWasSwiped:(UIPanGestureRecognizer *)sender { 200 | CGFloat velocity = -[sender velocityInView:self].x; 201 | CGFloat width = self.bounds.size.width; 202 | 203 | // Get the swiped distance. 204 | CGFloat x = self.offset - [sender translationInView:self].x; 205 | // Constrain the offset to be within bounds. 206 | x = MAX(MIN(x, self.bounds.size.width), -self.bounds.size.width); 207 | // Reset the pan gesture's translation offset. 208 | [sender setTranslation:CGPointZero inView:self]; 209 | 210 | // Handle the various dragging states. 211 | switch (sender.state) { 212 | case UIGestureRecognizerStateBegan: 213 | // Atomically set the currently swiping cell (or if one is already swiping, ignore this one). 214 | @synchronized([FSCSwipeCell class]) { 215 | if (FSCSwipeCellCurrentSwipingCell) { 216 | // Another cell is already being swiped, cancel this gesture. 217 | sender.enabled = NO; 218 | return; 219 | } 220 | FSCSwipeCellCurrentSwipingCell = self; 221 | } 222 | 223 | self.swiping = YES; 224 | 225 | // Notify the delegate that swiping has commenced. 226 | if ([self.delegate respondsToSelector:@selector(swipeCellWillBeginSwiping:)]) { 227 | [self.delegate swipeCellWillBeginSwiping:self]; 228 | } 229 | 230 | // Intentional fall-through of control. 231 | case UIGestureRecognizerStateChanged: 232 | // Move the cell content instantly. 233 | [self setOffset:x duration:0]; 234 | break; 235 | case UIGestureRecognizerStateEnded: 236 | { 237 | // Reduce the velocity based on how long has passed since the user dragged. 238 | velocity /= CFAbsoluteTimeGetCurrent() - self.lastPanEventTime + 1; 239 | 240 | // Atomically unset the currently swiping cell (or bail if this cell isn't the main swiping cell). 241 | @synchronized([FSCSwipeCell class]) { 242 | FSCSwipeCellCurrentSwipingCell = nil; 243 | } 244 | 245 | self.swiping = NO; 246 | 247 | BOOL goingLeft = (velocity > kFSCSwipeCellOpenVelocityThreshold); 248 | BOOL goingRight = (velocity < -kFSCSwipeCellOpenVelocityThreshold); 249 | 250 | BOOL resetOffset = NO; 251 | switch (self.currentSide) { 252 | case FSCSwipeCellSideLeft: 253 | // Return to default state unless the user swiped in the open direction. 254 | if (!goingRight && x > -width) { 255 | self.currentSide = FSCSwipeCellSideNone; 256 | } else { 257 | resetOffset = YES; 258 | } 259 | break; 260 | case FSCSwipeCellSideNone: 261 | // Open the relevant side (if it has a style and the user swiped beyond the threshold). 262 | if (self.leftView && ((x <= 0 && goingRight) || (x < -kFSCSwipeCellOpenDistanceThreshold && !goingLeft))) { 263 | self.currentSide = FSCSwipeCellSideLeft; 264 | } else if (self.rightView && ((x >= 0 && goingLeft) || (x > kFSCSwipeCellOpenDistanceThreshold && !goingRight))) { 265 | self.currentSide = FSCSwipeCellSideRight; 266 | } else { 267 | resetOffset = YES; 268 | } 269 | break; 270 | case FSCSwipeCellSideRight: 271 | // Return to default state unless the user swiped in the open direction. 272 | if (!goingLeft && x < width) { 273 | self.currentSide = FSCSwipeCellSideNone; 274 | } else { 275 | resetOffset = YES; 276 | } 277 | break; 278 | } 279 | 280 | if (resetOffset) { 281 | self.offset = self.currentSide * width; 282 | } 283 | 284 | // Notify the delegate that swiping has ended. 285 | if ([self.delegate respondsToSelector:@selector(swipeCellDidEndSwiping:)]) { 286 | [self.delegate swipeCellDidEndSwiping:self]; 287 | } 288 | 289 | break; 290 | } 291 | case UIGestureRecognizerStateCancelled: 292 | // Restore the gesture recognizer once it's been cancelled. 293 | sender.enabled = YES; 294 | break; 295 | default: 296 | return; 297 | } 298 | self.lastPanEventTime = CFAbsoluteTimeGetCurrent(); 299 | } 300 | 301 | - (void)frameTick:(CADisplayLink *)sender { 302 | CALayer *layer = self.wrapper.layer.presentationLayer; 303 | [self reportOffset:layer.bounds.origin.x]; 304 | } 305 | 306 | - (void)reportOffset:(CGFloat)x { 307 | FSCSwipeCellSide side = (x < 0 ? FSCSwipeCellSideLeft : (x > 0 ? FSCSwipeCellSideRight : FSCSwipeCellSideNone)); 308 | if (self.leftView) self.leftView.hidden = side != FSCSwipeCellSideLeft; 309 | if (self.rightView) self.rightView.hidden = side != FSCSwipeCellSideRight; 310 | if (x != 0 && [self.delegate respondsToSelector:@selector(swipeCell:didSwipe:side:)]) { 311 | [self.delegate swipeCell:self didSwipe:fabs(x) side:side]; 312 | } 313 | } 314 | 315 | #pragma mark UIGestureRecognizerDelegate 316 | 317 | - (BOOL)gestureRecognizerShouldBegin:(UIPanGestureRecognizer *)recognizer { 318 | if (recognizer != self.panGestureRecognizer) { 319 | if ([[FSCSwipeCell superclass] instancesRespondToSelector:@selector(gestureRecognizerShouldBegin:)]) { 320 | return [super gestureRecognizerShouldBegin:recognizer]; 321 | } else { 322 | return YES; 323 | } 324 | } 325 | 326 | CGPoint translation = [recognizer translationInView:self]; 327 | // Fail vertical swipes. 328 | return (fabs(translation.y) <= fabs(translation.x)); 329 | } 330 | 331 | - (BOOL)gestureRecognizer:(UIPanGestureRecognizer *)recognizer shouldBeRequiredToFailByGestureRecognizer:(UIGestureRecognizer *)other { 332 | if (recognizer != self.panGestureRecognizer) { 333 | if ([[FSCSwipeCell superclass] instancesRespondToSelector:@selector(gestureRecognizer:shouldBeRequiredToFailByGestureRecognizer:)]) { 334 | return [super gestureRecognizer:recognizer shouldBeRequiredToFailByGestureRecognizer:other]; 335 | } else { 336 | return NO; 337 | } 338 | } 339 | 340 | return [other isKindOfClass:[UIPanGestureRecognizer class]]; 341 | } 342 | 343 | #pragma mark UITableViewCell 344 | 345 | - (void)prepareForReuse { 346 | [self setOffset:0 duration:0]; 347 | } 348 | 349 | - (void)setSelected:(BOOL)selected animated:(BOOL)animated { 350 | [super setSelected:selected animated:animated]; 351 | if (selected) { 352 | // Move the selected background view into the wrapper. 353 | [self.wrapper insertSubview:self.selectedBackgroundView atIndex:0]; 354 | } 355 | } 356 | 357 | #pragma mark UIView 358 | 359 | - (void)layoutSubviews { 360 | [super layoutSubviews]; 361 | 362 | // Update the subview layouts. 363 | CGRect frame = CGRectMake(0, 0, self.bounds.size.width, self.bounds.size.height); 364 | self.wrapper.frame = frame; 365 | if (self.leftView) self.leftView.frame = frame; 366 | if (self.rightView) self.rightView.frame = frame; 367 | } 368 | 369 | - (void)willRemoveSubview:(UIView *)subview { 370 | if (_leftView == subview) _leftView = nil; 371 | if (_rightView == subview) _rightView = nil; 372 | } 373 | 374 | @end 375 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # FSCSwipeCell 2 | 3 | [![CI Status](http://img.shields.io/travis/47center/FSCSwipeCell.svg?style=flat)](https://travis-ci.org/47center/FSCSwipeCell) 4 | [![Version](https://img.shields.io/cocoapods/v/FSCSwipeCell.svg?style=flat)](http://cocoadocs.org/docsets/FSCSwipeCell) 5 | [![License](https://img.shields.io/cocoapods/l/FSCSwipeCell.svg?style=flat)](http://cocoadocs.org/docsets/FSCSwipeCell) 6 | [![Platform](https://img.shields.io/cocoapods/p/FSCSwipeCell.svg?style=flat)](http://cocoadocs.org/docsets/FSCSwipeCell) 7 | 8 | ## Installation 9 | 10 | FSCSwipeCell is available through [CocoaPods](http://cocoapods.org). To install it, simply 11 | add the following line to your Podfile: 12 | 13 | pod "FSCSwipeCell" 14 | 15 | ## What is it? 16 | 17 | 18 | 19 | This component was built to make swipeable cells behave as one would expect, without taking control of what appears 20 | when you swipe left or right. 21 | 22 | ### What it does 23 | 24 | * Displays additional views (that you provide) under the table view cell when the user swipes it left or right 25 | * Handles the physics of swiping a cell open/closed with distance and velocity thresholds 26 | * Notifies the (optional) delegate of all updates to the state of the cell, such as: 27 | * The left/right view is about to show (with the option of canceling it and keeping it "closed") 28 | * The cell has been swiped any distance 29 | * The cell is done closing (either because the user swiped it shut, or didn't swipe beyond the threshold) 30 | * Repurposes the standard `UITableViewCell`'s `contentView`, so all table view styles are supported 31 | * Lets you assign `leftView` or `rightView` immediately before showing them, so you can reuse one view for all cells 32 | 33 | ### What it doesn't do 34 | 35 | * It doesn't create or handle any content in the left/right views 36 | * It (currently) doesn't allow the cell to stay half-open (e.g., like the Mail app's more/delete buttons) 37 | 38 | ### Why not one of the other swipeable cell libraries out there? 39 | 40 | It really depends on what you're after. This library gives you some more control of how the cell renders when swiped, 41 | but if you just want to show a few buttons when the user swipes left, use [UITableViewRowAction][], or if you want 42 | some more functionality on the buttons, check out [SWTableViewCell][] or [MGSwipeTableCell][]. There are some things 43 | I needed for my app which are out of the scope of those libraries, which is why I made this library. Hopefully all 44 | these libraries together will help cover everyone's needs. 45 | 46 | ## A note on stability 47 | 48 | This is in a very early stage right now and may have bugs lurking in the water. For example, I have not tested it 49 | with iOS 7 so that might not work too well (yet). All pull requests are welcome! 50 | 51 | ## Usage 52 | 53 | To run the example project, clone the repo, and run `pod install` from the Example directory first. 54 | 55 | ## Author 56 | 57 | Blixt, blixt@47center.com 58 | 59 | ## License 60 | 61 | FSCSwipeCell is available under the MIT license. See the LICENSE file for more info. 62 | 63 | 64 | [UITableViewRowAction]: https://developer.apple.com/library/ios/documentation/UIKit/Reference/UITableViewRowAction_class/index.html 65 | [SWTableViewCell]: https://github.com/CEWendel/SWTableViewCell 66 | [MGSwipeTableCell]: https://github.com/MortimerGoro/MGSwipeTableCell 67 | --------------------------------------------------------------------------------