├── .gitignore ├── README.markdown ├── SwipeableExample ├── Classes │ ├── ExampleCell.h │ ├── ExampleCell.m │ ├── RootViewController.h │ ├── RootViewController.m │ ├── SwipeableExampleAppDelegate.h │ └── SwipeableExampleAppDelegate.m ├── Default-568h@2x.png ├── SwipeableExample-Info.plist ├── SwipeableExample.xcodeproj │ └── project.pbxproj ├── SwipeableExample_Prefix.pch ├── main.m ├── meshpattern.png └── tick.wav ├── TISwipeableTableView.h └── TISwipeableTableView.m /.gitignore: -------------------------------------------------------------------------------- 1 | # Mac OS X Finder 2 | .DS_Store 3 | 4 | # Xcode 5 | *.mode1 6 | *.mode1v3 7 | *.mode2v3 8 | *.perspective 9 | *.perspectivev3 10 | *.pbxuser 11 | */build/* 12 | 13 | *.xcworkspace 14 | xcuserdata -------------------------------------------------------------------------------- /README.markdown: -------------------------------------------------------------------------------- 1 | ## TISwipeableTableView 2 | 3 | Allows you to swipe a UITableViewCell and reveal a back view, as seen in the "Tweetie" app. 4 | 5 | ## Usage 6 | 7 | See the "SwipeableExample" app for the correct usage. 8 | 9 | ## ARC 10 | Yup 11 | 12 | ## License 13 | 14 | This control is dual licensed: 15 | You can use it for free under the BSD licence below or, 16 | If you require non-attribution you can purchase the commercial licence available at http://www.cocoacontrols.com/authors/thermogl 17 | 18 | Copyright (c) 2010 - 2013 Tom Irving. All rights reserved. 19 | 20 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 21 | 22 | 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 23 | 24 | 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 25 | 26 | THIS SOFTWARE IS PROVIDED BY TOM IRVING "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL TOM IRVING OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- /SwipeableExample/Classes/ExampleCell.h: -------------------------------------------------------------------------------- 1 | // 2 | // ExampleCell.h 3 | // SwipeableExample 4 | // 5 | // Created by Tom Irving on 16/06/2010. 6 | // Copyright 2010 Tom Irving. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "TISwipeableTableView.h" 11 | 12 | @class ExampleCell; 13 | 14 | @protocol ExampleCellDelegate 15 | - (void)cellBackButtonWasTapped:(ExampleCell *)cell; 16 | @end 17 | 18 | @interface ExampleCell : TISwipeableTableViewCell 19 | @property (nonatomic, weak) id delegate; 20 | @property (nonatomic, copy) NSString * text; 21 | - (void)drawShadowsWithHeight:(CGFloat)shadowHeight opacity:(CGFloat)opacity InRect:(CGRect)rect forContext:(CGContextRef)context; 22 | @end 23 | -------------------------------------------------------------------------------- /SwipeableExample/Classes/ExampleCell.m: -------------------------------------------------------------------------------- 1 | // 2 | // ExampleCell.m 3 | // SwipeableExample 4 | // 5 | // Created by Tom Irving on 16/06/2010. 6 | // Copyright 2010 Tom Irving. All rights reserved. 7 | // 8 | 9 | #import "ExampleCell.h" 10 | 11 | @implementation ExampleCell 12 | @synthesize delegate; 13 | @synthesize text = _text; 14 | 15 | - (void)setText:(NSString *)text { 16 | _text = [text copy]; 17 | [self setNeedsDisplay]; 18 | } 19 | 20 | - (void)buttonWasTapped:(UIButton *)button { 21 | 22 | if ([delegate respondsToSelector:@selector(cellBackButtonWasTapped:)]){ 23 | [delegate cellBackButtonWasTapped:self]; 24 | } 25 | } 26 | 27 | - (void)backViewWillAppear:(BOOL)animated { 28 | 29 | UIButton * button = [UIButton buttonWithType:UIButtonTypeRoundedRect]; 30 | [button addTarget:self action:@selector(buttonWasTapped:) forControlEvents:UIControlEventTouchUpInside]; 31 | [button setTitle:@"Tap me" forState:UIControlStateNormal]; 32 | [button setFrame:CGRectMake(20, 4, self.backView.frame.size.width - 40, self.backView.frame.size.height - 8)]; 33 | [self.backView addSubview:button]; 34 | } 35 | 36 | - (void)backViewDidDisappear:(BOOL)animated { 37 | // Remove any subviews from the backView. 38 | [self.backView.subviews makeObjectsPerformSelector:@selector(removeFromSuperview)]; 39 | } 40 | 41 | - (void)drawContentView:(CGRect)rect { 42 | 43 | UIColor * textColor = [UIColor blackColor]; 44 | if (self.selected || self.highlighted){ 45 | textColor = [UIColor whiteColor]; 46 | } 47 | else 48 | { 49 | [[UIColor whiteColor] set]; 50 | UIRectFill(self.bounds); 51 | } 52 | 53 | [textColor set]; 54 | 55 | UIFont * textFont = [UIFont boldSystemFontOfSize:22]; 56 | CGSize textSize = [_text sizeWithFont:textFont constrainedToSize:rect.size]; 57 | [_text drawInRect:CGRectMake((rect.size.width / 2) - (textSize.width / 2), 58 | (rect.size.height / 2) - (textSize.height / 2), 59 | textSize.width, textSize.height) 60 | withFont:textFont]; 61 | } 62 | 63 | - (void)drawBackView:(CGRect)rect { 64 | 65 | [[UIImage imageNamed:@"meshpattern.png"] drawAsPatternInRect:rect]; 66 | [self drawShadowsWithHeight:10 opacity:0.3 InRect:rect forContext:UIGraphicsGetCurrentContext()]; 67 | } 68 | 69 | - (void)drawShadowsWithHeight:(CGFloat)shadowHeight opacity:(CGFloat)opacity InRect:(CGRect)rect forContext:(CGContextRef)context { 70 | 71 | CGColorSpaceRef space = CGBitmapContextGetColorSpace(context); 72 | 73 | CGFloat topComponents[8] = {0, 0, 0, opacity, 0, 0, 0, 0}; 74 | CGGradientRef topGradient = CGGradientCreateWithColorComponents(space, topComponents, nil, 2); 75 | CGPoint finishTop = CGPointMake(rect.origin.x, rect.origin.y + shadowHeight); 76 | CGContextDrawLinearGradient(context, topGradient, rect.origin, finishTop, kCGGradientDrawsAfterEndLocation); 77 | 78 | CGFloat bottomComponents[8] = {0, 0, 0, 0, 0, 0, 0, opacity}; 79 | CGGradientRef bottomGradient = CGGradientCreateWithColorComponents(space, bottomComponents, nil, 2); 80 | CGPoint startBottom = CGPointMake(rect.origin.x, rect.size.height - shadowHeight); 81 | CGPoint finishBottom = CGPointMake(rect.origin.x, rect.size.height); 82 | CGContextDrawLinearGradient(context, bottomGradient, startBottom, finishBottom, kCGGradientDrawsAfterEndLocation); 83 | 84 | CGGradientRelease(topGradient); 85 | CGGradientRelease(bottomGradient); 86 | } 87 | 88 | 89 | @end 90 | -------------------------------------------------------------------------------- /SwipeableExample/Classes/RootViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // RootViewController.h 3 | // SwipeableExample 4 | // 5 | // Created by Tom Irving on 16/06/2010. 6 | // Copyright Tom Irving 2010. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "ExampleCell.h" 11 | 12 | @interface RootViewController : TISwipeableTableViewController 13 | @end 14 | -------------------------------------------------------------------------------- /SwipeableExample/Classes/RootViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // RootViewController.m 3 | // SwipeableExample 4 | // 5 | // Created by Tom Irving on 16/06/2010. 6 | // Copyright Tom Irving 2010. All rights reserved. 7 | // 8 | 9 | #import "RootViewController.h" 10 | #import 11 | 12 | @implementation RootViewController 13 | 14 | #pragma mark - 15 | #pragma mark View lifecycle 16 | 17 | - (void)viewDidLoad { 18 | 19 | [self.tableView setRowHeight:54]; 20 | [self.navigationItem setTitle:@"Swipeable TableView"]; 21 | [super viewDidLoad]; 22 | } 23 | 24 | #pragma mark - 25 | #pragma mark Table view data source 26 | 27 | - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { 28 | return 1; 29 | } 30 | 31 | - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { 32 | return 100; 33 | } 34 | 35 | - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { 36 | 37 | static NSString *CellIdentifier = @"Cell"; 38 | 39 | ExampleCell * cell = (ExampleCell *)[tableView dequeueReusableCellWithIdentifier:CellIdentifier]; 40 | if (!cell) cell = [[ExampleCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier]; 41 | 42 | [cell setText:[NSString stringWithFormat:@"Swipe me! (Row %i)", indexPath.row]]; 43 | [cell setDelegate:self]; 44 | 45 | return cell; 46 | } 47 | 48 | #pragma mark - 49 | #pragma mark Table view delegate 50 | 51 | 52 | - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { 53 | 54 | UIAlertView * alertView = [[UIAlertView alloc] initWithTitle:@"Cell Selected" 55 | message:[NSString stringWithFormat:@"You tapped cell %i", indexPath.row] 56 | delegate:nil 57 | cancelButtonTitle:@"OK" 58 | otherButtonTitles:nil]; 59 | 60 | [alertView show]; 61 | 62 | [tableView deselectRowAtIndexPath:indexPath animated:YES]; 63 | [super tableView:tableView didSelectRowAtIndexPath:indexPath]; 64 | } 65 | 66 | 67 | static void completionCallback(SystemSoundID soundID, void * clientData) { 68 | AudioServicesRemoveSystemSoundCompletion(soundID); 69 | } 70 | 71 | - (void)tableView:(UITableView *)tableView didSwipeCellAtIndexPath:(NSIndexPath *)indexPath { 72 | 73 | [super tableView:tableView didSwipeCellAtIndexPath:indexPath]; 74 | 75 | NSString * path = [[NSBundle mainBundle] pathForResource:@"tick" ofType:@"wav"]; 76 | NSURL * fileURL = [NSURL fileURLWithPath:path isDirectory:NO]; 77 | 78 | SystemSoundID soundID; 79 | AudioServicesCreateSystemSoundID((__bridge CFURLRef)fileURL, &soundID); 80 | AudioServicesPlaySystemSound(soundID); 81 | AudioServicesAddSystemSoundCompletion (soundID, NULL, NULL, completionCallback, NULL); 82 | } 83 | 84 | - (void)cellBackButtonWasTapped:(ExampleCell *)cell { 85 | 86 | UIAlertView * alertView = [[UIAlertView alloc] initWithTitle:@"BackView Button" 87 | message:@"WHOA! YOU TAPPED A BACKVIEW BUTTON!" 88 | delegate:nil cancelButtonTitle:@"Sorry" 89 | otherButtonTitles:nil]; 90 | [alertView show]; 91 | 92 | [self tableView:self.tableView hideVisibleBackView:YES]; 93 | } 94 | 95 | - (void)scrollViewDidScroll:(UIScrollView *)scrollView { 96 | [super scrollViewDidScroll:scrollView]; 97 | 98 | // You gotta call super in all the methods you see here doing it. 99 | // Otherwise, you will end up with cells not hiding their backViews. 100 | } 101 | 102 | @end 103 | 104 | -------------------------------------------------------------------------------- /SwipeableExample/Classes/SwipeableExampleAppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // SwipeableExampleAppDelegate.h 3 | // SwipeableExample 4 | // 5 | // Created by Tom Irving on 16/06/2010. 6 | // Copyright Tom Irving 2010. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface SwipeableExampleAppDelegate : NSObject { 12 | UIWindow * window; 13 | } 14 | @end -------------------------------------------------------------------------------- /SwipeableExample/Classes/SwipeableExampleAppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // SwipeableExampleAppDelegate.m 3 | // SwipeableExample 4 | // 5 | // Created by Tom Irving on 16/06/2010. 6 | // Copyright Tom Irving 2010. All rights reserved. 7 | // 8 | 9 | #import "SwipeableExampleAppDelegate.h" 10 | #import "RootViewController.h" 11 | 12 | @implementation SwipeableExampleAppDelegate 13 | 14 | #pragma mark - 15 | #pragma mark Application lifecycle 16 | 17 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 18 | 19 | window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; 20 | 21 | RootViewController * viewController = [[RootViewController alloc] initWithStyle:UITableViewStylePlain]; 22 | UINavigationController * navigationController = [[UINavigationController alloc] initWithRootViewController:viewController]; 23 | 24 | [window setRootViewController:navigationController]; 25 | 26 | [window makeKeyAndVisible]; 27 | 28 | return YES; 29 | } 30 | 31 | #pragma mark - 32 | #pragma mark Memory management 33 | 34 | @end -------------------------------------------------------------------------------- /SwipeableExample/Default-568h@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thermogl/TISwipeableTableView/23897ee65786b3a2d07a0740bd65d7e82eb861f1/SwipeableExample/Default-568h@2x.png -------------------------------------------------------------------------------- /SwipeableExample/SwipeableExample-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | English 7 | CFBundleDisplayName 8 | ${PRODUCT_NAME} 9 | CFBundleExecutable 10 | ${EXECUTABLE_NAME} 11 | CFBundleIconFile 12 | 13 | CFBundleIdentifier 14 | com.yourcompany.${PRODUCT_NAME:rfc1034identifier} 15 | CFBundleInfoDictionaryVersion 16 | 6.0 17 | CFBundleName 18 | ${PRODUCT_NAME} 19 | CFBundlePackageType 20 | APPL 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | 1.0 25 | LSRequiresIPhoneOS 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /SwipeableExample/SwipeableExample.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 1D3623260D0F684500981E51 /* SwipeableExampleAppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 1D3623250D0F684500981E51 /* SwipeableExampleAppDelegate.m */; }; 11 | 1D60589B0D05DD56006BFB54 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 29B97316FDCFA39411CA2CEA /* main.m */; }; 12 | 1D60589F0D05DD5A006BFB54 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1D30AB110D05D00D00671497 /* Foundation.framework */; }; 13 | 1DF5F4E00D08C38300B7A737 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1DF5F4DF0D08C38300B7A737 /* UIKit.framework */; }; 14 | 2892E4100DC94CBA00A64D0F /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2892E40F0DC94CBA00A64D0F /* CoreGraphics.framework */; }; 15 | 28C286E10D94DF7D0034E888 /* RootViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 28C286E00D94DF7D0034E888 /* RootViewController.m */; }; 16 | E204DB5F12ADC1820063A748 /* AudioToolbox.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E204DB5E12ADC1820063A748 /* AudioToolbox.framework */; }; 17 | E230196616C53BE2001685F6 /* Default-568h@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = E230196516C53BE2001685F6 /* Default-568h@2x.png */; }; 18 | E238A58B125BED4500055319 /* TISwipeableTableView.m in Sources */ = {isa = PBXBuildFile; fileRef = E238A58A125BED4500055319 /* TISwipeableTableView.m */; }; 19 | E24E6A4711E532710082E0CD /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E24E6A4611E532710082E0CD /* QuartzCore.framework */; }; 20 | E2C99CDC11C8CC3B00FA14C3 /* ExampleCell.m in Sources */ = {isa = PBXBuildFile; fileRef = E2C99CDB11C8CC3B00FA14C3 /* ExampleCell.m */; }; 21 | E2C99D0811C8CF8F00FA14C3 /* meshpattern.png in Resources */ = {isa = PBXBuildFile; fileRef = E2C99D0711C8CF8F00FA14C3 /* meshpattern.png */; }; 22 | E2C99D6911C8D25300FA14C3 /* tick.wav in Resources */ = {isa = PBXBuildFile; fileRef = E2C99D6811C8D25300FA14C3 /* tick.wav */; }; 23 | /* End PBXBuildFile section */ 24 | 25 | /* Begin PBXFileReference section */ 26 | 1D30AB110D05D00D00671497 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; 27 | 1D3623240D0F684500981E51 /* SwipeableExampleAppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SwipeableExampleAppDelegate.h; sourceTree = ""; }; 28 | 1D3623250D0F684500981E51 /* SwipeableExampleAppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SwipeableExampleAppDelegate.m; sourceTree = ""; }; 29 | 1D6058910D05DD3D006BFB54 /* SwipeableExample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = SwipeableExample.app; sourceTree = BUILT_PRODUCTS_DIR; }; 30 | 1DF5F4DF0D08C38300B7A737 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; }; 31 | 2892E40F0DC94CBA00A64D0F /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; }; 32 | 28A0AAE50D9B0CCF005BE974 /* SwipeableExample_Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SwipeableExample_Prefix.pch; sourceTree = ""; }; 33 | 28C286DF0D94DF7D0034E888 /* RootViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RootViewController.h; sourceTree = ""; }; 34 | 28C286E00D94DF7D0034E888 /* RootViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RootViewController.m; sourceTree = ""; }; 35 | 29B97316FDCFA39411CA2CEA /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 36 | 8D1107310486CEB800E47090 /* SwipeableExample-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "SwipeableExample-Info.plist"; plistStructureDefinitionIdentifier = "com.apple.xcode.plist.structure-definition.iphone.info-plist"; sourceTree = ""; }; 37 | E204DB5E12ADC1820063A748 /* AudioToolbox.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AudioToolbox.framework; path = System/Library/Frameworks/AudioToolbox.framework; sourceTree = SDKROOT; }; 38 | E230196516C53BE2001685F6 /* Default-568h@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Default-568h@2x.png"; sourceTree = ""; }; 39 | E238A589125BED4500055319 /* TISwipeableTableView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = TISwipeableTableView.h; path = ../TISwipeableTableView.h; sourceTree = SOURCE_ROOT; }; 40 | E238A58A125BED4500055319 /* TISwipeableTableView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = TISwipeableTableView.m; path = ../TISwipeableTableView.m; sourceTree = SOURCE_ROOT; }; 41 | E24E6A4611E532710082E0CD /* QuartzCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuartzCore.framework; path = System/Library/Frameworks/QuartzCore.framework; sourceTree = SDKROOT; }; 42 | E2C99CDA11C8CC3B00FA14C3 /* ExampleCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ExampleCell.h; sourceTree = ""; }; 43 | E2C99CDB11C8CC3B00FA14C3 /* ExampleCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ExampleCell.m; sourceTree = ""; }; 44 | E2C99D0711C8CF8F00FA14C3 /* meshpattern.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = meshpattern.png; sourceTree = ""; }; 45 | E2C99D6811C8D25300FA14C3 /* tick.wav */ = {isa = PBXFileReference; lastKnownFileType = audio.wav; path = tick.wav; sourceTree = ""; }; 46 | /* End PBXFileReference section */ 47 | 48 | /* Begin PBXFrameworksBuildPhase section */ 49 | 1D60588F0D05DD3D006BFB54 /* Frameworks */ = { 50 | isa = PBXFrameworksBuildPhase; 51 | buildActionMask = 2147483647; 52 | files = ( 53 | 1D60589F0D05DD5A006BFB54 /* Foundation.framework in Frameworks */, 54 | 1DF5F4E00D08C38300B7A737 /* UIKit.framework in Frameworks */, 55 | 2892E4100DC94CBA00A64D0F /* CoreGraphics.framework in Frameworks */, 56 | E24E6A4711E532710082E0CD /* QuartzCore.framework in Frameworks */, 57 | E204DB5F12ADC1820063A748 /* AudioToolbox.framework in Frameworks */, 58 | ); 59 | runOnlyForDeploymentPostprocessing = 0; 60 | }; 61 | /* End PBXFrameworksBuildPhase section */ 62 | 63 | /* Begin PBXGroup section */ 64 | 080E96DDFE201D6D7F000001 /* Classes */ = { 65 | isa = PBXGroup; 66 | children = ( 67 | E2C99D9511C8D3BA00FA14C3 /* AppStuff */, 68 | E2C99D9211C8D3B000FA14C3 /* TableviewStuff */, 69 | ); 70 | path = Classes; 71 | sourceTree = ""; 72 | }; 73 | 19C28FACFE9D520D11CA2CBB /* Products */ = { 74 | isa = PBXGroup; 75 | children = ( 76 | 1D6058910D05DD3D006BFB54 /* SwipeableExample.app */, 77 | ); 78 | name = Products; 79 | sourceTree = ""; 80 | }; 81 | 29B97314FDCFA39411CA2CEA /* CustomTemplate */ = { 82 | isa = PBXGroup; 83 | children = ( 84 | E230196516C53BE2001685F6 /* Default-568h@2x.png */, 85 | 080E96DDFE201D6D7F000001 /* Classes */, 86 | 29B97315FDCFA39411CA2CEA /* Other Sources */, 87 | 29B97317FDCFA39411CA2CEA /* Resources */, 88 | 29B97323FDCFA39411CA2CEA /* Frameworks */, 89 | 19C28FACFE9D520D11CA2CBB /* Products */, 90 | ); 91 | name = CustomTemplate; 92 | sourceTree = ""; 93 | }; 94 | 29B97315FDCFA39411CA2CEA /* Other Sources */ = { 95 | isa = PBXGroup; 96 | children = ( 97 | 28A0AAE50D9B0CCF005BE974 /* SwipeableExample_Prefix.pch */, 98 | 29B97316FDCFA39411CA2CEA /* main.m */, 99 | ); 100 | name = "Other Sources"; 101 | sourceTree = ""; 102 | }; 103 | 29B97317FDCFA39411CA2CEA /* Resources */ = { 104 | isa = PBXGroup; 105 | children = ( 106 | E2C99D6811C8D25300FA14C3 /* tick.wav */, 107 | E2C99D0711C8CF8F00FA14C3 /* meshpattern.png */, 108 | 8D1107310486CEB800E47090 /* SwipeableExample-Info.plist */, 109 | ); 110 | name = Resources; 111 | sourceTree = ""; 112 | }; 113 | 29B97323FDCFA39411CA2CEA /* Frameworks */ = { 114 | isa = PBXGroup; 115 | children = ( 116 | E204DB5E12ADC1820063A748 /* AudioToolbox.framework */, 117 | 1DF5F4DF0D08C38300B7A737 /* UIKit.framework */, 118 | 1D30AB110D05D00D00671497 /* Foundation.framework */, 119 | 2892E40F0DC94CBA00A64D0F /* CoreGraphics.framework */, 120 | E24E6A4611E532710082E0CD /* QuartzCore.framework */, 121 | ); 122 | name = Frameworks; 123 | sourceTree = ""; 124 | }; 125 | E2C99D9211C8D3B000FA14C3 /* TableviewStuff */ = { 126 | isa = PBXGroup; 127 | children = ( 128 | E238A589125BED4500055319 /* TISwipeableTableView.h */, 129 | E238A58A125BED4500055319 /* TISwipeableTableView.m */, 130 | E2C99CDA11C8CC3B00FA14C3 /* ExampleCell.h */, 131 | E2C99CDB11C8CC3B00FA14C3 /* ExampleCell.m */, 132 | ); 133 | name = TableviewStuff; 134 | sourceTree = ""; 135 | }; 136 | E2C99D9511C8D3BA00FA14C3 /* AppStuff */ = { 137 | isa = PBXGroup; 138 | children = ( 139 | 1D3623240D0F684500981E51 /* SwipeableExampleAppDelegate.h */, 140 | 1D3623250D0F684500981E51 /* SwipeableExampleAppDelegate.m */, 141 | 28C286DF0D94DF7D0034E888 /* RootViewController.h */, 142 | 28C286E00D94DF7D0034E888 /* RootViewController.m */, 143 | ); 144 | name = AppStuff; 145 | sourceTree = ""; 146 | }; 147 | /* End PBXGroup section */ 148 | 149 | /* Begin PBXNativeTarget section */ 150 | 1D6058900D05DD3D006BFB54 /* SwipeableExample */ = { 151 | isa = PBXNativeTarget; 152 | buildConfigurationList = 1D6058960D05DD3E006BFB54 /* Build configuration list for PBXNativeTarget "SwipeableExample" */; 153 | buildPhases = ( 154 | 1D60588D0D05DD3D006BFB54 /* Resources */, 155 | 1D60588E0D05DD3D006BFB54 /* Sources */, 156 | 1D60588F0D05DD3D006BFB54 /* Frameworks */, 157 | ); 158 | buildRules = ( 159 | ); 160 | dependencies = ( 161 | ); 162 | name = SwipeableExample; 163 | productName = SwipeableExample; 164 | productReference = 1D6058910D05DD3D006BFB54 /* SwipeableExample.app */; 165 | productType = "com.apple.product-type.application"; 166 | }; 167 | /* End PBXNativeTarget section */ 168 | 169 | /* Begin PBXProject section */ 170 | 29B97313FDCFA39411CA2CEA /* Project object */ = { 171 | isa = PBXProject; 172 | attributes = { 173 | LastUpgradeCheck = 0460; 174 | }; 175 | buildConfigurationList = C01FCF4E08A954540054247B /* Build configuration list for PBXProject "SwipeableExample" */; 176 | compatibilityVersion = "Xcode 3.2"; 177 | developmentRegion = English; 178 | hasScannedForEncodings = 1; 179 | knownRegions = ( 180 | English, 181 | Japanese, 182 | French, 183 | German, 184 | en, 185 | ); 186 | mainGroup = 29B97314FDCFA39411CA2CEA /* CustomTemplate */; 187 | projectDirPath = ""; 188 | projectRoot = ""; 189 | targets = ( 190 | 1D6058900D05DD3D006BFB54 /* SwipeableExample */, 191 | ); 192 | }; 193 | /* End PBXProject section */ 194 | 195 | /* Begin PBXResourcesBuildPhase section */ 196 | 1D60588D0D05DD3D006BFB54 /* Resources */ = { 197 | isa = PBXResourcesBuildPhase; 198 | buildActionMask = 2147483647; 199 | files = ( 200 | E2C99D0811C8CF8F00FA14C3 /* meshpattern.png in Resources */, 201 | E2C99D6911C8D25300FA14C3 /* tick.wav in Resources */, 202 | E230196616C53BE2001685F6 /* Default-568h@2x.png in Resources */, 203 | ); 204 | runOnlyForDeploymentPostprocessing = 0; 205 | }; 206 | /* End PBXResourcesBuildPhase section */ 207 | 208 | /* Begin PBXSourcesBuildPhase section */ 209 | 1D60588E0D05DD3D006BFB54 /* Sources */ = { 210 | isa = PBXSourcesBuildPhase; 211 | buildActionMask = 2147483647; 212 | files = ( 213 | 1D60589B0D05DD56006BFB54 /* main.m in Sources */, 214 | 1D3623260D0F684500981E51 /* SwipeableExampleAppDelegate.m in Sources */, 215 | 28C286E10D94DF7D0034E888 /* RootViewController.m in Sources */, 216 | E2C99CDC11C8CC3B00FA14C3 /* ExampleCell.m in Sources */, 217 | E238A58B125BED4500055319 /* TISwipeableTableView.m in Sources */, 218 | ); 219 | runOnlyForDeploymentPostprocessing = 0; 220 | }; 221 | /* End PBXSourcesBuildPhase section */ 222 | 223 | /* Begin XCBuildConfiguration section */ 224 | 1D6058940D05DD3E006BFB54 /* Debug */ = { 225 | isa = XCBuildConfiguration; 226 | buildSettings = { 227 | ALWAYS_SEARCH_USER_PATHS = NO; 228 | CLANG_ENABLE_OBJC_ARC = YES; 229 | COPY_PHASE_STRIP = NO; 230 | GCC_DYNAMIC_NO_PIC = NO; 231 | GCC_OPTIMIZATION_LEVEL = 0; 232 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 233 | GCC_PREFIX_HEADER = SwipeableExample_Prefix.pch; 234 | GCC_VERSION = com.apple.compilers.llvm.clang.1_0; 235 | INFOPLIST_FILE = "SwipeableExample-Info.plist"; 236 | IPHONEOS_DEPLOYMENT_TARGET = 5.0; 237 | PRODUCT_NAME = SwipeableExample; 238 | }; 239 | name = Debug; 240 | }; 241 | 1D6058950D05DD3E006BFB54 /* Release */ = { 242 | isa = XCBuildConfiguration; 243 | buildSettings = { 244 | ALWAYS_SEARCH_USER_PATHS = NO; 245 | CLANG_ENABLE_OBJC_ARC = YES; 246 | COPY_PHASE_STRIP = YES; 247 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 248 | GCC_PREFIX_HEADER = SwipeableExample_Prefix.pch; 249 | GCC_VERSION = com.apple.compilers.llvm.clang.1_0; 250 | INFOPLIST_FILE = "SwipeableExample-Info.plist"; 251 | IPHONEOS_DEPLOYMENT_TARGET = 5.0; 252 | PRODUCT_NAME = SwipeableExample; 253 | VALIDATE_PRODUCT = YES; 254 | }; 255 | name = Release; 256 | }; 257 | C01FCF4F08A954540054247B /* Debug */ = { 258 | isa = XCBuildConfiguration; 259 | buildSettings = { 260 | ARCHS = "$(ARCHS_STANDARD_32_BIT)"; 261 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 262 | GCC_C_LANGUAGE_STANDARD = c99; 263 | GCC_WARN_ABOUT_RETURN_TYPE = YES; 264 | GCC_WARN_UNUSED_VARIABLE = YES; 265 | IPHONEOS_DEPLOYMENT_TARGET = 4.0; 266 | SDKROOT = iphoneos; 267 | }; 268 | name = Debug; 269 | }; 270 | C01FCF5008A954540054247B /* Release */ = { 271 | isa = XCBuildConfiguration; 272 | buildSettings = { 273 | ARCHS = "$(ARCHS_STANDARD_32_BIT)"; 274 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 275 | GCC_C_LANGUAGE_STANDARD = c99; 276 | GCC_WARN_ABOUT_RETURN_TYPE = YES; 277 | GCC_WARN_UNUSED_VARIABLE = YES; 278 | IPHONEOS_DEPLOYMENT_TARGET = 4.0; 279 | OTHER_CFLAGS = "-DNS_BLOCK_ASSERTIONS=1"; 280 | SDKROOT = iphoneos; 281 | }; 282 | name = Release; 283 | }; 284 | /* End XCBuildConfiguration section */ 285 | 286 | /* Begin XCConfigurationList section */ 287 | 1D6058960D05DD3E006BFB54 /* Build configuration list for PBXNativeTarget "SwipeableExample" */ = { 288 | isa = XCConfigurationList; 289 | buildConfigurations = ( 290 | 1D6058940D05DD3E006BFB54 /* Debug */, 291 | 1D6058950D05DD3E006BFB54 /* Release */, 292 | ); 293 | defaultConfigurationIsVisible = 0; 294 | defaultConfigurationName = Release; 295 | }; 296 | C01FCF4E08A954540054247B /* Build configuration list for PBXProject "SwipeableExample" */ = { 297 | isa = XCConfigurationList; 298 | buildConfigurations = ( 299 | C01FCF4F08A954540054247B /* Debug */, 300 | C01FCF5008A954540054247B /* Release */, 301 | ); 302 | defaultConfigurationIsVisible = 0; 303 | defaultConfigurationName = Release; 304 | }; 305 | /* End XCConfigurationList section */ 306 | }; 307 | rootObject = 29B97313FDCFA39411CA2CEA /* Project object */; 308 | } 309 | -------------------------------------------------------------------------------- /SwipeableExample/SwipeableExample_Prefix.pch: -------------------------------------------------------------------------------- 1 | // 2 | // Prefix header for all source files of the 'SwipeableExample' target in the 'SwipeableExample' project 3 | // 4 | #import 5 | 6 | #ifndef __IPHONE_3_0 7 | #warning "This project uses features only available in iPhone SDK 3.0 and later." 8 | #endif 9 | 10 | 11 | #ifdef __OBJC__ 12 | #import 13 | #import 14 | #endif 15 | -------------------------------------------------------------------------------- /SwipeableExample/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // SwipeableExample 4 | // 5 | // Created by Tom Irving on 16/06/2010. 6 | // Copyright Tom Irving 2010. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | int main(int argc, char *argv[]) { 12 | 13 | @autoreleasepool { 14 | int retVal = UIApplicationMain(argc, argv, nil, @"SwipeableExampleAppDelegate"); 15 | return retVal; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /SwipeableExample/meshpattern.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thermogl/TISwipeableTableView/23897ee65786b3a2d07a0740bd65d7e82eb861f1/SwipeableExample/meshpattern.png -------------------------------------------------------------------------------- /SwipeableExample/tick.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thermogl/TISwipeableTableView/23897ee65786b3a2d07a0740bd65d7e82eb861f1/SwipeableExample/tick.wav -------------------------------------------------------------------------------- /TISwipeableTableView.h: -------------------------------------------------------------------------------- 1 | // 2 | // TISwipeableTableView.h 3 | // TISwipeableTableView 4 | // 5 | // Created by Tom Irving on 28/05/2010. 6 | // Copyright 2010 Tom Irving. All rights reserved. 7 | // 8 | // Redistribution and use in source and binary forms, with or without modification, 9 | // are permitted provided that the following conditions are met: 10 | // 11 | // 1. Redistributions of source code must retain the above copyright notice, this list of 12 | // conditions and the following disclaimer. 13 | // 14 | // 2. Redistributions in binary form must reproduce the above copyright notice, this list 15 | // of conditions and the following disclaimer in the documentation and/or other materials 16 | // provided with the distribution. 17 | // 18 | // THIS SOFTWARE IS PROVIDED BY TOM IRVING "AS IS" AND ANY EXPRESS OR IMPLIED 19 | // WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 20 | // FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL TOM IRVING OR 21 | // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 | // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 23 | // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 24 | // ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 25 | // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 26 | // ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 | // 28 | 29 | #import 30 | 31 | //========================================================== 32 | // - TISwipeableTableViewController 33 | //========================================================== 34 | 35 | @interface TISwipeableTableViewController : UITableViewController 36 | @property (nonatomic, strong, readonly) NSIndexPath * indexOfVisibleBackView; 37 | 38 | // Thanks to Martin Destagnol (@mdestagnol) for this method. 39 | - (BOOL)tableView:(UITableView *)tableView shouldSwipeCellAtIndexPath:(NSIndexPath *)indexPath; 40 | - (void)tableView:(UITableView *)tableView didSwipeCellAtIndexPath:(NSIndexPath *)indexPath; 41 | 42 | - (void)tableView:(UITableView *)tableView revealBackViewAtIndexPath:(NSIndexPath *)indexPath animated:(BOOL)animated; 43 | - (void)tableView:(UITableView *)tableView hideVisibleBackView:(BOOL)animated; 44 | 45 | @end 46 | 47 | //========================================================== 48 | // - TISwipeableTableViewCell 49 | //========================================================== 50 | 51 | @interface TISwipeableTableViewCellView : UIView 52 | @end 53 | 54 | @interface TISwipeableTableViewCellBackView : UIView 55 | @end 56 | 57 | @interface TISwipeableTableViewCell : UITableViewCell 58 | @property (nonatomic, readonly) UIView * backView; 59 | @property (nonatomic, assign) BOOL contentViewMoving; 60 | @property (nonatomic, assign) BOOL shouldBounce; 61 | 62 | - (void)drawContentView:(CGRect)rect; 63 | - (void)drawBackView:(CGRect)rect; 64 | 65 | - (void)backViewWillAppear:(BOOL)animated; 66 | - (void)backViewDidAppear:(BOOL)animated; 67 | - (void)backViewWillDisappear:(BOOL)animated; 68 | - (void)backViewDidDisappear:(BOOL)animated; 69 | 70 | - (void)revealBackViewAnimated:(BOOL)animated; 71 | - (void)hideBackViewAnimated:(BOOL)animated; 72 | 73 | @end 74 | -------------------------------------------------------------------------------- /TISwipeableTableView.m: -------------------------------------------------------------------------------- 1 | // 2 | // TISwipeableTableView.m 3 | // TISwipeableTableView 4 | // 5 | // Created by Tom Irving on 28/05/2010. 6 | // Copyright 2010 Tom Irving. All rights reserved. 7 | // 8 | 9 | #import "TISwipeableTableView.h" 10 | #import 11 | 12 | //========================================================== 13 | // - TISwipeableTableViewController 14 | //========================================================== 15 | 16 | @interface TISwipeableTableViewController () 17 | @property (nonatomic, strong) NSIndexPath * indexOfVisibleBackView; 18 | @end 19 | 20 | @implementation TISwipeableTableViewController 21 | @synthesize indexOfVisibleBackView = _indexOfVisibleBackView; 22 | 23 | - (NSIndexPath *)tableView:(UITableView *)tableView willSelectRowAtIndexPath:(NSIndexPath *)indexPath { 24 | return ([indexPath compare:_indexOfVisibleBackView] == NSOrderedSame) ? nil : indexPath; 25 | } 26 | 27 | - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { 28 | [self tableView:tableView hideVisibleBackView:YES]; 29 | } 30 | 31 | - (BOOL)tableView:(UITableView *)tableView shouldSwipeCellAtIndexPath:(NSIndexPath *)indexPath { 32 | return YES; 33 | } 34 | 35 | - (void)tableView:(UITableView *)tableView didSwipeCellAtIndexPath:(NSIndexPath *)indexPath { 36 | 37 | [self tableView:tableView hideVisibleBackView:YES]; 38 | [self setIndexOfVisibleBackView:indexPath]; 39 | } 40 | 41 | - (void)scrollViewDidScroll:(UIScrollView *)scrollView { 42 | [self tableView:(UITableView *)scrollView hideVisibleBackView:YES]; 43 | } 44 | 45 | - (void)tableView:(UITableView *)tableView revealBackViewAtIndexPath:(NSIndexPath *)indexPath animated:(BOOL)animated; 46 | { 47 | UITableViewCell * cell = [tableView cellForRowAtIndexPath:indexPath]; 48 | 49 | [self tableView:tableView hideVisibleBackView:animated]; 50 | 51 | if ([cell respondsToSelector:@selector(revealBackViewAnimated:)]){ 52 | [(TISwipeableTableViewCell *)cell revealBackViewAnimated:animated]; 53 | [self setIndexOfVisibleBackView:indexPath]; 54 | } 55 | } 56 | 57 | - (void)tableView:(UITableView *)tableView hideVisibleBackView:(BOOL)animated { 58 | 59 | UITableViewCell * cell = [tableView cellForRowAtIndexPath:_indexOfVisibleBackView]; 60 | if ([cell respondsToSelector:@selector(hideBackViewAnimated:)]){ 61 | [(TISwipeableTableViewCell *)cell hideBackViewAnimated:animated]; 62 | [self setIndexOfVisibleBackView:nil]; 63 | } 64 | } 65 | 66 | 67 | @end 68 | 69 | //========================================================== 70 | // - TISwipeableTableViewCell 71 | //========================================================== 72 | 73 | @interface UIView (FindSwipeableCellSuperview) 74 | @property (nonatomic, readonly) TISwipeableTableViewCell * tisw_findSuperview; 75 | @property (nonatomic, readonly) UITableView * tisw_findTableview; 76 | @end 77 | 78 | @implementation UIView (FindSwipeableCellSuperview) 79 | - (TISwipeableTableViewCell *)tisw_findSuperview { 80 | 81 | UIView * superview = self.superview; 82 | if ([superview isKindOfClass:[TISwipeableTableViewCell class]]) return (TISwipeableTableViewCell *)superview; 83 | else if (superview) return superview.tisw_findSuperview; 84 | else return nil; 85 | } 86 | 87 | - (UITableView *)tisw_findTableview { 88 | UIView * tableView = self.superview; 89 | if ([tableView isKindOfClass:[UITableView class]]) return (UITableView *)tableView; 90 | else if (tableView) return tableView.tisw_findTableview; 91 | else return nil; 92 | } 93 | 94 | @end 95 | 96 | @implementation TISwipeableTableViewCellView 97 | - (void)drawRect:(CGRect)rect { 98 | [self.tisw_findSuperview drawContentView:rect]; 99 | } 100 | @end 101 | 102 | @implementation TISwipeableTableViewCellBackView 103 | - (void)drawRect:(CGRect)rect { 104 | [self.tisw_findSuperview drawBackView:rect]; 105 | } 106 | 107 | @end 108 | 109 | @interface TISwipeableTableViewCell (Private) 110 | - (void)initialSetup; 111 | - (void)resetViews:(BOOL)animated; 112 | - (CAAnimationGroup *)bounceAnimationWithHideDuration:(CGFloat)hideDuration initialXOrigin:(CGFloat)originalX; 113 | @end 114 | 115 | @implementation TISwipeableTableViewCell { 116 | UIView * _contentView; 117 | UITableViewCellSelectionStyle _oldStyle; 118 | } 119 | @synthesize contentView = _contentView; 120 | @synthesize backView = _backView; 121 | @synthesize contentViewMoving = _contentViewMoving; 122 | @synthesize shouldBounce = _shouldBounce; 123 | 124 | #pragma mark - Init / Overrides 125 | - (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier { 126 | 127 | if ((self = [super initWithStyle:style reuseIdentifier:reuseIdentifier])){ 128 | [self initialSetup]; 129 | } 130 | 131 | return self; 132 | } 133 | 134 | - (instancetype)initWithCoder:(NSCoder *)aDecoder { 135 | 136 | if ((self = [super initWithCoder:aDecoder])){ 137 | [self initialSetup]; 138 | } 139 | 140 | return self; 141 | } 142 | 143 | - (void)initialSetup { 144 | 145 | [self setBackgroundColor:[UIColor clearColor]]; 146 | 147 | _contentView = [[TISwipeableTableViewCellView alloc] initWithFrame:CGRectZero]; 148 | [_contentView setClipsToBounds:YES]; 149 | [_contentView setOpaque:YES]; 150 | 151 | UISwipeGestureRecognizer * swipeRecognizer = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(cellWasSwiped:)]; 152 | [swipeRecognizer setDirection:(UISwipeGestureRecognizerDirectionLeft | 153 | UISwipeGestureRecognizerDirectionRight)]; 154 | [_contentView addGestureRecognizer:swipeRecognizer]; 155 | 156 | _backView = [[TISwipeableTableViewCellBackView alloc] initWithFrame:CGRectZero]; 157 | [_backView setOpaque:YES]; 158 | [_backView setClipsToBounds:YES]; 159 | [_backView setHidden:YES]; 160 | 161 | [self addSubview:_backView]; 162 | [self addSubview:_contentView]; 163 | 164 | 165 | _contentViewMoving = NO; 166 | _shouldBounce = YES; 167 | _oldStyle = self.selectionStyle; 168 | } 169 | 170 | - (void)prepareForReuse { 171 | 172 | [self resetViews:NO]; 173 | [super prepareForReuse]; 174 | } 175 | 176 | - (void)setFrame:(CGRect)aFrame { 177 | 178 | [super setFrame:aFrame]; 179 | 180 | CGRect newBounds = self.bounds; 181 | newBounds.size.height -= 1; 182 | [_backView setFrame:newBounds]; 183 | [_contentView setFrame:newBounds]; 184 | } 185 | 186 | - (void)setNeedsDisplay { 187 | 188 | [super setNeedsDisplay]; 189 | if (!_contentView.hidden) [_contentView setNeedsDisplay]; 190 | if (!_backView.hidden) [_backView setNeedsDisplay]; 191 | } 192 | 193 | - (void)setAccessoryType:(UITableViewCellAccessoryType)accessoryType { 194 | // Having an accessory buggers swiping right up, so we override. 195 | // It's easier just to draw the accessory yourself. 196 | } 197 | 198 | - (void)setAccessoryView:(UIView *)accessoryView { 199 | // Same as above. 200 | } 201 | 202 | - (void)setHighlighted:(BOOL)highlighted { 203 | [self setHighlighted:highlighted animated:NO]; 204 | } 205 | 206 | - (void)setHighlighted:(BOOL)highlighted animated:(BOOL)animated { 207 | [super setHighlighted:highlighted animated:animated]; 208 | [self setNeedsDisplay]; 209 | } 210 | 211 | - (void)setSelected:(BOOL)flag { 212 | [self setSelected:flag animated:NO]; 213 | } 214 | 215 | - (void)setSelected:(BOOL)selected animated:(BOOL)animated { 216 | [super setSelected:selected animated:animated]; 217 | [self setNeedsDisplay]; 218 | } 219 | 220 | #pragma mark - Subclass Methods 221 | // Implement the following in a subclass 222 | - (void)drawContentView:(CGRect)rect { 223 | 224 | } 225 | 226 | - (void)drawBackView:(CGRect)rect { 227 | 228 | } 229 | 230 | // Optional implementation 231 | - (void)backViewWillAppear:(BOOL)animated { 232 | 233 | } 234 | 235 | - (void)backViewDidAppear:(BOOL)animated { 236 | 237 | } 238 | 239 | - (void)backViewWillDisappear:(BOOL)animated { 240 | 241 | } 242 | 243 | - (void)backViewDidDisappear:(BOOL)animated { 244 | 245 | } 246 | 247 | //===============================// 248 | 249 | #pragma mark - Back View Show / Hide 250 | - (void)cellWasSwiped:(UISwipeGestureRecognizer *)recognizer { 251 | 252 | UITableView * tableView = self.tisw_findTableview; 253 | 254 | id delegate = tableView.nextResponder; // Hopefully this is a TISwipeableTableViewController. 255 | if(![delegate isKindOfClass:[TISwipeableTableViewController class]]) 256 | delegate = [delegate nextResponder]; 257 | 258 | if ([delegate respondsToSelector:@selector(tableView:shouldSwipeCellAtIndexPath:)]){ 259 | 260 | NSIndexPath * myIndexPath = [tableView indexPathForCell:self]; 261 | 262 | if ([delegate tableView:tableView shouldSwipeCellAtIndexPath:myIndexPath]){ 263 | 264 | [self revealBackViewAnimated:YES]; 265 | 266 | if ([delegate respondsToSelector:@selector(tableView:didSwipeCellAtIndexPath:)]){ 267 | [delegate tableView:tableView didSwipeCellAtIndexPath:myIndexPath]; 268 | } 269 | } 270 | } 271 | } 272 | 273 | - (void)revealBackViewAnimated:(BOOL)animated { 274 | 275 | if (!_contentViewMoving && _backView.hidden){ 276 | 277 | _contentViewMoving = YES; 278 | 279 | [_backView.layer setHidden:NO]; 280 | [_backView setNeedsDisplay]; 281 | 282 | [self backViewWillAppear:animated]; 283 | 284 | _oldStyle = self.selectionStyle; 285 | [self setSelectionStyle:UITableViewCellSelectionStyleNone]; 286 | 287 | [_contentView.layer setAnchorPoint:CGPointMake(0, 0.5)]; 288 | [_contentView.layer setPosition:CGPointMake(_contentView.frame.size.width, _contentView.layer.position.y)]; 289 | 290 | if (animated){ 291 | 292 | CABasicAnimation * animation = [CABasicAnimation animationWithKeyPath:@"position.x"]; 293 | [animation setRemovedOnCompletion:NO]; 294 | [animation setDelegate:self]; 295 | [animation setDuration:0.14]; 296 | [animation setTimingFunction:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear]]; 297 | [_contentView.layer addAnimation:animation forKey:@"reveal"]; 298 | } 299 | else 300 | { 301 | [self backViewDidAppear:animated]; 302 | [self setSelected:NO]; 303 | 304 | _contentViewMoving = NO; 305 | } 306 | } 307 | } 308 | 309 | - (void)hideBackViewAnimated:(BOOL)animated { 310 | 311 | if (!_backView.hidden){ 312 | 313 | _contentViewMoving = YES; 314 | 315 | [self backViewWillDisappear:animated]; 316 | 317 | if (animated){ 318 | 319 | CGFloat hideDuration = 0.09; 320 | 321 | [_backView.layer setOpacity:0.0]; 322 | CABasicAnimation * hideAnimation = [CABasicAnimation animationWithKeyPath:@"opacity"]; 323 | [hideAnimation setFromValue:[NSNumber numberWithFloat:1.0]]; 324 | [hideAnimation setToValue:[NSNumber numberWithFloat:0.0]]; 325 | [hideAnimation setDuration:hideDuration]; 326 | [hideAnimation setRemovedOnCompletion:NO]; 327 | [hideAnimation setDelegate:self]; 328 | [_backView.layer addAnimation:hideAnimation forKey:@"hide"]; 329 | 330 | CGFloat originalX = _contentView.layer.position.x; 331 | [_contentView.layer setAnchorPoint:CGPointMake(0, 0.5)]; 332 | [_contentView.layer setPosition:CGPointMake(0, _contentView.layer.position.y)]; 333 | [_contentView.layer addAnimation:[self bounceAnimationWithHideDuration:hideDuration initialXOrigin:originalX] 334 | forKey:@"bounce"]; 335 | 336 | 337 | } 338 | else 339 | { 340 | [self resetViews:NO]; 341 | } 342 | } 343 | } 344 | 345 | - (void)resetViews:(BOOL)animated { 346 | 347 | [_contentView.layer removeAllAnimations]; 348 | [_backView.layer removeAllAnimations]; 349 | 350 | _contentViewMoving = NO; 351 | 352 | [_contentView.layer setAnchorPoint:CGPointMake(0, 0.5)]; 353 | [_contentView.layer setPosition:CGPointMake(0, _contentView.layer.position.y)]; 354 | 355 | [_backView.layer setHidden:YES]; 356 | [_backView.layer setOpacity:1.0]; 357 | 358 | [self setSelectionStyle:_oldStyle]; 359 | 360 | [self backViewDidDisappear:animated]; 361 | } 362 | 363 | - (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag { 364 | 365 | if (anim == [_contentView.layer animationForKey:@"reveal"]){ 366 | [_contentView.layer removeAnimationForKey:@"reveal"]; 367 | 368 | [self backViewDidAppear:YES]; 369 | [self setSelected:NO]; 370 | 371 | _contentViewMoving = NO; 372 | } 373 | 374 | if (anim == [_contentView.layer animationForKey:@"bounce"]){ 375 | [_contentView.layer removeAnimationForKey:@"bounce"]; 376 | [self resetViews:YES]; 377 | } 378 | 379 | if (anim == [_backView.layer animationForKey:@"hide"]){ 380 | [_backView.layer removeAnimationForKey:@"hide"]; 381 | } 382 | } 383 | 384 | - (CAAnimationGroup *)bounceAnimationWithHideDuration:(CGFloat)hideDuration initialXOrigin:(CGFloat)originalX { 385 | 386 | CABasicAnimation * animation0 = [CABasicAnimation animationWithKeyPath:@"position.x"]; 387 | [animation0 setFromValue:[NSNumber numberWithFloat:originalX]]; 388 | [animation0 setToValue:[NSNumber numberWithFloat:0]]; 389 | [animation0 setDuration:hideDuration]; 390 | [animation0 setTimingFunction:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear]]; 391 | [animation0 setBeginTime:0]; 392 | 393 | CAAnimationGroup * hideAnimations = [CAAnimationGroup animation]; 394 | [hideAnimations setAnimations:[NSArray arrayWithObject:animation0]]; 395 | 396 | CGFloat fullDuration = hideDuration; 397 | 398 | if (_shouldBounce){ 399 | 400 | CGFloat bounceDuration = 0.04; 401 | 402 | CABasicAnimation * animation1 = [CABasicAnimation animationWithKeyPath:@"position.x"]; 403 | [animation1 setFromValue:[NSNumber numberWithFloat:0]]; 404 | [animation1 setToValue:[NSNumber numberWithFloat:-20]]; 405 | [animation1 setDuration:bounceDuration]; 406 | [animation1 setTimingFunction:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear]]; 407 | [animation1 setBeginTime:hideDuration]; 408 | 409 | CABasicAnimation * animation2 = [CABasicAnimation animationWithKeyPath:@"position.x"]; 410 | [animation2 setFromValue:[NSNumber numberWithFloat:-20]]; 411 | [animation2 setToValue:[NSNumber numberWithFloat:15]]; 412 | [animation2 setDuration:bounceDuration]; 413 | [animation2 setTimingFunction:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear]]; 414 | [animation2 setBeginTime:(hideDuration + bounceDuration)]; 415 | 416 | CABasicAnimation * animation3 = [CABasicAnimation animationWithKeyPath:@"position.x"]; 417 | [animation3 setFromValue:[NSNumber numberWithFloat:15]]; 418 | [animation3 setToValue:[NSNumber numberWithFloat:0]]; 419 | [animation3 setDuration:bounceDuration]; 420 | [animation3 setTimingFunction:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear]]; 421 | [animation3 setBeginTime:(hideDuration + (bounceDuration * 2))]; 422 | 423 | [hideAnimations setAnimations:[NSArray arrayWithObjects:animation0, animation1, animation2, animation3, nil]]; 424 | 425 | fullDuration = hideDuration + (bounceDuration * 3); 426 | } 427 | 428 | [hideAnimations setDuration:fullDuration]; 429 | [hideAnimations setTimingFunction:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear]]; 430 | [hideAnimations setDelegate:self]; 431 | [hideAnimations setRemovedOnCompletion:NO]; 432 | 433 | return hideAnimations; 434 | } 435 | 436 | #pragma mark - Other 437 | - (NSString *)description { 438 | 439 | NSString * extraInfo = _backView.hidden ? @"ContentView visible": @"BackView visible"; 440 | return [NSString stringWithFormat:@"", self, extraInfo]; 441 | } 442 | @end 443 | --------------------------------------------------------------------------------