├── .gitignore ├── CHANGELOG.md ├── LICENSE ├── MGSwipeTableCell.podspec ├── MGSwipeTableCell.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist └── xcshareddata │ └── xcschemes │ └── MGSwipeTableCell.xcscheme ├── MGSwipeTableCell ├── Info.plist ├── MGSwipeButton.h ├── MGSwipeButton.m ├── MGSwipeTableCell.h ├── MGSwipeTableCell.m └── include │ └── MGSwipeTableCell │ ├── MGSwipeButton.h │ └── MGSwipeTableCell.h ├── Package.swift ├── README.md ├── demo ├── MGSwipeDemo │ ├── MGSwipeDemo.xcodeproj │ │ ├── project.pbxproj │ │ └── project.xcworkspace │ │ │ ├── contents.xcworkspacedata │ │ │ └── xcshareddata │ │ │ └── IDEWorkspaceChecks.plist │ └── MGSwipeDemo │ │ ├── AppDelegate.h │ │ ├── AppDelegate.m │ │ ├── DemoViewController.h │ │ ├── DemoViewController.m │ │ ├── Images.xcassets │ │ ├── AppIcon.appiconset │ │ │ └── Contents.json │ │ └── LaunchImage.launchimage │ │ │ └── Contents.json │ │ ├── LaunchScreen.xib │ │ ├── MGSwipeDemo-Info.plist │ │ ├── MGSwipeDemo-Prefix.pch │ │ ├── TestData.h │ │ ├── TestData.m │ │ ├── assets │ │ ├── background.jpg │ │ ├── cat@2x.png │ │ ├── check@2x.png │ │ ├── fav@2x.png │ │ └── menu@2x.png │ │ ├── autolayout_test.storyboard │ │ ├── en.lproj │ │ └── InfoPlist.strings │ │ └── main.m ├── MailAppDemo │ ├── MailAppDemo.xcodeproj │ │ ├── project.pbxproj │ │ └── project.xcworkspace │ │ │ ├── contents.xcworkspacedata │ │ │ └── xcshareddata │ │ │ └── IDEWorkspaceChecks.plist │ └── MailAppDemo │ │ ├── AppDelegate.h │ │ ├── AppDelegate.m │ │ ├── Images.xcassets │ │ ├── AppIcon.appiconset │ │ │ └── Contents.json │ │ └── LaunchImage.launchimage │ │ │ └── Contents.json │ │ ├── Info.plist │ │ ├── MailTableCell.h │ │ ├── MailTableCell.m │ │ ├── MailViewController.h │ │ ├── MailViewController.m │ │ └── main.m ├── MailAppDemoSwift │ ├── MailAppDemoSwift.xcodeproj │ │ ├── project.pbxproj │ │ └── project.xcworkspace │ │ │ ├── contents.xcworkspacedata │ │ │ └── xcshareddata │ │ │ └── IDEWorkspaceChecks.plist │ ├── MailAppDemoSwift │ │ ├── AppDelegate.swift │ │ ├── Base.lproj │ │ │ └── LaunchScreen.xib │ │ ├── Images.xcassets │ │ │ └── AppIcon.appiconset │ │ │ │ └── Contents.json │ │ ├── Info.plist │ │ ├── MailTableCell.swift │ │ ├── MailViewController.swift │ │ └── ObjCBridgingHeader.h │ └── MailAppDemoSwiftTests │ │ ├── Info.plist │ │ └── MailAppDemoSwiftTests.swift └── SpotifyDemo │ ├── SpotifyDemo.xcodeproj │ ├── project.pbxproj │ └── project.xcworkspace │ │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist │ └── SpotifyDemo │ ├── AppDelegate.h │ ├── AppDelegate.m │ ├── Images.xcassets │ ├── AppIcon.appiconset │ │ └── Contents.json │ └── LaunchImage.launchimage │ │ └── Contents.json │ ├── Info.plist │ ├── SpotifyViewController.h │ ├── SpotifyViewController.m │ ├── main.m │ └── more@2x.png └── readme-assets ├── 3d.gif ├── RoundTableViewCell.png ├── border.gif ├── clip.gif ├── drag.gif └── static.gif /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | # 3 | build/ 4 | *.pbxuser 5 | !default.pbxuser 6 | *.mode1v3 7 | !default.mode1v3 8 | *.mode2v3 9 | !default.mode2v3 10 | *.perspectivev3 11 | !default.perspectivev3 12 | xcuserdata 13 | *.xccheckout 14 | *.moved-aside 15 | DerivedData 16 | *.hmap 17 | *.ipa 18 | *.xcuserstate 19 | 20 | # CocoaPods 21 | # 22 | # We recommend against adding the Pods directory to your .gitignore. However 23 | # you should judge for yourself, the pros and cons are mentioned at: 24 | # http://guides.cocoapods.org/using/using-cocoapods.html#should-i-ignore-the-pods-directory-in-source-control 25 | # 26 | # Pods/ 27 | 28 | # Swift Package Manager 29 | .build/ 30 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## 1.6.14 4 | 5 | - Fix Visual glitch/flash on refreshContentView() while cell is swiped open (see #363) 6 | - Include Swift Package Manager support in the tag version 7 | 8 | ## 1.6.11 9 | 10 | - Also apply the accessoryType fix on iOS 12 11 | 12 | ## 1.6.10 13 | 14 | - Fix cut view when UITableViewCell's accessoryType is set to disclosureIndicator in a storyboard. 15 | 16 | ## 1.6.9 17 | 18 | - iOS 13 compatibility fixes. 19 | 20 | ## 1.6.8 21 | 22 | - Fix duplicaged buttons on orientation change. 23 | 24 | ## 1.6.7 25 | 26 | - Fix quality loss in snapshot image. 27 | - Got rid from Block implicitly warning. 28 | 29 | ## 1.6.6 30 | 31 | - Improve notch code to better handle edge cases like orientation changes while swiping. 32 | - Correctly handle safeAreaInsets for RTL language. 33 | - Add allowsButtonWithDifferentWidth property to MGSwipeSettings and deprecate the one in MGSwipeTableCell. 34 | 35 | ## 1.6.5 36 | 37 | - Expand the first or last button width to make appearance look good with a notch (e.g. iPhone X). 38 | - Add expandLastButtonBySafeAreaInsets property to optionally enable/disable the default behaviour. 39 | 40 | ## 1.6.4 41 | 42 | - Backwards compatibility with XCode 8 compilation 43 | 44 | ## 1.6.2 45 | 46 | - Handle iOS 11 safeAreaInsets. 47 | - Fix issue #302: Right Buttons are covered iPhone X when landscape. 48 | - Use availability checks for Objective-C to remove some warings in XCode 9. 49 | 50 | ## 1.6.1 51 | 52 | - Fix issue #294: iOS 11 crash. 53 | 54 | ## 1.6.0 55 | 56 | - Fix glitch when dragging before animation completes. 57 | - Fix cell separator overlap when swiping. 58 | - Add buttonsDistance property to MGSwipeSettings: allows to control distance between the buttons. 59 | - Add MGSwipeExpansionLayoutNone type that allows not to performont button layout in expansions. 60 | - Improved AppExtension and RTL language support. 61 | - Improved Swift interoperation. 62 | 63 | ## 1.5.6 64 | 65 | - Improve iOS 10.0 support 66 | - Objective-C nullable annotations to improve Swift 3.0 interoperation. Add Swift 3.0 sample. 67 | - Improve accesibility implementation to allow recording UI Tests 68 | - Add preprocessor macro to allow compilations on App Extension projects 69 | - Add swipeBounceRate property. Coefficient applied to cell movement in bounce zone 70 | - Fix issue #216: Crash when combining programmatically created offset animations in consecutive block callbacks 71 | - Fix issue #139: Accessing Buttons in UITests does not work 72 | - Fix issue #134: Crash due to zombie object in expansion 73 | - Fix button text alignment for RTL languages 74 | 75 | ## 1.5.5 76 | 77 | - Fix iOS 8.0 compatibility issue 78 | - Fix issue #190: duplicate callback 79 | 80 | ## 1.5.4 81 | 82 | - Safer animation and callbacks checks after a cell is deleted. Fixes issue #91 caused by a null cell.indexPath because the callback was called while/after a cell is deleted 83 | - Fix issue #168: problem with devices that read right to left (Arabic and Hebrew) 84 | - Fix swipe issues after reloading a table 85 | - Fix issue #163: Swipe not working after deleting a cell 86 | - Add enableSwipeBounces property. Enabled by default, if disabled the swipe bounces will be disabled and the swipe motion will stop right after the button. 87 | - Add allowsOppositeSwipe property. Controls whether swipe gesture is allowed in opposite directions. NO value disables swiping in opposite direction once started in one direction 88 | - Add topMargin and bottomMargin properties to MGSwipeSettings 89 | - Add touchOnDismissSwipe property. Controls whether dismissing a swiped cell when clicking outside of the cell generates a real touch event on the other cell. Default behaviour is the same as the Mail app on iOS. Enable it if you want to allow to start a new swipe while a cell is already in swiped. 90 | - Add a 'BOOL finished' parameter to completion animation callbacks 91 | 92 | ## 1.5.3 93 | 94 | - Fix duplicated callbacks with reused button instances among many cells 95 | - Fix Carthage header for normal projects 96 | 97 | ## 1.5.2 98 | 99 | - Add Carthage support 100 | - Fix issues #160 and #162: Delegate call multiple time on single touch on iOS 9 101 | - Fix programmatic call to setSwipeOffset when swipeView is not yet created 102 | - Fix Swipe backgroundColor change without re-initializing the cell 103 | - Fix UITableCellSelectionStyle on end swipe 104 | - centerIconOverText now takes a spacing argument 105 | 106 | ## 1.5.1 107 | 108 | - Fix default animation values 109 | 110 | ## 1.5.0 111 | 112 | - Feature: Add predefined and configurable easing functions to the API. Swipe animations are now splitted into different settings so you can now use different animations (easing function, duration, etc) for each kind of animations: show swipe, hide swipe, stretch cell from already swiped buttons and more. 113 | - Feature: Full support for Spotify App like swipe animations. Create new Spotify Demo project. 114 | - Feature: add onlySwipeButtons property that allows to only swipe the buttons and keep the cell content static 115 | - Feature: add fromOffset argument to the canSwipe delegate method. Useful for supporting sidebar menus with swipe to open. You can use it to restrict the cell from being swiped if the offset is to close to the edge. 116 | - Feature: add preservesSelectionStatus property that allows to control whether selection/highlight is changed when swiped. 117 | - Feature: add keepButtonsSwiped property to Swipe settings 118 | - Feature: new delegate method: shouldHideSwipeOnTap:(CGPoint) point. Now you can cancel to hide opened swipe depending on the tap point 119 | - Feature: new delegate methods: swipeTableCellWillBeginSwiping & swipeTableCellWillEndSwiping. Useful to make cell changes that only are shown after the cell is swiped open 120 | - Fixed issue #113: Bug when changing the orientation of the device 121 | - Fixed typo throughout readme and source of "swiping" 122 | - Fixed issue #100: canSwipe not working when swipe to the allowed direction and swipe with inertia to the forbidden direction 123 | - Fixed clip mask when animating expansion 124 | - Keep expansion color while expansion is animating to 0 125 | 126 | 127 | ## 1.4.3 128 | 129 | - Fixed crash with different class cells in multiple section tables 130 | - Add completion handlers to convenience show/hide methods 131 | 132 | ## 1.4.2 133 | 134 | - Fixed issue #89: Swipe fails after table reload while swipping gesture is active 135 | 136 | ## 1.4.1 137 | 138 | - Implemented Spotify App style cell expansion: added expansionLayout and expansionColor properties to MGSwipeExpansionSettings 139 | - New property allowsSwipeWhenTappingButtons. Fixed issue #80: swiping the cell when you swipe the buttons in the cell 140 | - Fixed issue #78: Swift problem with one enum 141 | - Fixed issue #82: fillOnTrigger not working properly with UITableViewCellAccessoryDisclosureIndicator 142 | - Fixed issue #84: can't compile in xcode 6.3 143 | - Fixed issue #76: Swiped cell state issue when table view is scrolling 144 | 145 | ## 1.4.0 146 | 147 | - Implemented allowsButtonsWithDifferentWidth property to support buttons with different widths 148 | - Implemented allowsMultipleSwipe feature 149 | - Allow starting swiping on one cell while another one is open 150 | - Keep cell's selection state when swipe is closed 151 | - Fixed flash of the cell in its default state before the cell is deleted when fillOnTrigger is set. 152 | - Improved MGSwipeButton. New utility methods to Center image over text, set edge insets, set a fixed with and more. 153 | - Other minor bugfixes 154 | 155 | ## 1.3.6 156 | 157 | - Improved gestureRecognizer ending detection. Fixed issue #50: Not able to swipe to reveal right after scrolling collection view children of cell content view 158 | - Fixed the white background issue when the cell.backgroundColor is set to UIColor.clearColor 159 | - Added utility method to perform swipe expansions programmatically 160 | 161 | ## 1.3.5 162 | 163 | - Fixed issue #43: Two Cell Can Swipe simultaneously 164 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2018 Imanol Fernandez @MortimerGoro 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /MGSwipeTableCell.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |s| 2 | s.name = 'MGSwipeTableCell' 3 | s.version = '1.6.14' 4 | s.author = { 'Imanol Fernandez' => 'mortimergoro@gmail.com' } 5 | s.homepage = 'https://github.com/MortimerGoro/MGSwipeTableCell' 6 | s.summary = 'An easy to use UITableViewCell subclass that allows to display swipeable buttons with a variety of transitions' 7 | s.license = 'MIT' 8 | s.source = { :git => 'https://github.com/MortimerGoro/MGSwipeTableCell.git', :tag => s.version.to_s } 9 | s.source_files = 'MGSwipeTableCell' 10 | s.platform = :ios 11 | s.ios.deployment_target = '10.0' 12 | s.requires_arc = true 13 | end 14 | -------------------------------------------------------------------------------- /MGSwipeTableCell.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | C63EE56F1BBA07ED008F46BB /* MGSwipeTableCell.h in Headers */ = {isa = PBXBuildFile; fileRef = C63EE56E1BBA07ED008F46BB /* MGSwipeTableCell.h */; settings = {ATTRIBUTES = (Public, ); }; }; 11 | C63EE5881BBA083B008F46BB /* MGSwipeButton.h in Headers */ = {isa = PBXBuildFile; fileRef = C63EE5851BBA083B008F46BB /* MGSwipeButton.h */; settings = {ATTRIBUTES = (Public, ); }; }; 12 | C63EE5891BBA083B008F46BB /* MGSwipeButton.m in Sources */ = {isa = PBXBuildFile; fileRef = C63EE5861BBA083B008F46BB /* MGSwipeButton.m */; }; 13 | C63EE58A1BBA083B008F46BB /* MGSwipeTableCell.m in Sources */ = {isa = PBXBuildFile; fileRef = C63EE5871BBA083B008F46BB /* MGSwipeTableCell.m */; }; 14 | /* End PBXBuildFile section */ 15 | 16 | /* Begin PBXFileReference section */ 17 | C63EE56B1BBA07ED008F46BB /* MGSwipeTableCell.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = MGSwipeTableCell.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 18 | C63EE56E1BBA07ED008F46BB /* MGSwipeTableCell.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MGSwipeTableCell.h; sourceTree = ""; }; 19 | C63EE5701BBA07ED008F46BB /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 20 | C63EE5851BBA083B008F46BB /* MGSwipeButton.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGSwipeButton.h; sourceTree = ""; }; 21 | C63EE5861BBA083B008F46BB /* MGSwipeButton.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MGSwipeButton.m; sourceTree = ""; }; 22 | C63EE5871BBA083B008F46BB /* MGSwipeTableCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MGSwipeTableCell.m; sourceTree = ""; }; 23 | /* End PBXFileReference section */ 24 | 25 | /* Begin PBXFrameworksBuildPhase section */ 26 | C63EE5671BBA07ED008F46BB /* Frameworks */ = { 27 | isa = PBXFrameworksBuildPhase; 28 | buildActionMask = 2147483647; 29 | files = ( 30 | ); 31 | runOnlyForDeploymentPostprocessing = 0; 32 | }; 33 | /* End PBXFrameworksBuildPhase section */ 34 | 35 | /* Begin PBXGroup section */ 36 | C63EE5611BBA07ED008F46BB = { 37 | isa = PBXGroup; 38 | children = ( 39 | C63EE56D1BBA07ED008F46BB /* MGSwipeTableCell */, 40 | C63EE56C1BBA07ED008F46BB /* Products */, 41 | ); 42 | sourceTree = ""; 43 | }; 44 | C63EE56C1BBA07ED008F46BB /* Products */ = { 45 | isa = PBXGroup; 46 | children = ( 47 | C63EE56B1BBA07ED008F46BB /* MGSwipeTableCell.framework */, 48 | ); 49 | name = Products; 50 | sourceTree = ""; 51 | }; 52 | C63EE56D1BBA07ED008F46BB /* MGSwipeTableCell */ = { 53 | isa = PBXGroup; 54 | children = ( 55 | C63EE56E1BBA07ED008F46BB /* MGSwipeTableCell.h */, 56 | C63EE5851BBA083B008F46BB /* MGSwipeButton.h */, 57 | C63EE5861BBA083B008F46BB /* MGSwipeButton.m */, 58 | C63EE5871BBA083B008F46BB /* MGSwipeTableCell.m */, 59 | C63EE5701BBA07ED008F46BB /* Info.plist */, 60 | ); 61 | path = MGSwipeTableCell; 62 | sourceTree = ""; 63 | }; 64 | /* End PBXGroup section */ 65 | 66 | /* Begin PBXHeadersBuildPhase section */ 67 | C63EE5681BBA07ED008F46BB /* Headers */ = { 68 | isa = PBXHeadersBuildPhase; 69 | buildActionMask = 2147483647; 70 | files = ( 71 | C63EE56F1BBA07ED008F46BB /* MGSwipeTableCell.h in Headers */, 72 | C63EE5881BBA083B008F46BB /* MGSwipeButton.h in Headers */, 73 | ); 74 | runOnlyForDeploymentPostprocessing = 0; 75 | }; 76 | /* End PBXHeadersBuildPhase section */ 77 | 78 | /* Begin PBXNativeTarget section */ 79 | C63EE56A1BBA07ED008F46BB /* MGSwipeTableCell */ = { 80 | isa = PBXNativeTarget; 81 | buildConfigurationList = C63EE57F1BBA07ED008F46BB /* Build configuration list for PBXNativeTarget "MGSwipeTableCell" */; 82 | buildPhases = ( 83 | C63EE5661BBA07ED008F46BB /* Sources */, 84 | C63EE5671BBA07ED008F46BB /* Frameworks */, 85 | C63EE5681BBA07ED008F46BB /* Headers */, 86 | C63EE5691BBA07ED008F46BB /* Resources */, 87 | ); 88 | buildRules = ( 89 | ); 90 | dependencies = ( 91 | ); 92 | name = MGSwipeTableCell; 93 | productName = MGSwipeTableCell; 94 | productReference = C63EE56B1BBA07ED008F46BB /* MGSwipeTableCell.framework */; 95 | productType = "com.apple.product-type.framework"; 96 | }; 97 | /* End PBXNativeTarget section */ 98 | 99 | /* Begin PBXProject section */ 100 | C63EE5621BBA07ED008F46BB /* Project object */ = { 101 | isa = PBXProject; 102 | attributes = { 103 | LastUpgradeCheck = 0940; 104 | ORGANIZATIONNAME = mortimergoro; 105 | TargetAttributes = { 106 | C63EE56A1BBA07ED008F46BB = { 107 | CreatedOnToolsVersion = 7.0; 108 | }; 109 | }; 110 | }; 111 | buildConfigurationList = C63EE5651BBA07ED008F46BB /* Build configuration list for PBXProject "MGSwipeTableCell" */; 112 | compatibilityVersion = "Xcode 3.2"; 113 | developmentRegion = English; 114 | hasScannedForEncodings = 0; 115 | knownRegions = ( 116 | English, 117 | en, 118 | ); 119 | mainGroup = C63EE5611BBA07ED008F46BB; 120 | productRefGroup = C63EE56C1BBA07ED008F46BB /* Products */; 121 | projectDirPath = ""; 122 | projectRoot = ""; 123 | targets = ( 124 | C63EE56A1BBA07ED008F46BB /* MGSwipeTableCell */, 125 | ); 126 | }; 127 | /* End PBXProject section */ 128 | 129 | /* Begin PBXResourcesBuildPhase section */ 130 | C63EE5691BBA07ED008F46BB /* Resources */ = { 131 | isa = PBXResourcesBuildPhase; 132 | buildActionMask = 2147483647; 133 | files = ( 134 | ); 135 | runOnlyForDeploymentPostprocessing = 0; 136 | }; 137 | /* End PBXResourcesBuildPhase section */ 138 | 139 | /* Begin PBXSourcesBuildPhase section */ 140 | C63EE5661BBA07ED008F46BB /* Sources */ = { 141 | isa = PBXSourcesBuildPhase; 142 | buildActionMask = 2147483647; 143 | files = ( 144 | C63EE5891BBA083B008F46BB /* MGSwipeButton.m in Sources */, 145 | C63EE58A1BBA083B008F46BB /* MGSwipeTableCell.m in Sources */, 146 | ); 147 | runOnlyForDeploymentPostprocessing = 0; 148 | }; 149 | /* End PBXSourcesBuildPhase section */ 150 | 151 | /* Begin XCBuildConfiguration section */ 152 | C63EE57D1BBA07ED008F46BB /* Debug */ = { 153 | isa = XCBuildConfiguration; 154 | buildSettings = { 155 | ALWAYS_SEARCH_USER_PATHS = NO; 156 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 157 | CLANG_CXX_LIBRARY = "libc++"; 158 | CLANG_ENABLE_MODULES = YES; 159 | CLANG_ENABLE_OBJC_ARC = YES; 160 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 161 | CLANG_WARN_BOOL_CONVERSION = YES; 162 | CLANG_WARN_COMMA = YES; 163 | CLANG_WARN_CONSTANT_CONVERSION = YES; 164 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 165 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 166 | CLANG_WARN_EMPTY_BODY = YES; 167 | CLANG_WARN_ENUM_CONVERSION = YES; 168 | CLANG_WARN_INFINITE_RECURSION = YES; 169 | CLANG_WARN_INT_CONVERSION = YES; 170 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 171 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 172 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 173 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 174 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 175 | CLANG_WARN_STRICT_PROTOTYPES = YES; 176 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 177 | CLANG_WARN_UNREACHABLE_CODE = YES; 178 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 179 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 180 | COPY_PHASE_STRIP = NO; 181 | CURRENT_PROJECT_VERSION = 1; 182 | DEBUG_INFORMATION_FORMAT = dwarf; 183 | ENABLE_STRICT_OBJC_MSGSEND = YES; 184 | ENABLE_TESTABILITY = YES; 185 | GCC_C_LANGUAGE_STANDARD = gnu99; 186 | GCC_DYNAMIC_NO_PIC = NO; 187 | GCC_NO_COMMON_BLOCKS = YES; 188 | GCC_OPTIMIZATION_LEVEL = 0; 189 | GCC_PREPROCESSOR_DEFINITIONS = ( 190 | "DEBUG=1", 191 | "$(inherited)", 192 | ); 193 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 194 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 195 | GCC_WARN_UNDECLARED_SELECTOR = YES; 196 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 197 | GCC_WARN_UNUSED_FUNCTION = YES; 198 | GCC_WARN_UNUSED_VARIABLE = YES; 199 | IPHONEOS_DEPLOYMENT_TARGET = 9.0; 200 | MTL_ENABLE_DEBUG_INFO = YES; 201 | ONLY_ACTIVE_ARCH = YES; 202 | SDKROOT = iphoneos; 203 | TARGETED_DEVICE_FAMILY = "1,2"; 204 | VERSIONING_SYSTEM = "apple-generic"; 205 | VERSION_INFO_PREFIX = ""; 206 | }; 207 | name = Debug; 208 | }; 209 | C63EE57E1BBA07ED008F46BB /* Release */ = { 210 | isa = XCBuildConfiguration; 211 | buildSettings = { 212 | ALWAYS_SEARCH_USER_PATHS = NO; 213 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 214 | CLANG_CXX_LIBRARY = "libc++"; 215 | CLANG_ENABLE_MODULES = YES; 216 | CLANG_ENABLE_OBJC_ARC = YES; 217 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 218 | CLANG_WARN_BOOL_CONVERSION = YES; 219 | CLANG_WARN_COMMA = YES; 220 | CLANG_WARN_CONSTANT_CONVERSION = YES; 221 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 222 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 223 | CLANG_WARN_EMPTY_BODY = YES; 224 | CLANG_WARN_ENUM_CONVERSION = YES; 225 | CLANG_WARN_INFINITE_RECURSION = YES; 226 | CLANG_WARN_INT_CONVERSION = YES; 227 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 228 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 229 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 230 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 231 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 232 | CLANG_WARN_STRICT_PROTOTYPES = YES; 233 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 234 | CLANG_WARN_UNREACHABLE_CODE = YES; 235 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 236 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 237 | COPY_PHASE_STRIP = NO; 238 | CURRENT_PROJECT_VERSION = 1; 239 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 240 | ENABLE_NS_ASSERTIONS = NO; 241 | ENABLE_STRICT_OBJC_MSGSEND = YES; 242 | GCC_C_LANGUAGE_STANDARD = gnu99; 243 | GCC_NO_COMMON_BLOCKS = YES; 244 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 245 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 246 | GCC_WARN_UNDECLARED_SELECTOR = YES; 247 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 248 | GCC_WARN_UNUSED_FUNCTION = YES; 249 | GCC_WARN_UNUSED_VARIABLE = YES; 250 | IPHONEOS_DEPLOYMENT_TARGET = 9.0; 251 | MTL_ENABLE_DEBUG_INFO = NO; 252 | SDKROOT = iphoneos; 253 | TARGETED_DEVICE_FAMILY = "1,2"; 254 | VALIDATE_PRODUCT = YES; 255 | VERSIONING_SYSTEM = "apple-generic"; 256 | VERSION_INFO_PREFIX = ""; 257 | }; 258 | name = Release; 259 | }; 260 | C63EE5801BBA07ED008F46BB /* Debug */ = { 261 | isa = XCBuildConfiguration; 262 | buildSettings = { 263 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; 264 | DEFINES_MODULE = YES; 265 | DYLIB_COMPATIBILITY_VERSION = 1; 266 | DYLIB_CURRENT_VERSION = 1; 267 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 268 | INFOPLIST_FILE = MGSwipeTableCell/Info.plist; 269 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 270 | IPHONEOS_DEPLOYMENT_TARGET = 10.0; 271 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 272 | PRODUCT_BUNDLE_IDENTIFIER = com.mortimergoro.MGSwipeTableCell; 273 | PRODUCT_NAME = "$(TARGET_NAME)"; 274 | SKIP_INSTALL = YES; 275 | }; 276 | name = Debug; 277 | }; 278 | C63EE5811BBA07ED008F46BB /* Release */ = { 279 | isa = XCBuildConfiguration; 280 | buildSettings = { 281 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; 282 | DEFINES_MODULE = YES; 283 | DYLIB_COMPATIBILITY_VERSION = 1; 284 | DYLIB_CURRENT_VERSION = 1; 285 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 286 | INFOPLIST_FILE = MGSwipeTableCell/Info.plist; 287 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 288 | IPHONEOS_DEPLOYMENT_TARGET = 10.0; 289 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 290 | PRODUCT_BUNDLE_IDENTIFIER = com.mortimergoro.MGSwipeTableCell; 291 | PRODUCT_NAME = "$(TARGET_NAME)"; 292 | SKIP_INSTALL = YES; 293 | }; 294 | name = Release; 295 | }; 296 | /* End XCBuildConfiguration section */ 297 | 298 | /* Begin XCConfigurationList section */ 299 | C63EE5651BBA07ED008F46BB /* Build configuration list for PBXProject "MGSwipeTableCell" */ = { 300 | isa = XCConfigurationList; 301 | buildConfigurations = ( 302 | C63EE57D1BBA07ED008F46BB /* Debug */, 303 | C63EE57E1BBA07ED008F46BB /* Release */, 304 | ); 305 | defaultConfigurationIsVisible = 0; 306 | defaultConfigurationName = Release; 307 | }; 308 | C63EE57F1BBA07ED008F46BB /* Build configuration list for PBXNativeTarget "MGSwipeTableCell" */ = { 309 | isa = XCConfigurationList; 310 | buildConfigurations = ( 311 | C63EE5801BBA07ED008F46BB /* Debug */, 312 | C63EE5811BBA07ED008F46BB /* Release */, 313 | ); 314 | defaultConfigurationIsVisible = 0; 315 | defaultConfigurationName = Release; 316 | }; 317 | /* End XCConfigurationList section */ 318 | }; 319 | rootObject = C63EE5621BBA07ED008F46BB /* Project object */; 320 | } 321 | -------------------------------------------------------------------------------- /MGSwipeTableCell.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /MGSwipeTableCell.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /MGSwipeTableCell.xcodeproj/xcshareddata/xcschemes/MGSwipeTableCell.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 33 | 39 | 40 | 41 | 42 | 43 | 49 | 50 | 51 | 52 | 53 | 54 | 64 | 65 | 71 | 72 | 73 | 74 | 75 | 76 | 82 | 83 | 89 | 90 | 91 | 92 | 94 | 95 | 98 | 99 | 100 | -------------------------------------------------------------------------------- /MGSwipeTableCell/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | $(CURRENT_PROJECT_VERSION) 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /MGSwipeTableCell/MGSwipeButton.h: -------------------------------------------------------------------------------- 1 | /* 2 | * MGSwipeTableCell is licensed under MIT license. See LICENSE.md file for more information. 3 | * Copyright (c) 2016 Imanol Fernandez @MortimerGoro 4 | */ 5 | 6 | #import 7 | 8 | 9 | @class MGSwipeTableCell; 10 | 11 | /** 12 | * This is a convenience class to create MGSwipeTableCell buttons 13 | * Using this class is optional because MGSwipeTableCell is button agnostic and can use any UIView for that purpose 14 | * Anyway, it's recommended that you use this class because is totally tested and easy to use ;) 15 | */ 16 | @interface MGSwipeButton : UIButton 17 | 18 | /** 19 | * Convenience block callback for developers lazy to implement the MGSwipeTableCellDelegate. 20 | * @return Return YES to autohide the swipe view 21 | */ 22 | typedef BOOL(^ MGSwipeButtonCallback)(MGSwipeTableCell * _Nonnull cell); 23 | @property (nonatomic, strong, nullable) MGSwipeButtonCallback callback; 24 | 25 | /** A width for the expanded buttons. Defaults to 0, which means sizeToFit will be called. */ 26 | @property (nonatomic, assign) CGFloat buttonWidth; 27 | 28 | /** 29 | * Convenience static constructors 30 | */ 31 | +(nonnull instancetype) buttonWithTitle:(nonnull NSString *) title backgroundColor:(nullable UIColor *) color; 32 | +(nonnull instancetype) buttonWithTitle:(nonnull NSString *) title backgroundColor:(nullable UIColor *) color padding:(NSInteger) padding; 33 | +(nonnull instancetype) buttonWithTitle:(nonnull NSString *) title backgroundColor:(nullable UIColor *) color insets:(UIEdgeInsets) insets; 34 | +(nonnull instancetype) buttonWithTitle:(nonnull NSString *) title backgroundColor:(nullable UIColor *) color callback:(nullable MGSwipeButtonCallback) callback; 35 | +(nonnull instancetype) buttonWithTitle:(nonnull NSString *) title backgroundColor:(nullable UIColor *) color padding:(NSInteger) padding callback:(nullable MGSwipeButtonCallback) callback; 36 | +(nonnull instancetype) buttonWithTitle:(nonnull NSString *) title backgroundColor:(nullable UIColor *) color insets:(UIEdgeInsets) insets callback:(nullable MGSwipeButtonCallback) callback; 37 | +(nonnull instancetype) buttonWithTitle:(nonnull NSString *) title icon:(nullable UIImage*) icon backgroundColor:(nullable UIColor *) color; 38 | +(nonnull instancetype) buttonWithTitle:(nonnull NSString *) title icon:(nullable UIImage*) icon backgroundColor:(nullable UIColor *) color padding:(NSInteger) padding; 39 | +(nonnull instancetype) buttonWithTitle:(nonnull NSString *) title icon:(nullable UIImage*) icon backgroundColor:(nullable UIColor *) color insets:(UIEdgeInsets) insets; 40 | +(nonnull instancetype) buttonWithTitle:(nonnull NSString *) title icon:(nullable UIImage*) icon backgroundColor:(nullable UIColor *) color callback:(nullable MGSwipeButtonCallback) callback; 41 | +(nonnull instancetype) buttonWithTitle:(nonnull NSString *) title icon:(nullable UIImage*) icon backgroundColor:(nullable UIColor *) color padding:(NSInteger) padding callback:(nullable MGSwipeButtonCallback) callback; 42 | +(nonnull instancetype) buttonWithTitle:(nonnull NSString *) title icon:(nullable UIImage*) icon backgroundColor:(nullable UIColor *) color insets:(UIEdgeInsets) insets callback:(nullable MGSwipeButtonCallback) callback; 43 | 44 | -(void) setPadding:(CGFloat) padding; 45 | -(void) setEdgeInsets:(UIEdgeInsets)insets; 46 | -(void) centerIconOverText; 47 | -(void) centerIconOverTextWithSpacing: (CGFloat) spacing; 48 | -(void) iconTintColor:(nullable UIColor *)tintColor; 49 | 50 | 51 | @end 52 | -------------------------------------------------------------------------------- /MGSwipeTableCell/MGSwipeButton.m: -------------------------------------------------------------------------------- 1 | /* 2 | * MGSwipeTableCell is licensed under MIT license. See LICENSE.md file for more information. 3 | * Copyright (c) 2016 Imanol Fernandez @MortimerGoro 4 | */ 5 | 6 | #import "MGSwipeButton.h" 7 | 8 | @class MGSwipeTableCell; 9 | 10 | @implementation MGSwipeButton 11 | 12 | +(instancetype) buttonWithTitle:(NSString *) title backgroundColor:(UIColor *) color 13 | { 14 | return [self buttonWithTitle:title icon:nil backgroundColor:color]; 15 | } 16 | 17 | +(instancetype) buttonWithTitle:(NSString *) title backgroundColor:(UIColor *) color padding:(NSInteger) padding 18 | { 19 | return [self buttonWithTitle:title icon:nil backgroundColor:color insets:UIEdgeInsetsMake(0, padding, 0, padding)]; 20 | } 21 | 22 | +(instancetype) buttonWithTitle:(NSString *) title backgroundColor:(UIColor *) color insets:(UIEdgeInsets) insets 23 | { 24 | return [self buttonWithTitle:title icon:nil backgroundColor:color insets:insets]; 25 | } 26 | 27 | +(instancetype) buttonWithTitle:(NSString *) title backgroundColor:(UIColor *) color callback:(MGSwipeButtonCallback) callback 28 | { 29 | return [self buttonWithTitle:title icon:nil backgroundColor:color callback:callback]; 30 | } 31 | 32 | +(instancetype) buttonWithTitle:(NSString *) title backgroundColor:(UIColor *) color padding:(NSInteger) padding callback:(MGSwipeButtonCallback) callback 33 | { 34 | return [self buttonWithTitle:title icon:nil backgroundColor:color insets:UIEdgeInsetsMake(0, padding, 0, padding) callback:callback]; 35 | } 36 | 37 | +(instancetype) buttonWithTitle:(NSString *) title backgroundColor:(UIColor *) color insets:(UIEdgeInsets) insets callback:(MGSwipeButtonCallback) callback 38 | { 39 | return [self buttonWithTitle:title icon:nil backgroundColor:color insets:insets callback:callback]; 40 | } 41 | 42 | +(instancetype) buttonWithTitle:(NSString *) title icon:(UIImage*) icon backgroundColor:(UIColor *) color 43 | { 44 | return [self buttonWithTitle:title icon:icon backgroundColor:color callback:nil]; 45 | } 46 | 47 | +(instancetype) buttonWithTitle:(NSString *) title icon:(UIImage*) icon backgroundColor:(UIColor *) color padding:(NSInteger) padding 48 | { 49 | return [self buttonWithTitle:title icon:icon backgroundColor:color insets:UIEdgeInsetsMake(0, padding, 0, padding) callback:nil]; 50 | } 51 | 52 | +(instancetype) buttonWithTitle:(NSString *) title icon:(UIImage*) icon backgroundColor:(UIColor *) color insets:(UIEdgeInsets) insets 53 | { 54 | return [self buttonWithTitle:title icon:icon backgroundColor:color insets:insets callback:nil]; 55 | } 56 | 57 | +(instancetype) buttonWithTitle:(NSString *) title icon:(UIImage*) icon backgroundColor:(UIColor *) color callback:(MGSwipeButtonCallback) callback 58 | { 59 | return [self buttonWithTitle:title icon:icon backgroundColor:color padding:10 callback:callback]; 60 | } 61 | 62 | +(instancetype) buttonWithTitle:(NSString *) title icon:(UIImage*) icon backgroundColor:(UIColor *) color padding:(NSInteger) padding callback:(MGSwipeButtonCallback) callback 63 | { 64 | return [self buttonWithTitle:title icon:icon backgroundColor:color insets:UIEdgeInsetsMake(0, padding, 0, padding) callback:callback]; 65 | } 66 | 67 | +(instancetype) buttonWithTitle:(NSString *) title icon:(UIImage*) icon backgroundColor:(UIColor *) color insets:(UIEdgeInsets) insets callback:(MGSwipeButtonCallback) callback 68 | { 69 | MGSwipeButton * button = [self buttonWithType:UIButtonTypeCustom]; 70 | button.backgroundColor = color; 71 | button.titleLabel.lineBreakMode = NSLineBreakByWordWrapping; 72 | button.titleLabel.textAlignment = NSTextAlignmentCenter; 73 | [button setTitle:title forState:UIControlStateNormal]; 74 | [button setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal]; 75 | [button setImage:icon forState:UIControlStateNormal]; 76 | button.callback = callback; 77 | [button setEdgeInsets:insets]; 78 | return button; 79 | } 80 | 81 | -(BOOL) callMGSwipeConvenienceCallback: (MGSwipeTableCell *) sender 82 | { 83 | if (_callback) { 84 | return _callback(sender); 85 | } 86 | return NO; 87 | } 88 | 89 | -(void) centerIconOverText 90 | { 91 | [self centerIconOverTextWithSpacing: 3.0]; 92 | } 93 | 94 | -(void) centerIconOverTextWithSpacing: (CGFloat) spacing { 95 | CGSize size = self.imageView.image.size; 96 | 97 | if ([UIDevice currentDevice].systemVersion.floatValue >= 9.0 && [self isRTLLocale]) { 98 | self.titleEdgeInsets = UIEdgeInsetsMake(0.0, 99 | 0.0, 100 | -(size.height + spacing), 101 | -size.width); 102 | size = [self.titleLabel.text sizeWithAttributes:@{ NSFontAttributeName: self.titleLabel.font }]; 103 | self.imageEdgeInsets = UIEdgeInsetsMake(-(size.height + spacing), 104 | -size.width, 105 | 0.0, 106 | 0.0); 107 | } 108 | else 109 | { 110 | self.titleEdgeInsets = UIEdgeInsetsMake(0.0, 111 | -size.width, 112 | -(size.height + spacing), 113 | 0.0); 114 | size = [self.titleLabel.text sizeWithAttributes:@{ NSFontAttributeName: self.titleLabel.font }]; 115 | self.imageEdgeInsets = UIEdgeInsetsMake(-(size.height + spacing), 116 | 0.0, 117 | 0.0, 118 | -size.width); 119 | } 120 | } 121 | 122 | -(void) setPadding:(CGFloat) padding 123 | { 124 | self.contentEdgeInsets = UIEdgeInsetsMake(0, padding, 0, padding); 125 | [self sizeToFit]; 126 | } 127 | 128 | - (void)setButtonWidth:(CGFloat)buttonWidth 129 | { 130 | _buttonWidth = buttonWidth; 131 | if (_buttonWidth > 0) 132 | { 133 | CGRect frame = self.frame; 134 | frame.size.width = _buttonWidth; 135 | self.frame = frame; 136 | } 137 | else 138 | { 139 | [self sizeToFit]; 140 | } 141 | } 142 | 143 | -(void) setEdgeInsets:(UIEdgeInsets)insets 144 | { 145 | self.contentEdgeInsets = insets; 146 | [self sizeToFit]; 147 | } 148 | 149 | -(void) iconTintColor:(UIColor *)tintColor 150 | { 151 | UIImage *currentIcon = self.imageView.image; 152 | if (currentIcon.renderingMode != UIImageRenderingModeAlwaysTemplate) { 153 | currentIcon = [currentIcon imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate]; 154 | [self setImage:currentIcon forState:UIControlStateNormal]; 155 | } 156 | self.tintColor = tintColor; 157 | } 158 | 159 | - (BOOL)isAppExtension 160 | { 161 | return [[NSBundle mainBundle].executablePath rangeOfString:@".appex/"].location != NSNotFound; 162 | } 163 | 164 | -(BOOL) isRTLLocale 165 | { 166 | #if __IPHONE_OS_VERSION_MAX_ALLOWED >= 110000 167 | if (@available(iOS 9, *)) { 168 | #else 169 | if ([[UIView class] respondsToSelector:@selector(userInterfaceLayoutDirectionForSemanticContentAttribute:)]) { 170 | #endif 171 | return [UIView userInterfaceLayoutDirectionForSemanticContentAttribute:self.semanticContentAttribute] == UIUserInterfaceLayoutDirectionRightToLeft; 172 | } 173 | else if ([self isAppExtension]) { 174 | return [NSLocale characterDirectionForLanguage:[[NSLocale currentLocale] objectForKey:NSLocaleLanguageCode]]==NSLocaleLanguageDirectionRightToLeft; 175 | } else { 176 | UIApplication *application = [UIApplication performSelector:@selector(sharedApplication)]; 177 | return application.userInterfaceLayoutDirection == UIUserInterfaceLayoutDirectionRightToLeft; 178 | } 179 | } 180 | 181 | @end 182 | -------------------------------------------------------------------------------- /MGSwipeTableCell/MGSwipeTableCell.h: -------------------------------------------------------------------------------- 1 | /* 2 | * MGSwipeTableCell is licensed under MIT license. See LICENSE.md file for more information. 3 | * Copyright (c) 2016 Imanol Fernandez @MortimerGoro 4 | */ 5 | 6 | #import 7 | #import "MGSwipeButton.h" 8 | 9 | 10 | /** Transition types */ 11 | typedef NS_ENUM(NSInteger, MGSwipeTransition) { 12 | MGSwipeTransitionBorder = 0, 13 | MGSwipeTransitionStatic, 14 | MGSwipeTransitionDrag, 15 | MGSwipeTransitionClipCenter, 16 | MGSwipeTransitionRotate3D 17 | }; 18 | 19 | /** Compatibility with older versions */ 20 | #define MGSwipeTransition3D MGSwipeTransitionRotate3D 21 | #define MGSwipeStateSwippingLeftToRight MGSwipeStateSwipingLeftToRight 22 | #define MGSwipeStateSwippingRightToLeft MGSwipeStateSwipingRightToLeft 23 | 24 | /** Swipe directions */ 25 | typedef NS_ENUM(NSInteger, MGSwipeDirection) { 26 | MGSwipeDirectionLeftToRight = 0, 27 | MGSwipeDirectionRightToLeft 28 | }; 29 | 30 | /** Swipe state */ 31 | typedef NS_ENUM(NSInteger, MGSwipeState) { 32 | MGSwipeStateNone = 0, 33 | MGSwipeStateSwipingLeftToRight, 34 | MGSwipeStateSwipingRightToLeft, 35 | MGSwipeStateExpandingLeftToRight, 36 | MGSwipeStateExpandingRightToLeft, 37 | }; 38 | 39 | /** Swipe Expansion Layout */ 40 | typedef NS_ENUM(NSInteger, MGSwipeExpansionLayout) { 41 | MGSwipeExpansionLayoutBorder = 0, 42 | MGSwipeExpansionLayoutCenter, 43 | MGSwipeExpansionLayoutNone 44 | }; 45 | 46 | /** Swipe Easing Function */ 47 | typedef NS_ENUM(NSInteger, MGSwipeEasingFunction) { 48 | MGSwipeEasingFunctionLinear = 0, 49 | MGSwipeEasingFunctionQuadIn, 50 | MGSwipeEasingFunctionQuadOut, 51 | MGSwipeEasingFunctionQuadInOut, 52 | MGSwipeEasingFunctionCubicIn, 53 | MGSwipeEasingFunctionCubicOut, 54 | MGSwipeEasingFunctionCubicInOut, 55 | MGSwipeEasingFunctionBounceIn, 56 | MGSwipeEasingFunctionBounceOut, 57 | MGSwipeEasingFunctionBounceInOut 58 | }; 59 | 60 | /** 61 | * Swipe animation settings 62 | **/ 63 | @interface MGSwipeAnimation : NSObject 64 | /** Animation duration in seconds. Default value 0.3 */ 65 | @property (nonatomic, assign) CGFloat duration; 66 | /** Animation easing function. Default value EaseOutBounce */ 67 | @property (nonatomic, assign) MGSwipeEasingFunction easingFunction; 68 | /** Override this method to implement custom easing functions */ 69 | -(CGFloat) value:(CGFloat) elapsed duration:(CGFloat) duration from:(CGFloat) from to:(CGFloat) to; 70 | 71 | @end 72 | 73 | /** 74 | * Swipe settings 75 | **/ 76 | @interface MGSwipeSettings: NSObject 77 | /** Transition used while swiping buttons */ 78 | @property (nonatomic, assign) MGSwipeTransition transition; 79 | /** Size proportional threshold to hide/keep the buttons when the user ends swiping. Default value 0.5 */ 80 | @property (nonatomic, assign) CGFloat threshold; 81 | /** Optional offset to change the swipe buttons position. Relative to the cell border position. Default value: 0 82 | ** For example it can be used to avoid cropped buttons when sectionIndexTitlesForTableView is used in the UITableView 83 | **/ 84 | @property (nonatomic, assign) CGFloat offset; 85 | /** Top margin of the buttons relative to the contentView */ 86 | @property (nonatomic, assign) CGFloat topMargin; 87 | /** Bottom margin of the buttons relative to the contentView */ 88 | @property (nonatomic, assign) CGFloat bottomMargin; 89 | /** Distance between the buttons. Default value : 0 */ 90 | @property (nonatomic, assign) CGFloat buttonsDistance; 91 | /** If true, expands the last button length by safeAreaInsets. Useful for devices with a notch (e.g. iPhone X) */ 92 | @property (nonatomic, assign) BOOL expandLastButtonBySafeAreaInsets; 93 | 94 | /** Animation settings when the swipe buttons are shown */ 95 | @property (nonatomic, strong, nonnull) MGSwipeAnimation * showAnimation; 96 | /** Animation settings when the swipe buttons are hided */ 97 | @property (nonatomic, strong, nonnull) MGSwipeAnimation * hideAnimation; 98 | /** Animation settings when the cell is stretched from the swipe buttons */ 99 | @property (nonatomic, strong, nonnull) MGSwipeAnimation * stretchAnimation; 100 | 101 | /** Property to read or change swipe animation durations. Default value 0.3 */ 102 | @property (nonatomic, assign) CGFloat animationDuration DEPRECATED_ATTRIBUTE; 103 | 104 | /** If true the buttons are kept swiped when the threshold is reached and the user ends the gesture 105 | * If false, the buttons are always hidden when the user ends the swipe gesture 106 | */ 107 | @property (nonatomic, assign) BOOL keepButtonsSwiped; 108 | 109 | /** If true the table cell is not swiped, just the buttons **/ 110 | @property (nonatomic, assign) BOOL onlySwipeButtons; 111 | 112 | /** If NO the swipe bounces will be disabled, the swipe motion will stop right after the button */ 113 | @property (nonatomic, assign) BOOL enableSwipeBounces; 114 | 115 | /** Coefficient applied to cell movement in bounce zone. Set to value between 0.0 and 1.0 116 | to make the cell 'resist' swiping after buttons are revealed. Default is 1.0 */ 117 | @property (nonatomic, assign) CGFloat swipeBounceRate; 118 | 119 | // default is NO. Controls whether buttons with different width are allowed. Buttons are resized to have the same size by default. 120 | @property (nonatomic) BOOL allowsButtonsWithDifferentWidth; 121 | 122 | @end 123 | 124 | 125 | /** 126 | * Expansion settings to make expandable buttons 127 | * Swipe button are not expandable by default 128 | **/ 129 | @interface MGSwipeExpansionSettings: NSObject 130 | /** index of the expandable button (in the left or right buttons arrays) */ 131 | @property (nonatomic, assign) NSInteger buttonIndex; 132 | /** if true the button fills the cell on trigger, else it bounces back to its initial position */ 133 | @property (nonatomic, assign) BOOL fillOnTrigger; 134 | /** Size proportional threshold to trigger the expansion button. Default value 1.5 */ 135 | @property (nonatomic, assign) CGFloat threshold; 136 | /** Optional expansion color. Expanded button's background color is used by default **/ 137 | @property (nonatomic, strong, nullable) UIColor * expansionColor; 138 | /** Defines the layout of the expanded button **/ 139 | @property (nonatomic, assign) MGSwipeExpansionLayout expansionLayout; 140 | /** Animation settings when the expansion is triggered **/ 141 | @property (nonatomic, strong, nonnull) MGSwipeAnimation * triggerAnimation; 142 | 143 | /** Property to read or change expansion animation durations. Default value 0.2 144 | * The target animation is the change of a button from normal state to expanded state 145 | */ 146 | @property (nonatomic, assign) CGFloat animationDuration; 147 | @end 148 | 149 | 150 | /** helper forward declaration */ 151 | @class MGSwipeTableCell; 152 | 153 | /** 154 | * Optional delegate to configure swipe buttons or to receive triggered actions. 155 | * Buttons can be configured inline when the cell is created instead of using this delegate, 156 | * but using the delegate improves memory usage because buttons are only created in demand 157 | */ 158 | @protocol MGSwipeTableCellDelegate 159 | 160 | @optional 161 | /** 162 | * Delegate method to enable/disable swipe gestures 163 | * @return YES if swipe is allowed 164 | **/ 165 | -(BOOL) swipeTableCell:(nonnull MGSwipeTableCell*) cell canSwipe:(MGSwipeDirection) direction fromPoint:(CGPoint) point; 166 | -(BOOL) swipeTableCell:(nonnull MGSwipeTableCell*) cell canSwipe:(MGSwipeDirection) direction DEPRECATED_ATTRIBUTE; //backwards compatibility 167 | 168 | /** 169 | * Delegate method invoked when the current swipe state changes 170 | @param state the current Swipe State 171 | @param gestureIsActive YES if the user swipe gesture is active. No if the uses has already ended the gesture 172 | **/ 173 | -(void) swipeTableCell:(nonnull MGSwipeTableCell*) cell didChangeSwipeState:(MGSwipeState) state gestureIsActive:(BOOL) gestureIsActive; 174 | 175 | /** 176 | * Called when the user clicks a swipe button or when a expandable button is automatically triggered 177 | * @return YES to autohide the current swipe buttons 178 | **/ 179 | -(BOOL) swipeTableCell:(nonnull MGSwipeTableCell*) cell tappedButtonAtIndex:(NSInteger) index direction:(MGSwipeDirection)direction fromExpansion:(BOOL) fromExpansion; 180 | /** 181 | * Delegate method to setup the swipe buttons and swipe/expansion settings 182 | * Buttons can be any kind of UIView but it's recommended to use the convenience MGSwipeButton class 183 | * Setting up buttons with this delegate instead of using cell properties improves memory usage because buttons are only created in demand 184 | * @param cell the UITableViewCell to configure. You can get the indexPath using [tableView indexPathForCell:cell] 185 | * @param direction The swipe direction (left to right or right to left) 186 | * @param swipeSettings instance to configure the swipe transition and setting (optional) 187 | * @param expansionSettings instance to configure button expansions (optional) 188 | * @return Buttons array 189 | **/ 190 | -(nullable NSArray*) swipeTableCell:(nonnull MGSwipeTableCell*) cell swipeButtonsForDirection:(MGSwipeDirection)direction 191 | swipeSettings:(nonnull MGSwipeSettings*) swipeSettings expansionSettings:(nonnull MGSwipeExpansionSettings*) expansionSettings; 192 | 193 | /** 194 | * Called when the user taps on a swiped cell 195 | * @return YES to autohide the current swipe buttons 196 | **/ 197 | -(BOOL) swipeTableCell:(nonnull MGSwipeTableCell *)cell shouldHideSwipeOnTap:(CGPoint) point; 198 | 199 | /** 200 | * Called when the cell will begin swiping 201 | * Useful to make cell changes that only are shown after the cell is swiped open 202 | **/ 203 | -(void) swipeTableCellWillBeginSwiping:(nonnull MGSwipeTableCell *) cell; 204 | 205 | /** 206 | * Called when the cell will end swiping 207 | **/ 208 | -(void) swipeTableCellWillEndSwiping:(nonnull MGSwipeTableCell *) cell; 209 | 210 | @end 211 | 212 | 213 | /** 214 | * Swipe Cell class 215 | * To implement swipe cells you have to override from this class 216 | * You can create the cells programmatically, using xibs or storyboards 217 | */ 218 | @interface MGSwipeTableCell : UITableViewCell 219 | 220 | /** optional delegate (not retained) */ 221 | @property (nonatomic, weak, nullable) id delegate; 222 | 223 | /** optional to use contentView alternative. Use this property instead of contentView to support animated views while swiping */ 224 | @property (nonatomic, strong, readonly, nonnull) UIView * swipeContentView; 225 | 226 | /** 227 | * Left and right swipe buttons and its settings. 228 | * Buttons can be any kind of UIView but it's recommended to use the convenience MGSwipeButton class 229 | */ 230 | @property (nonatomic, copy, nonnull) NSArray * leftButtons; 231 | @property (nonatomic, copy, nonnull) NSArray * rightButtons; 232 | @property (nonatomic, strong, nonnull) MGSwipeSettings * leftSwipeSettings; 233 | @property (nonatomic, strong, nonnull) MGSwipeSettings * rightSwipeSettings; 234 | 235 | /** Optional settings to allow expandable buttons */ 236 | @property (nonatomic, strong, nonnull) MGSwipeExpansionSettings * leftExpansion; 237 | @property (nonatomic, strong, nonnull) MGSwipeExpansionSettings * rightExpansion; 238 | 239 | /** Readonly property to fetch the current swipe state */ 240 | @property (nonatomic, readonly) MGSwipeState swipeState; 241 | /** Readonly property to check if the user swipe gesture is currently active */ 242 | @property (nonatomic, readonly) BOOL isSwipeGestureActive; 243 | 244 | // default is NO. Controls whether multiple cells can be swiped simultaneously 245 | @property (nonatomic) BOOL allowsMultipleSwipe; 246 | // default is NO. Controls whether buttons with different width are allowed. Buttons are resized to have the same size by default. 247 | @property (nonatomic) BOOL allowsButtonsWithDifferentWidth DEPRECATED_MSG_ATTRIBUTE("Use MGSwipeSettings.allowsButtonsWithDifferentWidth instead"); 248 | //default is YES. Controls whether swipe gesture is allowed when the touch starts into the swiped buttons 249 | @property (nonatomic) BOOL allowsSwipeWhenTappingButtons; 250 | //default is YES. Controls whether swipe gesture is allowed in opposite directions. NO value disables swiping in opposite direction once started in one direction 251 | @property (nonatomic) BOOL allowsOppositeSwipe; 252 | // default is NO. Controls whether the cell selection/highlight status is preserved when expansion occurs 253 | @property (nonatomic) BOOL preservesSelectionStatus; 254 | /* default is NO. Controls whether dismissing a swiped cell when tapping outside of the cell generates a real touch event on the other cell. 255 | Default behaviour is the same as the Mail app on iOS. Enable it if you want to allow to start a new swipe while a cell is already in swiped in a single step. */ 256 | @property (nonatomic) BOOL touchOnDismissSwipe; 257 | 258 | /** Optional background color for swipe overlay. If not set, its inferred automatically from the cell contentView */ 259 | @property (nonatomic, strong, nullable) UIColor * swipeBackgroundColor; 260 | /** Property to read or change the current swipe offset programmatically */ 261 | @property (nonatomic, assign) CGFloat swipeOffset; 262 | 263 | /** Utility methods to show or hide swipe buttons programmatically */ 264 | -(void) hideSwipeAnimated: (BOOL) animated; 265 | -(void) hideSwipeAnimated: (BOOL) animated completion:(nullable void(^)(BOOL finished)) completion; 266 | -(void) showSwipe: (MGSwipeDirection) direction animated: (BOOL) animated; 267 | -(void) showSwipe: (MGSwipeDirection) direction animated: (BOOL) animated completion:(nullable void(^)(BOOL finished)) completion; 268 | -(void) setSwipeOffset:(CGFloat)offset animated: (BOOL) animated completion:(nullable void(^)(BOOL finished)) completion; 269 | -(void) setSwipeOffset:(CGFloat)offset animation: (nullable MGSwipeAnimation *) animation completion:(nullable void(^)(BOOL finished)) completion; 270 | -(void) expandSwipe: (MGSwipeDirection) direction animated: (BOOL) animated; 271 | 272 | /** Refresh method to be used when you want to update the cell contents while the user is swiping */ 273 | -(void) refreshContentView; 274 | /** Refresh method to be used when you want to dynamically change the left or right buttons (add or remove) 275 | * If you only want to change the title or the backgroundColor of a button you can change it's properties (get the button instance from leftButtons or rightButtons arrays) 276 | * @param usingDelegate if YES new buttons will be fetched using the MGSwipeTableCellDelegate. Otherwise new buttons will be fetched from leftButtons/rightButtons properties. 277 | */ 278 | -(void) refreshButtons: (BOOL) usingDelegate; 279 | 280 | @end 281 | 282 | -------------------------------------------------------------------------------- /MGSwipeTableCell/include/MGSwipeTableCell/MGSwipeButton.h: -------------------------------------------------------------------------------- 1 | ../../MGSwipeButton.h -------------------------------------------------------------------------------- /MGSwipeTableCell/include/MGSwipeTableCell/MGSwipeTableCell.h: -------------------------------------------------------------------------------- 1 | ../../MGSwipeTableCell.h -------------------------------------------------------------------------------- /Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version:5.3 2 | 3 | import PackageDescription 4 | 5 | let package = Package( 6 | name: "MGSwipeTableCell", 7 | platforms: [ 8 | .iOS(.v9), 9 | ], 10 | products: [ 11 | .library( 12 | name: "MGSwipeTableCell", 13 | targets: ["MGSwipeTableCell"] 14 | ), 15 | ], 16 | dependencies: [], 17 | targets: [ 18 | .target( 19 | name: "MGSwipeTableCell", 20 | path: "MGSwipeTableCell", 21 | exclude: [ 22 | "Info.plist", 23 | ], 24 | linkerSettings: [ 25 | .linkedFramework("UIKit"), 26 | ] 27 | ), 28 | ] 29 | ) 30 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | MGSwipeTableCell 2 | ================ 3 | 4 | [![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage) 5 | 6 | MGSwipeTableCell is an easy to use UITableViewCell subclass that allows to display swipeable buttons with a variety of transitions. 7 | 8 | This library is compatible with all the different ways to create a UITableViewCell: system predefined styles, programmatically created cells, cells loaded from a xib and prototype cells within a storyboard. You can use autolayout if you want. 9 | 10 | Works on iOS >= 5.0. Tested on all iOS versions on iPhone and iPad: iOS 7, iOS 8, iOS 9, iOS 10, iOS 11, iOS 12, iOS 13, iOS 14. 11 | 12 | ## Transitions demo 13 | 14 | ### Border transition 15 | 16 |

17 | 18 | ### Clip transition 19 | 20 |

21 | 22 | ### 3D transition 23 | 24 |

25 | 26 | ### Static transition 27 | 28 |

29 | 30 | ### Drag transition 31 | 32 |

33 | 34 | ## API Reference 35 | See [`MGSwipeTableCell.h`](MGSwipeTableCell/MGSwipeTableCell.h) header file for a complete overview of the capabilities of the class. 36 | 37 | See [`MailAppDemo`](demo/MailAppDemo) for a complete project which mimics Apple's Mail App (written in Objective-C) 38 | 39 | See [`MailAppDemoSwift`](demo/MailAppDemoSwift) for a complete project which mimics Apple's Mail App (Written in Swift) 40 | 41 | See [`SpotifyDemo`](demo/SpotifyDemo) for a complete project which mimics Spotify App swipe style 42 | 43 | See [`MGSwipeDemo`](demo/MGSwipeDemo) for a complete project where you can test the variety of transitions on a real device/simulator. 44 | 45 | ## Setup your project 46 | 47 | You can use CocoaPods to include MGSwipeTableCell into you project: 48 | 49 | pod 'MGSwipeTableCell' 50 | 51 | You can use Carthage to include MGSwipeTableCell into your project. Just add this dependency to your Cartfile: 52 | 53 | github "MortimerGoro/MGSwipeTableCell" 54 | 55 | You can use Swift Package Manager to include MGSwipeTableCell into you project: 56 | 57 | .package(url: "https://github.com/MortimerGoro/MGSwipeTableCell.git", from: "1.6.0") 58 | 59 | ## Usage 60 | 61 | ### Basic 62 | Integrating MGSwipeTableCell in your project is very easy. Basically, you only have to inherit from MGSwipeTableCell instead of UITableViewCell, or directly instantiate MGSwipeTableCell instances with iOS predefined cell styles. You can layout your cell content as you are used to do, MGSwipeTableCell doesn't force you to change layouts. 63 | 64 | Here is a example of a MGSwipeTableCell using iOS predefined styles. You can set an array of buttons to cell.leftButtons and/or cell.rightButtons properties. MGSwipeButton is a convenience class, you are not forced to use it. You can use your own UIButtons or UIViews. You can configure transitions (and swipe thresholds) with the leftSwipeSettings and/or rightSwipeSettings properties 65 | 66 | ##### Objective-C 67 | ```objc 68 | - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath 69 | { 70 | static NSString * reuseIdentifier = @"programmaticCell"; 71 | MGSwipeTableCell * cell = [self.tableView dequeueReusableCellWithIdentifier:reuseIdentifier]; 72 | if (!cell) { 73 | cell = [[MGSwipeTableCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:reuseIdentifier]; 74 | } 75 | 76 | cell.textLabel.text = @"Title"; 77 | cell.detailTextLabel.text = @"Detail text"; 78 | cell.delegate = self; //optional 79 | 80 | 81 | //configure left buttons 82 | cell.leftButtons = @[[MGSwipeButton buttonWithTitle:@"" icon:[UIImage imageNamed:@"check.png"] backgroundColor:[UIColor greenColor]], 83 | [MGSwipeButton buttonWithTitle:@"" icon:[UIImage imageNamed:@"fav.png"] backgroundColor:[UIColor blueColor]]]; 84 | cell.leftSwipeSettings.transition = MGSwipeTransition3D; 85 | 86 | //configure right buttons 87 | cell.rightButtons = @[[MGSwipeButton buttonWithTitle:@"Delete" backgroundColor:[UIColor redColor]], 88 | [MGSwipeButton buttonWithTitle:@"More" backgroundColor:[UIColor lightGrayColor]]]; 89 | cell.rightSwipeSettings.transition = MGSwipeTransition3D; 90 | return cell; 91 | } 92 | ``` 93 | ##### Swift 94 | ```swift 95 | func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell 96 | { 97 | let reuseIdentifier = "programmaticCell" 98 | var cell = tableView.dequeueReusableCell(withIdentifier: reuseIdentifier, for: indexPath) as! MGSwipeTableCell 99 | 100 | cell.textLabel!.text = "Title" 101 | cell.detailTextLabel!.text = "Detail text" 102 | cell.delegate = self //optional 103 | 104 | //configure left buttons 105 | cell.leftButtons = [MGSwipeButton(title: "", icon: UIImage(named:"check.png"), backgroundColor: .green), 106 | MGSwipeButton(title: "", icon: UIImage(named:"fav.png"), backgroundColor: .blue)] 107 | cell.leftSwipeSettings.transition = .rotate3D 108 | 109 | //configure right buttons 110 | cell.rightButtons = [MGSwipeButton(title: "Delete", backgroundColor: .red), 111 | MGSwipeButton(title: "More",backgroundColor: .lightGray)] 112 | cell.rightSwipeSettings.transition = .rotate3D 113 | 114 | return cell 115 | } 116 | ``` 117 | 118 | In order to listen for button click events you can implement the optional MGSwipeTableCellDelegate, or if you are too lazy to do that, the MGSwipeButton class comes with a convenience block callback ;) 119 | 120 | ##### Objective-c 121 | ```objc 122 | [MGSwipeButton buttonWithTitle:@"More" backgroundColor:[UIColor lightGrayColor] callback:^BOOL(MGSwipeTableCell *sender) { 123 | NSLog(@"Convenience callback for swipe buttons!"); 124 | }] 125 | ``` 126 | ##### Swift 127 | ```swift 128 | MGSwipeButton(title: "Delete", backgroundColor: .red) { 129 | (sender: MGSwipeTableCell!) -> Bool in 130 | print("Convenience callback for swipe buttons!") 131 | return true 132 | } 133 | ``` 134 | 135 | ### Delegate 136 | 137 | MGSwipeTableCellDelegate is an optional delegate to configure swipe buttons or to receive triggered actions or another events. Buttons can be configured inline when the cell is created instead of using this delegate, but using the delegate improves memory usage since buttons are only created on demand. 138 | 139 | ```objc 140 | @protocol MGSwipeTableCellDelegate 141 | 142 | @optional 143 | /** 144 | * Delegate method to enable/disable swipe gestures 145 | * @return YES if swipe is allowed 146 | **/ 147 | -(BOOL) swipeTableCell:(MGSwipeTableCell*) cell canSwipe:(MGSwipeDirection) direction; 148 | /** 149 | * Delegate method invoked when the current swipe state changes 150 | @param state the current Swipe State 151 | @param gestureIsActive YES if the user swipe gesture is active. No if the uses has already ended the gesture 152 | **/ 153 | -(void) swipeTableCell:(MGSwipeTableCell*) cell didChangeSwipeState:(MGSwipeState) state gestureIsActive:(BOOL) gestureIsActive; 154 | /** 155 | * Called when the user clicks a swipe button or when a expandable button is automatically triggered 156 | * @return YES to autohide the current swipe buttons 157 | **/ 158 | -(BOOL) swipeTableCell:(MGSwipeTableCell*) cell tappedButtonAtIndex:(NSInteger) index direction:(MGSwipeDirection)direction fromExpansion:(BOOL) fromExpansion; 159 | /** 160 | * Delegate method to setup the swipe buttons and swipe/expansion settings 161 | * Buttons can be any kind of UIView but it's recommended to use the convenience MGSwipeButton class 162 | * Setting up buttons with this delegate instead of using cell properties improves memory usage because buttons are only created in demand 163 | * @param swipeTableCell the UITableViewCell to configure. You can get the indexPath using [tableView indexPathForCell:cell] 164 | * @param direction The swipe direction (left to right or right to left) 165 | * @param swipeSettings instance to configure the swipe transition and setting (optional) 166 | * @param expansionSettings instance to configure button expansions (optional) 167 | * @return Buttons array 168 | **/ 169 | -(NSArray*) swipeTableCell:(MGSwipeTableCell*) cell swipeButtonsForDirection:(MGSwipeDirection)direction 170 | swipeSettings:(MGSwipeSettings*) swipeSettings expansionSettings:(MGSwipeExpansionSettings*) expansionSettings; 171 | 172 | @end 173 | ``` 174 | 175 | ### Expandable buttons 176 | 177 | Buttons are not expandable by default. You can set up expandable buttons using cell.leftExpansion and cell.rightExpansion properties 178 | 179 | Expandable button events are triggered automatically when the user ends the swipe gesture and the expansion is active (configurable via threshold value). Triggered expandable buttons can bounce back to their initial position or fill the entire UITableViewCell, you can select the desired animation using fillOnTrigger property. 180 | 181 | ```objc 182 | @interface MGSwipeExpansionSettings: NSObject 183 | /** index of the expandable button (in the left or right buttons arrays) */ 184 | @property (nonatomic, assign) NSInteger buttonIndex; 185 | /** if true the button fills the cell on trigger, else it bounces back to its initial position */ 186 | @property (nonatomic, assign) BOOL fillOnTrigger; 187 | /** Size proportional threshold to trigger the expansion button. Default value 1.5 */ 188 | @property (nonatomic, assign) CGFloat threshold; 189 | @end 190 | ``` 191 | ### Rounded corners and swipe buttons 192 | MGSwipeTableCell supports rounded corners. Example: 193 | 194 | ```objc 195 | cell.layer.cornerRadius = 50 196 | cell.backgroundColor = UIColor.gray 197 | cell.clipsToBounds = true 198 | cell.swipeBackgroundColor = UIColor.gray 199 | ``` 200 |

201 | 202 | 203 | ## License 204 | 205 | The MIT License (MIT) 206 | 207 | Copyright (c) 2014 Imanol Fernandez @MortimerGoro 208 | 209 | Permission is hereby granted, free of charge, to any person obtaining a copy 210 | of this software and associated documentation files (the "Software"), to deal 211 | in the Software without restriction, including without limitation the rights 212 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 213 | copies of the Software, and to permit persons to whom the Software is 214 | furnished to do so, subject to the following conditions: 215 | 216 | The above copyright notice and this permission notice shall be included in all 217 | copies or substantial portions of the Software. 218 | 219 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 220 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 221 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 222 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 223 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 224 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 225 | SOFTWARE. 226 | -------------------------------------------------------------------------------- /demo/MGSwipeDemo/MGSwipeDemo.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /demo/MGSwipeDemo/MGSwipeDemo.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /demo/MGSwipeDemo/MGSwipeDemo/AppDelegate.h: -------------------------------------------------------------------------------- 1 | /* 2 | * MGSwipeTableCell is licensed under MIT licensed. See LICENSE.md file for more information. 3 | * Copyright (c) 2014 Imanol Fernandez @MortimerGoro 4 | */ 5 | 6 | #import 7 | 8 | @interface AppDelegate : UIResponder 9 | 10 | @property (strong, nonatomic) UIWindow *window; 11 | 12 | @end 13 | -------------------------------------------------------------------------------- /demo/MGSwipeDemo/MGSwipeDemo/AppDelegate.m: -------------------------------------------------------------------------------- 1 | /* 2 | * MGSwipeTableCell is licensed under MIT licensed. See LICENSE.md file for more information. 3 | * Copyright (c) 2014 Imanol Fernandez @MortimerGoro 4 | */ 5 | 6 | #import "AppDelegate.h" 7 | #import "DemoViewController.h" 8 | 9 | @implementation AppDelegate 10 | 11 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions 12 | { 13 | self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; 14 | 15 | DemoViewController * vc = [[DemoViewController alloc] init]; 16 | UINavigationController * navigation = [[UINavigationController alloc] initWithRootViewController:vc]; 17 | navigation.navigationBar.translucent = NO; 18 | self.window.rootViewController = navigation; 19 | 20 | self.window.backgroundColor = [UIColor whiteColor]; 21 | [self.window makeKeyAndVisible]; 22 | return YES; 23 | } 24 | 25 | - (void)applicationWillResignActive:(UIApplication *)application 26 | { 27 | // 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. 28 | // 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. 29 | } 30 | 31 | - (void)applicationDidEnterBackground:(UIApplication *)application 32 | { 33 | // 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. 34 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 35 | } 36 | 37 | - (void)applicationWillEnterForeground:(UIApplication *)application 38 | { 39 | // 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. 40 | } 41 | 42 | - (void)applicationDidBecomeActive:(UIApplication *)application 43 | { 44 | // 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. 45 | } 46 | 47 | - (void)applicationWillTerminate:(UIApplication *)application 48 | { 49 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 50 | } 51 | 52 | @end 53 | -------------------------------------------------------------------------------- /demo/MGSwipeDemo/MGSwipeDemo/DemoViewController.h: -------------------------------------------------------------------------------- 1 | /* 2 | * MGSwipeTableCell is licensed under MIT license. See LICENSE.md file for more information. 3 | * Copyright (c) 2014 Imanol Fernandez @MortimerGoro 4 | */ 5 | 6 | #import 7 | #import "MGSwipeTableCell.h" 8 | 9 | @interface DemoViewController : UIViewController 10 | 11 | @property (nonatomic, assign) BOOL testingStoryboardCell; 12 | @property (strong, nonatomic) IBOutlet UITableView *tableView; 13 | 14 | @end 15 | -------------------------------------------------------------------------------- /demo/MGSwipeDemo/MGSwipeDemo/DemoViewController.m: -------------------------------------------------------------------------------- 1 | /* 2 | * MGSwipeTableCell is licensed under MIT license. See LICENSE.md file for more information. 3 | * Copyright (c) 2014 Imanol Fernandez @MortimerGoro 4 | */ 5 | 6 | #import "DemoViewController.h" 7 | #import "TestData.h" 8 | #import "MGSwipeButton.h" 9 | 10 | #define TEST_USE_MG_DELEGATE 1 11 | 12 | @implementation DemoViewController 13 | { 14 | NSMutableArray * tests; 15 | UIBarButtonItem * prevButton; 16 | UITableViewCellAccessoryType accessory; 17 | UIImageView * background; //used for transparency test 18 | BOOL allowMultipleSwipe; 19 | } 20 | 21 | 22 | -(void) cancelTableEditClick: (id) sender 23 | { 24 | [_tableView setEditing: NO animated: YES]; 25 | self.navigationItem.rightBarButtonItem = prevButton; 26 | prevButton = nil; 27 | } 28 | 29 | - (void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex 30 | { 31 | if (buttonIndex == actionSheet.cancelButtonIndex) { 32 | return; 33 | } 34 | 35 | if (buttonIndex == 1) { 36 | tests = [TestData data]; 37 | [_tableView reloadData]; 38 | } 39 | else if (buttonIndex == 2) { 40 | _tableView.allowsMultipleSelectionDuringEditing = YES; 41 | [_tableView setEditing: YES animated: YES]; 42 | prevButton = self.navigationItem.rightBarButtonItem; 43 | self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:@"Cancel" style:UIBarButtonItemStyleDone target:self action:@selector(cancelTableEditClick:)]; 44 | } 45 | else if (buttonIndex == 3) { 46 | accessory++; 47 | if (accessory >=4) { 48 | accessory = 0; 49 | } 50 | [_tableView reloadData]; 51 | } 52 | else if (buttonIndex == 4) { 53 | if (background) { 54 | [background removeFromSuperview]; 55 | _tableView.backgroundColor = [UIColor whiteColor]; 56 | } 57 | else { 58 | background = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"background.jpg"]]; 59 | background.frame = self.view.bounds; 60 | background.contentMode = UIViewContentModeScaleToFill; 61 | background.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; 62 | [self.view insertSubview:background belowSubview:_tableView]; 63 | _tableView.backgroundColor = [UIColor clearColor]; 64 | } 65 | [_tableView reloadData]; 66 | } 67 | else if (buttonIndex == 5) { 68 | allowMultipleSwipe = !allowMultipleSwipe; 69 | [_tableView reloadData]; 70 | } 71 | else { 72 | UIStoryboard *sb = [UIStoryboard storyboardWithName:@"autolayout_test" bundle:nil]; 73 | DemoViewController *vc = [sb instantiateInitialViewController]; 74 | vc.testingStoryboardCell = YES; 75 | [self.navigationController pushViewController:vc animated:YES]; 76 | } 77 | } 78 | 79 | -(void) actionClick: (id) sender 80 | { 81 | 82 | UIActionSheet * sheet = [[UIActionSheet alloc] initWithTitle:@"Select action" delegate:self cancelButtonTitle:@"Cancel" destructiveButtonTitle:nil otherButtonTitles: nil]; 83 | [sheet addButtonWithTitle:@"Reload test"]; 84 | [sheet addButtonWithTitle:@"Multiselect test"]; 85 | [sheet addButtonWithTitle:@"Change accessory button"]; 86 | [sheet addButtonWithTitle:@"Transparency test"]; 87 | [sheet addButtonWithTitle: allowMultipleSwipe ? @"Single Swipe" : @"Multiple Swipe"]; 88 | if (!_testingStoryboardCell) { 89 | [sheet addButtonWithTitle:@"Storyboard test"]; 90 | } 91 | [sheet showInView:self.view]; 92 | } 93 | 94 | - (void)viewDidLoad 95 | { 96 | [super viewDidLoad]; 97 | 98 | tests = [TestData data]; 99 | self.title = @"MGSwipeCell"; 100 | 101 | if (!_testingStoryboardCell) { 102 | _tableView = [[UITableView alloc] initWithFrame:self.view.bounds style:UITableViewStylePlain]; 103 | _tableView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; 104 | _tableView.dataSource = self; 105 | _tableView.delegate = self; 106 | [self.view addSubview:_tableView]; 107 | } 108 | 109 | self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemAction target:self action:@selector(actionClick:)]; 110 | } 111 | 112 | 113 | -(NSArray *) createLeftButtons: (int) number 114 | { 115 | NSMutableArray * result = [NSMutableArray array]; 116 | UIColor * colors[3] = {[UIColor greenColor], 117 | [UIColor colorWithRed:0 green:0x99/255.0 blue:0xcc/255.0 alpha:1.0], 118 | [UIColor colorWithRed:0.59 green:0.29 blue:0.08 alpha:1.0]}; 119 | UIImage * icons[3] = {[UIImage imageNamed:@"check.png"], [UIImage imageNamed:@"fav.png"], [UIImage imageNamed:@"menu.png"]}; 120 | for (int i = 0; i < number; ++i) 121 | { 122 | MGSwipeButton * button = [MGSwipeButton buttonWithTitle:@"" icon:icons[i] backgroundColor:colors[i] padding:15 callback:^BOOL(MGSwipeTableCell * sender){ 123 | NSLog(@"Convenience callback received (left)."); 124 | return YES; 125 | }]; 126 | [result addObject:button]; 127 | } 128 | return result; 129 | } 130 | 131 | 132 | -(NSArray *) createRightButtons: (int) number 133 | { 134 | NSMutableArray * result = [NSMutableArray array]; 135 | NSString* titles[2] = {@"Delete", @"More"}; 136 | UIColor * colors[2] = {[UIColor redColor], [UIColor lightGrayColor]}; 137 | for (int i = 0; i < number; ++i) 138 | { 139 | MGSwipeButton * button = [MGSwipeButton buttonWithTitle:titles[i] backgroundColor:colors[i] callback:^BOOL(MGSwipeTableCell * sender){ 140 | NSLog(@"Convenience callback received (right)."); 141 | BOOL autoHide = i != 0; 142 | return autoHide; //Don't autohide in delete button to improve delete expansion animation 143 | }]; 144 | [result addObject:button]; 145 | } 146 | return result; 147 | } 148 | 149 | 150 | 151 | - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section; 152 | { 153 | return tests.count; 154 | } 155 | 156 | - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath 157 | { 158 | MGSwipeTableCell * cell; 159 | 160 | if (_testingStoryboardCell) { 161 | /** 162 | * Test using storyboard and prototype cell that uses autolayout 163 | **/ 164 | cell = [_tableView dequeueReusableCellWithIdentifier:@"prototypeCell"]; 165 | } 166 | else { 167 | /** 168 | * Test using programmatically created cells 169 | **/ 170 | static NSString * reuseIdentifier = @"programmaticCell"; 171 | cell = [_tableView dequeueReusableCellWithIdentifier:reuseIdentifier]; 172 | if (!cell) { 173 | cell = [[MGSwipeTableCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:reuseIdentifier]; 174 | } 175 | } 176 | 177 | TestData * data = [tests objectAtIndex:indexPath.row]; 178 | 179 | cell.textLabel.text = data.title; 180 | cell.textLabel.font = [UIFont systemFontOfSize:16]; 181 | cell.detailTextLabel.text = data.detailTitle; 182 | cell.accessoryType = accessory; 183 | cell.delegate = self; 184 | cell.allowsMultipleSwipe = allowMultipleSwipe; 185 | 186 | if (background) { //transparency test 187 | cell.backgroundColor = [UIColor clearColor]; 188 | cell.selectedBackgroundView = [[UIView alloc] init]; 189 | cell.selectedBackgroundView.backgroundColor = [[UIColor yellowColor] colorWithAlphaComponent:0.3]; 190 | cell.contentView.backgroundColor = [UIColor clearColor]; 191 | cell.swipeBackgroundColor = [UIColor clearColor]; 192 | cell.textLabel.textColor = [UIColor yellowColor]; 193 | cell.detailTextLabel.textColor = [UIColor yellowColor]; 194 | } 195 | 196 | #if !TEST_USE_MG_DELEGATE 197 | cell.leftSwipeSettings.transition = data.transition; 198 | cell.rightSwipeSettings.transition = data.transition; 199 | cell.leftExpansion.buttonIndex = data.leftExpandableIndex; 200 | cell.leftExpansion.fillOnTrigger = NO; 201 | cell.rightExpansion.buttonIndex = data.rightExpandableIndex; 202 | cell.rightExpansion.fillOnTrigger = YES; 203 | cell.leftButtons = [self createLeftButtons:data.leftButtonsCount]; 204 | cell.rightButtons = [self createRightButtons:data.rightButtonsCount]; 205 | #endif 206 | 207 | return cell; 208 | } 209 | 210 | #if TEST_USE_MG_DELEGATE 211 | -(NSArray*) swipeTableCell:(MGSwipeTableCell*) cell swipeButtonsForDirection:(MGSwipeDirection)direction 212 | swipeSettings:(MGSwipeSettings*) swipeSettings expansionSettings:(MGSwipeExpansionSettings*) expansionSettings; 213 | { 214 | TestData * data = [tests objectAtIndex:[_tableView indexPathForCell:cell].row]; 215 | swipeSettings.transition = data.transition; 216 | 217 | if (direction == MGSwipeDirectionLeftToRight) { 218 | expansionSettings.buttonIndex = data.leftExpandableIndex; 219 | expansionSettings.fillOnTrigger = NO; 220 | return [self createLeftButtons:data.leftButtonsCount]; 221 | } 222 | else { 223 | expansionSettings.buttonIndex = data.rightExpandableIndex; 224 | expansionSettings.fillOnTrigger = YES; 225 | return [self createRightButtons:data.rightButtonsCount]; 226 | } 227 | } 228 | #endif 229 | 230 | 231 | - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath 232 | { 233 | return 60; 234 | } 235 | 236 | -(BOOL) swipeTableCell:(MGSwipeTableCell*) cell tappedButtonAtIndex:(NSInteger) index direction:(MGSwipeDirection)direction fromExpansion:(BOOL) fromExpansion 237 | { 238 | NSLog(@"Delegate: button tapped, %@ position, index %d, from Expansion: %@", 239 | direction == MGSwipeDirectionLeftToRight ? @"left" : @"right", (int)index, fromExpansion ? @"YES" : @"NO"); 240 | 241 | if (direction == MGSwipeDirectionRightToLeft && index == 0) { 242 | //delete button 243 | NSIndexPath * path = [_tableView indexPathForCell:cell]; 244 | [tests removeObjectAtIndex:path.row]; 245 | [_tableView deleteRowsAtIndexPaths:@[path] withRowAnimation:UITableViewRowAnimationLeft]; 246 | return NO; //Don't autohide to improve delete expansion animation 247 | } 248 | 249 | return YES; 250 | } 251 | 252 | -(void) tableView:(UITableView *)tableView accessoryButtonTappedForRowWithIndexPath:(NSIndexPath *)indexPath 253 | { 254 | NSLog(@"Tapped accessory button"); 255 | } 256 | 257 | @end 258 | -------------------------------------------------------------------------------- /demo/MGSwipeDemo/MGSwipeDemo/Images.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "29x29", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "40x40", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "60x60", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "60x60", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "ipad", 25 | "size" : "29x29", 26 | "scale" : "1x" 27 | }, 28 | { 29 | "idiom" : "ipad", 30 | "size" : "29x29", 31 | "scale" : "2x" 32 | }, 33 | { 34 | "idiom" : "ipad", 35 | "size" : "40x40", 36 | "scale" : "1x" 37 | }, 38 | { 39 | "idiom" : "ipad", 40 | "size" : "40x40", 41 | "scale" : "2x" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "size" : "76x76", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "size" : "76x76", 51 | "scale" : "2x" 52 | } 53 | ], 54 | "info" : { 55 | "version" : 1, 56 | "author" : "xcode" 57 | } 58 | } -------------------------------------------------------------------------------- /demo/MGSwipeDemo/MGSwipeDemo/Images.xcassets/LaunchImage.launchimage/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "orientation" : "portrait", 5 | "idiom" : "iphone", 6 | "extent" : "full-screen", 7 | "minimum-system-version" : "7.0", 8 | "scale" : "2x" 9 | }, 10 | { 11 | "orientation" : "portrait", 12 | "idiom" : "iphone", 13 | "subtype" : "retina4", 14 | "extent" : "full-screen", 15 | "minimum-system-version" : "7.0", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "orientation" : "portrait", 20 | "idiom" : "ipad", 21 | "extent" : "full-screen", 22 | "minimum-system-version" : "7.0", 23 | "scale" : "1x" 24 | }, 25 | { 26 | "orientation" : "landscape", 27 | "idiom" : "ipad", 28 | "extent" : "full-screen", 29 | "minimum-system-version" : "7.0", 30 | "scale" : "1x" 31 | }, 32 | { 33 | "orientation" : "portrait", 34 | "idiom" : "ipad", 35 | "extent" : "full-screen", 36 | "minimum-system-version" : "7.0", 37 | "scale" : "2x" 38 | }, 39 | { 40 | "orientation" : "landscape", 41 | "idiom" : "ipad", 42 | "extent" : "full-screen", 43 | "minimum-system-version" : "7.0", 44 | "scale" : "2x" 45 | } 46 | ], 47 | "info" : { 48 | "version" : 1, 49 | "author" : "xcode" 50 | } 51 | } -------------------------------------------------------------------------------- /demo/MGSwipeDemo/MGSwipeDemo/LaunchScreen.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 23 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /demo/MGSwipeDemo/MGSwipeDemo/MGSwipeDemo-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleDisplayName 8 | ${PRODUCT_NAME} 9 | CFBundleExecutable 10 | ${EXECUTABLE_NAME} 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 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 | UILaunchStoryboardName 28 | LaunchScreen 29 | UIRequiredDeviceCapabilities 30 | 31 | armv7 32 | 33 | UISupportedInterfaceOrientations 34 | 35 | UIInterfaceOrientationPortrait 36 | UIInterfaceOrientationLandscapeLeft 37 | UIInterfaceOrientationLandscapeRight 38 | 39 | UISupportedInterfaceOrientations~ipad 40 | 41 | UIInterfaceOrientationPortrait 42 | UIInterfaceOrientationPortraitUpsideDown 43 | UIInterfaceOrientationLandscapeLeft 44 | UIInterfaceOrientationLandscapeRight 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /demo/MGSwipeDemo/MGSwipeDemo/MGSwipeDemo-Prefix.pch: -------------------------------------------------------------------------------- 1 | // 2 | // Prefix header 3 | // 4 | // The contents of this file are implicitly included at the beginning of every source file. 5 | // 6 | 7 | #import 8 | 9 | #ifndef __IPHONE_3_0 10 | #warning "This project uses features only available in iOS SDK 3.0 and later." 11 | #endif 12 | 13 | #ifdef __OBJC__ 14 | #import 15 | #import 16 | #endif 17 | -------------------------------------------------------------------------------- /demo/MGSwipeDemo/MGSwipeDemo/TestData.h: -------------------------------------------------------------------------------- 1 | /* 2 | * MGSwipeTableCell is licensed under MIT licensed. See LICENSE.md file for more information. 3 | * Copyright (c) 2014 Imanol Fernandez @MortimerGoro 4 | */ 5 | 6 | #import "MGSwipeTableCell.h" 7 | 8 | @interface TestData : NSObject 9 | 10 | @property (nonatomic) MGSwipeTransition transition; 11 | @property (nonatomic) int leftButtonsCount; 12 | @property (nonatomic) int rightButtonsCount; 13 | @property (nonatomic) int leftExpandableIndex; 14 | @property (nonatomic) int rightExpandableIndex; 15 | 16 | 17 | -(NSString *) title; 18 | -(NSString *) detailTitle; 19 | 20 | +(NSMutableArray*) data; 21 | 22 | 23 | @end 24 | -------------------------------------------------------------------------------- /demo/MGSwipeDemo/MGSwipeDemo/TestData.m: -------------------------------------------------------------------------------- 1 | /* 2 | * MGSwipeTableCell is licensed under MIT licensed. See LICENSE.md file for more information. 3 | * Copyright (c) 2014 Imanol Fernandez @MortimerGoro 4 | */ 5 | 6 | #import "TestData.h" 7 | #import "MGSwipeButton.h" 8 | 9 | @implementation TestData 10 | 11 | 12 | -(NSString *) title 13 | { 14 | NSString * modeTitle; 15 | switch (_transition) { 16 | case MGSwipeTransition3D: modeTitle = @"3D"; break; 17 | case MGSwipeTransitionClipCenter: modeTitle = @"clip"; break; 18 | case MGSwipeTransitionDrag: modeTitle = @"drag"; break; 19 | case MGSwipeTransitionBorder: modeTitle = @"border"; break; 20 | case MGSwipeTransitionStatic: modeTitle = @"static";break; 21 | } 22 | modeTitle = [NSString stringWithFormat:@"%@ transition", modeTitle]; 23 | NSString * buttons; 24 | if (_leftButtonsCount <= 0 && _rightButtonsCount <= 0) { 25 | return @"No buttons"; 26 | } 27 | else if (_leftButtonsCount > 0 && _rightButtonsCount > 0) { 28 | buttons = [NSString stringWithFormat:@"%d left, %d right", _leftButtonsCount, _rightButtonsCount]; 29 | } 30 | else if (_leftButtonsCount > 0) { 31 | buttons = [NSString stringWithFormat:@"%d left", _leftButtonsCount]; 32 | } 33 | else { 34 | buttons = [NSString stringWithFormat:@"%d right", _rightButtonsCount]; 35 | } 36 | 37 | return [NSString stringWithFormat:@"%@, %@", buttons, modeTitle]; 38 | } 39 | 40 | -(NSString *) detailTitle 41 | { 42 | return _leftExpandableIndex >=0 || _rightExpandableIndex >= 0 ? @"Expandable" : @"Not expandable"; 43 | } 44 | 45 | 46 | +(NSMutableArray *) data 47 | { 48 | NSMutableArray * tests = [NSMutableArray array]; 49 | 50 | MGSwipeTransition transitions[] = {MGSwipeTransitionBorder, MGSwipeTransitionStatic, MGSwipeTransitionClipCenter, MGSwipeTransitionDrag, MGSwipeTransition3D}; 51 | int numTransitions = sizeof(transitions)/sizeof(MGSwipeTransition); 52 | 53 | int buttonCombinations[] = {3,2, 2,1, 0,2, 2,0}; 54 | int numCombinations = sizeof(buttonCombinations) / (sizeof(int) * 2); 55 | 56 | int expansionCombinations[] = {-1, -1, 0, 0}; 57 | int numExpansions = sizeof(expansionCombinations)/ (sizeof(int) * 2); 58 | 59 | for (int i = 0; i < numCombinations; ++i) { 60 | for (int j = 0; j < numTransitions; ++j) { 61 | for (int z = 0; z < numExpansions; ++z) { 62 | TestData * data = [[TestData alloc] init]; 63 | data.leftButtonsCount = buttonCombinations[2 * i]; 64 | data.rightButtonsCount = buttonCombinations[2 * i + 1]; 65 | data.transition = transitions[j]; 66 | data.leftExpandableIndex = data.leftButtonsCount ? expansionCombinations[2 * z] : -1; 67 | data.rightExpandableIndex = data.rightButtonsCount ? expansionCombinations[2 * z + 1] : -1; 68 | [tests addObject:data]; 69 | } 70 | } 71 | } 72 | return tests; 73 | } 74 | 75 | @end -------------------------------------------------------------------------------- /demo/MGSwipeDemo/MGSwipeDemo/assets/background.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MortimerGoro/MGSwipeTableCell/2d02e9d228b4684eda87ea696cdb3dd77713e782/demo/MGSwipeDemo/MGSwipeDemo/assets/background.jpg -------------------------------------------------------------------------------- /demo/MGSwipeDemo/MGSwipeDemo/assets/cat@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MortimerGoro/MGSwipeTableCell/2d02e9d228b4684eda87ea696cdb3dd77713e782/demo/MGSwipeDemo/MGSwipeDemo/assets/cat@2x.png -------------------------------------------------------------------------------- /demo/MGSwipeDemo/MGSwipeDemo/assets/check@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MortimerGoro/MGSwipeTableCell/2d02e9d228b4684eda87ea696cdb3dd77713e782/demo/MGSwipeDemo/MGSwipeDemo/assets/check@2x.png -------------------------------------------------------------------------------- /demo/MGSwipeDemo/MGSwipeDemo/assets/fav@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MortimerGoro/MGSwipeTableCell/2d02e9d228b4684eda87ea696cdb3dd77713e782/demo/MGSwipeDemo/MGSwipeDemo/assets/fav@2x.png -------------------------------------------------------------------------------- /demo/MGSwipeDemo/MGSwipeDemo/assets/menu@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MortimerGoro/MGSwipeTableCell/2d02e9d228b4684eda87ea696cdb3dd77713e782/demo/MGSwipeDemo/MGSwipeDemo/assets/menu@2x.png -------------------------------------------------------------------------------- /demo/MGSwipeDemo/MGSwipeDemo/autolayout_test.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | -------------------------------------------------------------------------------- /demo/MGSwipeDemo/MGSwipeDemo/en.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* Localized versions of Info.plist keys */ 2 | 3 | -------------------------------------------------------------------------------- /demo/MGSwipeDemo/MGSwipeDemo/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // MGSwipeDemo 4 | // 5 | // Created by Imanol Fernandez Gorostizag on 09/08/14. 6 | // Copyright (c) 2014 Imanol Fernandez Gorostizaga. 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 | -------------------------------------------------------------------------------- /demo/MailAppDemo/MailAppDemo.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 11754E2919D5C1EA009CE7B5 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 11754E2819D5C1EA009CE7B5 /* main.m */; }; 11 | 11754E2C19D5C1EA009CE7B5 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 11754E2B19D5C1EA009CE7B5 /* AppDelegate.m */; }; 12 | 11754E2F19D5C1EA009CE7B5 /* MailViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 11754E2E19D5C1EA009CE7B5 /* MailViewController.m */; }; 13 | 11754E3419D5C1EA009CE7B5 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 11754E3319D5C1EA009CE7B5 /* Images.xcassets */; }; 14 | 11754E4E19D5C3A1009CE7B5 /* MailTableCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 11754E4D19D5C3A1009CE7B5 /* MailTableCell.m */; }; 15 | 11754E5419D5C3E5009CE7B5 /* MGSwipeButton.m in Sources */ = {isa = PBXBuildFile; fileRef = 11754E5219D5C3E5009CE7B5 /* MGSwipeButton.m */; }; 16 | 11754E5519D5C3E5009CE7B5 /* MGSwipeTableCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 11754E5319D5C3E5009CE7B5 /* MGSwipeTableCell.m */; }; 17 | /* End PBXBuildFile section */ 18 | 19 | /* Begin PBXFileReference section */ 20 | 11754E2319D5C1EA009CE7B5 /* MailAppDemo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = MailAppDemo.app; sourceTree = BUILT_PRODUCTS_DIR; }; 21 | 11754E2719D5C1EA009CE7B5 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 22 | 11754E2819D5C1EA009CE7B5 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 23 | 11754E2A19D5C1EA009CE7B5 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 24 | 11754E2B19D5C1EA009CE7B5 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; 25 | 11754E2D19D5C1EA009CE7B5 /* MailViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MailViewController.h; sourceTree = ""; }; 26 | 11754E2E19D5C1EA009CE7B5 /* MailViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MailViewController.m; sourceTree = ""; }; 27 | 11754E3319D5C1EA009CE7B5 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; 28 | 11754E4C19D5C3A1009CE7B5 /* MailTableCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MailTableCell.h; sourceTree = ""; }; 29 | 11754E4D19D5C3A1009CE7B5 /* MailTableCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MailTableCell.m; sourceTree = ""; }; 30 | 11754E5019D5C3E5009CE7B5 /* MGSwipeButton.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MGSwipeButton.h; path = ../../MGSwipeTableCell/MGSwipeButton.h; sourceTree = ""; }; 31 | 11754E5119D5C3E5009CE7B5 /* MGSwipeTableCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MGSwipeTableCell.h; path = ../../MGSwipeTableCell/MGSwipeTableCell.h; sourceTree = ""; }; 32 | 11754E5219D5C3E5009CE7B5 /* MGSwipeButton.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = MGSwipeButton.m; path = ../../MGSwipeTableCell/MGSwipeButton.m; sourceTree = ""; }; 33 | 11754E5319D5C3E5009CE7B5 /* MGSwipeTableCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = MGSwipeTableCell.m; path = ../../MGSwipeTableCell/MGSwipeTableCell.m; sourceTree = ""; }; 34 | /* End PBXFileReference section */ 35 | 36 | /* Begin PBXFrameworksBuildPhase section */ 37 | 11754E2019D5C1EA009CE7B5 /* Frameworks */ = { 38 | isa = PBXFrameworksBuildPhase; 39 | buildActionMask = 2147483647; 40 | files = ( 41 | ); 42 | runOnlyForDeploymentPostprocessing = 0; 43 | }; 44 | /* End PBXFrameworksBuildPhase section */ 45 | 46 | /* Begin PBXGroup section */ 47 | 11754E1A19D5C1EA009CE7B5 = { 48 | isa = PBXGroup; 49 | children = ( 50 | 11754E4F19D5C3CE009CE7B5 /* MGSwipeTableCell */, 51 | 11754E2519D5C1EA009CE7B5 /* MailAppDemo */, 52 | 11754E2419D5C1EA009CE7B5 /* Products */, 53 | ); 54 | sourceTree = ""; 55 | }; 56 | 11754E2419D5C1EA009CE7B5 /* Products */ = { 57 | isa = PBXGroup; 58 | children = ( 59 | 11754E2319D5C1EA009CE7B5 /* MailAppDemo.app */, 60 | ); 61 | name = Products; 62 | sourceTree = ""; 63 | }; 64 | 11754E2519D5C1EA009CE7B5 /* MailAppDemo */ = { 65 | isa = PBXGroup; 66 | children = ( 67 | 11754E2A19D5C1EA009CE7B5 /* AppDelegate.h */, 68 | 11754E2B19D5C1EA009CE7B5 /* AppDelegate.m */, 69 | 11754E2D19D5C1EA009CE7B5 /* MailViewController.h */, 70 | 11754E2E19D5C1EA009CE7B5 /* MailViewController.m */, 71 | 11754E4C19D5C3A1009CE7B5 /* MailTableCell.h */, 72 | 11754E4D19D5C3A1009CE7B5 /* MailTableCell.m */, 73 | 11754E3319D5C1EA009CE7B5 /* Images.xcassets */, 74 | 11754E2619D5C1EA009CE7B5 /* Supporting Files */, 75 | ); 76 | path = MailAppDemo; 77 | sourceTree = ""; 78 | }; 79 | 11754E2619D5C1EA009CE7B5 /* Supporting Files */ = { 80 | isa = PBXGroup; 81 | children = ( 82 | 11754E2719D5C1EA009CE7B5 /* Info.plist */, 83 | 11754E2819D5C1EA009CE7B5 /* main.m */, 84 | ); 85 | name = "Supporting Files"; 86 | sourceTree = ""; 87 | }; 88 | 11754E4F19D5C3CE009CE7B5 /* MGSwipeTableCell */ = { 89 | isa = PBXGroup; 90 | children = ( 91 | 11754E5019D5C3E5009CE7B5 /* MGSwipeButton.h */, 92 | 11754E5219D5C3E5009CE7B5 /* MGSwipeButton.m */, 93 | 11754E5119D5C3E5009CE7B5 /* MGSwipeTableCell.h */, 94 | 11754E5319D5C3E5009CE7B5 /* MGSwipeTableCell.m */, 95 | ); 96 | name = MGSwipeTableCell; 97 | sourceTree = ""; 98 | }; 99 | /* End PBXGroup section */ 100 | 101 | /* Begin PBXNativeTarget section */ 102 | 11754E2219D5C1EA009CE7B5 /* MailAppDemo */ = { 103 | isa = PBXNativeTarget; 104 | buildConfigurationList = 11754E4619D5C1EA009CE7B5 /* Build configuration list for PBXNativeTarget "MailAppDemo" */; 105 | buildPhases = ( 106 | 11754E1F19D5C1EA009CE7B5 /* Sources */, 107 | 11754E2019D5C1EA009CE7B5 /* Frameworks */, 108 | 11754E2119D5C1EA009CE7B5 /* Resources */, 109 | ); 110 | buildRules = ( 111 | ); 112 | dependencies = ( 113 | ); 114 | name = MailAppDemo; 115 | productName = MailAppDemo; 116 | productReference = 11754E2319D5C1EA009CE7B5 /* MailAppDemo.app */; 117 | productType = "com.apple.product-type.application"; 118 | }; 119 | /* End PBXNativeTarget section */ 120 | 121 | /* Begin PBXProject section */ 122 | 11754E1B19D5C1EA009CE7B5 /* Project object */ = { 123 | isa = PBXProject; 124 | attributes = { 125 | LastUpgradeCheck = 0900; 126 | ORGANIZATIONNAME = Mortimer; 127 | TargetAttributes = { 128 | 11754E2219D5C1EA009CE7B5 = { 129 | CreatedOnToolsVersion = 6.0.1; 130 | }; 131 | }; 132 | }; 133 | buildConfigurationList = 11754E1E19D5C1EA009CE7B5 /* Build configuration list for PBXProject "MailAppDemo" */; 134 | compatibilityVersion = "Xcode 3.2"; 135 | developmentRegion = English; 136 | hasScannedForEncodings = 0; 137 | knownRegions = ( 138 | English, 139 | en, 140 | Base, 141 | ); 142 | mainGroup = 11754E1A19D5C1EA009CE7B5; 143 | productRefGroup = 11754E2419D5C1EA009CE7B5 /* Products */; 144 | projectDirPath = ""; 145 | projectRoot = ""; 146 | targets = ( 147 | 11754E2219D5C1EA009CE7B5 /* MailAppDemo */, 148 | ); 149 | }; 150 | /* End PBXProject section */ 151 | 152 | /* Begin PBXResourcesBuildPhase section */ 153 | 11754E2119D5C1EA009CE7B5 /* Resources */ = { 154 | isa = PBXResourcesBuildPhase; 155 | buildActionMask = 2147483647; 156 | files = ( 157 | 11754E3419D5C1EA009CE7B5 /* Images.xcassets in Resources */, 158 | ); 159 | runOnlyForDeploymentPostprocessing = 0; 160 | }; 161 | /* End PBXResourcesBuildPhase section */ 162 | 163 | /* Begin PBXSourcesBuildPhase section */ 164 | 11754E1F19D5C1EA009CE7B5 /* Sources */ = { 165 | isa = PBXSourcesBuildPhase; 166 | buildActionMask = 2147483647; 167 | files = ( 168 | 11754E2F19D5C1EA009CE7B5 /* MailViewController.m in Sources */, 169 | 11754E5519D5C3E5009CE7B5 /* MGSwipeTableCell.m in Sources */, 170 | 11754E4E19D5C3A1009CE7B5 /* MailTableCell.m in Sources */, 171 | 11754E5419D5C3E5009CE7B5 /* MGSwipeButton.m in Sources */, 172 | 11754E2C19D5C1EA009CE7B5 /* AppDelegate.m in Sources */, 173 | 11754E2919D5C1EA009CE7B5 /* main.m in Sources */, 174 | ); 175 | runOnlyForDeploymentPostprocessing = 0; 176 | }; 177 | /* End PBXSourcesBuildPhase section */ 178 | 179 | /* Begin XCBuildConfiguration section */ 180 | 11754E4419D5C1EA009CE7B5 /* Debug */ = { 181 | isa = XCBuildConfiguration; 182 | buildSettings = { 183 | ALWAYS_SEARCH_USER_PATHS = NO; 184 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 185 | CLANG_CXX_LIBRARY = "libc++"; 186 | CLANG_ENABLE_MODULES = YES; 187 | CLANG_ENABLE_OBJC_ARC = YES; 188 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 189 | CLANG_WARN_BOOL_CONVERSION = YES; 190 | CLANG_WARN_COMMA = YES; 191 | CLANG_WARN_CONSTANT_CONVERSION = YES; 192 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 193 | CLANG_WARN_EMPTY_BODY = YES; 194 | CLANG_WARN_ENUM_CONVERSION = YES; 195 | CLANG_WARN_INFINITE_RECURSION = YES; 196 | CLANG_WARN_INT_CONVERSION = YES; 197 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 198 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 199 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 200 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 201 | CLANG_WARN_STRICT_PROTOTYPES = YES; 202 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 203 | CLANG_WARN_UNREACHABLE_CODE = YES; 204 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 205 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 206 | COPY_PHASE_STRIP = NO; 207 | ENABLE_STRICT_OBJC_MSGSEND = YES; 208 | ENABLE_TESTABILITY = YES; 209 | GCC_C_LANGUAGE_STANDARD = gnu99; 210 | GCC_DYNAMIC_NO_PIC = NO; 211 | GCC_NO_COMMON_BLOCKS = YES; 212 | GCC_OPTIMIZATION_LEVEL = 0; 213 | GCC_PREPROCESSOR_DEFINITIONS = ( 214 | "DEBUG=1", 215 | "$(inherited)", 216 | ); 217 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 218 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 219 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 220 | GCC_WARN_UNDECLARED_SELECTOR = YES; 221 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 222 | GCC_WARN_UNUSED_FUNCTION = YES; 223 | GCC_WARN_UNUSED_VARIABLE = YES; 224 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 225 | MTL_ENABLE_DEBUG_INFO = YES; 226 | ONLY_ACTIVE_ARCH = YES; 227 | SDKROOT = iphoneos; 228 | TARGETED_DEVICE_FAMILY = "1,2"; 229 | }; 230 | name = Debug; 231 | }; 232 | 11754E4519D5C1EA009CE7B5 /* Release */ = { 233 | isa = XCBuildConfiguration; 234 | buildSettings = { 235 | ALWAYS_SEARCH_USER_PATHS = NO; 236 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 237 | CLANG_CXX_LIBRARY = "libc++"; 238 | CLANG_ENABLE_MODULES = YES; 239 | CLANG_ENABLE_OBJC_ARC = YES; 240 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 241 | CLANG_WARN_BOOL_CONVERSION = YES; 242 | CLANG_WARN_COMMA = YES; 243 | CLANG_WARN_CONSTANT_CONVERSION = YES; 244 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 245 | CLANG_WARN_EMPTY_BODY = YES; 246 | CLANG_WARN_ENUM_CONVERSION = YES; 247 | CLANG_WARN_INFINITE_RECURSION = YES; 248 | CLANG_WARN_INT_CONVERSION = YES; 249 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 250 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 251 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 252 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 253 | CLANG_WARN_STRICT_PROTOTYPES = YES; 254 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 255 | CLANG_WARN_UNREACHABLE_CODE = YES; 256 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 257 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 258 | COPY_PHASE_STRIP = YES; 259 | ENABLE_NS_ASSERTIONS = NO; 260 | ENABLE_STRICT_OBJC_MSGSEND = YES; 261 | GCC_C_LANGUAGE_STANDARD = gnu99; 262 | GCC_NO_COMMON_BLOCKS = YES; 263 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 264 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 265 | GCC_WARN_UNDECLARED_SELECTOR = YES; 266 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 267 | GCC_WARN_UNUSED_FUNCTION = YES; 268 | GCC_WARN_UNUSED_VARIABLE = YES; 269 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 270 | MTL_ENABLE_DEBUG_INFO = NO; 271 | SDKROOT = iphoneos; 272 | TARGETED_DEVICE_FAMILY = "1,2"; 273 | VALIDATE_PRODUCT = YES; 274 | }; 275 | name = Release; 276 | }; 277 | 11754E4719D5C1EA009CE7B5 /* Debug */ = { 278 | isa = XCBuildConfiguration; 279 | buildSettings = { 280 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 281 | ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; 282 | ENABLE_BITCODE = NO; 283 | INFOPLIST_FILE = MailAppDemo/Info.plist; 284 | IPHONEOS_DEPLOYMENT_TARGET = 10.0; 285 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 286 | PRODUCT_BUNDLE_IDENTIFIER = "Mortimer.$(PRODUCT_NAME:rfc1034identifier)"; 287 | PRODUCT_NAME = "$(TARGET_NAME)"; 288 | }; 289 | name = Debug; 290 | }; 291 | 11754E4819D5C1EA009CE7B5 /* Release */ = { 292 | isa = XCBuildConfiguration; 293 | buildSettings = { 294 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 295 | ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; 296 | ENABLE_BITCODE = NO; 297 | INFOPLIST_FILE = MailAppDemo/Info.plist; 298 | IPHONEOS_DEPLOYMENT_TARGET = 10.0; 299 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 300 | PRODUCT_BUNDLE_IDENTIFIER = "Mortimer.$(PRODUCT_NAME:rfc1034identifier)"; 301 | PRODUCT_NAME = "$(TARGET_NAME)"; 302 | }; 303 | name = Release; 304 | }; 305 | /* End XCBuildConfiguration section */ 306 | 307 | /* Begin XCConfigurationList section */ 308 | 11754E1E19D5C1EA009CE7B5 /* Build configuration list for PBXProject "MailAppDemo" */ = { 309 | isa = XCConfigurationList; 310 | buildConfigurations = ( 311 | 11754E4419D5C1EA009CE7B5 /* Debug */, 312 | 11754E4519D5C1EA009CE7B5 /* Release */, 313 | ); 314 | defaultConfigurationIsVisible = 0; 315 | defaultConfigurationName = Release; 316 | }; 317 | 11754E4619D5C1EA009CE7B5 /* Build configuration list for PBXNativeTarget "MailAppDemo" */ = { 318 | isa = XCConfigurationList; 319 | buildConfigurations = ( 320 | 11754E4719D5C1EA009CE7B5 /* Debug */, 321 | 11754E4819D5C1EA009CE7B5 /* Release */, 322 | ); 323 | defaultConfigurationIsVisible = 0; 324 | defaultConfigurationName = Release; 325 | }; 326 | /* End XCConfigurationList section */ 327 | }; 328 | rootObject = 11754E1B19D5C1EA009CE7B5 /* Project object */; 329 | } 330 | -------------------------------------------------------------------------------- /demo/MailAppDemo/MailAppDemo.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /demo/MailAppDemo/MailAppDemo.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /demo/MailAppDemo/MailAppDemo/AppDelegate.h: -------------------------------------------------------------------------------- 1 | /* 2 | * MGSwipeTableCell is licensed under MIT license. See LICENSE.md file for more information. 3 | * Copyright (c) 2014 Imanol Fernandez @MortimerGoro 4 | */ 5 | 6 | #import 7 | 8 | @interface AppDelegate : UIResponder 9 | 10 | @property (strong, nonatomic) UIWindow *window; 11 | 12 | 13 | @end 14 | 15 | -------------------------------------------------------------------------------- /demo/MailAppDemo/MailAppDemo/AppDelegate.m: -------------------------------------------------------------------------------- 1 | /* 2 | * MGSwipeTableCell is licensed under MIT license. See LICENSE.md file for more information. 3 | * Copyright (c) 2014 Imanol Fernandez @MortimerGoro 4 | */ 5 | 6 | #import "AppDelegate.h" 7 | #import "MailViewController.h" 8 | 9 | @implementation AppDelegate 10 | 11 | 12 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 13 | 14 | self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; 15 | 16 | MailViewController * vc = [[MailViewController alloc] init]; 17 | UINavigationController * navigation = [[UINavigationController alloc] initWithRootViewController:vc]; 18 | navigation.navigationBar.translucent = NO; 19 | self.window.rootViewController = navigation; 20 | 21 | self.window.backgroundColor = [UIColor whiteColor]; 22 | [self.window makeKeyAndVisible]; 23 | return YES; 24 | } 25 | 26 | - (void)applicationWillResignActive:(UIApplication *)application { 27 | // 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. 28 | // 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. 29 | } 30 | 31 | - (void)applicationDidEnterBackground:(UIApplication *)application { 32 | // 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. 33 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 34 | } 35 | 36 | - (void)applicationWillEnterForeground:(UIApplication *)application { 37 | // 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. 38 | } 39 | 40 | - (void)applicationDidBecomeActive:(UIApplication *)application { 41 | // 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. 42 | } 43 | 44 | - (void)applicationWillTerminate:(UIApplication *)application { 45 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 46 | } 47 | 48 | @end 49 | -------------------------------------------------------------------------------- /demo/MailAppDemo/MailAppDemo/Images.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "29x29", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "29x29", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "40x40", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "40x40", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "60x60", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "60x60", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "idiom" : "ipad", 35 | "size" : "29x29", 36 | "scale" : "1x" 37 | }, 38 | { 39 | "idiom" : "ipad", 40 | "size" : "29x29", 41 | "scale" : "2x" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "size" : "40x40", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "size" : "40x40", 51 | "scale" : "2x" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "size" : "76x76", 56 | "scale" : "1x" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "size" : "76x76", 61 | "scale" : "2x" 62 | } 63 | ], 64 | "info" : { 65 | "version" : 1, 66 | "author" : "xcode" 67 | } 68 | } -------------------------------------------------------------------------------- /demo/MailAppDemo/MailAppDemo/Images.xcassets/LaunchImage.launchimage/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "scale" : "1x", 6 | "orientation" : "portrait" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "scale" : "2x", 11 | "orientation" : "portrait" 12 | }, 13 | { 14 | "orientation" : "portrait", 15 | "idiom" : "iphone", 16 | "subtype" : "retina4", 17 | "scale" : "2x" 18 | }, 19 | { 20 | "orientation" : "portrait", 21 | "idiom" : "iphone", 22 | "minimum-system-version" : "7.0", 23 | "scale" : "2x" 24 | }, 25 | { 26 | "orientation" : "portrait", 27 | "idiom" : "iphone", 28 | "minimum-system-version" : "7.0", 29 | "subtype" : "retina4", 30 | "scale" : "2x" 31 | }, 32 | { 33 | "orientation" : "portrait", 34 | "idiom" : "ipad", 35 | "extent" : "to-status-bar", 36 | "scale" : "1x" 37 | }, 38 | { 39 | "orientation" : "portrait", 40 | "idiom" : "ipad", 41 | "extent" : "to-status-bar", 42 | "scale" : "2x" 43 | }, 44 | { 45 | "orientation" : "landscape", 46 | "idiom" : "ipad", 47 | "extent" : "to-status-bar", 48 | "scale" : "1x" 49 | }, 50 | { 51 | "orientation" : "landscape", 52 | "idiom" : "ipad", 53 | "extent" : "to-status-bar", 54 | "scale" : "2x" 55 | }, 56 | { 57 | "orientation" : "portrait", 58 | "idiom" : "ipad", 59 | "minimum-system-version" : "7.0", 60 | "extent" : "full-screen", 61 | "scale" : "1x" 62 | }, 63 | { 64 | "orientation" : "portrait", 65 | "idiom" : "ipad", 66 | "minimum-system-version" : "7.0", 67 | "extent" : "full-screen", 68 | "scale" : "2x" 69 | }, 70 | { 71 | "orientation" : "landscape", 72 | "idiom" : "ipad", 73 | "minimum-system-version" : "7.0", 74 | "extent" : "full-screen", 75 | "scale" : "1x" 76 | }, 77 | { 78 | "orientation" : "landscape", 79 | "idiom" : "ipad", 80 | "minimum-system-version" : "7.0", 81 | "extent" : "full-screen", 82 | "scale" : "2x" 83 | } 84 | ], 85 | "info" : { 86 | "version" : 1, 87 | "author" : "xcode" 88 | } 89 | } -------------------------------------------------------------------------------- /demo/MailAppDemo/MailAppDemo/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIRequiredDeviceCapabilities 28 | 29 | armv7 30 | 31 | UISupportedInterfaceOrientations 32 | 33 | UIInterfaceOrientationPortrait 34 | UIInterfaceOrientationLandscapeLeft 35 | UIInterfaceOrientationLandscapeRight 36 | 37 | UISupportedInterfaceOrientations~ipad 38 | 39 | UIInterfaceOrientationPortrait 40 | UIInterfaceOrientationPortraitUpsideDown 41 | UIInterfaceOrientationLandscapeLeft 42 | UIInterfaceOrientationLandscapeRight 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /demo/MailAppDemo/MailAppDemo/MailTableCell.h: -------------------------------------------------------------------------------- 1 | /* 2 | * MGSwipeTableCell is licensed under MIT license. See LICENSE.md file for more information. 3 | * Copyright (c) 2014 Imanol Fernandez @MortimerGoro 4 | */ 5 | 6 | #import 7 | #import "MGSwipeTableCell.h" 8 | 9 | 10 | @interface MailIndicatorView : UIView 11 | @property (nonatomic, strong) UIColor * indicatorColor; 12 | @property (nonatomic, strong) UIColor * innerColor; 13 | @end 14 | 15 | @interface MailTableCell : MGSwipeTableCell 16 | 17 | @property (nonatomic, strong) UILabel * mailFrom; 18 | @property (nonatomic, strong) UILabel * mailSubject; 19 | @property (nonatomic, strong) UITextView * mailMessage; 20 | @property (nonatomic, strong) UILabel * mailTime; 21 | @property (nonatomic, strong) MailIndicatorView * indicatorView; 22 | 23 | -(instancetype) initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier; 24 | 25 | @end 26 | -------------------------------------------------------------------------------- /demo/MailAppDemo/MailAppDemo/MailTableCell.m: -------------------------------------------------------------------------------- 1 | /* 2 | * MGSwipeTableCell is licensed under MIT license. See LICENSE.md file for more information. 3 | * Copyright (c) 2014 Imanol Fernandez @MortimerGoro 4 | */ 5 | 6 | #import "MailTableCell.h" 7 | 8 | @implementation MailIndicatorView 9 | 10 | -(void) drawRect:(CGRect)rect 11 | { 12 | CGContextRef ctx = UIGraphicsGetCurrentContext(); 13 | CGContextAddEllipseInRect(ctx, rect); 14 | CGContextSetFillColor(ctx, CGColorGetComponents(_indicatorColor.CGColor)); 15 | CGContextFillPath(ctx); 16 | 17 | if (_innerColor) { 18 | CGFloat innerSize = rect.size.width * 0.5; 19 | CGRect innerRect = CGRectMake(rect.origin.x + rect.size.width * 0.5 - innerSize * 0.5, 20 | rect.origin.y + rect.size.height * 0.5 - innerSize * 0.5, 21 | innerSize, innerSize); 22 | CGContextAddEllipseInRect(ctx, innerRect); 23 | CGContextSetFillColor(ctx, CGColorGetComponents(_innerColor.CGColor)); 24 | CGContextFillPath(ctx); 25 | } 26 | } 27 | 28 | -(void) setIndicatorColor:(UIColor *)indicatorColor 29 | { 30 | _indicatorColor = indicatorColor; 31 | [self setNeedsDisplay]; 32 | } 33 | 34 | -(void) setInnerColor:(UIColor *)innerColor 35 | { 36 | _innerColor = innerColor; 37 | [self setNeedsDisplay]; 38 | } 39 | 40 | @end 41 | 42 | @implementation MailTableCell 43 | 44 | -(instancetype) initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier 45 | { 46 | if (self = [super initWithStyle:style reuseIdentifier:reuseIdentifier]) { 47 | _mailFrom = [[UILabel alloc] initWithFrame:CGRectZero]; 48 | _mailMessage = [[UITextView alloc] initWithFrame:CGRectZero]; 49 | _mailSubject = [[UILabel alloc] initWithFrame:CGRectZero]; 50 | _mailTime = [[UILabel alloc] initWithFrame:CGRectZero]; 51 | 52 | _mailFrom.font = [UIFont fontWithName:@"HelveticaNeue" size:18.0f]; 53 | _mailSubject.font = [UIFont fontWithName:@"HelveticaNeue-Light" size:15.0f]; 54 | _mailMessage.font = [UIFont fontWithName:@"HelveticaNeue-Light" size:14.0f]; 55 | _mailTime.font = [UIFont fontWithName:@"HelveticaNeue-Light" size:13.0f]; 56 | 57 | _mailMessage.scrollEnabled = NO; 58 | _mailMessage.editable = NO; 59 | _mailMessage.backgroundColor = [UIColor clearColor]; 60 | _mailMessage.contentInset = UIEdgeInsetsMake(-5, -5, 0, 0); 61 | _mailMessage.textColor = [UIColor grayColor]; 62 | _mailMessage.userInteractionEnabled = NO; 63 | 64 | _indicatorView = [[MailIndicatorView alloc] initWithFrame:CGRectMake(0, 0, 10, 10)]; 65 | _indicatorView.backgroundColor = [UIColor clearColor]; 66 | 67 | [self.contentView addSubview:_mailFrom]; 68 | [self.contentView addSubview:_mailMessage]; 69 | [self.contentView addSubview:_mailSubject]; 70 | [self.contentView addSubview:_mailTime]; 71 | [self.contentView addSubview:_indicatorView]; 72 | } 73 | return self; 74 | } 75 | 76 | -(void) layoutSubviews 77 | { 78 | [super layoutSubviews]; 79 | 80 | CGFloat leftPadding = 25.0; 81 | CGFloat topPadding = 3.0; 82 | CGFloat textWidth = self.contentView.bounds.size.width - leftPadding * 2; 83 | CGFloat dateWidth = 40; 84 | 85 | _mailFrom.frame = CGRectMake(leftPadding, topPadding, textWidth, 20); 86 | _mailSubject.frame = CGRectMake(leftPadding, _mailFrom.frame.origin.y + _mailFrom.frame.size.height + topPadding, textWidth - dateWidth, 17); 87 | CGFloat messageHeight = self.contentView.bounds.size.height - (_mailSubject.frame.origin.y + _mailSubject.frame.size.height) - topPadding * 2; 88 | _mailMessage.frame = CGRectMake(leftPadding, _mailSubject.frame.origin.y + _mailSubject.frame.size.height + topPadding, textWidth, messageHeight); 89 | 90 | CGRect frame = _mailFrom.frame; 91 | frame.origin.x = self.contentView.frame.size.width - leftPadding - dateWidth; 92 | frame.size.width = dateWidth; 93 | _mailTime.frame = frame; 94 | 95 | _indicatorView.center = CGPointMake(leftPadding * 0.5, _mailFrom.frame.origin.y + _mailFrom.frame.size.height * 0.5); 96 | 97 | } 98 | 99 | @end 100 | -------------------------------------------------------------------------------- /demo/MailAppDemo/MailAppDemo/MailViewController.h: -------------------------------------------------------------------------------- 1 | /* 2 | * MGSwipeTableCell is licensed under MIT license. See LICENSE.md file for more information. 3 | * Copyright (c) 2014 Imanol Fernandez @MortimerGoro 4 | */ 5 | 6 | #import 7 | #import "MGSwipeTableCell.h" 8 | 9 | @interface MailViewController : UIViewController 10 | 11 | @property (nonatomic, strong) UITableView * tableView; 12 | 13 | @end 14 | 15 | -------------------------------------------------------------------------------- /demo/MailAppDemo/MailAppDemo/MailViewController.m: -------------------------------------------------------------------------------- 1 | /* 2 | * MGSwipeTableCell is licensed under MIT license. See LICENSE.md file for more information. 3 | * Copyright (c) 2014 Imanol Fernandez @MortimerGoro 4 | */ 5 | 6 | #import "MailViewController.h" 7 | #import "MailTableCell.h" 8 | #import "MGSwipeButton.h" 9 | 10 | 11 | @interface MailData : NSObject 12 | @property (nonatomic, strong) NSString * from; 13 | @property (nonatomic, strong) NSString * subject; 14 | @property (nonatomic, strong) NSString * message; 15 | @property (nonatomic, strong) NSString * date; 16 | @property (nonatomic, assign) BOOL read; 17 | @property (nonatomic, assign) BOOL flag; 18 | 19 | @end 20 | 21 | @implementation MailData 22 | @end 23 | 24 | 25 | typedef void(^MailActionCallback)(BOOL cancelled, BOOL deleted, NSInteger actionIndex); 26 | 27 | @implementation MailViewController 28 | { 29 | NSMutableArray * demoData; 30 | MailActionCallback actionCallback; 31 | UIRefreshControl * refreshControl; 32 | } 33 | 34 | 35 | -(void) prepareDemoData 36 | { 37 | demoData = [NSMutableArray array]; 38 | 39 | NSArray * from = @[ 40 | @"Vincent", 41 | @"Mr Glass", 42 | @"Marsellus", 43 | @"Ringo", 44 | @"Sullivan", 45 | @"Mr Wolf", 46 | @"Butch Coolidge", 47 | @"Marvin", 48 | @"Captain Koons", 49 | @"Jules", 50 | @"Jimmie Dimmick" 51 | ]; 52 | 53 | NSArray * subjects = @[ 54 | @"You think water moves fast?", 55 | @"They called me Mr Glass", 56 | @"The path of the righteous man", 57 | @"Do you see any Teletubbies in here?", 58 | @"Now that we know who you are", 59 | @"My money's in that office, right?", 60 | @"Now we took an oath", 61 | @"That show's called a pilot", 62 | @"I know who I am. I'm not a mistake", 63 | @"It all makes sense!", 64 | @"The selfish and the tyranny of evil men", 65 | ]; 66 | 67 | NSArray * messages = @[ 68 | @"You should see ice. It moves like it has a mind. Like it knows it killed the world once and got a taste for murder. After the avalanche, it took us a week to climb out.", 69 | @"And I will strike down upon thee with great vengeance and furious anger those who would attempt to poison and destroy My brothers.", 70 | @"Look, just because I don't be givin' no man a foot massage don't make it right for Marsellus to throw Antwone into a glass motherfuckin' house", 71 | @"No? Well, that's what you see at a toy store. And you must think you're in a toy store, because you're here shopping for an infant named Jeb.", 72 | @"In a comic, you know how you can tell who the arch-villain's going to be? He's the exact opposite of the hero", 73 | @"If she start giving me some bullshit about it ain't there, and we got to go someplace else and get it, I'm gonna shoot you in the head then and there.", 74 | @"that I'm breaking now. We said we'd say it was the snow that killed the other two, but it wasn't. Nature is lethal but it doesn't hold a candle to man.", 75 | @"Then they show that show to the people who make shows, and on the strength of that one show they decide if they're going to make more shows.", 76 | @"And most times they're friends, like you and me! I should've known way back when...", 77 | @"After the avalanche, it took us a week to climb out. Now, I don't know exactly when we turned on each other, but I know that seven of us survived the slide", 78 | @"Blessed is he who, in the name of charity and good will, shepherds the weak through the valley of darkness, for he is truly his brother's keeper and the finder of lost children", 79 | ]; 80 | 81 | 82 | for (int i = 0; i < messages.count; ++i) { 83 | MailData * mail = [[MailData alloc] init]; 84 | mail.from = [from objectAtIndex:i]; 85 | mail.subject = [subjects objectAtIndex:i]; 86 | mail.message = [messages objectAtIndex:i]; 87 | mail.date = [NSString stringWithFormat:@"11:%d", 43 - i]; 88 | [demoData addObject:mail]; 89 | } 90 | 91 | } 92 | 93 | -(void) refreshCallback 94 | { 95 | [self prepareDemoData]; 96 | [_tableView reloadData]; 97 | [refreshControl endRefreshing]; 98 | } 99 | 100 | - (void)viewDidLoad { 101 | [super viewDidLoad]; 102 | 103 | _tableView = [[UITableView alloc] initWithFrame:self.view.bounds style:UITableViewStylePlain]; 104 | _tableView.delegate = self; 105 | _tableView.dataSource = self; 106 | [self.view addSubview:_tableView]; 107 | 108 | self.title = @"MSwipeTableCell MailApp"; 109 | 110 | refreshControl = [[UIRefreshControl alloc] init]; 111 | [refreshControl addTarget:self action:@selector(refreshCallback) forControlEvents:UIControlEventValueChanged]; 112 | [self.tableView addSubview:refreshControl]; 113 | 114 | [self prepareDemoData]; 115 | } 116 | 117 | -(void) viewDidAppear:(BOOL)animated 118 | { 119 | [self.tableView reloadData]; 120 | } 121 | 122 | -(void) viewWillLayoutSubviews 123 | { 124 | [super viewWillLayoutSubviews]; 125 | _tableView.frame = self.view.bounds; 126 | } 127 | 128 | -(void) deleteMail:(NSIndexPath *) indexPath 129 | { 130 | [demoData removeObjectAtIndex:indexPath.row]; 131 | [_tableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationLeft]; 132 | } 133 | 134 | -(MailData *) mailForIndexPath:(NSIndexPath*) path 135 | { 136 | return [demoData objectAtIndex:path.row]; 137 | } 138 | 139 | -(void) updateCellIndicactor:(MailData *) mail cell:(MailTableCell*) cell 140 | { 141 | UIColor * color; 142 | UIColor * innerColor; 143 | if (!mail.read && mail.flag) { 144 | color = [UIColor colorWithRed:1.0 green:149/255.0 blue:0.05 alpha:1.0]; 145 | innerColor = [UIColor colorWithRed:0 green:122/255.0 blue:1.0 alpha:1.0]; 146 | } 147 | else if (mail.flag) { 148 | color = [UIColor colorWithRed:1.0 green:149/255.0 blue:0.05 alpha:1.0]; 149 | } 150 | else if (mail.read) { 151 | color = [UIColor clearColor]; 152 | } 153 | else { 154 | color = [UIColor colorWithRed:0 green:122/255.0 blue:1.0 alpha:1.0]; 155 | } 156 | 157 | cell.indicatorView.indicatorColor = color; 158 | cell.indicatorView.innerColor = innerColor; 159 | } 160 | 161 | -(void) showMailActions:(MailData *) mail callback:(MailActionCallback) callback 162 | { 163 | actionCallback = callback; 164 | UIActionSheet * sheet = [[UIActionSheet alloc] initWithTitle:@"Actions" delegate:self cancelButtonTitle:@"Cancel" destructiveButtonTitle:@"Trash" otherButtonTitles:mail.read ? @"Mark as unread": @"Mark as read", @"Flag", nil]; 165 | [sheet showInView:self.view]; 166 | } 167 | 168 | - (void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex 169 | { 170 | actionCallback(buttonIndex == actionSheet.cancelButtonIndex, buttonIndex == actionSheet.destructiveButtonIndex, buttonIndex); 171 | actionCallback = nil; 172 | } 173 | 174 | -(NSString *) readButtonText:(BOOL) read 175 | { 176 | return read ? @"Mark as\nunread" :@"Mark as\nread"; 177 | } 178 | 179 | 180 | #pragma mark Table Delegate 181 | 182 | - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section 183 | { 184 | return demoData.count; 185 | } 186 | 187 | - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath 188 | { 189 | static NSString * identifier = @"MailCell"; 190 | MailTableCell * cell = [tableView dequeueReusableCellWithIdentifier:identifier]; 191 | if (!cell) { 192 | cell = [[MailTableCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:identifier]; 193 | } 194 | cell.delegate = self; 195 | 196 | MailData * data = [demoData objectAtIndex:indexPath.row]; 197 | cell.mailFrom.text = data.from; 198 | cell.mailSubject.text = data.subject; 199 | cell.mailMessage.text = data.message; 200 | cell.mailTime.text = data.date; 201 | [self updateCellIndicactor:data cell:cell]; 202 | return cell; 203 | } 204 | 205 | - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath 206 | { 207 | return 110; 208 | } 209 | 210 | #pragma mark Swipe Delegate 211 | 212 | -(BOOL) swipeTableCell:(MGSwipeTableCell*) cell canSwipe:(MGSwipeDirection) direction; 213 | { 214 | return YES; 215 | } 216 | 217 | -(NSArray*) swipeTableCell:(MGSwipeTableCell*) cell swipeButtonsForDirection:(MGSwipeDirection)direction 218 | swipeSettings:(MGSwipeSettings*) swipeSettings expansionSettings:(MGSwipeExpansionSettings*) expansionSettings 219 | { 220 | 221 | swipeSettings.transition = MGSwipeTransitionBorder; 222 | expansionSettings.buttonIndex = 0; 223 | 224 | __weak MailViewController * me = self; 225 | MailData * mail = [me mailForIndexPath:[self.tableView indexPathForCell:cell]]; 226 | 227 | if (direction == MGSwipeDirectionLeftToRight) { 228 | 229 | expansionSettings.fillOnTrigger = NO; 230 | expansionSettings.threshold = 2; 231 | return @[[MGSwipeButton buttonWithTitle:[me readButtonText:mail.read] backgroundColor:[UIColor colorWithRed:0 green:122/255.0 blue:1.0 alpha:1.0] padding:5 callback:^BOOL(MGSwipeTableCell *sender) { 232 | 233 | MailData * mail = [me mailForIndexPath:[me.tableView indexPathForCell:sender]]; 234 | mail.read = !mail.read; 235 | [me updateCellIndicactor:mail cell:(MailTableCell*)sender]; 236 | [cell refreshContentView]; //needed to refresh cell contents while swipping 237 | 238 | //change button text 239 | [(UIButton*)[cell.leftButtons objectAtIndex:0] setTitle:[me readButtonText:mail.read] forState:UIControlStateNormal]; 240 | 241 | return YES; 242 | }]]; 243 | } 244 | else { 245 | 246 | expansionSettings.fillOnTrigger = YES; 247 | expansionSettings.threshold = 1.1; 248 | 249 | CGFloat padding = 15; 250 | 251 | MGSwipeButton * trash = [MGSwipeButton buttonWithTitle:@"Trash" backgroundColor:[UIColor colorWithRed:1.0 green:59/255.0 blue:50/255.0 alpha:1.0] padding:padding callback:^BOOL(MGSwipeTableCell *sender) { 252 | 253 | NSIndexPath * indexPath = [me.tableView indexPathForCell:sender]; 254 | [me deleteMail:indexPath]; 255 | return NO; //don't autohide to improve delete animation 256 | }]; 257 | MGSwipeButton * flag = [MGSwipeButton buttonWithTitle:@"Flag" backgroundColor:[UIColor colorWithRed:1.0 green:149/255.0 blue:0.05 alpha:1.0] padding:padding callback:^BOOL(MGSwipeTableCell *sender) { 258 | 259 | MailData * mail = [me mailForIndexPath:[me.tableView indexPathForCell:sender]]; 260 | mail.flag = !mail.flag; 261 | [me updateCellIndicactor:mail cell:(MailTableCell*)sender]; 262 | [cell refreshContentView]; //needed to refresh cell contents while swipping 263 | return YES; 264 | }]; 265 | MGSwipeButton * more = [MGSwipeButton buttonWithTitle:@"More" backgroundColor:[UIColor colorWithRed:200/255.0 green:200/255.0 blue:205/255.0 alpha:1.0] padding:padding callback:^BOOL(MGSwipeTableCell *sender) { 266 | 267 | NSIndexPath * indexPath = [me.tableView indexPathForCell:sender]; 268 | MailData * mail = [me mailForIndexPath:indexPath]; 269 | MailTableCell * cell = (MailTableCell*) sender; 270 | [me showMailActions:mail callback:^(BOOL cancelled, BOOL deleted, NSInteger actionIndex) { 271 | if (cancelled) { 272 | return; 273 | } 274 | if (deleted) { 275 | [me deleteMail:indexPath]; 276 | } 277 | else if (actionIndex == 1) { 278 | mail.read = !mail.read; 279 | [(UIButton*)[cell.leftButtons objectAtIndex:0] setTitle:[me readButtonText:mail.read] forState:UIControlStateNormal]; 280 | [me updateCellIndicactor:mail cell:cell]; 281 | [cell refreshContentView]; //needed to refresh cell contents while swipping 282 | } 283 | else if (actionIndex == 2) { 284 | mail.flag = !mail.flag; 285 | [me updateCellIndicactor:mail cell:cell]; 286 | [cell refreshContentView]; //needed to refresh cell contents while swipping 287 | } 288 | 289 | [cell hideSwipeAnimated:YES]; 290 | 291 | }]; 292 | 293 | return NO; //avoid autohide swipe 294 | }]; 295 | 296 | return @[trash, flag, more]; 297 | } 298 | 299 | return nil; 300 | 301 | } 302 | 303 | -(void) swipeTableCell:(MGSwipeTableCell*) cell didChangeSwipeState:(MGSwipeState)state gestureIsActive:(BOOL)gestureIsActive 304 | { 305 | NSString * str; 306 | switch (state) { 307 | case MGSwipeStateNone: str = @"None"; break; 308 | case MGSwipeStateSwippingLeftToRight: str = @"SwippingLeftToRight"; break; 309 | case MGSwipeStateSwippingRightToLeft: str = @"SwippingRightToLeft"; break; 310 | case MGSwipeStateExpandingLeftToRight: str = @"ExpandingLeftToRight"; break; 311 | case MGSwipeStateExpandingRightToLeft: str = @"ExpandingRightToLeft"; break; 312 | } 313 | NSLog(@"Swipe state: %@ ::: Gesture: %@", str, gestureIsActive ? @"Active" : @"Ended"); 314 | } 315 | 316 | @end 317 | -------------------------------------------------------------------------------- /demo/MailAppDemo/MailAppDemo/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // MailAppDemo 4 | // 5 | // Created by Imanol Fernandez Gorostizaga on 26/09/14. 6 | // Copyright (c) 2014 Mortimer. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "AppDelegate.h" 11 | 12 | int main(int argc, char * argv[]) { 13 | @autoreleasepool { 14 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /demo/MailAppDemoSwift/MailAppDemoSwift.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /demo/MailAppDemoSwift/MailAppDemoSwift.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /demo/MailAppDemoSwift/MailAppDemoSwift/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // MailAppDemoSwift 4 | // 5 | 6 | import UIKit 7 | 8 | @UIApplicationMain 9 | class AppDelegate: UIResponder, UIApplicationDelegate { 10 | 11 | var window: UIWindow? 12 | 13 | 14 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { 15 | 16 | window = UIWindow(frame: UIScreen.main.bounds); 17 | let controller = MailViewController(); 18 | let navigation = UINavigationController(rootViewController: controller); 19 | window?.rootViewController = navigation; 20 | window?.backgroundColor = UIColor.white; 21 | window?.makeKeyAndVisible(); 22 | 23 | 24 | // Override point for customization after application launch. 25 | return true 26 | } 27 | 28 | func applicationWillResignActive(_ application: UIApplication) { 29 | // 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. 30 | // 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. 31 | } 32 | 33 | func applicationDidEnterBackground(_ application: UIApplication) { 34 | // 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. 35 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 36 | } 37 | 38 | func applicationWillEnterForeground(_ application: UIApplication) { 39 | // 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. 40 | } 41 | 42 | func applicationDidBecomeActive(_ application: UIApplication) { 43 | // 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. 44 | } 45 | 46 | func applicationWillTerminate(_ application: UIApplication) { 47 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 48 | } 49 | 50 | 51 | } 52 | 53 | -------------------------------------------------------------------------------- /demo/MailAppDemoSwift/MailAppDemoSwift/Base.lproj/LaunchScreen.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 20 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /demo/MailAppDemoSwift/MailAppDemoSwift/Images.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "29x29", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "29x29", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "40x40", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "40x40", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "60x60", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "60x60", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "idiom" : "ipad", 35 | "size" : "29x29", 36 | "scale" : "1x" 37 | }, 38 | { 39 | "idiom" : "ipad", 40 | "size" : "29x29", 41 | "scale" : "2x" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "size" : "40x40", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "size" : "40x40", 51 | "scale" : "2x" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "size" : "76x76", 56 | "scale" : "1x" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "size" : "76x76", 61 | "scale" : "2x" 62 | } 63 | ], 64 | "info" : { 65 | "version" : 1, 66 | "author" : "xcode" 67 | } 68 | } -------------------------------------------------------------------------------- /demo/MailAppDemoSwift/MailAppDemoSwift/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | mortimergoro.$(PRODUCT_NAME:rfc1034identifier) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIRequiredDeviceCapabilities 28 | 29 | armv7 30 | 31 | UISupportedInterfaceOrientations 32 | 33 | UIInterfaceOrientationPortrait 34 | UIInterfaceOrientationLandscapeLeft 35 | UIInterfaceOrientationLandscapeRight 36 | 37 | UISupportedInterfaceOrientations~ipad 38 | 39 | UIInterfaceOrientationPortrait 40 | UIInterfaceOrientationPortraitUpsideDown 41 | UIInterfaceOrientationLandscapeLeft 42 | UIInterfaceOrientationLandscapeRight 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /demo/MailAppDemoSwift/MailAppDemoSwift/MailTableCell.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MailTableCell.swift 3 | // MailAppDemoSwift 4 | // 5 | 6 | import UIKit 7 | 8 | class MailIndicatorView: UIView { 9 | 10 | var indicatorColor : UIColor { 11 | didSet { 12 | self.setNeedsDisplay(); 13 | } 14 | } 15 | var innerColor : UIColor? { 16 | didSet { 17 | self.setNeedsDisplay(); 18 | } 19 | } 20 | 21 | override init(frame:CGRect) { 22 | indicatorColor = UIColor.clear; 23 | super.init(frame:frame); 24 | } 25 | 26 | required init?(coder aDecoder: NSCoder) { 27 | indicatorColor = UIColor.clear; 28 | super.init(coder: aDecoder); 29 | } 30 | 31 | override func draw(_ rect: CGRect) { 32 | let ctx = UIGraphicsGetCurrentContext(); 33 | ctx?.addEllipse(in: rect); 34 | ctx?.setFillColor(indicatorColor.cgColor.components!); 35 | ctx?.fillPath(); 36 | 37 | if innerColor != nil { 38 | let innerSize = rect.size.width * 0.5; 39 | let innerRect = CGRect(x: rect.origin.x + rect.size.width * 0.5 - innerSize * 0.5, 40 | y: rect.origin.y + rect.size.height * 0.5 - innerSize * 0.5, 41 | width: innerSize, height: innerSize); 42 | ctx?.addEllipse(in: innerRect); 43 | ctx?.setFillColor(innerColor!.cgColor.components!); 44 | ctx?.fillPath(); 45 | } 46 | } 47 | } 48 | 49 | class MailTableCell: MGSwipeTableCell { 50 | 51 | var mailFrom: UILabel!; 52 | var mailSubject: UILabel!; 53 | var mailMessage: UITextView!; 54 | var mailTime: UILabel!; 55 | var indicatorView: MailIndicatorView!; 56 | 57 | func initViews() { 58 | mailFrom = UILabel(frame: CGRect.zero); 59 | mailMessage = UITextView(frame: CGRect.zero); 60 | mailSubject = UILabel(frame: CGRect.zero); 61 | mailTime = UILabel(frame: CGRect.zero); 62 | 63 | mailFrom.font = UIFont(name:"HelveticaNeue", size:18.0); 64 | mailSubject.font = UIFont(name:"HelveticaNeue-Light", size:15.0); 65 | mailMessage.font = UIFont(name:"HelveticaNeue-Light", size:14.0); 66 | mailTime.font = UIFont(name:"HelveticaNeue-Light", size:13.0); 67 | 68 | mailMessage.isScrollEnabled = false; 69 | mailMessage.isEditable = false; 70 | mailMessage.backgroundColor = UIColor.clear; 71 | mailMessage.contentInset = UIEdgeInsets.init(top: -5, left: -5, bottom: 0, right: 0); 72 | mailMessage.textColor = UIColor.gray; 73 | mailMessage.isUserInteractionEnabled = false; 74 | 75 | indicatorView = MailIndicatorView(frame: CGRect(x: 0, y: 0, width: 10, height: 10)); 76 | indicatorView.backgroundColor = UIColor.clear; 77 | 78 | contentView.addSubview(mailFrom); 79 | contentView.addSubview(mailMessage); 80 | contentView.addSubview(mailSubject); 81 | contentView.addSubview(mailTime); 82 | contentView.addSubview(indicatorView); 83 | 84 | } 85 | 86 | override init(style: UITableViewCell.CellStyle, reuseIdentifier:String?) 87 | { 88 | super.init(style:style, reuseIdentifier: reuseIdentifier); 89 | initViews(); 90 | } 91 | 92 | required init?(coder aDecoder: NSCoder) { 93 | super.init(coder: aDecoder); 94 | initViews(); 95 | } 96 | 97 | override func layoutSubviews() { 98 | 99 | super.layoutSubviews(); 100 | 101 | let leftPadding:CGFloat = 25.0; 102 | let topPadding:CGFloat = 3.0; 103 | let textWidth = contentView.bounds.size.width - 2.0 * leftPadding; 104 | let dateWidth:CGFloat = 40; 105 | 106 | mailFrom.frame = CGRect(x: leftPadding, y: topPadding, width: textWidth, height: 20); 107 | mailSubject.frame = CGRect(x: leftPadding, y: mailFrom.frame.origin.y + mailFrom.frame.size.height + topPadding, width: textWidth - dateWidth, height: 17); 108 | let messageHeight = contentView.bounds.size.height - (mailSubject.frame.origin.y + mailSubject.frame.size.height) - topPadding * 2; 109 | mailMessage.frame = CGRect(x: leftPadding, y: mailSubject.frame.origin.y + mailSubject.frame.size.height + topPadding, width: textWidth, height: messageHeight); 110 | 111 | var frame = mailFrom.frame; 112 | frame.origin.x = self.contentView.frame.size.width - leftPadding - dateWidth; 113 | frame.size.width = dateWidth; 114 | mailTime.frame = frame; 115 | 116 | indicatorView.center = CGPoint(x: leftPadding * 0.5, y: mailFrom.frame.origin.y + mailFrom.frame.size.height * 0.5); 117 | } 118 | 119 | } 120 | 121 | -------------------------------------------------------------------------------- /demo/MailAppDemoSwift/MailAppDemoSwift/MailViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.swift 3 | // MailAppDemoSwift 4 | // 5 | 6 | import UIKit 7 | 8 | class MailData { 9 | var from: String!; 10 | var subject: String!; 11 | var message: String!; 12 | var date: String!; 13 | var read = false; 14 | var flag = false; 15 | } 16 | 17 | typealias MailActionCallback = (_ cancelled: Bool, _ deleted: Bool, _ actionIndex: Int) -> Void 18 | 19 | class MailViewController: UIViewController, UITableViewDataSource, UITableViewDelegate, MGSwipeTableCellDelegate, UIActionSheetDelegate { 20 | 21 | var tableView: UITableView!; 22 | var demoData = [MailData](); 23 | var refreshControl: UIRefreshControl!; 24 | var actionCallback: MailActionCallback?; 25 | 26 | func prepareDemoData() { 27 | var from = [ 28 | "Vincent", 29 | "Mr Glass", 30 | "Marsellus", 31 | "Ringo", 32 | "Sullivan", 33 | "Mr Wolf", 34 | "Butch Coolidge", 35 | "Marvin", 36 | "Captain Koons", 37 | "Jules", 38 | "Jimmie Dimmick" 39 | ]; 40 | 41 | var subjects = [ 42 | "You think water moves fast?", 43 | "They called me Mr Glass", 44 | "The path of the righteous man", 45 | "Do you see any Teletubbies in here?", 46 | "Now that we know who you are", 47 | "My money's in that office, right?", 48 | "Now we took an oath", 49 | "That show's called a pilot", 50 | "I know who I am. I'm not a mistake", 51 | "It all makes sense!", 52 | "The selfish and the tyranny of evil men", 53 | ]; 54 | 55 | var messages = [ 56 | "You should see ice. It moves like it has a mind. Like it knows it killed the world once and got a taste for murder. After the avalanche, it took us a week to climb out.", 57 | "And I will strike down upon thee with great vengeance and furious anger those who would attempt to poison and destroy My brothers.", 58 | "Look, just because I don't be givin' no man a foot massage don't make it right for Marsellus to throw Antwone into a glass motherfuckin' house", 59 | "No? Well, that's what you see at a toy store. And you must think you're in a toy store, because you're here shopping for an infant named Jeb.", 60 | "In a comic, you know how you can tell who the arch-villain's going to be? He's the exact opposite of the hero", 61 | "If she start giving me some bullshit about it ain't there, and we got to go someplace else and get it, I'm gonna shoot you in the head then and there.", 62 | "that I'm breaking now. We said we'd say it was the snow that killed the other two, but it wasn't. Nature is lethal but it doesn't hold a candle to man.", 63 | "Then they show that show to the people who make shows, and on the strength of that one show they decide if they're going to make more shows.", 64 | "And most times they're friends, like you and me! I should've known way back when...", 65 | "After the avalanche, it took us a week to climb out. Now, I don't know exactly when we turned on each other, but I know that seven of us survived the slide", 66 | "Blessed is he who, in the name of charity and good will, shepherds the weak through the valley of darkness, for he is truly his brother's keeper and the finder of lost children", 67 | ]; 68 | 69 | 70 | for i in 0 ..< messages.count { 71 | let mail = MailData(); 72 | mail.from = from[i]; 73 | mail.subject = subjects[i]; 74 | mail.message = messages[i]; 75 | mail.date = String(format: "11:%d", arguments: [43 - i]); 76 | demoData.append(mail); 77 | } 78 | } 79 | 80 | func mailForIndexPath(_ path: IndexPath) -> MailData { 81 | return demoData[(path as NSIndexPath).row]; 82 | } 83 | @objc 84 | func refreshCallback() { 85 | prepareDemoData(); 86 | tableView.reloadData(); 87 | refreshControl.endRefreshing(); 88 | } 89 | 90 | func deleteMail(_ path:IndexPath) { 91 | demoData.remove(at: (path as NSIndexPath).row); 92 | tableView.deleteRows(at: [path], with: .left); 93 | } 94 | 95 | func updateCellIndicator(_ mail: MailData, cell: MailTableCell) { 96 | var color: UIColor; 97 | var innerColor : UIColor?; 98 | 99 | if !mail.read && mail.flag { 100 | color = UIColor.init(red: 1.0, green: 149/255.0, blue: 0.05, alpha: 1.0); 101 | innerColor = UIColor.init(red: 0.0, green: 122/255.0, blue: 1.0, alpha: 1.0); 102 | } 103 | else if mail.flag { 104 | color = UIColor.init(red: 1.0, green: 149/255.0, blue: 0.05, alpha: 1.0); 105 | } 106 | else if mail.read { 107 | color = UIColor.clear; 108 | } 109 | else { 110 | color = UIColor.init(red: 0.0, green: 122/255.0, blue: 1.0, alpha: 1.0); 111 | } 112 | 113 | cell.indicatorView.indicatorColor = color; 114 | cell.indicatorView.innerColor = innerColor; 115 | } 116 | 117 | func showMailActions(_ mail: MailData, callback: @escaping MailActionCallback) { 118 | actionCallback = callback; 119 | let sheet = UIActionSheet.init(title: "Actions", delegate: self, cancelButtonTitle: "Cancel", destructiveButtonTitle: "Trash"); 120 | sheet.addButton(withTitle: "Mark as unread"); 121 | sheet.addButton(withTitle: "Mark as read"); 122 | sheet.addButton(withTitle: "Flag"); 123 | 124 | sheet.show(in: self.view); 125 | } 126 | 127 | func actionSheet(_ actionSheet: UIActionSheet, clickedButtonAt index: Int) { 128 | if let action = actionCallback { 129 | action(index == actionSheet.cancelButtonIndex, 130 | index == actionSheet.destructiveButtonIndex, 131 | index); 132 | actionCallback = nil; 133 | } 134 | } 135 | 136 | func readButtonText(_ read:Bool) -> String { 137 | return read ? "Mark as\nunread" : "Mark as\nread"; 138 | } 139 | 140 | override func viewDidLoad() { 141 | super.viewDidLoad() 142 | 143 | tableView = UITableView(frame: view.bounds, style: UITableView.Style.plain); 144 | tableView.delegate = self; 145 | tableView.dataSource = self; 146 | view.addSubview(tableView); 147 | 148 | self.title = "MSwipeTableCell MailApp"; 149 | 150 | refreshControl = UIRefreshControl(); 151 | refreshControl.addTarget(self, action: #selector(refreshCallback), for: UIControl.Event.valueChanged); 152 | tableView.addSubview(refreshControl); 153 | prepareDemoData(); 154 | } 155 | 156 | override func didReceiveMemoryWarning() { 157 | super.didReceiveMemoryWarning() 158 | // Dispose of any resources that can be recreated. 159 | } 160 | 161 | func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int 162 | { 163 | return demoData.count; 164 | } 165 | 166 | 167 | func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { 168 | let identifier = "MailCell"; 169 | 170 | var cell: MailTableCell! = tableView.dequeueReusableCell(withIdentifier: identifier) as? MailTableCell; 171 | if cell == nil { 172 | cell = MailTableCell(style: UITableViewCell.CellStyle.default, reuseIdentifier: identifier); 173 | } 174 | cell.delegate = self; 175 | 176 | let data: MailData = demoData[(indexPath as NSIndexPath).row]; 177 | cell!.mailFrom.text = data.from; 178 | cell!.mailSubject.text = data.subject; 179 | cell!.mailMessage.text = data.message; 180 | cell!.mailTime.text = data.date; 181 | updateCellIndicator(data, cell: cell); 182 | return cell; 183 | } 184 | 185 | func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat { 186 | return 110; 187 | } 188 | 189 | func swipeTableCell(_ cell: MGSwipeTableCell, canSwipe direction: MGSwipeDirection) -> Bool { 190 | return true; 191 | } 192 | 193 | 194 | func swipeTableCell(_ cell: MGSwipeTableCell, swipeButtonsFor direction: MGSwipeDirection, swipeSettings: MGSwipeSettings, expansionSettings: MGSwipeExpansionSettings) -> [UIView]? { 195 | 196 | swipeSettings.transition = MGSwipeTransition.border; 197 | expansionSettings.buttonIndex = 0; 198 | 199 | 200 | let mail = mailForIndexPath(tableView.indexPath(for: cell)!); 201 | 202 | if direction == MGSwipeDirection.leftToRight { 203 | expansionSettings.fillOnTrigger = false; 204 | expansionSettings.threshold = 2; 205 | let color = UIColor.init(red:0.0, green:122/255.0, blue:1.0, alpha:1.0); 206 | 207 | return [ 208 | MGSwipeButton(title: readButtonText(mail.read), backgroundColor: color, callback: { (cell) -> Bool in 209 | mail.read = !mail.read; 210 | self.updateCellIndicator(mail, cell: cell as! MailTableCell); 211 | cell.refreshContentView(); 212 | (cell.leftButtons[0] as! UIButton).setTitle(self.readButtonText(mail.read), for: UIControl.State()); 213 | 214 | return true; 215 | }) 216 | ] 217 | } 218 | else { 219 | expansionSettings.fillOnTrigger = true; 220 | expansionSettings.threshold = 1.1; 221 | let padding = 15; 222 | let color1 = UIColor.init(red:1.0, green:59/255.0, blue:50/255.0, alpha:1.0); 223 | let color2 = UIColor.init(red:1.0, green:149/255.0, blue:0.05, alpha:1.0); 224 | let color3 = UIColor.init(red:200/255.0, green:200/255.0, blue:205/255.0, alpha:1.0); 225 | 226 | let trash = MGSwipeButton(title: "Trash", backgroundColor: color1, padding: padding, callback: { (cell) -> Bool in 227 | self.deleteMail(self.tableView.indexPath(for: cell)!); 228 | return false; //don't autohide to improve delete animation 229 | }); 230 | 231 | let flag = MGSwipeButton(title: "Flag", backgroundColor: color2, padding: padding, callback: { (cell) -> Bool in 232 | let mail = self.mailForIndexPath(self.tableView.indexPath(for: cell)!); 233 | mail.flag = !mail.flag; 234 | self.updateCellIndicator(mail, cell: cell as! MailTableCell); 235 | cell.refreshContentView(); //needed to refresh cell contents while swipping 236 | return true; //autohide 237 | }); 238 | 239 | let more = MGSwipeButton(title: "More", backgroundColor: color3, padding: padding, callback: { (cell) -> Bool in 240 | let path = self.tableView.indexPath(for: cell)!; 241 | let mail = self.mailForIndexPath(path); 242 | 243 | self.showMailActions(mail, callback: { (cancelled, deleted, index) in 244 | if cancelled { 245 | return; 246 | } 247 | else if deleted { 248 | self.deleteMail(path); 249 | } 250 | else if index == 1 { 251 | mail.read = !mail.read; 252 | self.updateCellIndicator(mail, cell: cell as! MailTableCell); 253 | cell.refreshContentView(); 254 | (cell.leftButtons[0] as! UIButton).setTitle(self.readButtonText(mail.read), for: UIControl.State()); 255 | cell.hideSwipe(animated: true); 256 | } 257 | else if index == 2 { 258 | mail.flag = !mail.flag; 259 | self.updateCellIndicator(mail, cell: cell as! MailTableCell); 260 | cell.refreshContentView(); //needed to refresh cell contents while swipping 261 | cell.hideSwipe(animated: true); 262 | } 263 | 264 | }); 265 | 266 | return false; // Don't autohide 267 | }); 268 | 269 | return [trash, flag, more]; 270 | } 271 | 272 | } 273 | 274 | 275 | 276 | } 277 | 278 | -------------------------------------------------------------------------------- /demo/MailAppDemoSwift/MailAppDemoSwift/ObjCBridgingHeader.h: -------------------------------------------------------------------------------- 1 | #import "MGSwipeTableCell.h" 2 | #import "MGSwipeButton.h" -------------------------------------------------------------------------------- /demo/MailAppDemoSwift/MailAppDemoSwiftTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /demo/MailAppDemoSwift/MailAppDemoSwiftTests/MailAppDemoSwiftTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MailAppDemoSwiftTests.swift 3 | // MailAppDemoSwiftTests 4 | // 5 | // Created by Imanol Fernandez Gorostizag on 17/11/14. 6 | // Copyright (c) 2014 Imanol Fernandez. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import XCTest 11 | 12 | class MailAppDemoSwiftTests: XCTestCase { 13 | 14 | override func setUp() { 15 | super.setUp() 16 | // Put setup code here. This method is called before the invocation of each test method in the class. 17 | } 18 | 19 | override func tearDown() { 20 | // Put teardown code here. This method is called after the invocation of each test method in the class. 21 | super.tearDown() 22 | } 23 | 24 | func testExample() { 25 | // This is an example of a functional test case. 26 | XCTAssert(true, "Pass") 27 | } 28 | 29 | func testPerformanceExample() { 30 | // This is an example of a performance test case. 31 | self.measure() { 32 | // Put the code you want to measure the time of here. 33 | } 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /demo/SpotifyDemo/SpotifyDemo.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 110B828F1B5E9E7C00423D6D /* more@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 110B828E1B5E9E7C00423D6D /* more@2x.png */; }; 11 | 11754E2919D5C1EA009CE7B5 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 11754E2819D5C1EA009CE7B5 /* main.m */; }; 12 | 11754E2C19D5C1EA009CE7B5 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 11754E2B19D5C1EA009CE7B5 /* AppDelegate.m */; }; 13 | 11754E2F19D5C1EA009CE7B5 /* SpotifyViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 11754E2E19D5C1EA009CE7B5 /* SpotifyViewController.m */; }; 14 | 11754E3419D5C1EA009CE7B5 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 11754E3319D5C1EA009CE7B5 /* Images.xcassets */; }; 15 | 11754E5419D5C3E5009CE7B5 /* MGSwipeButton.m in Sources */ = {isa = PBXBuildFile; fileRef = 11754E5219D5C3E5009CE7B5 /* MGSwipeButton.m */; }; 16 | 11754E5519D5C3E5009CE7B5 /* MGSwipeTableCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 11754E5319D5C3E5009CE7B5 /* MGSwipeTableCell.m */; }; 17 | /* End PBXBuildFile section */ 18 | 19 | /* Begin PBXFileReference section */ 20 | 110B828E1B5E9E7C00423D6D /* more@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "more@2x.png"; sourceTree = ""; }; 21 | 11754E2319D5C1EA009CE7B5 /* SpotifyDemo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = SpotifyDemo.app; sourceTree = BUILT_PRODUCTS_DIR; }; 22 | 11754E2719D5C1EA009CE7B5 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 23 | 11754E2819D5C1EA009CE7B5 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 24 | 11754E2A19D5C1EA009CE7B5 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 25 | 11754E2B19D5C1EA009CE7B5 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; 26 | 11754E2D19D5C1EA009CE7B5 /* SpotifyViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SpotifyViewController.h; sourceTree = ""; }; 27 | 11754E2E19D5C1EA009CE7B5 /* SpotifyViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SpotifyViewController.m; sourceTree = ""; }; 28 | 11754E3319D5C1EA009CE7B5 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; 29 | 11754E5019D5C3E5009CE7B5 /* MGSwipeButton.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MGSwipeButton.h; path = ../../MGSwipeTableCell/MGSwipeButton.h; sourceTree = ""; }; 30 | 11754E5119D5C3E5009CE7B5 /* MGSwipeTableCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MGSwipeTableCell.h; path = ../../MGSwipeTableCell/MGSwipeTableCell.h; sourceTree = ""; }; 31 | 11754E5219D5C3E5009CE7B5 /* MGSwipeButton.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = MGSwipeButton.m; path = ../../MGSwipeTableCell/MGSwipeButton.m; sourceTree = ""; }; 32 | 11754E5319D5C3E5009CE7B5 /* MGSwipeTableCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = MGSwipeTableCell.m; path = ../../MGSwipeTableCell/MGSwipeTableCell.m; sourceTree = ""; }; 33 | /* End PBXFileReference section */ 34 | 35 | /* Begin PBXFrameworksBuildPhase section */ 36 | 11754E2019D5C1EA009CE7B5 /* Frameworks */ = { 37 | isa = PBXFrameworksBuildPhase; 38 | buildActionMask = 2147483647; 39 | files = ( 40 | ); 41 | runOnlyForDeploymentPostprocessing = 0; 42 | }; 43 | /* End PBXFrameworksBuildPhase section */ 44 | 45 | /* Begin PBXGroup section */ 46 | 11754E1A19D5C1EA009CE7B5 = { 47 | isa = PBXGroup; 48 | children = ( 49 | 11754E4F19D5C3CE009CE7B5 /* MGSwipeTableCell */, 50 | 11754E2519D5C1EA009CE7B5 /* SpotifyDemo */, 51 | 11754E2419D5C1EA009CE7B5 /* Products */, 52 | ); 53 | sourceTree = ""; 54 | }; 55 | 11754E2419D5C1EA009CE7B5 /* Products */ = { 56 | isa = PBXGroup; 57 | children = ( 58 | 11754E2319D5C1EA009CE7B5 /* SpotifyDemo.app */, 59 | ); 60 | name = Products; 61 | sourceTree = ""; 62 | }; 63 | 11754E2519D5C1EA009CE7B5 /* SpotifyDemo */ = { 64 | isa = PBXGroup; 65 | children = ( 66 | 110B828E1B5E9E7C00423D6D /* more@2x.png */, 67 | 11754E2A19D5C1EA009CE7B5 /* AppDelegate.h */, 68 | 11754E2B19D5C1EA009CE7B5 /* AppDelegate.m */, 69 | 11754E2D19D5C1EA009CE7B5 /* SpotifyViewController.h */, 70 | 11754E2E19D5C1EA009CE7B5 /* SpotifyViewController.m */, 71 | 11754E3319D5C1EA009CE7B5 /* Images.xcassets */, 72 | 11754E2619D5C1EA009CE7B5 /* Supporting Files */, 73 | ); 74 | path = SpotifyDemo; 75 | sourceTree = ""; 76 | }; 77 | 11754E2619D5C1EA009CE7B5 /* Supporting Files */ = { 78 | isa = PBXGroup; 79 | children = ( 80 | 11754E2719D5C1EA009CE7B5 /* Info.plist */, 81 | 11754E2819D5C1EA009CE7B5 /* main.m */, 82 | ); 83 | name = "Supporting Files"; 84 | sourceTree = ""; 85 | }; 86 | 11754E4F19D5C3CE009CE7B5 /* MGSwipeTableCell */ = { 87 | isa = PBXGroup; 88 | children = ( 89 | 11754E5019D5C3E5009CE7B5 /* MGSwipeButton.h */, 90 | 11754E5219D5C3E5009CE7B5 /* MGSwipeButton.m */, 91 | 11754E5119D5C3E5009CE7B5 /* MGSwipeTableCell.h */, 92 | 11754E5319D5C3E5009CE7B5 /* MGSwipeTableCell.m */, 93 | ); 94 | name = MGSwipeTableCell; 95 | sourceTree = ""; 96 | }; 97 | /* End PBXGroup section */ 98 | 99 | /* Begin PBXNativeTarget section */ 100 | 11754E2219D5C1EA009CE7B5 /* SpotifyDemo */ = { 101 | isa = PBXNativeTarget; 102 | buildConfigurationList = 11754E4619D5C1EA009CE7B5 /* Build configuration list for PBXNativeTarget "SpotifyDemo" */; 103 | buildPhases = ( 104 | 11754E1F19D5C1EA009CE7B5 /* Sources */, 105 | 11754E2019D5C1EA009CE7B5 /* Frameworks */, 106 | 11754E2119D5C1EA009CE7B5 /* Resources */, 107 | ); 108 | buildRules = ( 109 | ); 110 | dependencies = ( 111 | ); 112 | name = SpotifyDemo; 113 | productName = SpotifyDemo; 114 | productReference = 11754E2319D5C1EA009CE7B5 /* SpotifyDemo.app */; 115 | productType = "com.apple.product-type.application"; 116 | }; 117 | /* End PBXNativeTarget section */ 118 | 119 | /* Begin PBXProject section */ 120 | 11754E1B19D5C1EA009CE7B5 /* Project object */ = { 121 | isa = PBXProject; 122 | attributes = { 123 | LastUpgradeCheck = 1320; 124 | ORGANIZATIONNAME = Mortimer; 125 | TargetAttributes = { 126 | 11754E2219D5C1EA009CE7B5 = { 127 | CreatedOnToolsVersion = 6.0.1; 128 | }; 129 | }; 130 | }; 131 | buildConfigurationList = 11754E1E19D5C1EA009CE7B5 /* Build configuration list for PBXProject "SpotifyDemo" */; 132 | compatibilityVersion = "Xcode 3.2"; 133 | developmentRegion = English; 134 | hasScannedForEncodings = 0; 135 | knownRegions = ( 136 | English, 137 | en, 138 | Base, 139 | ); 140 | mainGroup = 11754E1A19D5C1EA009CE7B5; 141 | productRefGroup = 11754E2419D5C1EA009CE7B5 /* Products */; 142 | projectDirPath = ""; 143 | projectRoot = ""; 144 | targets = ( 145 | 11754E2219D5C1EA009CE7B5 /* SpotifyDemo */, 146 | ); 147 | }; 148 | /* End PBXProject section */ 149 | 150 | /* Begin PBXResourcesBuildPhase section */ 151 | 11754E2119D5C1EA009CE7B5 /* Resources */ = { 152 | isa = PBXResourcesBuildPhase; 153 | buildActionMask = 2147483647; 154 | files = ( 155 | 11754E3419D5C1EA009CE7B5 /* Images.xcassets in Resources */, 156 | 110B828F1B5E9E7C00423D6D /* more@2x.png in Resources */, 157 | ); 158 | runOnlyForDeploymentPostprocessing = 0; 159 | }; 160 | /* End PBXResourcesBuildPhase section */ 161 | 162 | /* Begin PBXSourcesBuildPhase section */ 163 | 11754E1F19D5C1EA009CE7B5 /* Sources */ = { 164 | isa = PBXSourcesBuildPhase; 165 | buildActionMask = 2147483647; 166 | files = ( 167 | 11754E2F19D5C1EA009CE7B5 /* SpotifyViewController.m in Sources */, 168 | 11754E5519D5C3E5009CE7B5 /* MGSwipeTableCell.m in Sources */, 169 | 11754E5419D5C3E5009CE7B5 /* MGSwipeButton.m in Sources */, 170 | 11754E2C19D5C1EA009CE7B5 /* AppDelegate.m in Sources */, 171 | 11754E2919D5C1EA009CE7B5 /* main.m in Sources */, 172 | ); 173 | runOnlyForDeploymentPostprocessing = 0; 174 | }; 175 | /* End PBXSourcesBuildPhase section */ 176 | 177 | /* Begin XCBuildConfiguration section */ 178 | 11754E4419D5C1EA009CE7B5 /* Debug */ = { 179 | isa = XCBuildConfiguration; 180 | buildSettings = { 181 | ALWAYS_SEARCH_USER_PATHS = NO; 182 | CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; 183 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 184 | CLANG_CXX_LIBRARY = "libc++"; 185 | CLANG_ENABLE_MODULES = YES; 186 | CLANG_ENABLE_OBJC_ARC = YES; 187 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 188 | CLANG_WARN_BOOL_CONVERSION = YES; 189 | CLANG_WARN_COMMA = YES; 190 | CLANG_WARN_CONSTANT_CONVERSION = YES; 191 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 192 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 193 | CLANG_WARN_EMPTY_BODY = YES; 194 | CLANG_WARN_ENUM_CONVERSION = YES; 195 | CLANG_WARN_INFINITE_RECURSION = YES; 196 | CLANG_WARN_INT_CONVERSION = YES; 197 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 198 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 199 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 200 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 201 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 202 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 203 | CLANG_WARN_STRICT_PROTOTYPES = YES; 204 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 205 | CLANG_WARN_UNREACHABLE_CODE = YES; 206 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 207 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 208 | COPY_PHASE_STRIP = NO; 209 | ENABLE_STRICT_OBJC_MSGSEND = YES; 210 | ENABLE_TESTABILITY = YES; 211 | GCC_C_LANGUAGE_STANDARD = gnu99; 212 | GCC_DYNAMIC_NO_PIC = NO; 213 | GCC_NO_COMMON_BLOCKS = YES; 214 | GCC_OPTIMIZATION_LEVEL = 0; 215 | GCC_PREPROCESSOR_DEFINITIONS = ( 216 | "DEBUG=1", 217 | "$(inherited)", 218 | ); 219 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 220 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 221 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 222 | GCC_WARN_UNDECLARED_SELECTOR = YES; 223 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 224 | GCC_WARN_UNUSED_FUNCTION = YES; 225 | GCC_WARN_UNUSED_VARIABLE = YES; 226 | IPHONEOS_DEPLOYMENT_TARGET = 12.0; 227 | MTL_ENABLE_DEBUG_INFO = YES; 228 | ONLY_ACTIVE_ARCH = YES; 229 | SDKROOT = iphoneos; 230 | TARGETED_DEVICE_FAMILY = "1,2"; 231 | }; 232 | name = Debug; 233 | }; 234 | 11754E4519D5C1EA009CE7B5 /* Release */ = { 235 | isa = XCBuildConfiguration; 236 | buildSettings = { 237 | ALWAYS_SEARCH_USER_PATHS = NO; 238 | CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; 239 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 240 | CLANG_CXX_LIBRARY = "libc++"; 241 | CLANG_ENABLE_MODULES = YES; 242 | CLANG_ENABLE_OBJC_ARC = YES; 243 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 244 | CLANG_WARN_BOOL_CONVERSION = YES; 245 | CLANG_WARN_COMMA = YES; 246 | CLANG_WARN_CONSTANT_CONVERSION = YES; 247 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 248 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 249 | CLANG_WARN_EMPTY_BODY = YES; 250 | CLANG_WARN_ENUM_CONVERSION = YES; 251 | CLANG_WARN_INFINITE_RECURSION = YES; 252 | CLANG_WARN_INT_CONVERSION = YES; 253 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 254 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 255 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 256 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 257 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 258 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 259 | CLANG_WARN_STRICT_PROTOTYPES = YES; 260 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 261 | CLANG_WARN_UNREACHABLE_CODE = YES; 262 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 263 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 264 | COPY_PHASE_STRIP = YES; 265 | ENABLE_NS_ASSERTIONS = NO; 266 | ENABLE_STRICT_OBJC_MSGSEND = YES; 267 | GCC_C_LANGUAGE_STANDARD = gnu99; 268 | GCC_NO_COMMON_BLOCKS = YES; 269 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 270 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 271 | GCC_WARN_UNDECLARED_SELECTOR = YES; 272 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 273 | GCC_WARN_UNUSED_FUNCTION = YES; 274 | GCC_WARN_UNUSED_VARIABLE = YES; 275 | IPHONEOS_DEPLOYMENT_TARGET = 12.0; 276 | MTL_ENABLE_DEBUG_INFO = NO; 277 | SDKROOT = iphoneos; 278 | TARGETED_DEVICE_FAMILY = "1,2"; 279 | VALIDATE_PRODUCT = YES; 280 | }; 281 | name = Release; 282 | }; 283 | 11754E4719D5C1EA009CE7B5 /* Debug */ = { 284 | isa = XCBuildConfiguration; 285 | buildSettings = { 286 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 287 | ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; 288 | ENABLE_BITCODE = NO; 289 | INFOPLIST_FILE = SpotifyDemo/Info.plist; 290 | IPHONEOS_DEPLOYMENT_TARGET = 12.0; 291 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 292 | PRODUCT_BUNDLE_IDENTIFIER = com.mortimergoro.spotifydemo; 293 | PRODUCT_NAME = SpotifyDemo; 294 | }; 295 | name = Debug; 296 | }; 297 | 11754E4819D5C1EA009CE7B5 /* Release */ = { 298 | isa = XCBuildConfiguration; 299 | buildSettings = { 300 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 301 | ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; 302 | ENABLE_BITCODE = NO; 303 | INFOPLIST_FILE = SpotifyDemo/Info.plist; 304 | IPHONEOS_DEPLOYMENT_TARGET = 12.0; 305 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 306 | PRODUCT_BUNDLE_IDENTIFIER = com.mortimergoro.spotifydemo; 307 | PRODUCT_NAME = SpotifyDemo; 308 | }; 309 | name = Release; 310 | }; 311 | /* End XCBuildConfiguration section */ 312 | 313 | /* Begin XCConfigurationList section */ 314 | 11754E1E19D5C1EA009CE7B5 /* Build configuration list for PBXProject "SpotifyDemo" */ = { 315 | isa = XCConfigurationList; 316 | buildConfigurations = ( 317 | 11754E4419D5C1EA009CE7B5 /* Debug */, 318 | 11754E4519D5C1EA009CE7B5 /* Release */, 319 | ); 320 | defaultConfigurationIsVisible = 0; 321 | defaultConfigurationName = Release; 322 | }; 323 | 11754E4619D5C1EA009CE7B5 /* Build configuration list for PBXNativeTarget "SpotifyDemo" */ = { 324 | isa = XCConfigurationList; 325 | buildConfigurations = ( 326 | 11754E4719D5C1EA009CE7B5 /* Debug */, 327 | 11754E4819D5C1EA009CE7B5 /* Release */, 328 | ); 329 | defaultConfigurationIsVisible = 0; 330 | defaultConfigurationName = Release; 331 | }; 332 | /* End XCConfigurationList section */ 333 | }; 334 | rootObject = 11754E1B19D5C1EA009CE7B5 /* Project object */; 335 | } 336 | -------------------------------------------------------------------------------- /demo/SpotifyDemo/SpotifyDemo.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /demo/SpotifyDemo/SpotifyDemo/AppDelegate.h: -------------------------------------------------------------------------------- 1 | /* 2 | * MGSwipeTableCell is licensed under MIT license. See LICENSE.md file for more information. 3 | * Copyright (c) 2014 Imanol Fernandez @MortimerGoro 4 | */ 5 | 6 | #import 7 | 8 | @interface AppDelegate : UIResponder 9 | 10 | @property (strong, nonatomic) UIWindow *window; 11 | 12 | 13 | @end 14 | 15 | -------------------------------------------------------------------------------- /demo/SpotifyDemo/SpotifyDemo/AppDelegate.m: -------------------------------------------------------------------------------- 1 | /* 2 | * MGSwipeTableCell is licensed under MIT license. See LICENSE.md file for more information. 3 | * Copyright (c) 2014 Imanol Fernandez @MortimerGoro 4 | */ 5 | 6 | #import "AppDelegate.h" 7 | #import "SpotifyViewController.h" 8 | 9 | @implementation AppDelegate 10 | 11 | 12 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 13 | 14 | self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; 15 | 16 | SpotifyViewController * vc = [[SpotifyViewController alloc] init]; 17 | UINavigationController * navigation = [[UINavigationController alloc] initWithRootViewController:vc]; 18 | navigation.navigationBar.translucent = NO; 19 | self.window.rootViewController = navigation; 20 | 21 | self.window.backgroundColor = [UIColor whiteColor]; 22 | [self.window makeKeyAndVisible]; 23 | return YES; 24 | } 25 | 26 | - (void)applicationWillResignActive:(UIApplication *)application { 27 | // 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. 28 | // 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. 29 | } 30 | 31 | - (void)applicationDidEnterBackground:(UIApplication *)application { 32 | // 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. 33 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 34 | } 35 | 36 | - (void)applicationWillEnterForeground:(UIApplication *)application { 37 | // 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. 38 | } 39 | 40 | - (void)applicationDidBecomeActive:(UIApplication *)application { 41 | // 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. 42 | } 43 | 44 | - (void)applicationWillTerminate:(UIApplication *)application { 45 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 46 | } 47 | 48 | @end 49 | -------------------------------------------------------------------------------- /demo/SpotifyDemo/SpotifyDemo/Images.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "29x29", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "29x29", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "40x40", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "40x40", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "60x60", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "60x60", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "idiom" : "ipad", 35 | "size" : "29x29", 36 | "scale" : "1x" 37 | }, 38 | { 39 | "idiom" : "ipad", 40 | "size" : "29x29", 41 | "scale" : "2x" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "size" : "40x40", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "size" : "40x40", 51 | "scale" : "2x" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "size" : "76x76", 56 | "scale" : "1x" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "size" : "76x76", 61 | "scale" : "2x" 62 | } 63 | ], 64 | "info" : { 65 | "version" : 1, 66 | "author" : "xcode" 67 | } 68 | } -------------------------------------------------------------------------------- /demo/SpotifyDemo/SpotifyDemo/Images.xcassets/LaunchImage.launchimage/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "scale" : "1x", 6 | "orientation" : "portrait" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "scale" : "2x", 11 | "orientation" : "portrait" 12 | }, 13 | { 14 | "orientation" : "portrait", 15 | "idiom" : "iphone", 16 | "subtype" : "retina4", 17 | "scale" : "2x" 18 | }, 19 | { 20 | "orientation" : "portrait", 21 | "idiom" : "iphone", 22 | "minimum-system-version" : "7.0", 23 | "scale" : "2x" 24 | }, 25 | { 26 | "orientation" : "portrait", 27 | "idiom" : "iphone", 28 | "minimum-system-version" : "7.0", 29 | "subtype" : "retina4", 30 | "scale" : "2x" 31 | }, 32 | { 33 | "orientation" : "portrait", 34 | "idiom" : "ipad", 35 | "extent" : "to-status-bar", 36 | "scale" : "1x" 37 | }, 38 | { 39 | "orientation" : "portrait", 40 | "idiom" : "ipad", 41 | "extent" : "to-status-bar", 42 | "scale" : "2x" 43 | }, 44 | { 45 | "orientation" : "landscape", 46 | "idiom" : "ipad", 47 | "extent" : "to-status-bar", 48 | "scale" : "1x" 49 | }, 50 | { 51 | "orientation" : "landscape", 52 | "idiom" : "ipad", 53 | "extent" : "to-status-bar", 54 | "scale" : "2x" 55 | }, 56 | { 57 | "orientation" : "portrait", 58 | "idiom" : "ipad", 59 | "minimum-system-version" : "7.0", 60 | "extent" : "full-screen", 61 | "scale" : "1x" 62 | }, 63 | { 64 | "orientation" : "portrait", 65 | "idiom" : "ipad", 66 | "minimum-system-version" : "7.0", 67 | "extent" : "full-screen", 68 | "scale" : "2x" 69 | }, 70 | { 71 | "orientation" : "landscape", 72 | "idiom" : "ipad", 73 | "minimum-system-version" : "7.0", 74 | "extent" : "full-screen", 75 | "scale" : "1x" 76 | }, 77 | { 78 | "orientation" : "landscape", 79 | "idiom" : "ipad", 80 | "minimum-system-version" : "7.0", 81 | "extent" : "full-screen", 82 | "scale" : "2x" 83 | } 84 | ], 85 | "info" : { 86 | "version" : 1, 87 | "author" : "xcode" 88 | } 89 | } -------------------------------------------------------------------------------- /demo/SpotifyDemo/SpotifyDemo/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIRequiredDeviceCapabilities 28 | 29 | armv7 30 | 31 | UIStatusBarHidden 32 | 33 | UISupportedInterfaceOrientations 34 | 35 | UIInterfaceOrientationPortrait 36 | UIInterfaceOrientationLandscapeLeft 37 | UIInterfaceOrientationLandscapeRight 38 | 39 | UISupportedInterfaceOrientations~ipad 40 | 41 | UIInterfaceOrientationPortrait 42 | UIInterfaceOrientationPortraitUpsideDown 43 | UIInterfaceOrientationLandscapeLeft 44 | UIInterfaceOrientationLandscapeRight 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /demo/SpotifyDemo/SpotifyDemo/SpotifyViewController.h: -------------------------------------------------------------------------------- 1 | /* 2 | * MGSwipeTableCell is licensed under MIT license. See LICENSE.md file for more information. 3 | * Copyright (c) 2014 Imanol Fernandez @MortimerGoro 4 | */ 5 | 6 | #import 7 | #import "MGSwipeTableCell.h" 8 | 9 | @interface SpotifyViewController : UIViewController 10 | 11 | @property (nonatomic, strong) UITableView * tableView; 12 | 13 | @end 14 | 15 | -------------------------------------------------------------------------------- /demo/SpotifyDemo/SpotifyDemo/SpotifyViewController.m: -------------------------------------------------------------------------------- 1 | /* 2 | * MGSwipeTableCell is licensed under MIT license. See LICENSE.md file for more information. 3 | * Copyright (c) 2014 Imanol Fernandez @MortimerGoro 4 | */ 5 | 6 | #import "SpotifyViewController.h" 7 | #import "MGSwipeButton.h" 8 | 9 | 10 | @interface SongData : NSObject 11 | @property (nonatomic, strong) NSString * title; 12 | @property (nonatomic, strong) NSString * album; 13 | @end 14 | 15 | @implementation SongData 16 | @end 17 | 18 | 19 | typedef void(^MailActionCallback)(BOOL cancelled, BOOL deleted, NSInteger actionIndex); 20 | 21 | @implementation SpotifyViewController 22 | { 23 | NSMutableArray * demoData; 24 | MailActionCallback actionCallback; 25 | UIRefreshControl * refreshControl; 26 | } 27 | 28 | -(BOOL) prefersStatusBarHidden 29 | { 30 | return YES; 31 | } 32 | 33 | 34 | -(void) prepareDemoData 35 | { 36 | demoData = [NSMutableArray array]; 37 | 38 | NSArray * titles = @[ 39 | @"Vincent", 40 | @"Mr Glass", 41 | @"Marsellus", 42 | @"Ringo", 43 | @"Sullivan", 44 | @"Mr Wolf", 45 | @"Butch Coolidge", 46 | @"Marvin", 47 | @"Captain Koons", 48 | @"Jules", 49 | @"Jimmie Dimmick" 50 | ]; 51 | 52 | NSArray * albums = @[ 53 | @"You think water moves fast?", 54 | @"They called me Mr Glass", 55 | @"The path of the righteous man", 56 | @"Do you see any Teletubbies in here?", 57 | @"Now that we know who you are", 58 | @"My money's in that office, right?", 59 | @"Now we took an oath", 60 | @"That show's called a pilot", 61 | @"I know who I am. I'm not a mistake", 62 | @"It all makes sense!", 63 | @"The selfish and the tyranny of evil men", 64 | ]; 65 | 66 | for (int i = 0; i < titles.count; ++i) { 67 | SongData * song = [[SongData alloc] init]; 68 | song.title = [titles objectAtIndex:i]; 69 | song.album = [albums objectAtIndex:i]; 70 | [demoData addObject:song]; 71 | } 72 | } 73 | 74 | -(void) refreshCallback 75 | { 76 | [self prepareDemoData]; 77 | [_tableView reloadData]; 78 | [refreshControl endRefreshing]; 79 | } 80 | 81 | - (void)viewDidLoad { 82 | [super viewDidLoad]; 83 | 84 | [self.navigationController setNavigationBarHidden:YES animated:NO]; 85 | 86 | _tableView = [[UITableView alloc] initWithFrame:self.view.bounds style:UITableViewStylePlain]; 87 | _tableView.delegate = self; 88 | _tableView.dataSource = self; 89 | _tableView.separatorColor = [UIColor clearColor]; 90 | _tableView.backgroundColor = [UIColor colorWithRed:15/255.0 green:16/255.0 blue:16/255.0 alpha:1.0]; 91 | [self.view addSubview:_tableView]; 92 | 93 | self.title = @"Spotify App Demo"; 94 | 95 | refreshControl = [[UIRefreshControl alloc] init]; 96 | [refreshControl addTarget:self action:@selector(refreshCallback) forControlEvents:UIControlEventValueChanged]; 97 | [self.tableView addSubview:refreshControl]; 98 | 99 | [self prepareDemoData]; 100 | } 101 | 102 | -(void) viewDidAppear:(BOOL)animated 103 | { 104 | [self.tableView reloadData]; 105 | } 106 | 107 | -(void) viewWillLayoutSubviews 108 | { 109 | [super viewWillLayoutSubviews]; 110 | _tableView.frame = self.view.bounds; 111 | } 112 | 113 | -(void) deleteMail:(NSIndexPath *) indexPath 114 | { 115 | [demoData removeObjectAtIndex:indexPath.row]; 116 | [_tableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationLeft]; 117 | } 118 | 119 | -(SongData *) songForIndexPath:(NSIndexPath*) path 120 | { 121 | return [demoData objectAtIndex:path.row]; 122 | } 123 | 124 | #pragma mark Table Delegate 125 | 126 | - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section 127 | { 128 | return demoData.count; 129 | } 130 | 131 | - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath 132 | { 133 | static NSString * identifier = @"SongCell"; 134 | MGSwipeTableCell * cell = [tableView dequeueReusableCellWithIdentifier:identifier]; 135 | if (!cell) { 136 | cell = [[MGSwipeTableCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:identifier]; 137 | cell.textLabel.font = [UIFont fontWithName:@"HelveticaNeue-Light" size:21.0f]; 138 | cell.textLabel.textColor = [UIColor colorWithRed:152/255.0 green:152/255.0 blue:157/255.0 alpha:1.0]; 139 | cell.textLabel.font = [UIFont fontWithName:@"HelveticaNeue-Light" size:17.0f]; 140 | cell.detailTextLabel.textColor = cell.textLabel.textColor; 141 | cell.backgroundColor = [UIColor colorWithRed:15/255.0 green:16/255.0 blue:16/255.0 alpha:1.0]; 142 | cell.selectionStyle = UITableViewCellSelectionStyleNone; 143 | 144 | UIImageView * view = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"more.png"]]; 145 | view.contentMode = UIViewContentModeScaleAspectFit; 146 | view.frame = CGRectMake(0, 0, 25, 25); 147 | cell.accessoryView = view; 148 | } 149 | cell.delegate = self; 150 | 151 | SongData * data = [demoData objectAtIndex:indexPath.row]; 152 | cell.textLabel.text = data.title; 153 | cell.detailTextLabel.text = data.album; 154 | return cell; 155 | } 156 | 157 | - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath 158 | { 159 | return 50; 160 | } 161 | 162 | #pragma mark Swipe Delegate 163 | 164 | -(BOOL) swipeTableCell:(MGSwipeTableCell*) cell canSwipe:(MGSwipeDirection) direction; 165 | { 166 | return YES; 167 | } 168 | 169 | -(NSArray*) swipeTableCell:(MGSwipeTableCell*) cell swipeButtonsForDirection:(MGSwipeDirection)direction 170 | swipeSettings:(MGSwipeSettings*) swipeSettings expansionSettings:(MGSwipeExpansionSettings*) expansionSettings 171 | { 172 | 173 | swipeSettings.transition = MGSwipeTransitionClipCenter; 174 | swipeSettings.keepButtonsSwiped = NO; 175 | expansionSettings.buttonIndex = 0; 176 | expansionSettings.threshold = 1.0; 177 | expansionSettings.expansionLayout = MGSwipeExpansionLayoutCenter; 178 | expansionSettings.expansionColor = [UIColor colorWithRed:33/255.0 green:175/255.0 blue:67/255.0 alpha:1.0]; 179 | expansionSettings.triggerAnimation.easingFunction = MGSwipeEasingFunctionCubicOut; 180 | expansionSettings.fillOnTrigger = NO; 181 | 182 | __weak SpotifyViewController * me = self; 183 | UIColor * color = [UIColor colorWithRed:47/255.0 green:47/255.0 blue:49/255.0 alpha:1.0]; 184 | UIFont * font = [UIFont fontWithName:@"HelveticaNeue-Light" size:14.0f]; 185 | if (direction == MGSwipeDirectionLeftToRight) { 186 | MGSwipeButton * queueButton = [MGSwipeButton buttonWithTitle:@"QUEUE" backgroundColor:color padding:15 callback:^BOOL(MGSwipeTableCell *sender) { 187 | SongData * song = [me songForIndexPath:[me.tableView indexPathForCell:sender]]; 188 | NSLog(@"Queue song: %@", song.title); 189 | return YES; 190 | }]; 191 | queueButton.titleLabel.font = font; 192 | 193 | return @[queueButton]; 194 | } 195 | else { 196 | 197 | MGSwipeButton * saveButton = [MGSwipeButton buttonWithTitle:@"SAVE" backgroundColor:color padding:15 callback:^BOOL(MGSwipeTableCell *sender) { 198 | SongData * song = [me songForIndexPath:[me.tableView indexPathForCell:sender]]; 199 | NSLog(@"Save song: %@", song.title); 200 | return YES; //don't autohide to improve delete animation 201 | }]; 202 | saveButton.titleLabel.font = font; 203 | return @[saveButton]; 204 | } 205 | 206 | return nil; 207 | 208 | } 209 | 210 | -(void) swipeTableCell:(MGSwipeTableCell*) cell didChangeSwipeState:(MGSwipeState)state gestureIsActive:(BOOL)gestureIsActive 211 | { 212 | NSString * str; 213 | switch (state) { 214 | case MGSwipeStateNone: str = @"None"; break; 215 | case MGSwipeStateSwippingLeftToRight: str = @"SwippingLeftToRight"; break; 216 | case MGSwipeStateSwippingRightToLeft: str = @"SwippingRightToLeft"; break; 217 | case MGSwipeStateExpandingLeftToRight: str = @"ExpandingLeftToRight"; break; 218 | case MGSwipeStateExpandingRightToLeft: str = @"ExpandingRightToLeft"; break; 219 | } 220 | NSLog(@"Swipe state: %@ ::: Gesture: %@", str, gestureIsActive ? @"Active" : @"Ended"); 221 | } 222 | 223 | @end 224 | -------------------------------------------------------------------------------- /demo/SpotifyDemo/SpotifyDemo/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // MailAppDemo 4 | // 5 | // Created by Imanol Fernandez Gorostizaga on 26/09/14. 6 | // Copyright (c) 2014 Mortimer. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "AppDelegate.h" 11 | 12 | int main(int argc, char * argv[]) { 13 | @autoreleasepool { 14 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /demo/SpotifyDemo/SpotifyDemo/more@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MortimerGoro/MGSwipeTableCell/2d02e9d228b4684eda87ea696cdb3dd77713e782/demo/SpotifyDemo/SpotifyDemo/more@2x.png -------------------------------------------------------------------------------- /readme-assets/3d.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MortimerGoro/MGSwipeTableCell/2d02e9d228b4684eda87ea696cdb3dd77713e782/readme-assets/3d.gif -------------------------------------------------------------------------------- /readme-assets/RoundTableViewCell.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MortimerGoro/MGSwipeTableCell/2d02e9d228b4684eda87ea696cdb3dd77713e782/readme-assets/RoundTableViewCell.png -------------------------------------------------------------------------------- /readme-assets/border.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MortimerGoro/MGSwipeTableCell/2d02e9d228b4684eda87ea696cdb3dd77713e782/readme-assets/border.gif -------------------------------------------------------------------------------- /readme-assets/clip.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MortimerGoro/MGSwipeTableCell/2d02e9d228b4684eda87ea696cdb3dd77713e782/readme-assets/clip.gif -------------------------------------------------------------------------------- /readme-assets/drag.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MortimerGoro/MGSwipeTableCell/2d02e9d228b4684eda87ea696cdb3dd77713e782/readme-assets/drag.gif -------------------------------------------------------------------------------- /readme-assets/static.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MortimerGoro/MGSwipeTableCell/2d02e9d228b4684eda87ea696cdb3dd77713e782/readme-assets/static.gif --------------------------------------------------------------------------------