├── SWTableViewCell ├── en.lproj │ ├── InfoPlist.strings │ └── MainStoryboard.storyboard ├── MI.png ├── um.png ├── Default.png ├── check@2x.png ├── clock@2x.png ├── cross@2x.png ├── list@2x.png ├── Default@2x.png ├── Default-568h@2x.png ├── UMTableViewCell.m ├── PodFiles │ ├── SWCellScrollView.h │ ├── SWUtilityButtonTapGestureRecognizer.m │ ├── SWLongPressGestureRecognizer.h │ ├── Constants.h │ ├── SWUtilityButtonTapGestureRecognizer.h │ ├── NSMutableArray+SWUtilityButtons.h │ ├── SWLongPressGestureRecognizer.m │ ├── SWUtilityButtonView.h │ ├── NSMutableArray+SWUtilityButtons.m │ ├── SWCellScrollView.m │ ├── SWTableViewCell.h │ ├── SWUtilityButtonView.m │ └── SWTableViewCell.m ├── AppDelegate.h ├── ViewController.h ├── SWTableViewCell-Prefix.pch ├── main.m ├── UMTableViewCell.h ├── SWTableViewCell-Info.plist ├── AppDelegate.m └── ViewController.m ├── github-assets ├── example1.gif ├── example2.gif ├── example3.gif └── example4.gif ├── .gitignore ├── SWTableViewCell.podspec ├── LICENCE ├── README.md └── SWTableViewCell.xcodeproj └── project.pbxproj /SWTableViewCell/en.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* Localized versions of Info.plist keys */ 2 | 3 | -------------------------------------------------------------------------------- /SWTableViewCell/MI.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Imagine/SWTableViewCell/master/SWTableViewCell/MI.png -------------------------------------------------------------------------------- /SWTableViewCell/um.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Imagine/SWTableViewCell/master/SWTableViewCell/um.png -------------------------------------------------------------------------------- /github-assets/example1.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Imagine/SWTableViewCell/master/github-assets/example1.gif -------------------------------------------------------------------------------- /github-assets/example2.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Imagine/SWTableViewCell/master/github-assets/example2.gif -------------------------------------------------------------------------------- /github-assets/example3.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Imagine/SWTableViewCell/master/github-assets/example3.gif -------------------------------------------------------------------------------- /github-assets/example4.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Imagine/SWTableViewCell/master/github-assets/example4.gif -------------------------------------------------------------------------------- /SWTableViewCell/Default.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Imagine/SWTableViewCell/master/SWTableViewCell/Default.png -------------------------------------------------------------------------------- /SWTableViewCell/check@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Imagine/SWTableViewCell/master/SWTableViewCell/check@2x.png -------------------------------------------------------------------------------- /SWTableViewCell/clock@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Imagine/SWTableViewCell/master/SWTableViewCell/clock@2x.png -------------------------------------------------------------------------------- /SWTableViewCell/cross@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Imagine/SWTableViewCell/master/SWTableViewCell/cross@2x.png -------------------------------------------------------------------------------- /SWTableViewCell/list@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Imagine/SWTableViewCell/master/SWTableViewCell/list@2x.png -------------------------------------------------------------------------------- /SWTableViewCell/Default@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Imagine/SWTableViewCell/master/SWTableViewCell/Default@2x.png -------------------------------------------------------------------------------- /SWTableViewCell/Default-568h@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Imagine/SWTableViewCell/master/SWTableViewCell/Default-568h@2x.png -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | build/* 3 | *.pbxuser 4 | *.xcworkspace 5 | xcuserdata 6 | profile 7 | *.moved-aside 8 | xcuserdata/ 9 | project.xcworkspace/ 10 | xcshareddata/ -------------------------------------------------------------------------------- /SWTableViewCell/UMTableViewCell.m: -------------------------------------------------------------------------------- 1 | // 2 | // UMTableViewCell.m 3 | // SWTableViewCell 4 | // 5 | // Created by Matt Bowman on 12/2/13. 6 | // Copyright (c) 2013 Chris Wendel. All rights reserved. 7 | // 8 | 9 | #import "UMTableViewCell.h" 10 | 11 | @implementation UMTableViewCell 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /SWTableViewCell/PodFiles/SWCellScrollView.h: -------------------------------------------------------------------------------- 1 | // 2 | // SWCellScrollView.h 3 | // SWTableViewCell 4 | // 5 | // Created by Matt Bowman on 11/27/13. 6 | // Copyright (c) 2013 Chris Wendel. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface SWCellScrollView : UIScrollView 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /SWTableViewCell/AppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.h 3 | // SWTableViewCell 4 | // 5 | // Created by Chris Wendel on 9/10/13. 6 | // Copyright (c) 2013 Chris Wendel. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface AppDelegate : UIResponder 12 | 13 | @property (strong, nonatomic) UIWindow *window; 14 | 15 | @end 16 | -------------------------------------------------------------------------------- /SWTableViewCell/PodFiles/SWUtilityButtonTapGestureRecognizer.m: -------------------------------------------------------------------------------- 1 | // 2 | // SWUtilityButtonTapGestureRecognizer.m 3 | // SWTableViewCell 4 | // 5 | // Created by Matt Bowman on 11/27/13. 6 | // Copyright (c) 2013 Chris Wendel. All rights reserved. 7 | // 8 | 9 | #import "SWUtilityButtonTapGestureRecognizer.h" 10 | 11 | @implementation SWUtilityButtonTapGestureRecognizer 12 | 13 | @end 14 | 15 | -------------------------------------------------------------------------------- /SWTableViewCell/ViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.h 3 | // SWTableViewCell 4 | // 5 | // Created by Chris Wendel on 9/10/13. 6 | // Copyright (c) 2013 Chris Wendel. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "SWTableViewCell.h" 11 | 12 | @interface ViewController : UIViewController 13 | 14 | @end 15 | -------------------------------------------------------------------------------- /SWTableViewCell/PodFiles/SWLongPressGestureRecognizer.h: -------------------------------------------------------------------------------- 1 | // 2 | // SWLongPressGestureRecognizer.h 3 | // SWTableViewCell 4 | // 5 | // Created by Matt Bowman on 11/27/13. 6 | // Copyright (c) 2013 Chris Wendel. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | 12 | @interface SWLongPressGestureRecognizer : UILongPressGestureRecognizer 13 | 14 | @end 15 | 16 | -------------------------------------------------------------------------------- /SWTableViewCell/PodFiles/Constants.h: -------------------------------------------------------------------------------- 1 | // 2 | // Constants.h 3 | // SWTableViewCell 4 | // 5 | // Created by Matt Bowman on 11/27/13. 6 | // Copyright (c) 2013 Chris Wendel. All rights reserved. 7 | // 8 | 9 | #ifndef SWTableViewCell_Constants_h 10 | #define SWTableViewCell_Constants_h 11 | 12 | #define kUtilityButtonsWidthMax 260 13 | #define kUtilityButtonWidthDefault 90 14 | #define kSectionIndexWidth 15 15 | 16 | #endif 17 | -------------------------------------------------------------------------------- /SWTableViewCell/SWTableViewCell-Prefix.pch: -------------------------------------------------------------------------------- 1 | // 2 | // Prefix header for all source files of the 'SWTableViewCell' target in the 'SWTableViewCell' project 3 | // 4 | 5 | #import 6 | 7 | #ifndef __IPHONE_5_0 8 | #warning "This project uses features only available in iOS SDK 5.0 and later." 9 | #endif 10 | 11 | #ifdef __OBJC__ 12 | #import 13 | #import 14 | #endif 15 | -------------------------------------------------------------------------------- /SWTableViewCell/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // SWTableViewCell 4 | // 5 | // Created by Chris Wendel on 9/10/13. 6 | // Copyright (c) 2013 Chris Wendel. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | #import "AppDelegate.h" 12 | 13 | int main(int argc, char *argv[]) 14 | { 15 | @autoreleasepool { 16 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /SWTableViewCell/PodFiles/SWUtilityButtonTapGestureRecognizer.h: -------------------------------------------------------------------------------- 1 | // 2 | // SWUtilityButtonTapGestureRecognizer.h 3 | // SWTableViewCell 4 | // 5 | // Created by Matt Bowman on 11/27/13. 6 | // Copyright (c) 2013 Chris Wendel. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | 12 | @interface SWUtilityButtonTapGestureRecognizer : UITapGestureRecognizer 13 | 14 | @property (nonatomic) NSUInteger buttonIndex; 15 | 16 | @end 17 | 18 | -------------------------------------------------------------------------------- /SWTableViewCell/PodFiles/NSMutableArray+SWUtilityButtons.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSMutableArray+SWUtilityButtons.h 3 | // SWTableViewCell 4 | // 5 | // Created by Matt Bowman on 11/27/13. 6 | // Copyright (c) 2013 Chris Wendel. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface NSMutableArray (SWUtilityButtons) 12 | 13 | - (void)sw_addUtilityButtonWithColor:(UIColor *)color title:(NSString *)title; 14 | - (void)sw_addUtilityButtonWithColor:(UIColor *)color icon:(UIImage *)icon; 15 | 16 | @end 17 | -------------------------------------------------------------------------------- /SWTableViewCell/UMTableViewCell.h: -------------------------------------------------------------------------------- 1 | // 2 | // UMTableViewCell.h 3 | // SWTableViewCell 4 | // 5 | // Created by Matt Bowman on 12/2/13. 6 | // Copyright (c) 2013 Chris Wendel. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "SWTableViewCell.h" 11 | 12 | /* 13 | * Example of a custom cell built in Storyboard 14 | */ 15 | @interface UMTableViewCell : SWTableViewCell 16 | 17 | @property (weak, nonatomic) IBOutlet UIImageView *image; 18 | @property (weak, nonatomic) IBOutlet UILabel *label; 19 | 20 | @end 21 | -------------------------------------------------------------------------------- /SWTableViewCell.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |s| 2 | s.name = 'SWTableViewCell' 3 | s.version = '0.2.3' 4 | s.author = { 'Chris Wendel' => 'chriwend@umich.edu' } 5 | s.homepage = 'https://github.com/CEWendel/SWTableViewCell' 6 | s.summary = 'UITableViewCell subclass that implements a swipeable content view which exposes utility buttons.' 7 | s.license = 'MIT' 8 | s.source = { :git => 'https://github.com/CEWendel/SWTableViewCell.git', :tag => '0.2.3' } 9 | s.source_files = 'SWTableViewCell/PodFiles/*.{h,m}' 10 | s.platform = :ios 11 | s.ios.deployment_target = '6.1' 12 | s.requires_arc = true 13 | end 14 | -------------------------------------------------------------------------------- /SWTableViewCell/PodFiles/SWLongPressGestureRecognizer.m: -------------------------------------------------------------------------------- 1 | // 2 | // SWLongPressGestureRecognizer.m 3 | // SWTableViewCell 4 | // 5 | // Created by Matt Bowman on 11/27/13. 6 | // Copyright (c) 2013 Chris Wendel. All rights reserved. 7 | // 8 | 9 | #import "SWLongPressGestureRecognizer.h" 10 | 11 | @implementation SWLongPressGestureRecognizer 12 | 13 | -(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event 14 | { 15 | [super touchesBegan:touches withEvent:event]; 16 | } 17 | 18 | -(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event 19 | { 20 | [super touchesMoved:touches withEvent:event]; 21 | 22 | self.state = UIGestureRecognizerStateFailed; 23 | } 24 | 25 | -(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event 26 | { 27 | [super touchesEnded:touches withEvent:event]; 28 | 29 | self.state = UIGestureRecognizerStateFailed; 30 | } 31 | 32 | @end 33 | 34 | -------------------------------------------------------------------------------- /SWTableViewCell/PodFiles/SWUtilityButtonView.h: -------------------------------------------------------------------------------- 1 | // 2 | // SWUtilityButtonView.h 3 | // SWTableViewCell 4 | // 5 | // Created by Matt Bowman on 11/27/13. 6 | // Copyright (c) 2013 Chris Wendel. All rights reserved. 7 | // 8 | 9 | #import 10 | @class SWTableViewCell; 11 | 12 | @interface SWUtilityButtonView : UIView 13 | 14 | @property (nonatomic, strong) NSArray *utilityButtons; 15 | @property (nonatomic) CGFloat utilityButtonWidth; 16 | @property (nonatomic, weak) SWTableViewCell *parentCell; 17 | @property (nonatomic) SEL utilityButtonSelector; 18 | @property (nonatomic) CGFloat height; 19 | 20 | - (id)initWithUtilityButtons:(NSArray *)utilityButtons parentCell:(SWTableViewCell *)parentCell utilityButtonSelector:(SEL)utilityButtonSelector; 21 | 22 | - (id)initWithFrame:(CGRect)frame utilityButtons:(NSArray *)utilityButtons parentCell:(SWTableViewCell *)parentCell utilityButtonSelector:(SEL)utilityButtonSelector; 23 | 24 | - (void)populateUtilityButtons; 25 | - (CGFloat)utilityButtonsWidth; 26 | 27 | @end 28 | -------------------------------------------------------------------------------- /SWTableViewCell/PodFiles/NSMutableArray+SWUtilityButtons.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSMutableArray+SWUtilityButtons.m 3 | // SWTableViewCell 4 | // 5 | // Created by Matt Bowman on 11/27/13. 6 | // Copyright (c) 2013 Chris Wendel. All rights reserved. 7 | // 8 | 9 | #import "NSMutableArray+SWUtilityButtons.h" 10 | 11 | @implementation NSMutableArray (SWUtilityButtons) 12 | 13 | - (void)sw_addUtilityButtonWithColor:(UIColor *)color title:(NSString *)title 14 | { 15 | UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom]; 16 | button.backgroundColor = color; 17 | [button setTitle:title forState:UIControlStateNormal]; 18 | [button setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal]; 19 | [self addObject:button]; 20 | } 21 | 22 | - (void)sw_addUtilityButtonWithColor:(UIColor *)color icon:(UIImage *)icon 23 | { 24 | UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom]; 25 | button.backgroundColor = color; 26 | [button setImage:icon forState:UIControlStateNormal]; 27 | [self addObject:button]; 28 | } 29 | 30 | @end 31 | 32 | -------------------------------------------------------------------------------- /LICENCE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013 Christopher Wendel 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. -------------------------------------------------------------------------------- /SWTableViewCell/PodFiles/SWCellScrollView.m: -------------------------------------------------------------------------------- 1 | // 2 | // SWCellScrollView.m 3 | // SWTableViewCell 4 | // 5 | // Created by Matt Bowman on 11/27/13. 6 | // Copyright (c) 2013 Chris Wendel. All rights reserved. 7 | // 8 | 9 | #import "SWCellScrollView.h" 10 | 11 | @implementation SWCellScrollView 12 | 13 | - (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer { 14 | // Find out if the user is actively scrolling the tableView of which this is a member. 15 | // If they are, return NO, and don't let the gesture recognizers work simultaneously. 16 | // 17 | // This works very well in maintaining user expectations while still allowing for the user to 18 | // scroll the cell sideways when that is their true intent. 19 | if ([gestureRecognizer isKindOfClass:[UIPanGestureRecognizer class]]) { 20 | 21 | // Find the current scrolling velocity in that view, in the Y direction. 22 | CGFloat yVelocity = [(UIPanGestureRecognizer*)gestureRecognizer velocityInView:gestureRecognizer.view].y; 23 | 24 | // Return YES iff the user is not actively scrolling up. 25 | return fabs(yVelocity) <= 0.25; 26 | 27 | } 28 | return YES; 29 | } 30 | 31 | @end 32 | 33 | -------------------------------------------------------------------------------- /SWTableViewCell/SWTableViewCell-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleDisplayName 8 | ${PRODUCT_NAME} 9 | CFBundleExecutable 10 | ${EXECUTABLE_NAME} 11 | CFBundleIdentifier 12 | com.ChrisWendel.${PRODUCT_NAME:rfc1034identifier} 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | ${PRODUCT_NAME} 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | 1.0 25 | LSRequiresIPhoneOS 26 | 27 | UIMainStoryboardFile 28 | MainStoryboard 29 | UIRequiredDeviceCapabilities 30 | 31 | armv7 32 | 33 | UISupportedInterfaceOrientations 34 | 35 | UIInterfaceOrientationPortrait 36 | UIInterfaceOrientationLandscapeLeft 37 | UIInterfaceOrientationLandscapeRight 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /SWTableViewCell/PodFiles/SWTableViewCell.h: -------------------------------------------------------------------------------- 1 | // 2 | // SWTableViewCell.h 3 | // SWTableViewCell 4 | // 5 | // Created by Chris Wendel on 9/10/13. 6 | // Copyright (c) 2013 Chris Wendel. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | #import "SWCellScrollView.h" 12 | #import "SWLongPressGestureRecognizer.h" 13 | #import "SWUtilityButtonTapGestureRecognizer.h" 14 | #import "NSMutableArray+SWUtilityButtons.h" 15 | #import "Constants.h" 16 | 17 | @class SWTableViewCell; 18 | 19 | typedef enum { 20 | kCellStateCenter, 21 | kCellStateLeft, 22 | kCellStateRight 23 | } SWCellState; 24 | 25 | @protocol SWTableViewCellDelegate 26 | 27 | @optional 28 | - (void)swipeableTableViewCell:(SWTableViewCell *)cell didTriggerLeftUtilityButtonWithIndex:(NSInteger)index; 29 | - (void)swipeableTableViewCell:(SWTableViewCell *)cell didTriggerRightUtilityButtonWithIndex:(NSInteger)index; 30 | - (void)swipeableTableViewCell:(SWTableViewCell *)cell scrollingToState:(SWCellState)state; 31 | - (BOOL)swipeableTableViewCellShouldHideUtilityButtonsOnSwipe:(SWTableViewCell *)cell; 32 | - (BOOL)swipeableTableViewCell:(SWTableViewCell *)cell canSwipeToState:(SWCellState)state; 33 | 34 | @end 35 | 36 | @interface SWTableViewCell : UITableViewCell 37 | 38 | @property (nonatomic, strong) NSArray *leftUtilityButtons; 39 | @property (nonatomic, strong) NSArray *rightUtilityButtons; 40 | @property (nonatomic, weak) id delegate; 41 | @property (nonatomic, strong) SWCellScrollView *cellScrollView; 42 | @property (nonatomic, weak) UITableView *containingTableView; 43 | 44 | - (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier containingTableView:(UITableView *)containingTableView leftUtilityButtons:(NSArray *)leftUtilityButtons rightUtilityButtons:(NSArray *)rightUtilityButtons; 45 | 46 | - (void)setCellHeight:(CGFloat)height; 47 | - (void)setBackgroundColor:(UIColor *)backgroundColor; 48 | - (void)hideUtilityButtonsAnimated:(BOOL)animated; 49 | - (void)setAppearanceWithBlock:(void (^) ())appearanceBlock force:(BOOL)force; 50 | 51 | @end 52 | -------------------------------------------------------------------------------- /SWTableViewCell/AppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.m 3 | // SWTableViewCell 4 | // 5 | // Created by Chris Wendel on 9/10/13. 6 | // Copyright (c) 2013 Chris Wendel. All rights reserved. 7 | // 8 | 9 | #import "AppDelegate.h" 10 | 11 | @implementation AppDelegate 12 | 13 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions 14 | { 15 | // Override point for customization after application launch. 16 | return YES; 17 | } 18 | 19 | - (void)applicationWillResignActive:(UIApplication *)application 20 | { 21 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 22 | // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game. 23 | } 24 | 25 | - (void)applicationDidEnterBackground:(UIApplication *)application 26 | { 27 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 28 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 29 | } 30 | 31 | - (void)applicationWillEnterForeground:(UIApplication *)application 32 | { 33 | // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background. 34 | } 35 | 36 | - (void)applicationDidBecomeActive:(UIApplication *)application 37 | { 38 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 39 | } 40 | 41 | - (void)applicationWillTerminate:(UIApplication *)application 42 | { 43 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 44 | } 45 | 46 | @end 47 | -------------------------------------------------------------------------------- /SWTableViewCell/PodFiles/SWUtilityButtonView.m: -------------------------------------------------------------------------------- 1 | // 2 | // SWUtilityButtonView.m 3 | // SWTableViewCell 4 | // 5 | // Created by Matt Bowman on 11/27/13. 6 | // Copyright (c) 2013 Chris Wendel. All rights reserved. 7 | // 8 | 9 | #import "SWUtilityButtonView.h" 10 | #import "SWUtilityButtonTapGestureRecognizer.h" 11 | #import "Constants.h" 12 | 13 | @implementation SWUtilityButtonView 14 | 15 | #pragma mark - SWUtilityButonView initializers 16 | 17 | - (id)initWithUtilityButtons:(NSArray *)utilityButtons parentCell:(SWTableViewCell *)parentCell utilityButtonSelector:(SEL)utilityButtonSelector 18 | { 19 | self = [super init]; 20 | 21 | if (self) { 22 | self.utilityButtons = utilityButtons; 23 | self.utilityButtonWidth = [self calculateUtilityButtonWidth]; 24 | self.parentCell = parentCell; 25 | self.utilityButtonSelector = utilityButtonSelector; 26 | } 27 | 28 | return self; 29 | } 30 | 31 | - (id)initWithFrame:(CGRect)frame utilityButtons:(NSArray *)utilityButtons parentCell:(SWTableViewCell *)parentCell utilityButtonSelector:(SEL)utilityButtonSelector 32 | { 33 | self = [super initWithFrame:frame]; 34 | 35 | if (self) { 36 | self.utilityButtons = utilityButtons; 37 | self.utilityButtonWidth = [self calculateUtilityButtonWidth]; 38 | self.parentCell = parentCell; 39 | self.utilityButtonSelector = utilityButtonSelector; 40 | } 41 | 42 | return self; 43 | } 44 | 45 | #pragma mark Populating utility buttons 46 | 47 | - (CGFloat)calculateUtilityButtonWidth 48 | { 49 | CGFloat buttonWidth = kUtilityButtonWidthDefault; 50 | if (buttonWidth * _utilityButtons.count > kUtilityButtonsWidthMax) 51 | { 52 | CGFloat buffer = (buttonWidth * _utilityButtons.count) - kUtilityButtonsWidthMax; 53 | buttonWidth -= (buffer / _utilityButtons.count); 54 | } 55 | return buttonWidth; 56 | } 57 | 58 | - (CGFloat)utilityButtonsWidth 59 | { 60 | return (_utilityButtons.count * _utilityButtonWidth); 61 | } 62 | 63 | - (void)populateUtilityButtons 64 | { 65 | NSUInteger utilityButtonsCounter = 0; 66 | for (UIButton *utilityButton in _utilityButtons) 67 | { 68 | CGFloat utilityButtonXCord = 0; 69 | if (utilityButtonsCounter >= 1) utilityButtonXCord = _utilityButtonWidth * utilityButtonsCounter; 70 | [utilityButton setFrame:CGRectMake(utilityButtonXCord, 0, _utilityButtonWidth, CGRectGetHeight(self.bounds))]; 71 | [utilityButton setTag:utilityButtonsCounter]; 72 | SWUtilityButtonTapGestureRecognizer *utilityButtonTapGestureRecognizer = [[SWUtilityButtonTapGestureRecognizer alloc] initWithTarget:_parentCell 73 | action:_utilityButtonSelector]; 74 | utilityButtonTapGestureRecognizer.buttonIndex = utilityButtonsCounter; 75 | [utilityButton addGestureRecognizer:utilityButtonTapGestureRecognizer]; 76 | [self addSubview: utilityButton]; 77 | utilityButtonsCounter++; 78 | } 79 | } 80 | 81 | - (void)setHeight:(CGFloat)height 82 | { 83 | for (NSUInteger utilityButtonsCounter = 0; utilityButtonsCounter < _utilityButtons.count; utilityButtonsCounter++) 84 | { 85 | UIButton *utilityButton = (UIButton *)_utilityButtons[utilityButtonsCounter]; 86 | CGFloat utilityButtonXCord = 0; 87 | if (utilityButtonsCounter >= 1) utilityButtonXCord = _utilityButtonWidth * utilityButtonsCounter; 88 | [utilityButton setFrame:CGRectMake(utilityButtonXCord, 0, _utilityButtonWidth, height)]; 89 | } 90 | } 91 | 92 | @end 93 | 94 | -------------------------------------------------------------------------------- /SWTableViewCell/en.lproj/MainStoryboard.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 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | -------------------------------------------------------------------------------- /SWTableViewCell/ViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.m 3 | // SWTableViewCell 4 | // 5 | // Created by Chris Wendel on 9/10/13. 6 | // Copyright (c) 2013 Chris Wendel. All rights reserved. 7 | // 8 | 9 | #import "ViewController.h" 10 | #import "SWTableViewCell.h" 11 | #import "UMTableViewCell.h" 12 | 13 | @interface ViewController () { 14 | NSArray *_sections; 15 | NSMutableArray *_testArray; 16 | } 17 | 18 | @property (nonatomic, weak) IBOutlet UITableView *tableView; 19 | @property (nonatomic) BOOL useCustomCells; 20 | @property (nonatomic, weak) UIRefreshControl *refreshControl; 21 | 22 | @end 23 | 24 | @implementation ViewController 25 | 26 | - (void)viewDidLoad { 27 | [super viewDidLoad]; 28 | 29 | self.tableView.delegate = self; 30 | self.tableView.dataSource = self; 31 | self.tableView.rowHeight = 90; 32 | 33 | self.navigationItem.title = @"Pull to Toggle Cell Type"; 34 | 35 | // Setup refresh control for example app 36 | UIRefreshControl *refreshControl = [[UIRefreshControl alloc] init]; 37 | [refreshControl addTarget:self action:@selector(toggleCells:) forControlEvents:UIControlEventValueChanged]; 38 | refreshControl.tintColor = [UIColor blueColor]; 39 | 40 | [self.tableView addSubview:refreshControl]; 41 | self.refreshControl = refreshControl; 42 | 43 | // If you set the seperator inset on iOS 6 you get a NSInvalidArgumentException...weird 44 | if ([[[UIDevice currentDevice] systemVersion] floatValue] >= 7) { 45 | self.tableView.separatorInset = UIEdgeInsetsMake(0, 0, 0, 0); // Makes the horizontal row seperator stretch the entire length of the table view 46 | } 47 | 48 | _sections = [[UILocalizedIndexedCollation currentCollation] sectionIndexTitles]; 49 | 50 | _testArray = [[NSMutableArray alloc] init]; 51 | 52 | self.useCustomCells = NO; 53 | 54 | for (int i = 0; i < _sections.count; ++i) { 55 | [_testArray addObject:[NSMutableArray array]]; 56 | } 57 | 58 | for (int i = 0; i < 100; ++i) { 59 | NSString *string = [NSString stringWithFormat:@"%d", i]; 60 | [_testArray[i % _sections.count] addObject:string]; 61 | } 62 | } 63 | 64 | #pragma mark UITableViewDataSource 65 | 66 | - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { 67 | return _testArray.count; 68 | } 69 | 70 | - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { 71 | return [_testArray[section] count]; 72 | } 73 | 74 | - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { 75 | NSLog(@"cell selected at index path %d:%d", indexPath.section, indexPath.row); 76 | } 77 | 78 | - (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section { 79 | return _sections[section]; 80 | } 81 | 82 | // Show index titles 83 | 84 | //- (NSArray *)sectionIndexTitlesForTableView:(UITableView *)tableView { 85 | // return [[UILocalizedIndexedCollation currentCollation] sectionIndexTitles]; 86 | //} 87 | // 88 | //- (NSInteger)tableView:(UITableView *)tableView sectionForSectionIndexTitle:(NSString *)title atIndex:(NSInteger)index { 89 | // return [[UILocalizedIndexedCollation currentCollation] sectionForSectionIndexTitleAtIndex:index]; 90 | //} 91 | 92 | #pragma mark - UIRefreshControl Selector 93 | 94 | - (void)toggleCells:(UIRefreshControl*)refreshControl 95 | { 96 | [refreshControl beginRefreshing]; 97 | self.useCustomCells = !self.useCustomCells; 98 | if (self.useCustomCells) 99 | { 100 | self.refreshControl.tintColor = [UIColor yellowColor]; 101 | } 102 | else 103 | { 104 | self.refreshControl.tintColor = [UIColor blueColor]; 105 | } 106 | [self.tableView reloadData]; 107 | [refreshControl endRefreshing]; 108 | } 109 | 110 | #pragma mark - UIScrollViewDelegate 111 | 112 | - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { 113 | 114 | if (self.useCustomCells) 115 | { 116 | 117 | UMTableViewCell *cell = [self.tableView dequeueReusableCellWithIdentifier:@"UMCell" forIndexPath:indexPath]; 118 | 119 | UMTableViewCell __weak *weakCell = cell; 120 | 121 | [cell setAppearanceWithBlock:^{ 122 | weakCell.leftUtilityButtons = [self leftButtons]; 123 | weakCell.rightUtilityButtons = [self rightButtons]; 124 | weakCell.delegate = self; 125 | weakCell.containingTableView = tableView; 126 | } force:NO]; 127 | 128 | [cell setCellHeight:cell.frame.size.height]; 129 | 130 | cell.label.text = [NSString stringWithFormat:@"Section: %d, Seat: %d", indexPath.section, indexPath.row]; 131 | 132 | return cell; 133 | } 134 | else 135 | { 136 | static NSString *cellIdentifier = @"Cell"; 137 | 138 | SWTableViewCell *cell = (SWTableViewCell *)[tableView dequeueReusableCellWithIdentifier:cellIdentifier]; 139 | 140 | if (cell == nil) { 141 | 142 | cell = [[SWTableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle 143 | reuseIdentifier:cellIdentifier 144 | containingTableView:_tableView // Used for row height and selection 145 | leftUtilityButtons:[self leftButtons] 146 | rightUtilityButtons:[self rightButtons]]; 147 | cell.delegate = self; 148 | } 149 | 150 | NSDate *dateObject = _testArray[indexPath.section][indexPath.row]; 151 | cell.textLabel.text = [dateObject description]; 152 | cell.textLabel.backgroundColor = [UIColor whiteColor]; 153 | cell.detailTextLabel.backgroundColor = [UIColor whiteColor]; 154 | cell.detailTextLabel.text = @"Some detail text"; 155 | 156 | return cell; 157 | } 158 | 159 | } 160 | 161 | - (NSArray *)rightButtons 162 | { 163 | NSMutableArray *rightUtilityButtons = [NSMutableArray new]; 164 | [rightUtilityButtons sw_addUtilityButtonWithColor: 165 | [UIColor colorWithRed:0.78f green:0.78f blue:0.8f alpha:1.0] 166 | title:@"More"]; 167 | [rightUtilityButtons sw_addUtilityButtonWithColor: 168 | [UIColor colorWithRed:1.0f green:0.231f blue:0.188 alpha:1.0f] 169 | title:@"Delete"]; 170 | 171 | return rightUtilityButtons; 172 | } 173 | 174 | - (NSArray *)leftButtons 175 | { 176 | NSMutableArray *leftUtilityButtons = [NSMutableArray new]; 177 | 178 | [leftUtilityButtons sw_addUtilityButtonWithColor: 179 | [UIColor colorWithRed:0.07 green:0.75f blue:0.16f alpha:1.0] 180 | icon:[UIImage imageNamed:@"check.png"]]; 181 | [leftUtilityButtons sw_addUtilityButtonWithColor: 182 | [UIColor colorWithRed:1.0f green:1.0f blue:0.35f alpha:1.0] 183 | icon:[UIImage imageNamed:@"clock.png"]]; 184 | [leftUtilityButtons sw_addUtilityButtonWithColor: 185 | [UIColor colorWithRed:1.0f green:0.231f blue:0.188f alpha:1.0] 186 | icon:[UIImage imageNamed:@"cross.png"]]; 187 | [leftUtilityButtons sw_addUtilityButtonWithColor: 188 | [UIColor colorWithRed:0.55f green:0.27f blue:0.07f alpha:1.0] 189 | icon:[UIImage imageNamed:@"list.png"]]; 190 | 191 | return leftUtilityButtons; 192 | } 193 | 194 | // Set row height on an individual basis 195 | 196 | //- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{ 197 | // return [self rowHeightForIndexPath:indexPath]; 198 | //} 199 | // 200 | //- (CGFloat)rowHeightForIndexPath:(NSIndexPath *)indexPath { 201 | // return ([indexPath row] * 10) + 60; 202 | //} 203 | 204 | - (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath { 205 | // Set background color of cell here if you don't want default white 206 | } 207 | 208 | #pragma mark - SWTableViewDelegate 209 | 210 | - (void)swipeableTableViewCell:(SWTableViewCell *)cell didTriggerLeftUtilityButtonWithIndex:(NSInteger)index { 211 | switch (index) { 212 | case 0: 213 | NSLog(@"left button 0 was pressed"); 214 | break; 215 | case 1: 216 | NSLog(@"left button 1 was pressed"); 217 | break; 218 | case 2: 219 | NSLog(@"left button 2 was pressed"); 220 | break; 221 | case 3: 222 | NSLog(@"left btton 3 was pressed"); 223 | default: 224 | break; 225 | } 226 | } 227 | 228 | - (void)swipeableTableViewCell:(SWTableViewCell *)cell didTriggerRightUtilityButtonWithIndex:(NSInteger)index { 229 | switch (index) { 230 | case 0: 231 | { 232 | NSLog(@"More button was pressed"); 233 | UIAlertView *alertTest = [[UIAlertView alloc] initWithTitle:@"Hello" message:@"More more more" delegate:nil cancelButtonTitle:@"cancel" otherButtonTitles: nil]; 234 | [alertTest show]; 235 | 236 | [cell hideUtilityButtonsAnimated:YES]; 237 | break; 238 | } 239 | case 1: 240 | { 241 | // Delete button was pressed 242 | NSIndexPath *cellIndexPath = [self.tableView indexPathForCell:cell]; 243 | 244 | [_testArray[cellIndexPath.section] removeObjectAtIndex:cellIndexPath.row]; 245 | [self.tableView deleteRowsAtIndexPaths:@[cellIndexPath] withRowAnimation:UITableViewRowAnimationLeft]; 246 | break; 247 | } 248 | default: 249 | break; 250 | } 251 | } 252 | 253 | - (BOOL)swipeableTableViewCellShouldHideUtilityButtonsOnSwipe:(SWTableViewCell *)cell { 254 | return YES; 255 | } 256 | 257 | @end 258 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | SWTableViewCell 2 | =============== 3 | 4 |

5 | 6 | An easy-to-use UITableViewCell subclass that implements a swipeable content view which exposes utility buttons (similar to iOS 7 Mail Application) 7 | 8 | ##Functionality 9 | ###Right Utility Buttons 10 | Utility buttons that become visible on the right side of the Table View Cell when the user swipes left. This behavior is similar to that seen in the iOS apps Mail and Reminders. 11 | 12 |

13 | 14 | ###Left Utility Buttons 15 | Utility buttons that become visible on the left side of the Table View Cell when the user swipes right. 16 | 17 |

18 | 19 | ###Features 20 | * Dynamic utility button scalling. As you add more buttons to a cell, the other buttons on that side get smaller to make room 21 | * Smart selection: The cell will pick up touch events and either scroll the cell back to center or fire the delegate method `- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath` 22 |

23 | So the cell will not be considered selected when the user touches the cell while utility buttons are visible, instead the cell will slide back into place (same as iOS 7 Mail App functionality) 24 | * Create utilty buttons with either a title or an icon along with a RGB color 25 | * Tested on iOS 6.1 and above, including iOS 7 26 | 27 | ##Usage 28 | 29 | ###Standard Table View Cells 30 | 31 | In your `tableView:cellForRowAtIndexPath:` method you set up the SWTableView cell and add an arbitrary amount of utility buttons to it using the included `NSMutableArray+SWUtilityButtons` category. 32 | 33 | ```objc 34 | - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { 35 | static NSString *cellIdentifier = @"Cell"; 36 | 37 | SWTableViewCell *cell = (SWTableViewCell *)[tableView dequeueReusableCellWithIdentifier:cellIdentifier]; 38 | 39 | if (cell == nil) { 40 | NSMutableArray *leftUtilityButtons = [NSMutableArray new]; 41 | NSMutableArray *rightUtilityButtons = [NSMutableArray new]; 42 | 43 | [leftUtilityButtons sw_addUtilityButtonWithColor: 44 | [UIColor colorWithRed:0.07 green:0.75f blue:0.16f alpha:1.0] 45 | icon:[UIImage imageNamed:@"check.png"]]; 46 | [leftUtilityButtons sw_addUtilityButtonWithColor: 47 | [UIColor colorWithRed:1.0f green:1.0f blue:0.35f alpha:1.0] 48 | icon:[UIImage imageNamed:@"clock.png"]]; 49 | [leftUtilityButtons sw_addUtilityButtonWithColor: 50 | [UIColor colorWithRed:1.0f green:0.231f blue:0.188f alpha:1.0] 51 | icon:[UIImage imageNamed:@"cross.png"]]; 52 | [leftUtilityButtons sw_addUtilityButtonWithColor: 53 | [UIColor colorWithRed:0.55f green:0.27f blue:0.07f alpha:1.0] 54 | icon:[UIImage imageNamed:@"list.png"]]; 55 | 56 | [rightUtilityButtons sw_addUtilityButtonWithColor: 57 | [UIColor colorWithRed:0.78f green:0.78f blue:0.8f alpha:1.0] 58 | title:@"More"]; 59 | [rightUtilityButtons sw_addUtilityButtonWithColor: 60 | [UIColor colorWithRed:1.0f green:0.231f blue:0.188 alpha:1.0f] 61 | title:@"Delete"]; 62 | 63 | cell = [[SWTableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle 64 | reuseIdentifier:cellIdentifier 65 | containingTableView:_tableView // For row height and selection 66 | leftUtilityButtons:leftUtilityButtons 67 | rightUtilityButtons:rightUtilityButtons]; 68 | cell.delegate = self; 69 | } 70 | 71 | NSDate *dateObject = _testArray[indexPath.row]; 72 | cell.textLabel.text = [dateObject description]; 73 | cell.detailTextLabel.text = @"Some detail text"; 74 | 75 | return cell; 76 | } 77 | ``` 78 | 79 | ###Custom Table View Cells 80 | 81 | Thanks to [Matt Bowman](https://github.com/MattCBowman) you can now create custom table view cells using Interface Builder that have the capabilities of an SWTableViewCell 82 | 83 | The first step is to design your cell either in a standalone nib or inside of a 84 | table view using prototype cells. Make sure to set the custom class on the 85 | cell in interface builder to the subclass you made for it: 86 | 87 |

88 | 89 | Then set the cell reuse identifier: 90 | 91 |

92 | 93 | When writing your custom table view cell's code, make sure your cell is a 94 | subclass of SWTableViewCell: 95 | 96 | ```objc 97 | 98 | #import 99 | 100 | @interface MyCustomTableViewCell : SWTableViewCell 101 | 102 | @property (weak, nonatomic) UILabel *customLabel; 103 | @property (weak, nonatomic) UIImageView *customImageView; 104 | 105 | @end 106 | 107 | ``` 108 | 109 | If you are using a separate nib and not a prototype cell, you'll need to be sure to register the nib in your table view: 110 | 111 | ```objc 112 | 113 | - (void)viewDidLoad 114 | { 115 | [super viewDidLoad]; 116 | 117 | [self.tableView registerNib:[UINib nibWithNibName:@"MyCustomTableViewCellNibFileName" bundle:nil] forCellReuseIdentifier:@"MyCustomCell"]; 118 | } 119 | 120 | ``` 121 | 122 | Then, in the `tableView:cellForRowAtIndexPath:` method of your `UITableViewDelegate` (usually your view controller), initialize your custom cell: 123 | 124 | ```objc 125 | - (UITableViewCell*)tableView:(UITableView*)tableView cellForRowAtIndexPath:(NSIndexPath*)indexPath 126 | { 127 | static NSString *cellIdentifier = @"MyCustomCell"; 128 | 129 | MyCustomTableViewCell *cell = (MyCustomTableViewCell *)[tableView dequeueReusableCellWithIdentifier:cellIdentifier 130 | forIndexPath:indexPath]; 131 | __weak MyCustomTableViewCell *weakCell = cell; 132 | //Do any fixed setup here (will be executed once unless force is set to YES) 133 | [cell setAppearanceWithBlock:^{ 134 | cell.containingTableView = tableView; 135 | 136 | NSMutableArray *leftUtilityButtons = [NSMutableArray new]; 137 | NSMutableArray *rightUtilityButtons = [NSMutableArray new]; 138 | 139 | [leftUtilityButtons sw_addUtilityButtonWithColor: 140 | [UIColor colorWithRed:0.07 green:0.75f blue:0.16f alpha:1.0] 141 | icon:[UIImage imageNamed:@"check.png"]]; 142 | [leftUtilityButtons sw_addUtilityButtonWithColor: 143 | [UIColor colorWithRed:1.0f green:1.0f blue:0.35f alpha:1.0] 144 | icon:[UIImage imageNamed:@"clock.png"]]; 145 | [leftUtilityButtons sw_addUtilityButtonWithColor: 146 | [UIColor colorWithRed:1.0f green:0.231f blue:0.188f alpha:1.0] 147 | icon:[UIImage imageNamed:@"cross.png"]]; 148 | [leftUtilityButtons sw_addUtilityButtonWithColor: 149 | [UIColor colorWithRed:0.55f green:0.27f blue:0.07f alpha:1.0] 150 | icon:[UIImage imageNamed:@"list.png"]]; 151 | 152 | [rightUtilityButtons sw_addUtilityButtonWithColor: 153 | [UIColor colorWithRed:0.78f green:0.78f blue:0.8f alpha:1.0] 154 | title:@"More"]; 155 | [rightUtilityButtons sw_addUtilityButtonWithColor: 156 | [UIColor colorWithRed:1.0f green:0.231f blue:0.188 alpha:1.0f] 157 | title:@"Delete"]; 158 | 159 | cell.leftUtilityButtons = leftUtilityButtons; 160 | cell.rightUtilityButtons = rightUtilityButtons; 161 | 162 | cell.delegate = self; 163 | } force:NO]; 164 | 165 | cell.customLabel.text = @"Some Text"; 166 | cell.customImageView.image = [UIImage imageNamed:@"MyAwesomeTableCellImage"]; 167 | [cell setCellHeight:cell.frame.size.height]; 168 | return cell; 169 | } 170 | ``` 171 | 172 | ###Delegate 173 | 174 | The delegate `SWTableViewCellDelegate` is used by the developer to find out which button was pressed. There are two methods: 175 | 176 | ```objc 177 | - (void)swipeableTableViewCell:(SWTableViewCell *)cell didTriggerLeftUtilityButtonWithIndex:(NSInteger)index; 178 | - (void)swipeableTableViewCell:(SWTableViewCell *)cell didTriggerRightUtilityButtonWithIndex:(NSInteger)index; 179 | ``` 180 | 181 | The index signifies which utility button the user pressed, for each side the button indices are ordered from right to left 0...n 182 | 183 | ####Example 184 | 185 | ```objc 186 | #pragma mark - SWTableViewDelegate 187 | 188 | - (void)swipeableTableViewCell:(SWTableViewCell *)cell didTriggerLeftUtilityButtonWithIndex:(NSInteger)index { 189 | switch (index) { 190 | case 0: 191 | NSLog(@"check button was pressed"); 192 | break; 193 | case 1: 194 | NSLog(@"clock button was pressed"); 195 | break; 196 | case 2: 197 | NSLog(@"cross button was pressed"); 198 | break; 199 | case 3: 200 | NSLog(@"list button was pressed"); 201 | default: 202 | break; 203 | } 204 | } 205 | 206 | - (void)swipeableTableViewCell:(SWTableViewCell *)cell didTriggerRightUtilityButtonWithIndex:(NSInteger)index { 207 | switch (index) { 208 | case 0: 209 | NSLog(@"More button was pressed"); 210 | break; 211 | case 1: 212 | { 213 | // Delete button was pressed 214 | NSIndexPath *cellIndexPath = [self.tableView indexPathForCell:cell]; 215 | 216 | [_testArray removeObjectAtIndex:cellIndexPath.row]; 217 | [self.tableView deleteRowsAtIndexPaths:@[cellIndexPath] 218 | withRowAnimation:UITableViewRowAnimationAutomatic]; 219 | break; 220 | } 221 | default: 222 | break; 223 | } 224 | } 225 | ``` 226 | 227 | (This is all code from the included example project) 228 | 229 | ###Gotchas 230 | 231 | #### Custom `UITableViewCell` content 232 | * Accessing view of the cell object or managing the predefined content still works fine. So for example if you change the cell's `imageView` or `backgroundView`, `SWTableViewCell` will still work as expected 233 | * Don't use accessory views in your cell, because they live above the `contentView` and will stay in place when the cell scrolls. 234 | 235 | #### Seperator Insets 236 | * If you have left utility button on iOS 7, I recommend changing your Table View's seperatorInset so the seperator stretches the length of the screen 237 |
 tableView.separatorInset = UIEdgeInsetsMake(0, 0, 0, 0); 
238 | 239 | 240 | ##Contributing 241 | Use [Github issues](https://github.com/cewendel/SWTableViewCell/issues) to track bugs and feature requests. 242 | 243 | I'm really busy in college and not actively working on this, so pull requests would be greatly appreciated. 244 | 245 | ##Contact 246 | 247 | Chris Wendel 248 | 249 | - http://twitter.com/CEWendel 250 | 251 | ## Licence 252 | 253 | MIT 254 | 255 | 256 | 257 | 258 | 259 | -------------------------------------------------------------------------------- /SWTableViewCell.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 810308911846579B00C378F0 /* NSMutableArray+SWUtilityButtons.m in Sources */ = {isa = PBXBuildFile; fileRef = 810308861846579B00C378F0 /* NSMutableArray+SWUtilityButtons.m */; }; 11 | 810308921846579B00C378F0 /* SWCellScrollView.m in Sources */ = {isa = PBXBuildFile; fileRef = 810308881846579B00C378F0 /* SWCellScrollView.m */; }; 12 | 810308931846579B00C378F0 /* SWLongPressGestureRecognizer.m in Sources */ = {isa = PBXBuildFile; fileRef = 8103088A1846579B00C378F0 /* SWLongPressGestureRecognizer.m */; }; 13 | 810308941846579B00C378F0 /* SWTableViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 8103088C1846579B00C378F0 /* SWTableViewCell.m */; }; 14 | 810308951846579B00C378F0 /* SWUtilityButtonTapGestureRecognizer.m in Sources */ = {isa = PBXBuildFile; fileRef = 8103088E1846579B00C378F0 /* SWUtilityButtonTapGestureRecognizer.m */; }; 15 | 810308961846579B00C378F0 /* SWUtilityButtonView.m in Sources */ = {isa = PBXBuildFile; fileRef = 810308901846579B00C378F0 /* SWUtilityButtonView.m */; }; 16 | 810308A2184D682700C378F0 /* um.png in Resources */ = {isa = PBXBuildFile; fileRef = 810308A1184D682700C378F0 /* um.png */; }; 17 | 810308A5184D688D00C378F0 /* UMTableViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 810308A4184D688D00C378F0 /* UMTableViewCell.m */; }; 18 | AF28B0F117F77DA300A77ABB /* clock@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = AF28B0F017F77DA300A77ABB /* clock@2x.png */; }; 19 | AF28B0F317F77DA600A77ABB /* cross@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = AF28B0F217F77DA600A77ABB /* cross@2x.png */; }; 20 | AF28B0F517F77DB000A77ABB /* check@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = AF28B0F417F77DB000A77ABB /* check@2x.png */; }; 21 | AF28B0F717F77F1100A77ABB /* list@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = AF28B0F617F77F1100A77ABB /* list@2x.png */; }; 22 | AF34B76917DEE2B200BD9082 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = AF34B76817DEE2B200BD9082 /* UIKit.framework */; }; 23 | AF34B76B17DEE2B200BD9082 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = AF34B76A17DEE2B200BD9082 /* Foundation.framework */; }; 24 | AF34B76D17DEE2B200BD9082 /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = AF34B76C17DEE2B200BD9082 /* CoreGraphics.framework */; }; 25 | AF34B77317DEE2B400BD9082 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = AF34B77117DEE2B400BD9082 /* InfoPlist.strings */; }; 26 | AF34B77517DEE2B400BD9082 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = AF34B77417DEE2B400BD9082 /* main.m */; }; 27 | AF34B77917DEE2B400BD9082 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = AF34B77817DEE2B400BD9082 /* AppDelegate.m */; }; 28 | AF34B77B17DEE2B600BD9082 /* Default.png in Resources */ = {isa = PBXBuildFile; fileRef = AF34B77A17DEE2B600BD9082 /* Default.png */; }; 29 | AF34B77D17DEE2B600BD9082 /* Default@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = AF34B77C17DEE2B600BD9082 /* Default@2x.png */; }; 30 | AF34B77F17DEE2B600BD9082 /* Default-568h@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = AF34B77E17DEE2B600BD9082 /* Default-568h@2x.png */; }; 31 | AF34B78217DEE2B800BD9082 /* MainStoryboard.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = AF34B78017DEE2B700BD9082 /* MainStoryboard.storyboard */; }; 32 | AF34B78517DEE2B800BD9082 /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = AF34B78417DEE2B800BD9082 /* ViewController.m */; }; 33 | AFF15D1717F35E46007F5746 /* MI.png in Resources */ = {isa = PBXBuildFile; fileRef = AFF15D1617F35E46007F5746 /* MI.png */; }; 34 | /* End PBXBuildFile section */ 35 | 36 | /* Begin PBXFileReference section */ 37 | 810308851846579B00C378F0 /* NSMutableArray+SWUtilityButtons.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSMutableArray+SWUtilityButtons.h"; sourceTree = ""; }; 38 | 810308861846579B00C378F0 /* NSMutableArray+SWUtilityButtons.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSMutableArray+SWUtilityButtons.m"; sourceTree = ""; }; 39 | 810308871846579B00C378F0 /* SWCellScrollView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SWCellScrollView.h; sourceTree = ""; }; 40 | 810308881846579B00C378F0 /* SWCellScrollView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SWCellScrollView.m; sourceTree = ""; }; 41 | 810308891846579B00C378F0 /* SWLongPressGestureRecognizer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SWLongPressGestureRecognizer.h; sourceTree = ""; }; 42 | 8103088A1846579B00C378F0 /* SWLongPressGestureRecognizer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SWLongPressGestureRecognizer.m; sourceTree = ""; }; 43 | 8103088B1846579B00C378F0 /* SWTableViewCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SWTableViewCell.h; sourceTree = ""; }; 44 | 8103088C1846579B00C378F0 /* SWTableViewCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SWTableViewCell.m; sourceTree = ""; }; 45 | 8103088D1846579B00C378F0 /* SWUtilityButtonTapGestureRecognizer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SWUtilityButtonTapGestureRecognizer.h; sourceTree = ""; }; 46 | 8103088E1846579B00C378F0 /* SWUtilityButtonTapGestureRecognizer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SWUtilityButtonTapGestureRecognizer.m; sourceTree = ""; }; 47 | 8103088F1846579B00C378F0 /* SWUtilityButtonView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SWUtilityButtonView.h; sourceTree = ""; }; 48 | 810308901846579B00C378F0 /* SWUtilityButtonView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SWUtilityButtonView.m; sourceTree = ""; }; 49 | 810308971846584300C378F0 /* Constants.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Constants.h; sourceTree = ""; }; 50 | 810308A1184D682700C378F0 /* um.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = um.png; sourceTree = ""; }; 51 | 810308A3184D688D00C378F0 /* UMTableViewCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = UMTableViewCell.h; sourceTree = ""; }; 52 | 810308A4184D688D00C378F0 /* UMTableViewCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = UMTableViewCell.m; sourceTree = ""; }; 53 | AF28B0F017F77DA300A77ABB /* clock@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "clock@2x.png"; sourceTree = ""; }; 54 | AF28B0F217F77DA600A77ABB /* cross@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "cross@2x.png"; sourceTree = ""; }; 55 | AF28B0F417F77DB000A77ABB /* check@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "check@2x.png"; sourceTree = ""; }; 56 | AF28B0F617F77F1100A77ABB /* list@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "list@2x.png"; sourceTree = ""; }; 57 | AF2B0DA7183F029000334859 /* QuartzCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuartzCore.framework; path = System/Library/Frameworks/QuartzCore.framework; sourceTree = SDKROOT; }; 58 | AF34B76517DEE2B200BD9082 /* SWTableViewCell.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = SWTableViewCell.app; sourceTree = BUILT_PRODUCTS_DIR; }; 59 | AF34B76817DEE2B200BD9082 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; }; 60 | AF34B76A17DEE2B200BD9082 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; 61 | AF34B76C17DEE2B200BD9082 /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; }; 62 | AF34B77017DEE2B300BD9082 /* SWTableViewCell-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "SWTableViewCell-Info.plist"; sourceTree = ""; }; 63 | AF34B77217DEE2B400BD9082 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; 64 | AF34B77417DEE2B400BD9082 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 65 | AF34B77617DEE2B400BD9082 /* SWTableViewCell-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "SWTableViewCell-Prefix.pch"; sourceTree = ""; }; 66 | AF34B77717DEE2B400BD9082 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 67 | AF34B77817DEE2B400BD9082 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; 68 | AF34B77A17DEE2B600BD9082 /* Default.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = Default.png; sourceTree = ""; }; 69 | AF34B77C17DEE2B600BD9082 /* Default@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Default@2x.png"; sourceTree = ""; }; 70 | AF34B77E17DEE2B600BD9082 /* Default-568h@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Default-568h@2x.png"; sourceTree = ""; }; 71 | AF34B78117DEE2B700BD9082 /* en */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = en; path = en.lproj/MainStoryboard.storyboard; sourceTree = ""; }; 72 | AF34B78317DEE2B800BD9082 /* ViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = ""; }; 73 | AF34B78417DEE2B800BD9082 /* ViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = ""; }; 74 | AFF15D1617F35E46007F5746 /* MI.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = MI.png; sourceTree = ""; }; 75 | /* End PBXFileReference section */ 76 | 77 | /* Begin PBXFrameworksBuildPhase section */ 78 | AF34B76217DEE2B100BD9082 /* Frameworks */ = { 79 | isa = PBXFrameworksBuildPhase; 80 | buildActionMask = 2147483647; 81 | files = ( 82 | AF34B76917DEE2B200BD9082 /* UIKit.framework in Frameworks */, 83 | AF34B76B17DEE2B200BD9082 /* Foundation.framework in Frameworks */, 84 | AF34B76D17DEE2B200BD9082 /* CoreGraphics.framework in Frameworks */, 85 | ); 86 | runOnlyForDeploymentPostprocessing = 0; 87 | }; 88 | /* End PBXFrameworksBuildPhase section */ 89 | 90 | /* Begin PBXGroup section */ 91 | 810308841846579B00C378F0 /* PodFiles */ = { 92 | isa = PBXGroup; 93 | children = ( 94 | 810308971846584300C378F0 /* Constants.h */, 95 | 810308851846579B00C378F0 /* NSMutableArray+SWUtilityButtons.h */, 96 | 810308861846579B00C378F0 /* NSMutableArray+SWUtilityButtons.m */, 97 | 810308871846579B00C378F0 /* SWCellScrollView.h */, 98 | 810308881846579B00C378F0 /* SWCellScrollView.m */, 99 | 810308891846579B00C378F0 /* SWLongPressGestureRecognizer.h */, 100 | 8103088A1846579B00C378F0 /* SWLongPressGestureRecognizer.m */, 101 | 8103088B1846579B00C378F0 /* SWTableViewCell.h */, 102 | 8103088C1846579B00C378F0 /* SWTableViewCell.m */, 103 | 8103088D1846579B00C378F0 /* SWUtilityButtonTapGestureRecognizer.h */, 104 | 8103088E1846579B00C378F0 /* SWUtilityButtonTapGestureRecognizer.m */, 105 | 8103088F1846579B00C378F0 /* SWUtilityButtonView.h */, 106 | 810308901846579B00C378F0 /* SWUtilityButtonView.m */, 107 | ); 108 | path = PodFiles; 109 | sourceTree = ""; 110 | }; 111 | AF34B75C17DEE2AD00BD9082 = { 112 | isa = PBXGroup; 113 | children = ( 114 | AF34B76E17DEE2B200BD9082 /* SWTableViewCell */, 115 | AF34B76717DEE2B200BD9082 /* Frameworks */, 116 | AF34B76617DEE2B200BD9082 /* Products */, 117 | ); 118 | sourceTree = ""; 119 | }; 120 | AF34B76617DEE2B200BD9082 /* Products */ = { 121 | isa = PBXGroup; 122 | children = ( 123 | AF34B76517DEE2B200BD9082 /* SWTableViewCell.app */, 124 | ); 125 | name = Products; 126 | sourceTree = ""; 127 | }; 128 | AF34B76717DEE2B200BD9082 /* Frameworks */ = { 129 | isa = PBXGroup; 130 | children = ( 131 | AF2B0DA7183F029000334859 /* QuartzCore.framework */, 132 | AF34B76817DEE2B200BD9082 /* UIKit.framework */, 133 | AF34B76A17DEE2B200BD9082 /* Foundation.framework */, 134 | AF34B76C17DEE2B200BD9082 /* CoreGraphics.framework */, 135 | ); 136 | name = Frameworks; 137 | sourceTree = ""; 138 | }; 139 | AF34B76E17DEE2B200BD9082 /* SWTableViewCell */ = { 140 | isa = PBXGroup; 141 | children = ( 142 | 810308A1184D682700C378F0 /* um.png */, 143 | 810308841846579B00C378F0 /* PodFiles */, 144 | AF34B77717DEE2B400BD9082 /* AppDelegate.h */, 145 | AF34B77817DEE2B400BD9082 /* AppDelegate.m */, 146 | AF34B78017DEE2B700BD9082 /* MainStoryboard.storyboard */, 147 | AF34B78317DEE2B800BD9082 /* ViewController.h */, 148 | AF34B78417DEE2B800BD9082 /* ViewController.m */, 149 | AF34B76F17DEE2B300BD9082 /* Supporting Files */, 150 | 810308A3184D688D00C378F0 /* UMTableViewCell.h */, 151 | 810308A4184D688D00C378F0 /* UMTableViewCell.m */, 152 | ); 153 | path = SWTableViewCell; 154 | sourceTree = ""; 155 | }; 156 | AF34B76F17DEE2B300BD9082 /* Supporting Files */ = { 157 | isa = PBXGroup; 158 | children = ( 159 | AF28B0F617F77F1100A77ABB /* list@2x.png */, 160 | AF28B0F417F77DB000A77ABB /* check@2x.png */, 161 | AF28B0F217F77DA600A77ABB /* cross@2x.png */, 162 | AF28B0F017F77DA300A77ABB /* clock@2x.png */, 163 | AFF15D1617F35E46007F5746 /* MI.png */, 164 | AF34B77017DEE2B300BD9082 /* SWTableViewCell-Info.plist */, 165 | AF34B77117DEE2B400BD9082 /* InfoPlist.strings */, 166 | AF34B77417DEE2B400BD9082 /* main.m */, 167 | AF34B77617DEE2B400BD9082 /* SWTableViewCell-Prefix.pch */, 168 | AF34B77A17DEE2B600BD9082 /* Default.png */, 169 | AF34B77C17DEE2B600BD9082 /* Default@2x.png */, 170 | AF34B77E17DEE2B600BD9082 /* Default-568h@2x.png */, 171 | ); 172 | name = "Supporting Files"; 173 | sourceTree = ""; 174 | }; 175 | /* End PBXGroup section */ 176 | 177 | /* Begin PBXNativeTarget section */ 178 | AF34B76417DEE2B100BD9082 /* SWTableViewCell */ = { 179 | isa = PBXNativeTarget; 180 | buildConfigurationList = AF34B78817DEE2B900BD9082 /* Build configuration list for PBXNativeTarget "SWTableViewCell" */; 181 | buildPhases = ( 182 | AF34B76117DEE2B100BD9082 /* Sources */, 183 | AF34B76217DEE2B100BD9082 /* Frameworks */, 184 | AF34B76317DEE2B100BD9082 /* Resources */, 185 | ); 186 | buildRules = ( 187 | ); 188 | dependencies = ( 189 | ); 190 | name = SWTableViewCell; 191 | productName = SWTableViewCell; 192 | productReference = AF34B76517DEE2B200BD9082 /* SWTableViewCell.app */; 193 | productType = "com.apple.product-type.application"; 194 | }; 195 | /* End PBXNativeTarget section */ 196 | 197 | /* Begin PBXProject section */ 198 | AF34B75D17DEE2AE00BD9082 /* Project object */ = { 199 | isa = PBXProject; 200 | attributes = { 201 | LastUpgradeCheck = 0460; 202 | ORGANIZATIONNAME = "Chris Wendel"; 203 | }; 204 | buildConfigurationList = AF34B76017DEE2AE00BD9082 /* Build configuration list for PBXProject "SWTableViewCell" */; 205 | compatibilityVersion = "Xcode 3.2"; 206 | developmentRegion = English; 207 | hasScannedForEncodings = 0; 208 | knownRegions = ( 209 | en, 210 | ); 211 | mainGroup = AF34B75C17DEE2AD00BD9082; 212 | productRefGroup = AF34B76617DEE2B200BD9082 /* Products */; 213 | projectDirPath = ""; 214 | projectRoot = ""; 215 | targets = ( 216 | AF34B76417DEE2B100BD9082 /* SWTableViewCell */, 217 | ); 218 | }; 219 | /* End PBXProject section */ 220 | 221 | /* Begin PBXResourcesBuildPhase section */ 222 | AF34B76317DEE2B100BD9082 /* Resources */ = { 223 | isa = PBXResourcesBuildPhase; 224 | buildActionMask = 2147483647; 225 | files = ( 226 | AF34B77317DEE2B400BD9082 /* InfoPlist.strings in Resources */, 227 | AF34B77B17DEE2B600BD9082 /* Default.png in Resources */, 228 | AF28B0F517F77DB000A77ABB /* check@2x.png in Resources */, 229 | AF34B77D17DEE2B600BD9082 /* Default@2x.png in Resources */, 230 | AF28B0F317F77DA600A77ABB /* cross@2x.png in Resources */, 231 | 810308A2184D682700C378F0 /* um.png in Resources */, 232 | AF34B77F17DEE2B600BD9082 /* Default-568h@2x.png in Resources */, 233 | AF28B0F717F77F1100A77ABB /* list@2x.png in Resources */, 234 | AF28B0F117F77DA300A77ABB /* clock@2x.png in Resources */, 235 | AF34B78217DEE2B800BD9082 /* MainStoryboard.storyboard in Resources */, 236 | AFF15D1717F35E46007F5746 /* MI.png in Resources */, 237 | ); 238 | runOnlyForDeploymentPostprocessing = 0; 239 | }; 240 | /* End PBXResourcesBuildPhase section */ 241 | 242 | /* Begin PBXSourcesBuildPhase section */ 243 | AF34B76117DEE2B100BD9082 /* Sources */ = { 244 | isa = PBXSourcesBuildPhase; 245 | buildActionMask = 2147483647; 246 | files = ( 247 | AF34B77517DEE2B400BD9082 /* main.m in Sources */, 248 | AF34B77917DEE2B400BD9082 /* AppDelegate.m in Sources */, 249 | 810308921846579B00C378F0 /* SWCellScrollView.m in Sources */, 250 | 810308911846579B00C378F0 /* NSMutableArray+SWUtilityButtons.m in Sources */, 251 | 810308961846579B00C378F0 /* SWUtilityButtonView.m in Sources */, 252 | 810308A5184D688D00C378F0 /* UMTableViewCell.m in Sources */, 253 | 810308941846579B00C378F0 /* SWTableViewCell.m in Sources */, 254 | AF34B78517DEE2B800BD9082 /* ViewController.m in Sources */, 255 | 810308951846579B00C378F0 /* SWUtilityButtonTapGestureRecognizer.m in Sources */, 256 | 810308931846579B00C378F0 /* SWLongPressGestureRecognizer.m in Sources */, 257 | ); 258 | runOnlyForDeploymentPostprocessing = 0; 259 | }; 260 | /* End PBXSourcesBuildPhase section */ 261 | 262 | /* Begin PBXVariantGroup section */ 263 | AF34B77117DEE2B400BD9082 /* InfoPlist.strings */ = { 264 | isa = PBXVariantGroup; 265 | children = ( 266 | AF34B77217DEE2B400BD9082 /* en */, 267 | ); 268 | name = InfoPlist.strings; 269 | sourceTree = ""; 270 | }; 271 | AF34B78017DEE2B700BD9082 /* MainStoryboard.storyboard */ = { 272 | isa = PBXVariantGroup; 273 | children = ( 274 | AF34B78117DEE2B700BD9082 /* en */, 275 | ); 276 | name = MainStoryboard.storyboard; 277 | sourceTree = ""; 278 | }; 279 | /* End PBXVariantGroup section */ 280 | 281 | /* Begin XCBuildConfiguration section */ 282 | AF34B78617DEE2B800BD9082 /* Debug */ = { 283 | isa = XCBuildConfiguration; 284 | buildSettings = { 285 | ALWAYS_SEARCH_USER_PATHS = NO; 286 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 287 | CLANG_CXX_LIBRARY = "libc++"; 288 | CLANG_ENABLE_OBJC_ARC = YES; 289 | CLANG_WARN_CONSTANT_CONVERSION = YES; 290 | CLANG_WARN_EMPTY_BODY = YES; 291 | CLANG_WARN_ENUM_CONVERSION = YES; 292 | CLANG_WARN_INT_CONVERSION = YES; 293 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 294 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 295 | COPY_PHASE_STRIP = NO; 296 | GCC_C_LANGUAGE_STANDARD = gnu99; 297 | GCC_DYNAMIC_NO_PIC = NO; 298 | GCC_OPTIMIZATION_LEVEL = 0; 299 | GCC_PREPROCESSOR_DEFINITIONS = ( 300 | "DEBUG=1", 301 | "$(inherited)", 302 | ); 303 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 304 | GCC_WARN_ABOUT_RETURN_TYPE = YES; 305 | GCC_WARN_UNINITIALIZED_AUTOS = YES; 306 | GCC_WARN_UNUSED_VARIABLE = YES; 307 | IPHONEOS_DEPLOYMENT_TARGET = 6.1; 308 | ONLY_ACTIVE_ARCH = YES; 309 | SDKROOT = iphoneos; 310 | }; 311 | name = Debug; 312 | }; 313 | AF34B78717DEE2B900BD9082 /* Release */ = { 314 | isa = XCBuildConfiguration; 315 | buildSettings = { 316 | ALWAYS_SEARCH_USER_PATHS = NO; 317 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 318 | CLANG_CXX_LIBRARY = "libc++"; 319 | CLANG_ENABLE_OBJC_ARC = YES; 320 | CLANG_WARN_CONSTANT_CONVERSION = YES; 321 | CLANG_WARN_EMPTY_BODY = YES; 322 | CLANG_WARN_ENUM_CONVERSION = YES; 323 | CLANG_WARN_INT_CONVERSION = YES; 324 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 325 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 326 | COPY_PHASE_STRIP = YES; 327 | GCC_C_LANGUAGE_STANDARD = gnu99; 328 | GCC_WARN_ABOUT_RETURN_TYPE = YES; 329 | GCC_WARN_UNINITIALIZED_AUTOS = YES; 330 | GCC_WARN_UNUSED_VARIABLE = YES; 331 | IPHONEOS_DEPLOYMENT_TARGET = 6.1; 332 | OTHER_CFLAGS = "-DNS_BLOCK_ASSERTIONS=1"; 333 | SDKROOT = iphoneos; 334 | VALIDATE_PRODUCT = YES; 335 | }; 336 | name = Release; 337 | }; 338 | AF34B78917DEE2B900BD9082 /* Debug */ = { 339 | isa = XCBuildConfiguration; 340 | buildSettings = { 341 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 342 | GCC_PREFIX_HEADER = "SWTableViewCell/SWTableViewCell-Prefix.pch"; 343 | INFOPLIST_FILE = "SWTableViewCell/SWTableViewCell-Info.plist"; 344 | IPHONEOS_DEPLOYMENT_TARGET = 7.0; 345 | PRODUCT_NAME = "$(TARGET_NAME)"; 346 | WRAPPER_EXTENSION = app; 347 | }; 348 | name = Debug; 349 | }; 350 | AF34B78A17DEE2B900BD9082 /* Release */ = { 351 | isa = XCBuildConfiguration; 352 | buildSettings = { 353 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 354 | GCC_PREFIX_HEADER = "SWTableViewCell/SWTableViewCell-Prefix.pch"; 355 | INFOPLIST_FILE = "SWTableViewCell/SWTableViewCell-Info.plist"; 356 | IPHONEOS_DEPLOYMENT_TARGET = 7.0; 357 | PRODUCT_NAME = "$(TARGET_NAME)"; 358 | WRAPPER_EXTENSION = app; 359 | }; 360 | name = Release; 361 | }; 362 | /* End XCBuildConfiguration section */ 363 | 364 | /* Begin XCConfigurationList section */ 365 | AF34B76017DEE2AE00BD9082 /* Build configuration list for PBXProject "SWTableViewCell" */ = { 366 | isa = XCConfigurationList; 367 | buildConfigurations = ( 368 | AF34B78617DEE2B800BD9082 /* Debug */, 369 | AF34B78717DEE2B900BD9082 /* Release */, 370 | ); 371 | defaultConfigurationIsVisible = 0; 372 | defaultConfigurationName = Release; 373 | }; 374 | AF34B78817DEE2B900BD9082 /* Build configuration list for PBXNativeTarget "SWTableViewCell" */ = { 375 | isa = XCConfigurationList; 376 | buildConfigurations = ( 377 | AF34B78917DEE2B900BD9082 /* Debug */, 378 | AF34B78A17DEE2B900BD9082 /* Release */, 379 | ); 380 | defaultConfigurationIsVisible = 0; 381 | defaultConfigurationName = Release; 382 | }; 383 | /* End XCConfigurationList section */ 384 | }; 385 | rootObject = AF34B75D17DEE2AE00BD9082 /* Project object */; 386 | } 387 | -------------------------------------------------------------------------------- /SWTableViewCell/PodFiles/SWTableViewCell.m: -------------------------------------------------------------------------------- 1 | // 2 | // SWTableViewCell.m 3 | // SWTableViewCell 4 | // 5 | // Created by Chris Wendel on 9/10/13. 6 | // Copyright (c) 2013 Chris Wendel. All rights reserved. 7 | // 8 | 9 | #import "SWTableViewCell.h" 10 | #import 11 | #import "SWUtilityButtonView.h" 12 | 13 | static NSString * const kTableViewCellContentView = @"UITableViewCellContentView"; 14 | 15 | #pragma mark - SWUtilityButtonView 16 | 17 | @interface SWTableViewCell () 18 | { 19 | SWCellState _cellState; // The state of the cell within the scroll view, can be left, right or middle 20 | CGFloat additionalRightPadding; 21 | 22 | dispatch_once_t onceToken; 23 | } 24 | 25 | @property (nonatomic, strong) SWUtilityButtonView *scrollViewButtonViewLeft; 26 | @property (nonatomic, strong) SWUtilityButtonView *scrollViewButtonViewRight; 27 | @property (nonatomic, weak) UIView *scrollViewContentView; 28 | @property (nonatomic) CGFloat height; 29 | 30 | @property (nonatomic, strong) SWLongPressGestureRecognizer *longPressGestureRecognizer; 31 | @property (nonatomic, strong) UITapGestureRecognizer *tapGestureRecognizer; 32 | 33 | @end 34 | 35 | @implementation SWTableViewCell 36 | 37 | #pragma mark Initializers 38 | 39 | - (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier containingTableView:(UITableView *)containingTableView leftUtilityButtons:(NSArray *)leftUtilityButtons rightUtilityButtons:(NSArray *)rightUtilityButtons 40 | { 41 | self = [super initWithStyle:style reuseIdentifier:reuseIdentifier]; 42 | if (self) 43 | { 44 | self.height = containingTableView.rowHeight; 45 | self.containingTableView = containingTableView; 46 | self.highlighted = NO; 47 | [self initializer]; 48 | self.rightUtilityButtons = rightUtilityButtons; 49 | self.leftUtilityButtons = leftUtilityButtons; 50 | } 51 | 52 | return self; 53 | } 54 | 55 | - (instancetype)initWithCoder:(NSCoder *)aDecoder 56 | { 57 | self = [super initWithCoder:aDecoder]; 58 | 59 | if (self) 60 | { 61 | [self initializer]; 62 | } 63 | 64 | return self; 65 | } 66 | 67 | - (instancetype)init 68 | { 69 | self = [super init]; 70 | 71 | if (self) 72 | { 73 | [self initializer]; 74 | } 75 | 76 | return self; 77 | } 78 | 79 | - (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier 80 | { 81 | self = [super initWithStyle:style reuseIdentifier:reuseIdentifier]; 82 | 83 | if (self) 84 | { 85 | [self initializer]; 86 | } 87 | 88 | return self; 89 | } 90 | 91 | - (void)initializer 92 | { 93 | // Check if the UITableView will display Indices on the right. If that's the case, add a padding 94 | if([self.containingTableView.dataSource respondsToSelector:@selector(sectionIndexTitlesForTableView:)]) 95 | { 96 | NSArray *indices = [self.containingTableView.dataSource sectionIndexTitlesForTableView:self.containingTableView]; 97 | additionalRightPadding = indices == nil ? 0 : kSectionIndexWidth; 98 | } 99 | 100 | // Set up scroll view that will host our cell content 101 | SWCellScrollView *cellScrollView = [[SWCellScrollView alloc] initWithFrame:CGRectMake(0, 0, CGRectGetWidth(self.bounds), self.height)]; 102 | cellScrollView.delegate = self; 103 | cellScrollView.showsHorizontalScrollIndicator = NO; 104 | cellScrollView.scrollsToTop = NO; 105 | cellScrollView.scrollEnabled = YES; 106 | cellScrollView.bounces = NO; 107 | 108 | UITapGestureRecognizer *tapGestureRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self 109 | action:@selector(scrollViewUp:)]; 110 | [cellScrollView addGestureRecognizer:tapGestureRecognizer]; 111 | 112 | self.tapGestureRecognizer = tapGestureRecognizer; 113 | 114 | SWLongPressGestureRecognizer *longPressGestureRecognizer = [[SWLongPressGestureRecognizer alloc] initWithTarget:self 115 | action:@selector(scrollViewPressed:)]; 116 | longPressGestureRecognizer.minimumPressDuration = 0.1; 117 | [cellScrollView addGestureRecognizer:longPressGestureRecognizer]; 118 | 119 | self.longPressGestureRecognizer = longPressGestureRecognizer; 120 | 121 | self.cellScrollView = cellScrollView; 122 | 123 | // Create the content view that will live in our scroll view 124 | UIView *scrollViewContentView = [[UIView alloc] initWithFrame:CGRectMake([self leftUtilityButtonsWidth], 0, CGRectGetWidth(self.bounds), self.height)]; 125 | scrollViewContentView.backgroundColor = [UIColor whiteColor]; 126 | [self.cellScrollView addSubview:scrollViewContentView]; 127 | self.scrollViewContentView = scrollViewContentView; 128 | 129 | // Add the cell scroll view to the cell 130 | UIView *contentViewParent = self; 131 | if (![NSStringFromClass([[self.subviews objectAtIndex:0] class]) isEqualToString:kTableViewCellContentView]) 132 | { 133 | // iOS 7 134 | contentViewParent = [self.subviews objectAtIndex:0]; 135 | } 136 | NSArray *cellSubviews = [contentViewParent subviews]; 137 | [self insertSubview:cellScrollView atIndex:0]; 138 | for (UIView *subview in cellSubviews) 139 | { 140 | [self.scrollViewContentView addSubview:subview]; 141 | } 142 | 143 | self.containingTableView.directionalLockEnabled = YES; 144 | } 145 | 146 | - (void)layoutSubviews 147 | { 148 | 149 | [super layoutSubviews]; 150 | 151 | self.cellScrollView.frame = CGRectMake(0, 0, CGRectGetWidth(self.bounds), self.height); 152 | self.cellScrollView.contentSize = CGSizeMake(CGRectGetWidth(self.bounds) + [self utilityButtonsPadding], self.height); 153 | self.cellScrollView.contentOffset = CGPointMake([self leftUtilityButtonsWidth], 0); 154 | self.scrollViewButtonViewLeft.frame = CGRectMake([self leftUtilityButtonsWidth], 0, 0, self.height); 155 | self.scrollViewButtonViewLeft.layer.masksToBounds = YES; 156 | self.scrollViewButtonViewRight.frame = CGRectMake(CGRectGetWidth(self.bounds), 0, 0, self.height); 157 | self.scrollViewButtonViewRight.layer.masksToBounds = YES; 158 | self.scrollViewContentView.frame = CGRectMake([self leftUtilityButtonsWidth], 0, CGRectGetWidth(self.bounds), self.height); 159 | self.cellScrollView.scrollEnabled = YES; 160 | self.tapGestureRecognizer.enabled = YES; 161 | 162 | } 163 | 164 | #pragma mark - Properties 165 | 166 | - (void)setLeftUtilityButtons:(NSArray *)leftUtilityButtons 167 | { 168 | _leftUtilityButtons = leftUtilityButtons; 169 | SWUtilityButtonView *departingLeftButtons = self.scrollViewButtonViewLeft; 170 | SWUtilityButtonView *scrollViewButtonViewLeft = [[SWUtilityButtonView alloc] initWithUtilityButtons:leftUtilityButtons 171 | parentCell:self 172 | utilityButtonSelector:@selector(leftUtilityButtonHandler:)]; 173 | 174 | self.scrollViewButtonViewLeft = scrollViewButtonViewLeft; 175 | [scrollViewButtonViewLeft setFrame:CGRectMake([self leftUtilityButtonsWidth], 0, [self leftUtilityButtonsWidth], self.height)]; 176 | 177 | [self.cellScrollView insertSubview:scrollViewButtonViewLeft belowSubview:self.scrollViewContentView]; 178 | 179 | [departingLeftButtons removeFromSuperview]; 180 | [scrollViewButtonViewLeft populateUtilityButtons]; 181 | 182 | [self setNeedsLayout]; 183 | } 184 | 185 | - (void)setRightUtilityButtons:(NSArray *)rightUtilityButtons 186 | { 187 | _rightUtilityButtons = rightUtilityButtons; 188 | SWUtilityButtonView *departingLeftButtons = self.scrollViewButtonViewRight; 189 | SWUtilityButtonView *scrollViewButtonViewRight = [[SWUtilityButtonView alloc] initWithUtilityButtons:rightUtilityButtons 190 | parentCell:self 191 | utilityButtonSelector:@selector(rightUtilityButtonHandler:)]; 192 | 193 | self.scrollViewButtonViewRight = scrollViewButtonViewRight; 194 | [scrollViewButtonViewRight setFrame:CGRectMake(CGRectGetWidth(self.bounds), 0, [self rightUtilityButtonsWidth], self.height)]; 195 | 196 | [self.cellScrollView insertSubview:scrollViewButtonViewRight belowSubview:self.scrollViewContentView]; 197 | 198 | [departingLeftButtons removeFromSuperview]; 199 | [scrollViewButtonViewRight populateUtilityButtons]; 200 | 201 | [self setNeedsLayout]; 202 | } 203 | 204 | 205 | #pragma mark Selection 206 | 207 | - (void)scrollViewPressed:(id)sender 208 | { 209 | SWLongPressGestureRecognizer *longPressGestureRecognizer = (SWLongPressGestureRecognizer *)sender; 210 | 211 | if (longPressGestureRecognizer.state == UIGestureRecognizerStateEnded) 212 | { 213 | // Gesture recognizer ended without failing so we select the cell 214 | [self selectCell]; 215 | 216 | // Set back to deselected 217 | [self setSelected:NO]; 218 | } 219 | else 220 | { 221 | // Handle the highlighting of the cell 222 | if (self.isHighlighted) 223 | { 224 | [self setHighlighted:NO]; 225 | } 226 | else 227 | { 228 | [self highlightCell]; 229 | } 230 | } 231 | } 232 | 233 | - (void)scrollViewUp:(id)sender 234 | { 235 | [self selectCellWithTimedHighlight]; 236 | } 237 | 238 | - (void)selectCell 239 | { 240 | if (_cellState == kCellStateCenter) 241 | { 242 | if ([self.containingTableView.delegate respondsToSelector:@selector(tableView:didSelectRowAtIndexPath:)]) 243 | { 244 | NSIndexPath *cellIndexPath = [self.containingTableView indexPathForCell:self]; 245 | [self.containingTableView selectRowAtIndexPath:cellIndexPath animated:NO scrollPosition:UITableViewScrollPositionNone]; 246 | [self.containingTableView.delegate tableView:self.containingTableView didSelectRowAtIndexPath:cellIndexPath]; 247 | [self.containingTableView deselectRowAtIndexPath:cellIndexPath animated:NO]; 248 | } 249 | } 250 | } 251 | 252 | - (void)selectCellWithTimedHighlight 253 | { 254 | if(_cellState == kCellStateCenter) 255 | { 256 | // Selection 257 | if ([self.containingTableView.delegate respondsToSelector:@selector(tableView:didSelectRowAtIndexPath:)]) 258 | { 259 | NSIndexPath *cellIndexPath = [self.containingTableView indexPathForCell:self]; 260 | [self setSelected:YES]; 261 | [self.containingTableView selectRowAtIndexPath:cellIndexPath animated:NO scrollPosition:UITableViewScrollPositionNone]; 262 | [self.containingTableView.delegate tableView:self.containingTableView didSelectRowAtIndexPath:cellIndexPath]; 263 | [self.containingTableView deselectRowAtIndexPath:cellIndexPath animated:NO]; 264 | // Make the selection visible 265 | NSTimer *endHighlightTimer = [NSTimer scheduledTimerWithTimeInterval:0.20 266 | target:self 267 | selector:@selector(timerEndCellHighlight:) 268 | userInfo:nil 269 | repeats:NO]; 270 | [[NSRunLoop currentRunLoop] addTimer:endHighlightTimer forMode:NSRunLoopCommonModes]; 271 | } 272 | } 273 | else 274 | { 275 | // Scroll back to center 276 | [self hideUtilityButtonsAnimated:YES]; 277 | } 278 | } 279 | 280 | - (void)highlightCell 281 | { 282 | if (_cellState == kCellStateCenter) 283 | { 284 | [self setHighlighted:YES]; 285 | } 286 | } 287 | 288 | - (void)timerEndCellHighlight:(id)sender 289 | { 290 | [self setSelected:NO]; 291 | } 292 | 293 | #pragma mark UITableViewCell overrides 294 | 295 | - (void)setBackgroundColor:(UIColor *)backgroundColor 296 | { 297 | _scrollViewContentView.backgroundColor = backgroundColor; 298 | } 299 | 300 | - (void)setHighlighted:(BOOL)highlighted 301 | { 302 | [super setHighlighted:highlighted animated:NO]; 303 | self.scrollViewButtonViewLeft.hidden = highlighted; 304 | self.scrollViewButtonViewRight.hidden = highlighted; 305 | } 306 | 307 | - (void)setHighlighted:(BOOL)highlighted animated:(BOOL)animated 308 | { 309 | [super setHighlighted:highlighted animated:animated]; 310 | self.scrollViewButtonViewLeft.hidden = highlighted; 311 | self.scrollViewButtonViewRight.hidden = highlighted; 312 | } 313 | 314 | - (void)setSelected:(BOOL)selected 315 | { 316 | [self setHighlighted:selected]; 317 | } 318 | 319 | - (void)setSelected:(BOOL)selected animated:(BOOL)animated 320 | { 321 | [self setHighlighted:selected]; 322 | } 323 | 324 | #pragma mark Height methods 325 | 326 | - (void)setCellHeight:(CGFloat)height 327 | { 328 | _height = height; 329 | 330 | // update the utility button height 331 | [self.scrollViewButtonViewLeft setHeight:height]; 332 | [self.scrollViewButtonViewRight setHeight:height]; 333 | 334 | [self layoutSubviews]; 335 | } 336 | 337 | #pragma mark - Utility buttons handling 338 | 339 | - (void)rightUtilityButtonHandler:(id)sender 340 | { 341 | SWUtilityButtonTapGestureRecognizer *utilityButtonTapGestureRecognizer = (SWUtilityButtonTapGestureRecognizer *)sender; 342 | NSUInteger utilityButtonIndex = utilityButtonTapGestureRecognizer.buttonIndex; 343 | if ([self.delegate respondsToSelector:@selector(swipeableTableViewCell:didTriggerRightUtilityButtonWithIndex:)]) 344 | { 345 | [self.delegate swipeableTableViewCell:self didTriggerRightUtilityButtonWithIndex:utilityButtonIndex]; 346 | } 347 | } 348 | 349 | - (void)leftUtilityButtonHandler:(id)sender 350 | { 351 | SWUtilityButtonTapGestureRecognizer *utilityButtonTapGestureRecognizer = (SWUtilityButtonTapGestureRecognizer *)sender; 352 | NSUInteger utilityButtonIndex = utilityButtonTapGestureRecognizer.buttonIndex; 353 | if ([self.delegate respondsToSelector:@selector(swipeableTableViewCell:didTriggerLeftUtilityButtonWithIndex:)]) 354 | { 355 | [self.delegate swipeableTableViewCell:self didTriggerLeftUtilityButtonWithIndex:utilityButtonIndex]; 356 | } 357 | } 358 | 359 | - (void)hideUtilityButtonsAnimated:(BOOL)animated 360 | { 361 | // Scroll back to center 362 | 363 | // Force the scroll back to run on the main thread because of weird scroll view bugs 364 | dispatch_async(dispatch_get_main_queue(), ^{ 365 | [self.cellScrollView setContentOffset:CGPointMake([self leftUtilityButtonsWidth], 0) animated:YES]; 366 | }); 367 | _cellState = kCellStateCenter; 368 | 369 | if ([self.delegate respondsToSelector:@selector(swipeableTableViewCell:scrollingToState:)]) 370 | { 371 | [self.delegate swipeableTableViewCell:self scrollingToState:kCellStateCenter]; 372 | } 373 | } 374 | 375 | 376 | #pragma mark - Setup helpers 377 | 378 | - (CGFloat)leftUtilityButtonsWidth 379 | { 380 | return [self.scrollViewButtonViewLeft utilityButtonsWidth]; 381 | } 382 | 383 | - (CGFloat)rightUtilityButtonsWidth 384 | { 385 | return [self.scrollViewButtonViewRight utilityButtonsWidth] + additionalRightPadding; 386 | } 387 | 388 | - (CGFloat)utilityButtonsPadding 389 | { 390 | return [self leftUtilityButtonsWidth] + [self rightUtilityButtonsWidth]; 391 | } 392 | 393 | - (CGPoint)scrollViewContentOffset 394 | { 395 | return CGPointMake([self.scrollViewButtonViewLeft utilityButtonsWidth], 0); 396 | } 397 | 398 | - (void) setAppearanceWithBlock:(void (^)())appearanceBlock force:(BOOL)force 399 | { 400 | if (force) 401 | { 402 | appearanceBlock(); 403 | } 404 | else 405 | { 406 | dispatch_once(&onceToken, ^{ 407 | appearanceBlock(); 408 | }); 409 | } 410 | } 411 | 412 | #pragma mark UIScrollView helpers 413 | 414 | - (void)scrollToRight:(inout CGPoint *)targetContentOffset 415 | { 416 | targetContentOffset->x = [self utilityButtonsPadding]; 417 | _cellState = kCellStateRight; 418 | 419 | self.longPressGestureRecognizer.enabled = NO; 420 | self.tapGestureRecognizer.enabled = NO; 421 | 422 | if ([self.delegate respondsToSelector:@selector(swipeableTableViewCell:scrollingToState:)]) 423 | { 424 | [self.delegate swipeableTableViewCell:self scrollingToState:kCellStateRight]; 425 | } 426 | 427 | if ([self.delegate respondsToSelector:@selector(swipeableTableViewCellShouldHideUtilityButtonsOnSwipe:)]) 428 | { 429 | for (SWTableViewCell *cell in [self.containingTableView visibleCells]) { 430 | if (cell != self && [cell isKindOfClass:[SWTableViewCell class]] && [self.delegate swipeableTableViewCellShouldHideUtilityButtonsOnSwipe:cell]) { 431 | [cell hideUtilityButtonsAnimated:YES]; 432 | } 433 | } 434 | } 435 | } 436 | 437 | - (void)scrollToCenter:(inout CGPoint *)targetContentOffset 438 | { 439 | targetContentOffset->x = [self leftUtilityButtonsWidth]; 440 | _cellState = kCellStateCenter; 441 | 442 | self.longPressGestureRecognizer.enabled = YES; 443 | self.tapGestureRecognizer.enabled = NO; 444 | 445 | if ([self.delegate respondsToSelector:@selector(swipeableTableViewCell:scrollingToState:)]) 446 | { 447 | [self.delegate swipeableTableViewCell:self scrollingToState:kCellStateCenter]; 448 | } 449 | } 450 | 451 | - (void)scrollToLeft:(inout CGPoint *)targetContentOffset 452 | { 453 | targetContentOffset->x = 0; 454 | _cellState = kCellStateLeft; 455 | 456 | self.longPressGestureRecognizer.enabled = NO; 457 | self.tapGestureRecognizer.enabled = NO; 458 | 459 | if ([self.delegate respondsToSelector:@selector(swipeableTableViewCell:scrollingToState:)]) 460 | { 461 | [self.delegate swipeableTableViewCell:self scrollingToState:kCellStateLeft]; 462 | } 463 | 464 | if ([self.delegate respondsToSelector:@selector(swipeableTableViewCellShouldHideUtilityButtonsOnSwipe:)]) 465 | { 466 | for (SWTableViewCell *cell in [self.containingTableView visibleCells]) { 467 | if (cell != self && [cell isKindOfClass:[SWTableViewCell class]] && [self.delegate swipeableTableViewCellShouldHideUtilityButtonsOnSwipe:cell]) { 468 | [cell hideUtilityButtonsAnimated:YES]; 469 | } 470 | } 471 | } 472 | } 473 | 474 | #pragma mark UIScrollViewDelegate 475 | 476 | - (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset 477 | { 478 | switch (_cellState) 479 | { 480 | case kCellStateCenter: 481 | if (velocity.x >= 0.5f) 482 | { 483 | [self scrollToRight:targetContentOffset]; 484 | } 485 | else if (velocity.x <= -0.5f) 486 | { 487 | [self scrollToLeft:targetContentOffset]; 488 | } 489 | else 490 | { 491 | CGFloat rightThreshold = [self utilityButtonsPadding] - ([self rightUtilityButtonsWidth] / 2); 492 | CGFloat leftThreshold = [self leftUtilityButtonsWidth] / 2; 493 | if (targetContentOffset->x > rightThreshold) 494 | [self scrollToRight:targetContentOffset]; 495 | else if (targetContentOffset->x < leftThreshold) 496 | [self scrollToLeft:targetContentOffset]; 497 | else 498 | [self scrollToCenter:targetContentOffset]; 499 | } 500 | break; 501 | case kCellStateLeft: 502 | if (velocity.x >= 0.5f) 503 | { 504 | [self scrollToCenter:targetContentOffset]; 505 | } 506 | else if (velocity.x <= -0.5f) 507 | { 508 | // No-op 509 | } 510 | else 511 | { 512 | if (targetContentOffset->x >= ([self utilityButtonsPadding] - [self rightUtilityButtonsWidth] / 2)) 513 | [self scrollToRight:targetContentOffset]; 514 | else if (targetContentOffset->x > [self leftUtilityButtonsWidth] / 2) 515 | [self scrollToCenter:targetContentOffset]; 516 | else 517 | [self scrollToLeft:targetContentOffset]; 518 | } 519 | break; 520 | case kCellStateRight: 521 | if (velocity.x >= 0.5f) 522 | { 523 | // No-op 524 | } 525 | else if (velocity.x <= -0.5f) 526 | { 527 | [self scrollToCenter:targetContentOffset]; 528 | } 529 | else 530 | { 531 | if (targetContentOffset->x <= [self leftUtilityButtonsWidth] / 2) 532 | [self scrollToLeft:targetContentOffset]; 533 | else if (targetContentOffset->x < ([self utilityButtonsPadding] - [self rightUtilityButtonsWidth] / 2)) 534 | [self scrollToCenter:targetContentOffset]; 535 | else 536 | [self scrollToRight:targetContentOffset]; 537 | } 538 | break; 539 | default: 540 | break; 541 | } 542 | } 543 | 544 | - (void)scrollViewDidScroll:(UIScrollView *)scrollView 545 | { 546 | self.tapGestureRecognizer.enabled = NO; 547 | if (scrollView.contentOffset.x > [self leftUtilityButtonsWidth]) 548 | { 549 | if ([self rightUtilityButtonsWidth] > 0) 550 | { 551 | if (self.delegate && [self.delegate respondsToSelector:@selector(swipeableTableViewCell:canSwipeToState:)]) 552 | { 553 | scrollView.scrollEnabled = [self.delegate swipeableTableViewCell:self canSwipeToState:kCellStateRight]; 554 | if (!scrollView.scrollEnabled) 555 | { 556 | return; 557 | } 558 | } 559 | CGFloat scrollViewWidth = MIN(scrollView.contentOffset.x - [self leftUtilityButtonsWidth], [self rightUtilityButtonsWidth]); 560 | 561 | // Expose the right button view 562 | self.scrollViewButtonViewRight.frame = CGRectMake(scrollView.contentOffset.x + (CGRectGetWidth(self.bounds) - scrollViewWidth), 0.0f, scrollViewWidth,self.height); 563 | 564 | CGRect scrollViewBounds = self.scrollViewButtonViewRight.bounds; 565 | scrollViewBounds.origin.x = MAX([self rightUtilityButtonsWidth] - scrollViewWidth, [self rightUtilityButtonsWidth] - scrollView.contentOffset.x); 566 | self.scrollViewButtonViewRight.bounds = scrollViewBounds; 567 | } 568 | } 569 | else 570 | { 571 | // Expose the left button view 572 | if ([self leftUtilityButtonsWidth] > 0) 573 | { 574 | if (self.delegate && [self.delegate respondsToSelector:@selector(swipeableTableViewCell:canSwipeToState:)]) 575 | { 576 | scrollView.scrollEnabled = [self.delegate swipeableTableViewCell:self canSwipeToState:kCellStateLeft]; 577 | if (!scrollView.scrollEnabled) 578 | { 579 | return; 580 | } 581 | } 582 | CGFloat scrollViewWidth = MIN(scrollView.contentOffset.x - [self leftUtilityButtonsWidth], [self leftUtilityButtonsWidth]); 583 | 584 | self.scrollViewButtonViewLeft.frame = CGRectMake([self leftUtilityButtonsWidth], 0.0f, scrollViewWidth, self.height); 585 | } 586 | } 587 | } 588 | 589 | - (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView 590 | { 591 | self.tapGestureRecognizer.enabled = YES; 592 | } 593 | 594 | - (void)scrollViewDidEndScrollingAnimation:(UIScrollView *)scrollView 595 | { 596 | // Called when setContentOffset in hideUtilityButtonsAnimated: is done 597 | self.tapGestureRecognizer.enabled = YES; 598 | if (_cellState == kCellStateCenter) 599 | { 600 | self.longPressGestureRecognizer.enabled = YES; 601 | } 602 | } 603 | 604 | @end 605 | --------------------------------------------------------------------------------