├── .gitignore ├── Demo.gif ├── Examples ├── Advanced │ ├── JGScrollableTableViewCell Advanced.xcodeproj │ │ ├── project.pbxproj │ │ └── project.xcworkspace │ │ │ └── contents.xcworkspacedata │ └── JGScrollableTableViewCell Advanced │ │ ├── Images.xcassets │ │ ├── AppIcon.appiconset │ │ │ └── Contents.json │ │ └── LaunchImage.launchimage │ │ │ └── Contents.json │ │ ├── JGAppDelegate.h │ │ ├── JGAppDelegate.m │ │ ├── JGExampleScrollableTableViewCell.h │ │ ├── JGExampleScrollableTableViewCell.m │ │ ├── JGScrollableTableViewCell Advanced-Info.plist │ │ ├── JGScrollableTableViewCell Advanced-Prefix.pch │ │ ├── JGTestViewController.h │ │ ├── JGTestViewController.m │ │ ├── MMDrawerController │ │ ├── MMDrawerBarButtonItem.h │ │ ├── MMDrawerBarButtonItem.m │ │ ├── MMDrawerController+Subclass.h │ │ ├── MMDrawerController.h │ │ ├── MMDrawerController.m │ │ ├── MMDrawerVisualState.h │ │ ├── MMDrawerVisualState.m │ │ ├── UIViewController+MMDrawerController.h │ │ └── UIViewController+MMDrawerController.m │ │ ├── en.lproj │ │ └── InfoPlist.strings │ │ └── main.m └── Basic │ ├── JGScrollableTableViewCell Basic.xcodeproj │ ├── project.pbxproj │ └── project.xcworkspace │ │ └── contents.xcworkspacedata │ └── JGScrollableTableViewCell Basic │ ├── Images.xcassets │ ├── AppIcon.appiconset │ │ └── Contents.json │ └── LaunchImage.launchimage │ │ └── Contents.json │ ├── JGAppDelegate.h │ ├── JGAppDelegate.m │ ├── JGExampleScrollableTableViewCell.h │ ├── JGExampleScrollableTableViewCell.m │ ├── JGScrollableTableViewCell Basic-Info.plist │ ├── JGScrollableTableViewCell Basic-Prefix.pch │ ├── JGTestViewController.h │ ├── JGTestViewController.m │ ├── en.lproj │ └── InfoPlist.strings │ └── main.m ├── JGScrollableTableViewCell.podspec ├── JGScrollableTableViewCell ├── JGScrollableTableViewCell.h ├── JGScrollableTableViewCell.m ├── JGScrollableTableViewCellAccessoryButton.h └── JGScrollableTableViewCellAccessoryButton.m ├── LICENSE.txt └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | .DS_Store 3 | */build/* 4 | *.pbxuser 5 | !default.pbxuser 6 | *.mode1v3 7 | !default.mode1v3 8 | *.mode2v3 9 | !default.mode2v3 10 | *.perspectivev3 11 | !default.perspectivev3 12 | xcuserdata 13 | profile 14 | *.moved-aside 15 | DerivedData 16 | .idea/ 17 | *.hmap 18 | *.xccheckout -------------------------------------------------------------------------------- /Demo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JonasGessner/JGScrollableTableViewCell/499173dbcb950fe5262eba219b59598d8e0b68d2/Demo.gif -------------------------------------------------------------------------------- /Examples/Advanced/JGScrollableTableViewCell Advanced.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 6B3C712C1865BE0E000F50B7 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6B3C712B1865BE0E000F50B7 /* Foundation.framework */; }; 11 | 6B3C712E1865BE0E000F50B7 /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6B3C712D1865BE0E000F50B7 /* CoreGraphics.framework */; }; 12 | 6B3C71301865BE0E000F50B7 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6B3C712F1865BE0E000F50B7 /* UIKit.framework */; }; 13 | 6B3C71361865BE0E000F50B7 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 6B3C71341865BE0E000F50B7 /* InfoPlist.strings */; }; 14 | 6B3C71381865BE0E000F50B7 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 6B3C71371865BE0E000F50B7 /* main.m */; }; 15 | 6B3C713C1865BE0E000F50B7 /* JGAppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 6B3C713B1865BE0E000F50B7 /* JGAppDelegate.m */; }; 16 | 6B3C713E1865BE0E000F50B7 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 6B3C713D1865BE0E000F50B7 /* Images.xcassets */; }; 17 | 6B3C715C1865BED0000F50B7 /* JGTestViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 6B3C715B1865BED0000F50B7 /* JGTestViewController.m */; }; 18 | 6B3C716E1865C05F000F50B7 /* MMDrawerBarButtonItem.m in Sources */ = {isa = PBXBuildFile; fileRef = 6B3C71661865C05F000F50B7 /* MMDrawerBarButtonItem.m */; }; 19 | 6B3C716F1865C05F000F50B7 /* MMDrawerController.m in Sources */ = {isa = PBXBuildFile; fileRef = 6B3C71691865C05F000F50B7 /* MMDrawerController.m */; }; 20 | 6B3C71701865C05F000F50B7 /* MMDrawerVisualState.m in Sources */ = {isa = PBXBuildFile; fileRef = 6B3C716B1865C05F000F50B7 /* MMDrawerVisualState.m */; }; 21 | 6B3C71711865C05F000F50B7 /* UIViewController+MMDrawerController.m in Sources */ = {isa = PBXBuildFile; fileRef = 6B3C716D1865C05F000F50B7 /* UIViewController+MMDrawerController.m */; }; 22 | 6B3C717A1865C3F8000F50B7 /* JGScrollableTableViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 6B3C71771865C3F8000F50B7 /* JGScrollableTableViewCell.m */; }; 23 | 6B3C717B1865C3F8000F50B7 /* JGScrollableTableViewCellAccessoryButton.m in Sources */ = {isa = PBXBuildFile; fileRef = 6B3C71791865C3F8000F50B7 /* JGScrollableTableViewCellAccessoryButton.m */; }; 24 | 6B3C717E186612E6000F50B7 /* JGExampleScrollableTableViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 6B3C717D186612E6000F50B7 /* JGExampleScrollableTableViewCell.m */; }; 25 | /* End PBXBuildFile section */ 26 | 27 | /* Begin PBXFileReference section */ 28 | 6B3C71281865BE0E000F50B7 /* JGScrollableTableViewCell Advanced.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "JGScrollableTableViewCell Advanced.app"; sourceTree = BUILT_PRODUCTS_DIR; }; 29 | 6B3C712B1865BE0E000F50B7 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; 30 | 6B3C712D1865BE0E000F50B7 /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; }; 31 | 6B3C712F1865BE0E000F50B7 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; }; 32 | 6B3C71331865BE0E000F50B7 /* JGScrollableTableViewCell Advanced-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "JGScrollableTableViewCell Advanced-Info.plist"; sourceTree = ""; }; 33 | 6B3C71351865BE0E000F50B7 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; 34 | 6B3C71371865BE0E000F50B7 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 35 | 6B3C71391865BE0E000F50B7 /* JGScrollableTableViewCell Advanced-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "JGScrollableTableViewCell Advanced-Prefix.pch"; sourceTree = ""; }; 36 | 6B3C713A1865BE0E000F50B7 /* JGAppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = JGAppDelegate.h; sourceTree = ""; }; 37 | 6B3C713B1865BE0E000F50B7 /* JGAppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = JGAppDelegate.m; sourceTree = ""; }; 38 | 6B3C713D1865BE0E000F50B7 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; 39 | 6B3C71441865BE0E000F50B7 /* XCTest.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = XCTest.framework; path = Library/Frameworks/XCTest.framework; sourceTree = DEVELOPER_DIR; }; 40 | 6B3C715A1865BED0000F50B7 /* JGTestViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JGTestViewController.h; sourceTree = ""; }; 41 | 6B3C715B1865BED0000F50B7 /* JGTestViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = JGTestViewController.m; sourceTree = ""; }; 42 | 6B3C71651865C05F000F50B7 /* MMDrawerBarButtonItem.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MMDrawerBarButtonItem.h; sourceTree = ""; }; 43 | 6B3C71661865C05F000F50B7 /* MMDrawerBarButtonItem.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MMDrawerBarButtonItem.m; sourceTree = ""; }; 44 | 6B3C71671865C05F000F50B7 /* MMDrawerController+Subclass.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "MMDrawerController+Subclass.h"; sourceTree = ""; }; 45 | 6B3C71681865C05F000F50B7 /* MMDrawerController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MMDrawerController.h; sourceTree = ""; }; 46 | 6B3C71691865C05F000F50B7 /* MMDrawerController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MMDrawerController.m; sourceTree = ""; }; 47 | 6B3C716A1865C05F000F50B7 /* MMDrawerVisualState.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MMDrawerVisualState.h; sourceTree = ""; }; 48 | 6B3C716B1865C05F000F50B7 /* MMDrawerVisualState.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MMDrawerVisualState.m; sourceTree = ""; }; 49 | 6B3C716C1865C05F000F50B7 /* UIViewController+MMDrawerController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIViewController+MMDrawerController.h"; sourceTree = ""; }; 50 | 6B3C716D1865C05F000F50B7 /* UIViewController+MMDrawerController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIViewController+MMDrawerController.m"; sourceTree = ""; }; 51 | 6B3C71761865C3F8000F50B7 /* JGScrollableTableViewCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JGScrollableTableViewCell.h; sourceTree = ""; }; 52 | 6B3C71771865C3F8000F50B7 /* JGScrollableTableViewCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = JGScrollableTableViewCell.m; sourceTree = ""; }; 53 | 6B3C71781865C3F8000F50B7 /* JGScrollableTableViewCellAccessoryButton.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JGScrollableTableViewCellAccessoryButton.h; sourceTree = ""; }; 54 | 6B3C71791865C3F8000F50B7 /* JGScrollableTableViewCellAccessoryButton.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = JGScrollableTableViewCellAccessoryButton.m; sourceTree = ""; }; 55 | 6B3C717C186612E6000F50B7 /* JGExampleScrollableTableViewCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JGExampleScrollableTableViewCell.h; sourceTree = ""; }; 56 | 6B3C717D186612E6000F50B7 /* JGExampleScrollableTableViewCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = JGExampleScrollableTableViewCell.m; sourceTree = ""; }; 57 | /* End PBXFileReference section */ 58 | 59 | /* Begin PBXFrameworksBuildPhase section */ 60 | 6B3C71251865BE0E000F50B7 /* Frameworks */ = { 61 | isa = PBXFrameworksBuildPhase; 62 | buildActionMask = 2147483647; 63 | files = ( 64 | 6B3C712E1865BE0E000F50B7 /* CoreGraphics.framework in Frameworks */, 65 | 6B3C71301865BE0E000F50B7 /* UIKit.framework in Frameworks */, 66 | 6B3C712C1865BE0E000F50B7 /* Foundation.framework in Frameworks */, 67 | ); 68 | runOnlyForDeploymentPostprocessing = 0; 69 | }; 70 | /* End PBXFrameworksBuildPhase section */ 71 | 72 | /* Begin PBXGroup section */ 73 | 6B3C711F1865BE0E000F50B7 = { 74 | isa = PBXGroup; 75 | children = ( 76 | 6B3C71751865C3F8000F50B7 /* JGScrollableTableViewCell */, 77 | 6B3C71311865BE0E000F50B7 /* JGScrollableTableViewCell Advanced */, 78 | 6B3C712A1865BE0E000F50B7 /* Frameworks */, 79 | 6B3C71291865BE0E000F50B7 /* Products */, 80 | ); 81 | sourceTree = ""; 82 | }; 83 | 6B3C71291865BE0E000F50B7 /* Products */ = { 84 | isa = PBXGroup; 85 | children = ( 86 | 6B3C71281865BE0E000F50B7 /* JGScrollableTableViewCell Advanced.app */, 87 | ); 88 | name = Products; 89 | sourceTree = ""; 90 | }; 91 | 6B3C712A1865BE0E000F50B7 /* Frameworks */ = { 92 | isa = PBXGroup; 93 | children = ( 94 | 6B3C712B1865BE0E000F50B7 /* Foundation.framework */, 95 | 6B3C712D1865BE0E000F50B7 /* CoreGraphics.framework */, 96 | 6B3C712F1865BE0E000F50B7 /* UIKit.framework */, 97 | 6B3C71441865BE0E000F50B7 /* XCTest.framework */, 98 | ); 99 | name = Frameworks; 100 | sourceTree = ""; 101 | }; 102 | 6B3C71311865BE0E000F50B7 /* JGScrollableTableViewCell Advanced */ = { 103 | isa = PBXGroup; 104 | children = ( 105 | 6B3C713A1865BE0E000F50B7 /* JGAppDelegate.h */, 106 | 6B3C713B1865BE0E000F50B7 /* JGAppDelegate.m */, 107 | 6B3C717C186612E6000F50B7 /* JGExampleScrollableTableViewCell.h */, 108 | 6B3C717D186612E6000F50B7 /* JGExampleScrollableTableViewCell.m */, 109 | 6B3C715A1865BED0000F50B7 /* JGTestViewController.h */, 110 | 6B3C715B1865BED0000F50B7 /* JGTestViewController.m */, 111 | 6B3C71641865C05F000F50B7 /* MMDrawerController */, 112 | 6B3C71321865BE0E000F50B7 /* Supporting Files */, 113 | ); 114 | path = "JGScrollableTableViewCell Advanced"; 115 | sourceTree = ""; 116 | }; 117 | 6B3C71321865BE0E000F50B7 /* Supporting Files */ = { 118 | isa = PBXGroup; 119 | children = ( 120 | 6B3C713D1865BE0E000F50B7 /* Images.xcassets */, 121 | 6B3C71331865BE0E000F50B7 /* JGScrollableTableViewCell Advanced-Info.plist */, 122 | 6B3C71341865BE0E000F50B7 /* InfoPlist.strings */, 123 | 6B3C71371865BE0E000F50B7 /* main.m */, 124 | 6B3C71391865BE0E000F50B7 /* JGScrollableTableViewCell Advanced-Prefix.pch */, 125 | ); 126 | name = "Supporting Files"; 127 | sourceTree = ""; 128 | }; 129 | 6B3C71641865C05F000F50B7 /* MMDrawerController */ = { 130 | isa = PBXGroup; 131 | children = ( 132 | 6B3C71651865C05F000F50B7 /* MMDrawerBarButtonItem.h */, 133 | 6B3C71661865C05F000F50B7 /* MMDrawerBarButtonItem.m */, 134 | 6B3C71671865C05F000F50B7 /* MMDrawerController+Subclass.h */, 135 | 6B3C71681865C05F000F50B7 /* MMDrawerController.h */, 136 | 6B3C71691865C05F000F50B7 /* MMDrawerController.m */, 137 | 6B3C716A1865C05F000F50B7 /* MMDrawerVisualState.h */, 138 | 6B3C716B1865C05F000F50B7 /* MMDrawerVisualState.m */, 139 | 6B3C716C1865C05F000F50B7 /* UIViewController+MMDrawerController.h */, 140 | 6B3C716D1865C05F000F50B7 /* UIViewController+MMDrawerController.m */, 141 | ); 142 | path = MMDrawerController; 143 | sourceTree = ""; 144 | }; 145 | 6B3C71751865C3F8000F50B7 /* JGScrollableTableViewCell */ = { 146 | isa = PBXGroup; 147 | children = ( 148 | 6B3C71761865C3F8000F50B7 /* JGScrollableTableViewCell.h */, 149 | 6B3C71771865C3F8000F50B7 /* JGScrollableTableViewCell.m */, 150 | 6B3C71781865C3F8000F50B7 /* JGScrollableTableViewCellAccessoryButton.h */, 151 | 6B3C71791865C3F8000F50B7 /* JGScrollableTableViewCellAccessoryButton.m */, 152 | ); 153 | name = JGScrollableTableViewCell; 154 | path = ../../JGScrollableTableViewCell; 155 | sourceTree = ""; 156 | }; 157 | /* End PBXGroup section */ 158 | 159 | /* Begin PBXNativeTarget section */ 160 | 6B3C71271865BE0E000F50B7 /* JGScrollableTableViewCell Advanced */ = { 161 | isa = PBXNativeTarget; 162 | buildConfigurationList = 6B3C71541865BE0E000F50B7 /* Build configuration list for PBXNativeTarget "JGScrollableTableViewCell Advanced" */; 163 | buildPhases = ( 164 | 6B3C71241865BE0E000F50B7 /* Sources */, 165 | 6B3C71251865BE0E000F50B7 /* Frameworks */, 166 | 6B3C71261865BE0E000F50B7 /* Resources */, 167 | ); 168 | buildRules = ( 169 | ); 170 | dependencies = ( 171 | ); 172 | name = "JGScrollableTableViewCell Advanced"; 173 | productName = "JGScrollableTableViewCell Advanced"; 174 | productReference = 6B3C71281865BE0E000F50B7 /* JGScrollableTableViewCell Advanced.app */; 175 | productType = "com.apple.product-type.application"; 176 | }; 177 | /* End PBXNativeTarget section */ 178 | 179 | /* Begin PBXProject section */ 180 | 6B3C71201865BE0E000F50B7 /* Project object */ = { 181 | isa = PBXProject; 182 | attributes = { 183 | CLASSPREFIX = JG; 184 | LastUpgradeCheck = 0510; 185 | ORGANIZATIONNAME = "Jonas Gessner"; 186 | }; 187 | buildConfigurationList = 6B3C71231865BE0E000F50B7 /* Build configuration list for PBXProject "JGScrollableTableViewCell Advanced" */; 188 | compatibilityVersion = "Xcode 3.2"; 189 | developmentRegion = English; 190 | hasScannedForEncodings = 0; 191 | knownRegions = ( 192 | en, 193 | ); 194 | mainGroup = 6B3C711F1865BE0E000F50B7; 195 | productRefGroup = 6B3C71291865BE0E000F50B7 /* Products */; 196 | projectDirPath = ""; 197 | projectRoot = ""; 198 | targets = ( 199 | 6B3C71271865BE0E000F50B7 /* JGScrollableTableViewCell Advanced */, 200 | ); 201 | }; 202 | /* End PBXProject section */ 203 | 204 | /* Begin PBXResourcesBuildPhase section */ 205 | 6B3C71261865BE0E000F50B7 /* Resources */ = { 206 | isa = PBXResourcesBuildPhase; 207 | buildActionMask = 2147483647; 208 | files = ( 209 | 6B3C71361865BE0E000F50B7 /* InfoPlist.strings in Resources */, 210 | 6B3C713E1865BE0E000F50B7 /* Images.xcassets in Resources */, 211 | ); 212 | runOnlyForDeploymentPostprocessing = 0; 213 | }; 214 | /* End PBXResourcesBuildPhase section */ 215 | 216 | /* Begin PBXSourcesBuildPhase section */ 217 | 6B3C71241865BE0E000F50B7 /* Sources */ = { 218 | isa = PBXSourcesBuildPhase; 219 | buildActionMask = 2147483647; 220 | files = ( 221 | 6B3C71381865BE0E000F50B7 /* main.m in Sources */, 222 | 6B3C716E1865C05F000F50B7 /* MMDrawerBarButtonItem.m in Sources */, 223 | 6B3C716F1865C05F000F50B7 /* MMDrawerController.m in Sources */, 224 | 6B3C717B1865C3F8000F50B7 /* JGScrollableTableViewCellAccessoryButton.m in Sources */, 225 | 6B3C71701865C05F000F50B7 /* MMDrawerVisualState.m in Sources */, 226 | 6B3C715C1865BED0000F50B7 /* JGTestViewController.m in Sources */, 227 | 6B3C717A1865C3F8000F50B7 /* JGScrollableTableViewCell.m in Sources */, 228 | 6B3C71711865C05F000F50B7 /* UIViewController+MMDrawerController.m in Sources */, 229 | 6B3C717E186612E6000F50B7 /* JGExampleScrollableTableViewCell.m in Sources */, 230 | 6B3C713C1865BE0E000F50B7 /* JGAppDelegate.m in Sources */, 231 | ); 232 | runOnlyForDeploymentPostprocessing = 0; 233 | }; 234 | /* End PBXSourcesBuildPhase section */ 235 | 236 | /* Begin PBXVariantGroup section */ 237 | 6B3C71341865BE0E000F50B7 /* InfoPlist.strings */ = { 238 | isa = PBXVariantGroup; 239 | children = ( 240 | 6B3C71351865BE0E000F50B7 /* en */, 241 | ); 242 | name = InfoPlist.strings; 243 | sourceTree = ""; 244 | }; 245 | /* End PBXVariantGroup section */ 246 | 247 | /* Begin XCBuildConfiguration section */ 248 | 6B3C71521865BE0E000F50B7 /* Debug */ = { 249 | isa = XCBuildConfiguration; 250 | buildSettings = { 251 | ALWAYS_SEARCH_USER_PATHS = NO; 252 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 253 | CLANG_CXX_LIBRARY = "libc++"; 254 | CLANG_ENABLE_MODULES = YES; 255 | CLANG_ENABLE_OBJC_ARC = YES; 256 | CLANG_WARN_BOOL_CONVERSION = YES; 257 | CLANG_WARN_CONSTANT_CONVERSION = YES; 258 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 259 | CLANG_WARN_EMPTY_BODY = YES; 260 | CLANG_WARN_ENUM_CONVERSION = YES; 261 | CLANG_WARN_INT_CONVERSION = YES; 262 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 263 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 264 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 265 | COPY_PHASE_STRIP = NO; 266 | GCC_C_LANGUAGE_STANDARD = gnu99; 267 | GCC_DYNAMIC_NO_PIC = NO; 268 | GCC_OPTIMIZATION_LEVEL = 0; 269 | GCC_PREPROCESSOR_DEFINITIONS = ( 270 | "DEBUG=1", 271 | "$(inherited)", 272 | ); 273 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 274 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 275 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 276 | GCC_WARN_UNDECLARED_SELECTOR = YES; 277 | GCC_WARN_UNINITIALIZED_AUTOS = YES; 278 | GCC_WARN_UNUSED_FUNCTION = YES; 279 | GCC_WARN_UNUSED_VARIABLE = YES; 280 | IPHONEOS_DEPLOYMENT_TARGET = 5.1.1; 281 | ONLY_ACTIVE_ARCH = YES; 282 | SDKROOT = iphoneos; 283 | TARGETED_DEVICE_FAMILY = "1,2"; 284 | }; 285 | name = Debug; 286 | }; 287 | 6B3C71531865BE0E000F50B7 /* Release */ = { 288 | isa = XCBuildConfiguration; 289 | buildSettings = { 290 | ALWAYS_SEARCH_USER_PATHS = NO; 291 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 292 | CLANG_CXX_LIBRARY = "libc++"; 293 | CLANG_ENABLE_MODULES = YES; 294 | CLANG_ENABLE_OBJC_ARC = YES; 295 | CLANG_WARN_BOOL_CONVERSION = YES; 296 | CLANG_WARN_CONSTANT_CONVERSION = YES; 297 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 298 | CLANG_WARN_EMPTY_BODY = YES; 299 | CLANG_WARN_ENUM_CONVERSION = YES; 300 | CLANG_WARN_INT_CONVERSION = YES; 301 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 302 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 303 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 304 | COPY_PHASE_STRIP = YES; 305 | ENABLE_NS_ASSERTIONS = NO; 306 | GCC_C_LANGUAGE_STANDARD = gnu99; 307 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 308 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 309 | GCC_WARN_UNDECLARED_SELECTOR = YES; 310 | GCC_WARN_UNINITIALIZED_AUTOS = YES; 311 | GCC_WARN_UNUSED_FUNCTION = YES; 312 | GCC_WARN_UNUSED_VARIABLE = YES; 313 | IPHONEOS_DEPLOYMENT_TARGET = 5.1.1; 314 | SDKROOT = iphoneos; 315 | TARGETED_DEVICE_FAMILY = "1,2"; 316 | VALIDATE_PRODUCT = YES; 317 | }; 318 | name = Release; 319 | }; 320 | 6B3C71551865BE0E000F50B7 /* Debug */ = { 321 | isa = XCBuildConfiguration; 322 | buildSettings = { 323 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 324 | ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; 325 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 326 | GCC_PREFIX_HEADER = "JGScrollableTableViewCell Advanced/JGScrollableTableViewCell Advanced-Prefix.pch"; 327 | INFOPLIST_FILE = "JGScrollableTableViewCell Advanced/JGScrollableTableViewCell Advanced-Info.plist"; 328 | IPHONEOS_DEPLOYMENT_TARGET = 5.1.1; 329 | PRODUCT_NAME = "$(TARGET_NAME)"; 330 | WRAPPER_EXTENSION = app; 331 | }; 332 | name = Debug; 333 | }; 334 | 6B3C71561865BE0E000F50B7 /* Release */ = { 335 | isa = XCBuildConfiguration; 336 | buildSettings = { 337 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 338 | ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; 339 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 340 | GCC_PREFIX_HEADER = "JGScrollableTableViewCell Advanced/JGScrollableTableViewCell Advanced-Prefix.pch"; 341 | INFOPLIST_FILE = "JGScrollableTableViewCell Advanced/JGScrollableTableViewCell Advanced-Info.plist"; 342 | IPHONEOS_DEPLOYMENT_TARGET = 5.1.1; 343 | PRODUCT_NAME = "$(TARGET_NAME)"; 344 | WRAPPER_EXTENSION = app; 345 | }; 346 | name = Release; 347 | }; 348 | /* End XCBuildConfiguration section */ 349 | 350 | /* Begin XCConfigurationList section */ 351 | 6B3C71231865BE0E000F50B7 /* Build configuration list for PBXProject "JGScrollableTableViewCell Advanced" */ = { 352 | isa = XCConfigurationList; 353 | buildConfigurations = ( 354 | 6B3C71521865BE0E000F50B7 /* Debug */, 355 | 6B3C71531865BE0E000F50B7 /* Release */, 356 | ); 357 | defaultConfigurationIsVisible = 0; 358 | defaultConfigurationName = Release; 359 | }; 360 | 6B3C71541865BE0E000F50B7 /* Build configuration list for PBXNativeTarget "JGScrollableTableViewCell Advanced" */ = { 361 | isa = XCConfigurationList; 362 | buildConfigurations = ( 363 | 6B3C71551865BE0E000F50B7 /* Debug */, 364 | 6B3C71561865BE0E000F50B7 /* Release */, 365 | ); 366 | defaultConfigurationIsVisible = 0; 367 | defaultConfigurationName = Release; 368 | }; 369 | /* End XCConfigurationList section */ 370 | }; 371 | rootObject = 6B3C71201865BE0E000F50B7 /* Project object */; 372 | } 373 | -------------------------------------------------------------------------------- /Examples/Advanced/JGScrollableTableViewCell Advanced.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Examples/Advanced/JGScrollableTableViewCell Advanced/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" : "ipad", 20 | "size" : "29x29", 21 | "scale" : "1x" 22 | }, 23 | { 24 | "idiom" : "ipad", 25 | "size" : "29x29", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "ipad", 30 | "size" : "40x40", 31 | "scale" : "1x" 32 | }, 33 | { 34 | "idiom" : "ipad", 35 | "size" : "40x40", 36 | "scale" : "2x" 37 | }, 38 | { 39 | "idiom" : "ipad", 40 | "size" : "76x76", 41 | "scale" : "1x" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "size" : "76x76", 46 | "scale" : "2x" 47 | } 48 | ], 49 | "info" : { 50 | "version" : 1, 51 | "author" : "xcode" 52 | } 53 | } -------------------------------------------------------------------------------- /Examples/Advanced/JGScrollableTableViewCell Advanced/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 | } -------------------------------------------------------------------------------- /Examples/Advanced/JGScrollableTableViewCell Advanced/JGAppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // JGAppDelegate.h 3 | // JGScrollableTableViewCell Advanced 4 | // 5 | // Created by Jonas Gessner on 21.12.13. 6 | // Copyright (c) 2013 Jonas Gessner. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "MMDrawerController.h" 11 | 12 | @interface JGAppDelegate : UIResponder 13 | 14 | @property (strong, nonatomic) UIWindow *window; 15 | 16 | @property (nonatomic, strong) MMDrawerController *drawerController; 17 | 18 | @end 19 | -------------------------------------------------------------------------------- /Examples/Advanced/JGScrollableTableViewCell Advanced/JGAppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // JGAppDelegate.m 3 | // JGScrollableTableViewCell Advanced 4 | // 5 | // Created by Jonas Gessner on 21.12.13. 6 | // Copyright (c) 2013 Jonas Gessner. All rights reserved. 7 | // 8 | 9 | #import "JGAppDelegate.h" 10 | #import "JGTestViewController.h" 11 | 12 | @implementation JGAppDelegate 13 | 14 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions 15 | { 16 | self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; 17 | // Override point for customization after application launch. 18 | self.window.backgroundColor = [UIColor whiteColor]; 19 | 20 | UINavigationController *navigationController = [[UINavigationController alloc] initWithRootViewController:[[JGTestViewController alloc] initWithStyle:UITableViewStylePlain]]; 21 | 22 | UIViewController *left = [UIViewController new]; 23 | left.view.backgroundColor = [UIColor lightGrayColor]; 24 | 25 | self.drawerController = [[MMDrawerController alloc] initWithCenterViewController:navigationController leftDrawerViewController:left]; 26 | 27 | [self.drawerController setOpenDrawerGestureModeMask:MMOpenDrawerGestureModeAll]; 28 | [self.drawerController setCloseDrawerGestureModeMask:MMCloseDrawerGestureModeAll]; 29 | 30 | [self.drawerController openDrawerSide:MMDrawerSideLeft animated:NO completion:NULL]; 31 | 32 | self.window.rootViewController = self.drawerController; 33 | 34 | [self.window makeKeyAndVisible]; 35 | 36 | return YES; 37 | } 38 | 39 | - (void)applicationWillResignActive:(UIApplication *)application 40 | { 41 | // 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. 42 | // 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. 43 | } 44 | 45 | - (void)applicationDidEnterBackground:(UIApplication *)application 46 | { 47 | // 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. 48 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 49 | } 50 | 51 | - (void)applicationWillEnterForeground:(UIApplication *)application 52 | { 53 | // 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. 54 | } 55 | 56 | - (void)applicationDidBecomeActive:(UIApplication *)application 57 | { 58 | // 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. 59 | } 60 | 61 | - (void)applicationWillTerminate:(UIApplication *)application 62 | { 63 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 64 | } 65 | 66 | @end 67 | -------------------------------------------------------------------------------- /Examples/Advanced/JGScrollableTableViewCell Advanced/JGExampleScrollableTableViewCell.h: -------------------------------------------------------------------------------- 1 | // 2 | // JGExampleScrollableTableViewCell.h 3 | // JGScrollableTableViewCell Basic 4 | // 5 | // Created by Jonas Gessner on 21.12.13. 6 | // Copyright (c) 2013 Jonas Gessner. All rights reserved. 7 | // 8 | 9 | #import "JGScrollableTableViewCell.h" 10 | 11 | @interface JGExampleScrollableTableViewCell : JGScrollableTableViewCell 12 | 13 | - (void)setGrabberVisible:(BOOL)visible; 14 | 15 | @end 16 | -------------------------------------------------------------------------------- /Examples/Advanced/JGScrollableTableViewCell Advanced/JGExampleScrollableTableViewCell.m: -------------------------------------------------------------------------------- 1 | // 2 | // JGExampleScrollableTableViewCell.m 3 | // JGScrollableTableViewCell Basic 4 | // 5 | // Created by Jonas Gessner on 21.12.13. 6 | // Copyright (c) 2013 Jonas Gessner. All rights reserved. 7 | // 8 | 9 | #import "JGExampleScrollableTableViewCell.h" 10 | #import "JGScrollableTableViewCellAccessoryButton.h" 11 | 12 | @implementation JGExampleScrollableTableViewCell 13 | 14 | - (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier { 15 | self = [super initWithStyle:style reuseIdentifier:reuseIdentifier]; 16 | if (self) { 17 | [self setScrollViewBackgroundColor:[UIColor colorWithWhite:0.975f alpha:1.0f]]; 18 | self.contentView.backgroundColor = [UIColor grayColor]; 19 | 20 | JGScrollableTableViewCellAccessoryButton *actionView = [JGScrollableTableViewCellAccessoryButton button]; 21 | 22 | [actionView setButtonColor:[UIColor colorWithRed:0.975f green:0.0f blue:0.0f alpha:1.0f] forState:UIControlStateNormal]; 23 | [actionView setButtonColor:[UIColor colorWithRed:0.8f green:0.1f blue:0.1f alpha:1.0f] forState:UIControlStateHighlighted]; 24 | 25 | [actionView setTitle:@"Sample" forState:UIControlStateNormal]; 26 | 27 | CGFloat width = 80.0f; 28 | 29 | actionView.frame = CGRectMake(width, 0.0f, width, 0.0f); //width is the only frame parameter that needs to be set on the option view 30 | actionView.autoresizingMask = UIViewAutoresizingFlexibleHeight; 31 | 32 | 33 | JGScrollableTableViewCellAccessoryButton *moreView = [JGScrollableTableViewCellAccessoryButton button]; 34 | 35 | [moreView setButtonColor:[UIColor colorWithWhite:0.8f alpha:1.0f] forState:UIControlStateNormal]; 36 | [moreView setButtonColor:[UIColor colorWithWhite:0.65f alpha:1.0f] forState:UIControlStateHighlighted]; 37 | 38 | [moreView setTitle:@"Sample" forState:UIControlStateNormal]; 39 | 40 | moreView.frame = CGRectMake(0.0f, 0.0f, width, 0.0f); //width is the only frame parameter that needs to be set on the option view 41 | moreView.autoresizingMask = UIViewAutoresizingFlexibleHeight; 42 | 43 | 44 | UIView *optionView = [[UIView alloc] initWithFrame:CGRectMake(0.0f, 0.0f, width*2.0f, 0.0f)]; 45 | optionView.clipsToBounds = YES; 46 | 47 | [optionView addSubview:moreView]; 48 | [optionView addSubview:actionView]; 49 | 50 | [self setOptionView:optionView]; 51 | 52 | 53 | //Custom Touch handling: 54 | [self setShouldRecognizeSimultaneouslyWithGestureRecognizerBlock:^BOOL (JGScrollableTableViewCell *cell, UIGestureRecognizer *gesture, UIScrollView *scrollView) { 55 | if ([cell optionViewVisible]) { 56 | return NO; 57 | } 58 | else { 59 | return YES; 60 | } 61 | }]; 62 | 63 | [self setScrollViewDidScrollBlock:^(JGScrollableTableViewCell *cell, UIScrollView *scrollView) { 64 | if (scrollView.contentOffset.x < 0.0f) { 65 | scrollView.panGestureRecognizer.enabled = NO; 66 | scrollView.panGestureRecognizer.enabled = YES; 67 | } 68 | }]; 69 | 70 | } 71 | return self; 72 | } 73 | 74 | - (void)setGrabberVisible:(BOOL)visible { 75 | if (visible) { 76 | UIView *grabber = [[UIView alloc] initWithFrame:(CGRect){CGPointZero, {30.0f, 40.0f}}]; 77 | 78 | UIView *dot1 = [[UIView alloc] initWithFrame:(CGRect){{15.0f, 7.5f}, {5.0f, 5.0f}}]; 79 | 80 | UIView *dot2 = [[UIView alloc] initWithFrame:(CGRect){{15.0f, 17.5f}, {5.0f, 5.0f}}]; 81 | 82 | UIView *dot3 = [[UIView alloc] initWithFrame:(CGRect){{15.0f, 27.5f}, {5.0f, 5.0f}}]; 83 | 84 | dot1.backgroundColor = [UIColor colorWithWhite:0.6f alpha:1.0f]; 85 | dot2.backgroundColor = [UIColor colorWithWhite:0.6f alpha:1.0f]; 86 | dot3.backgroundColor = [UIColor colorWithWhite:0.6f alpha:1.0f]; 87 | 88 | [grabber addSubview:dot1]; 89 | [grabber addSubview:dot2]; 90 | [grabber addSubview:dot3]; 91 | 92 | [self setGrabberView:grabber]; 93 | } 94 | else { 95 | [self setGrabberView:nil]; 96 | } 97 | } 98 | 99 | @end 100 | -------------------------------------------------------------------------------- /Examples/Advanced/JGScrollableTableViewCell Advanced/JGScrollableTableViewCell Advanced-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleDisplayName 8 | ${PRODUCT_NAME} 9 | CFBundleExecutable 10 | ${EXECUTABLE_NAME} 11 | CFBundleIdentifier 12 | de.j-gessner.${PRODUCT_NAME:rfc1034identifier} 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | ${PRODUCT_NAME} 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | 1.0 25 | LSRequiresIPhoneOS 26 | 27 | UIRequiredDeviceCapabilities 28 | 29 | armv7 30 | 31 | 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 | -------------------------------------------------------------------------------- /Examples/Advanced/JGScrollableTableViewCell Advanced/JGScrollableTableViewCell Advanced-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 | -------------------------------------------------------------------------------- /Examples/Advanced/JGScrollableTableViewCell Advanced/JGTestViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // JGTestViewController.h 3 | // JGScrollableTableViewCell Examples 4 | // 5 | // Created by Jonas Gessner on 03.11.13. 6 | // Copyright (c) 2013 Jonas Gessner. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface JGTestViewController : UITableViewController 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /Examples/Advanced/JGScrollableTableViewCell Advanced/JGTestViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // JGTestViewController.m 3 | // JGScrollableTableViewCell Examples 4 | // 5 | // Created by Jonas Gessner on 03.11.13. 6 | // Copyright (c) 2013 Jonas Gessner. All rights reserved. 7 | // 8 | 9 | #import "JGTestViewController.h" 10 | 11 | #import "JGExampleScrollableTableViewCell.h" 12 | 13 | #import "JGScrollableTableViewCellAccessoryButton.h" 14 | 15 | @interface JGTestViewController () { 16 | NSIndexPath *_openedIndexPath; 17 | } 18 | 19 | @end 20 | 21 | @implementation JGTestViewController 22 | 23 | - (instancetype)initWithStyle:(UITableViewStyle)style { 24 | self = [super initWithStyle:style]; 25 | if (self) { 26 | self.tableView.separatorStyle = UITableViewCellSeparatorStyleSingleLine; 27 | 28 | [self.tableView registerClass:[JGExampleScrollableTableViewCell class] forCellReuseIdentifier:@"ScrollCell"]; 29 | 30 | self.title = @"-> Slide to reveal side menu"; 31 | } 32 | return self; 33 | } 34 | 35 | #pragma mark - JGScrollableTableViewCellDelegate 36 | 37 | - (void)cellDidBeginScrolling:(JGScrollableTableViewCell *)cell { 38 | [JGScrollableTableViewCellManager closeAllCellsWithExceptionOf:cell stopAfterFirst:YES]; 39 | } 40 | 41 | - (void)cellDidScroll:(JGScrollableTableViewCell *)cell { 42 | [JGScrollableTableViewCellManager closeAllCellsWithExceptionOf:cell stopAfterFirst:YES]; 43 | } 44 | 45 | - (void)cellDidEndScrolling:(JGScrollableTableViewCell *)cell { 46 | if (cell.optionViewVisible) { 47 | _openedIndexPath = [self.tableView indexPathForCell:cell]; 48 | } 49 | else { 50 | _openedIndexPath = nil; 51 | } 52 | 53 | [JGScrollableTableViewCellManager closeAllCellsWithExceptionOf:cell stopAfterFirst:YES]; 54 | } 55 | 56 | #pragma mark - Table view data source 57 | 58 | - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { 59 | return 2; 60 | } 61 | 62 | - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { 63 | return 99; 64 | } 65 | 66 | - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath { 67 | return 70.0f; 68 | } 69 | 70 | - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { 71 | static NSString *const CellIdentifier = @"ScrollCell"; 72 | 73 | JGExampleScrollableTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath]; 74 | 75 | [cell setGrabberVisible:((indexPath.row % 3) == 0)]; 76 | 77 | cell.scrollDelegate = self; 78 | 79 | [cell setOptionViewVisible:[_openedIndexPath isEqual:indexPath]]; 80 | 81 | return cell; 82 | } 83 | 84 | - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { 85 | NSLog(@"Selected Index Path %@", indexPath); 86 | [tableView deselectRowAtIndexPath:indexPath animated:YES]; 87 | } 88 | 89 | @end 90 | -------------------------------------------------------------------------------- /Examples/Advanced/JGScrollableTableViewCell Advanced/MMDrawerController/MMDrawerBarButtonItem.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013 Mutual Mobile (http://mutualmobile.com/) 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | #import 22 | 23 | /** 24 | `MMDrawerBarButtonItem` provides convenience methods to create `UIBarButtonItems` with a default hamburger-menu asset. 25 | */ 26 | 27 | @interface MMDrawerBarButtonItem : UIBarButtonItem 28 | 29 | ///--------------------------------------- 30 | /// @name Initializing a `MMDrawerBarButtonItem` 31 | ///--------------------------------------- 32 | 33 | /** 34 | Creates and initializes an `MMDrawerBarButtonItem` without a border. 35 | 36 | @param target The target to forward the `action` to when the button is pressed. 37 | @param action The action to call when the button is pressed. 38 | 39 | @return The newly-initialized bar button item. 40 | */ 41 | -(id)initWithTarget:(id)target action:(SEL)action; 42 | 43 | /** 44 | Returns the current color of the menu button for the state requested. This property is deprecated in iOS 7.0. Use `tintColor` instead. 45 | 46 | @param state The UIControl state that the color is being requested for. 47 | 48 | @return The menu button color for the requested state. 49 | */ 50 | -(UIColor *)menuButtonColorForState:(UIControlState)state __attribute__((deprecated("Use tintColor instead"))); 51 | 52 | /** 53 | Sets the color of the menu button for the specified state. For this control, only set colors for `UIControlStateNormal` and `UIControlStateHighlighted`. This property is deprecated in iOS 7.0. Use `tintColor` instead. 54 | 55 | @param color The color to set. 56 | @param state The state to set the color for. 57 | */ 58 | -(void)setMenuButtonColor:(UIColor *)color forState:(UIControlState)state __attribute__((deprecated("Use tintColor instead"))); 59 | 60 | /** 61 | Returns the current color of the shadow for the state requested. This property is deprecated in iOS 7.0. The menu button no longer supports a shadow. 62 | 63 | @param state The UIControl state that the color is being requested for. 64 | 65 | @return The menu button color for the requested state. 66 | */ 67 | -(UIColor *)shadowColorForState:(UIControlState)state __attribute__((deprecated("Shadow is no longer supported"))); 68 | 69 | /** 70 | Sets the color of the shadow for the specified state. For this control, only set colors for `UIControlStateNormal` and `UIControlStateHighlighted`. This property is deprecated in iOS 7.0. The menu button no longer supports a shadow. 71 | 72 | @param color The color to set. 73 | @param state The state to set the color for. 74 | */ 75 | -(void)setShadowColor:(UIColor *)color forState:(UIControlState)state __attribute__((deprecated("Shadow is no longer supported"))); 76 | 77 | @end 78 | -------------------------------------------------------------------------------- /Examples/Advanced/JGScrollableTableViewCell Advanced/MMDrawerController/MMDrawerBarButtonItem.m: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013 Mutual Mobile (http://mutualmobile.com/) 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | 22 | #import "MMDrawerBarButtonItem.h" 23 | 24 | @interface MMDrawerMenuButtonView : UIButton 25 | @property (nonatomic,strong) UIColor * menuButtonNormalColor; 26 | @property (nonatomic,strong) UIColor * menuButtonHighlightedColor; 27 | 28 | @property (nonatomic,strong) UIColor * shadowNormalColor; 29 | @property (nonatomic,strong) UIColor * shadowHighlightedColor; 30 | 31 | -(UIColor *)menuButtonColorForState:(UIControlState)state; 32 | -(void)setMenuButtonColor:(UIColor *)color forState:(UIControlState)state; 33 | 34 | -(UIColor *)shadowColorForState:(UIControlState)state; 35 | -(void)setShadowColor:(UIColor *)color forState:(UIControlState)state; 36 | 37 | @end 38 | 39 | @implementation MMDrawerMenuButtonView 40 | 41 | -(id)initWithFrame:(CGRect)frame{ 42 | self = [super initWithFrame:frame]; 43 | if(self){ 44 | [self setMenuButtonNormalColor:[[UIColor whiteColor] colorWithAlphaComponent:0.9f]]; 45 | [self setMenuButtonHighlightedColor:[UIColor colorWithRed:139.0/255.0 46 | green:135.0/255.0 47 | blue:136.0/255.0 48 | alpha:0.9f]]; 49 | 50 | [self setShadowNormalColor:[[UIColor blackColor] colorWithAlphaComponent:0.5f]]; 51 | [self setShadowHighlightedColor:[[UIColor blackColor] colorWithAlphaComponent:0.2f]]; 52 | } 53 | return self; 54 | } 55 | 56 | -(UIColor *)menuButtonColorForState:(UIControlState)state{ 57 | UIColor * color; 58 | switch (state) { 59 | case UIControlStateNormal: 60 | color = self.menuButtonNormalColor; 61 | break; 62 | case UIControlStateHighlighted: 63 | color = self.menuButtonHighlightedColor; 64 | break; 65 | default: 66 | break; 67 | } 68 | return color; 69 | } 70 | 71 | -(void)setMenuButtonColor:(UIColor *)color forState:(UIControlState)state{ 72 | switch (state) { 73 | case UIControlStateNormal: 74 | [self setMenuButtonNormalColor:color]; 75 | break; 76 | case UIControlStateHighlighted: 77 | [self setMenuButtonHighlightedColor:color]; 78 | break; 79 | default: 80 | break; 81 | } 82 | [self setNeedsDisplay]; 83 | } 84 | 85 | -(UIColor *)shadowColorForState:(UIControlState)state{ 86 | UIColor * color; 87 | switch (state) { 88 | case UIControlStateNormal: 89 | color = self.shadowNormalColor; 90 | break; 91 | case UIControlStateHighlighted: 92 | color = self.shadowHighlightedColor; 93 | break; 94 | default: 95 | break; 96 | } 97 | return color; 98 | } 99 | 100 | -(void)setShadowColor:(UIColor *)color forState:(UIControlState)state{ 101 | switch (state) { 102 | case UIControlStateNormal: 103 | [self setShadowNormalColor:color]; 104 | break; 105 | case UIControlStateHighlighted: 106 | [self setShadowHighlightedColor:color]; 107 | break; 108 | default: 109 | break; 110 | } 111 | [self setNeedsDisplay]; 112 | } 113 | 114 | -(void)drawRect:(CGRect)rect{ 115 | //// General Declarations 116 | CGContextRef context = UIGraphicsGetCurrentContext(); 117 | 118 | //Sizes 119 | CGFloat buttonWidth = CGRectGetWidth(self.bounds)*.80; 120 | CGFloat buttonHeight = CGRectGetHeight(self.bounds)*.16; 121 | CGFloat xOffset = CGRectGetWidth(self.bounds)*.10; 122 | CGFloat yOffset = CGRectGetHeight(self.bounds)*.12; 123 | CGFloat cornerRadius = 1.0; 124 | 125 | //// Color Declarations 126 | UIColor* buttonColor = [self menuButtonColorForState:self.state]; 127 | UIColor* shadowColor = [self shadowColorForState:self.state]; 128 | 129 | 130 | //// Shadow Declarations 131 | UIColor* shadow = shadowColor; 132 | CGSize shadowOffset = CGSizeMake(0.0, 1.0); 133 | CGFloat shadowBlurRadius = 0; 134 | 135 | //// Top Bun Drawing 136 | UIBezierPath* topBunPath = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(xOffset, yOffset, buttonWidth, buttonHeight) cornerRadius:cornerRadius]; 137 | CGContextSaveGState(context); 138 | CGContextSetShadowWithColor(context, shadowOffset, shadowBlurRadius, shadow.CGColor); 139 | [buttonColor setFill]; 140 | [topBunPath fill]; 141 | CGContextRestoreGState(context); 142 | 143 | //// Meat Drawing 144 | UIBezierPath* meatPath = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(xOffset, yOffset*2 + buttonHeight, buttonWidth, buttonHeight) cornerRadius:cornerRadius]; 145 | CGContextSaveGState(context); 146 | CGContextSetShadowWithColor(context, shadowOffset, shadowBlurRadius, shadow.CGColor); 147 | [buttonColor setFill]; 148 | [meatPath fill]; 149 | CGContextRestoreGState(context); 150 | 151 | //// Bottom Bun Drawing 152 | UIBezierPath* bottomBunPath = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(xOffset, yOffset*3 + buttonHeight*2, buttonWidth, buttonHeight) cornerRadius:cornerRadius]; 153 | CGContextSaveGState(context); 154 | CGContextSetShadowWithColor(context, shadowOffset, shadowBlurRadius, shadow.CGColor); 155 | [buttonColor setFill]; 156 | [bottomBunPath fill]; 157 | CGContextRestoreGState(context); 158 | } 159 | 160 | -(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{ 161 | [super touchesBegan:touches withEvent:event]; 162 | [self setNeedsDisplay]; 163 | } 164 | 165 | -(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event{ 166 | [super touchesEnded:touches withEvent:event]; 167 | [self setNeedsDisplay]; 168 | } 169 | 170 | -(void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event{ 171 | [super touchesCancelled:touches withEvent:event]; 172 | [self setNeedsDisplay]; 173 | } 174 | 175 | -(void)setSelected:(BOOL)selected{ 176 | [super setSelected:selected]; 177 | [self setNeedsDisplay]; 178 | } 179 | 180 | -(void)setHighlighted:(BOOL)highlighted{ 181 | [super setHighlighted:highlighted]; 182 | [self setNeedsDisplay]; 183 | } 184 | 185 | -(void)setTintColor:(UIColor *)tintColor{ 186 | if([super respondsToSelector:@selector(setTintColor:)]){ 187 | [super setTintColor:tintColor]; 188 | } 189 | } 190 | 191 | -(void)tintColorDidChange{ 192 | [self setNeedsDisplay]; 193 | } 194 | 195 | @end 196 | 197 | @interface MMDrawerBarButtonItem () 198 | @property (nonatomic,strong) MMDrawerMenuButtonView * buttonView; 199 | 200 | @end 201 | 202 | @implementation MMDrawerBarButtonItem 203 | 204 | +(UIImage*)drawerButtonItemImage{ 205 | 206 | static UIImage *drawerButtonImage = nil; 207 | static dispatch_once_t onceToken; 208 | dispatch_once(&onceToken, ^{ 209 | 210 | UIGraphicsBeginImageContextWithOptions( CGSizeMake(26, 26), NO, 0 ); 211 | 212 | //// Color Declarations 213 | UIColor* fillColor = [UIColor whiteColor]; 214 | 215 | //// Frames 216 | CGRect frame = CGRectMake(0, 0, 26, 26); 217 | 218 | //// Bottom Bar Drawing 219 | UIBezierPath* bottomBarPath = [UIBezierPath bezierPathWithRect: CGRectMake(CGRectGetMinX(frame) + floor((CGRectGetWidth(frame) - 16) * 0.50000 + 0.5), CGRectGetMinY(frame) + floor((CGRectGetHeight(frame) - 1) * 0.72000 + 0.5), 16, 1)]; 220 | [fillColor setFill]; 221 | [bottomBarPath fill]; 222 | 223 | 224 | //// Middle Bar Drawing 225 | UIBezierPath* middleBarPath = [UIBezierPath bezierPathWithRect: CGRectMake(CGRectGetMinX(frame) + floor((CGRectGetWidth(frame) - 16) * 0.50000 + 0.5), CGRectGetMinY(frame) + floor((CGRectGetHeight(frame) - 1) * 0.48000 + 0.5), 16, 1)]; 226 | [fillColor setFill]; 227 | [middleBarPath fill]; 228 | 229 | 230 | //// Top Bar Drawing 231 | UIBezierPath* topBarPath = [UIBezierPath bezierPathWithRect: CGRectMake(CGRectGetMinX(frame) + floor((CGRectGetWidth(frame) - 16) * 0.50000 + 0.5), CGRectGetMinY(frame) + floor((CGRectGetHeight(frame) - 1) * 0.24000 + 0.5), 16, 1)]; 232 | [fillColor setFill]; 233 | [topBarPath fill]; 234 | 235 | drawerButtonImage = UIGraphicsGetImageFromCurrentImageContext(); 236 | }); 237 | 238 | return drawerButtonImage; 239 | } 240 | 241 | -(id)initWithTarget:(id)target action:(SEL)action{ 242 | 243 | if((floor(NSFoundationVersionNumber) > NSFoundationVersionNumber_iOS_6_1)){ 244 | return [self initWithImage:[self.class drawerButtonItemImage] 245 | style:UIBarButtonItemStylePlain 246 | target:target 247 | action:action]; 248 | } 249 | else { 250 | MMDrawerMenuButtonView * buttonView = [[MMDrawerMenuButtonView alloc] initWithFrame:CGRectMake(0, 0, 26, 26)]; 251 | [buttonView addTarget:self action:@selector(touchUpInside:) forControlEvents:UIControlEventTouchUpInside]; 252 | self = [self initWithCustomView:buttonView]; 253 | if(self){ 254 | [self setButtonView:buttonView]; 255 | } 256 | self.action = action; 257 | self.target = target; 258 | return self; 259 | } 260 | } 261 | 262 | -(id)initWithCoder:(NSCoder *)aDecoder{ 263 | // non-ideal way to get the target/action, but it works 264 | UIBarButtonItem* barButtonItem = [[UIBarButtonItem alloc] initWithCoder: aDecoder]; 265 | return [self initWithTarget:barButtonItem.target action:barButtonItem.action]; 266 | } 267 | 268 | -(void)touchUpInside:(id)sender{ 269 | 270 | #pragma clang diagnostic push 271 | #pragma clang diagnostic ignored "-Warc-performSelector-leaks" 272 | [self.target performSelector:self.action withObject:sender]; 273 | #pragma clang diagnostic pop; 274 | 275 | } 276 | 277 | -(UIColor *)menuButtonColorForState:(UIControlState)state{ 278 | return [self.buttonView menuButtonColorForState:state]; 279 | } 280 | 281 | -(void)setMenuButtonColor:(UIColor *)color forState:(UIControlState)state{ 282 | [self.buttonView setMenuButtonColor:color forState:state]; 283 | } 284 | 285 | -(UIColor *)shadowColorForState:(UIControlState)state{ 286 | return [self.buttonView shadowColorForState:state]; 287 | } 288 | 289 | -(void)setShadowColor:(UIColor *)color forState:(UIControlState)state{ 290 | [self.buttonView setShadowColor:color forState:state]; 291 | } 292 | 293 | -(void)setTintColor:(UIColor *)tintColor{ 294 | if([super respondsToSelector:@selector(setTintColor:)]){ 295 | [super setTintColor:tintColor]; 296 | } 297 | if([self.buttonView respondsToSelector:@selector(setTintColor:)]){ 298 | [self.buttonView setTintColor:tintColor]; 299 | } 300 | } 301 | 302 | @end 303 | -------------------------------------------------------------------------------- /Examples/Advanced/JGScrollableTableViewCell Advanced/MMDrawerController/MMDrawerController+Subclass.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013 Mutual Mobile (http://mutualmobile.com/) 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | #import "MMDrawerController.h" 22 | 23 | /** 24 | This class extension is designed for use by subclasses of `MMDrawerController` to customize the functionality to support a specific use-case by a developer. When importing this file, there is no need to also call `#import MMDrawerController.h`. 25 | 26 | None of these methods are meant to be called by non-subclasses of `MMDrawerController`. 27 | */ 28 | 29 | @interface MMDrawerController (Subclass) 30 | ///--------------------------------------- 31 | /// @name Gesture Interaction 32 | ///--------------------------------------- 33 | /** 34 | `MMDrawerController`'s single-tap gesture recognizer callback. This method is called every time the `UITapGestureRecognizer` is triggered. 35 | 36 | @param tapGesture The single-tap gesture recognizer instance that triggered the callback 37 | */ 38 | -(void)tapGestureCallback:(UITapGestureRecognizer *)tapGesture __attribute((objc_requires_super)); 39 | 40 | /** 41 | `MMDrawerController`'s pan gesture recognizer callback. This method is called every time the `UIPanGestureRecognizer` is updated. 42 | 43 | @warning This method do the minimal amount of work to keep the pan gesture responsive. 44 | 45 | @param panGesture The pan gesture recognizer instance that triggered the callback 46 | */ 47 | -(void)panGestureCallback:(UIPanGestureRecognizer *)panGesture __attribute((objc_requires_super)); 48 | 49 | /** 50 | A `UIGestureRecognizerDelegate` method that is queried by `MMDrawerController`'s gestures to determine if it should receive the touch. 51 | 52 | @param gestureRecognizer The gesture recognizer that is asking if it should recieve a touch 53 | @param touch The touch in question in gestureRecognizer.view's coordinate space 54 | */ 55 | -(BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch __attribute((objc_requires_super)); 56 | 57 | ///--------------------------------------- 58 | /// @name Drawer Presentation 59 | ///--------------------------------------- 60 | /** 61 | Sets the initial conditions for `MMDrawerController` and its child view controllers to prepare the drawer for a transition. If a drawer is open and the opposite drawer is being presented, it prepares that drawer to be hidden and vice-versa for the closing drawer. 62 | 63 | @param drawer The drawer side that will be presented 64 | @param animated A boolean that indicates whether the presentation is being animated or not 65 | */ 66 | -(void)prepareToPresentDrawer:(MMDrawerSide)drawer animated:(BOOL)animated __attribute((objc_requires_super)); 67 | 68 | ///--------------------------------------- 69 | /// @name Opening/Closing Drawer 70 | ///--------------------------------------- 71 | /** 72 | The method that handles closing the drawer. You can subclass this method to get a callback every time the drawer is about to be closed. You can inspect the current open side to determine what side is about to be closed. 73 | 74 | @param animated A boolean that indicates whether the drawer should close with animation 75 | @param velocity A float indicating how fast the drawer should close 76 | @param animationOptions A mask defining the animation options of the animation 77 | @param completion A completion block to be called when the drawer is finished closing 78 | */ 79 | -(void)closeDrawerAnimated:(BOOL)animated velocity:(CGFloat)velocity animationOptions:(UIViewAnimationOptions)options completion:(void (^)(BOOL))completion __attribute((objc_requires_super)); 80 | 81 | /** 82 | The method that handles opening the drawer. You can subclass this method to get a callback every time the drawer is about to be opened. 83 | 84 | @param drawerSide The drawer side that will be opened 85 | @param animated A boolean that indicates whether the drawer should open with animation 86 | @param velocity A float indicating how fast the drawer should open 87 | @param animationOptions A mask defining the animation options of the animation 88 | @param completion A completion block to be called when the drawer is finished opening 89 | */ 90 | -(void)openDrawerSide:(MMDrawerSide)drawerSide animated:(BOOL)animated velocity:(CGFloat)velocity animationOptions:(UIViewAnimationOptions)options completion:(void (^)(BOOL))completion __attribute((objc_requires_super)); 91 | 92 | ///--------------------------------------- 93 | /// @name `UIViewController` Subclass Methods 94 | ///--------------------------------------- 95 | /** 96 | Included here to ensure subclasses call `super`. 97 | 98 | @param toInterfaceOrientation The interface orientation that the interface is moving to 99 | @param duration The duration of the interface orientation animation 100 | */ 101 | -(void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration __attribute((objc_requires_super)); 102 | 103 | /** 104 | Included here to ensure subclasses call `super`. 105 | 106 | @param toInterfaceOrientation The interface orientation that the interface is moving to 107 | @param duration The duration of the interface orientation animation 108 | */ 109 | -(void)willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration __attribute((objc_requires_super)); 110 | 111 | @end 112 | -------------------------------------------------------------------------------- /Examples/Advanced/JGScrollableTableViewCell Advanced/MMDrawerController/MMDrawerController.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013 Mutual Mobile (http://mutualmobile.com/) 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | 22 | #import 23 | 24 | /** 25 | `MMDrawerController` is a side drawer navigation container view controller designed to support the growing number of applications that leverage the side drawer paradigm. This library is designed to exclusively support side drawer navigation in light-weight, focused approach. 26 | 27 | ## Creating a MMDrawerController 28 | `MMDrawerController` is a container view controller, similiar to `UINavigationController` or `UITabBarController`, with up to three child view controllers - Center, LeftDrawer, and RightDrawer. To create a `MMDrawerController`, you must first instantiate the drawer view controllers and the initial center controller, then call one of the init methods listed in this class. 29 | 30 | ## Handling a UINavigationController as the centerViewController 31 | `MMDrawerController` automatically supports handling a `UINavigationController` as the `centerViewController`, and will correctly handle the proper gestures on each view (the navigation bar view as well as the content view for the visible view controller). Note that while this library does support other container view controllers, the open/close gestures are not customized to support them. 32 | 33 | ## Accessing MMDrawerController from the Child View Controller 34 | You can leverage the category class (UIViewController+MMDrawerController) included with this library to access information about the parent `MMDrawerController`. Note that if you are contained within a UINavigationController, the `drawerContainerViewController` will still return the proper reference to the `drawerContainerViewController` parent, even though it is not the direct parent. Refer to the documentation included with the category for more information. 35 | 36 | ## How MMDrawerOpenCenterInteractionMode is handled 37 | `MMDrawerOpenCenterInteractionMode` controls how the user should be able to interact with the center view controller when either drawer is open. By default, this is set to `MMDrawerOpenCenterInteractionModeNavigationBarOnly`, which allows the user to interact with UINavigationBarItems while either drawer is open (typicaly used to click the menu button to close). If you set the interaction mode to `MMDrawerOpenCenterInteractionModeNone`, no items within the center view will be able to be interacted with while a drawer is open. Note that this setting has no effect at all on the `MMCloseDrawerGestureMode`. 38 | 39 | ## How Open/Close Gestures are handled 40 | Two gestures are added to every instance of a drawer controller, one for pan and one for touch. `MMDrawerController` is the delegate for each of the gesture recoginzers, and determines if a touch should be sent to the appropriate gesture when a touch is detected compared with the masks set for open and close gestures and the state of the drawer controller. 41 | 42 | ## Integrating with State Restoration 43 | In order to opt in to state restoration for `MMDrawerController`, you must set the `restorationIdentifier` of your drawer controller. Instances of your centerViewController, leftDrawerViewController and rightDrawerViewController must also be configured with their own `restorationIdentifier` (and optionally a restorationClass) if you intend for those to be restored as well. If your MMDrawerController had an open drawer when your app was sent to the background, that state will also be restored. 44 | 45 | ## What this library doesn't do. 46 | This library is not meant for: 47 | - Top or bottom drawer views 48 | - Displaying both drawers at one time 49 | - Displaying a minimum drawer width 50 | - Support container view controllers other than `UINavigationController` as the center view controller. 51 | */ 52 | 53 | typedef NS_ENUM(NSInteger,MMDrawerSide){ 54 | MMDrawerSideNone = 0, 55 | MMDrawerSideLeft, 56 | MMDrawerSideRight, 57 | }; 58 | 59 | typedef NS_OPTIONS(NSInteger, MMOpenDrawerGestureMode) { 60 | MMOpenDrawerGestureModeNone = 0, 61 | MMOpenDrawerGestureModePanningNavigationBar = 1 << 1, 62 | MMOpenDrawerGestureModePanningCenterView = 1 << 2, 63 | MMOpenDrawerGestureModeBezelPanningCenterView = 1 << 3, 64 | MMOpenDrawerGestureModeCustom = 1 << 4, 65 | MMOpenDrawerGestureModeAll = MMOpenDrawerGestureModePanningNavigationBar | 66 | MMOpenDrawerGestureModePanningCenterView | 67 | MMOpenDrawerGestureModeBezelPanningCenterView | 68 | MMOpenDrawerGestureModeCustom, 69 | }; 70 | 71 | typedef NS_OPTIONS(NSInteger, MMCloseDrawerGestureMode) { 72 | MMCloseDrawerGestureModeNone = 0, 73 | MMCloseDrawerGestureModePanningNavigationBar = 1 << 1, 74 | MMCloseDrawerGestureModePanningCenterView = 1 << 2, 75 | MMCloseDrawerGestureModeBezelPanningCenterView = 1 << 3, 76 | MMCloseDrawerGestureModeTapNavigationBar = 1 << 4, 77 | MMCloseDrawerGestureModeTapCenterView = 1 << 5, 78 | MMCloseDrawerGestureModePanningDrawerView = 1 << 6, 79 | MMCloseDrawerGestureModeCustom = 1 << 7, 80 | MMCloseDrawerGestureModeAll = MMCloseDrawerGestureModePanningNavigationBar | 81 | MMCloseDrawerGestureModePanningCenterView | 82 | MMCloseDrawerGestureModeBezelPanningCenterView | 83 | MMCloseDrawerGestureModeTapNavigationBar | 84 | MMCloseDrawerGestureModeTapCenterView | 85 | MMCloseDrawerGestureModePanningDrawerView | 86 | MMCloseDrawerGestureModeCustom, 87 | }; 88 | 89 | typedef NS_ENUM(NSInteger, MMDrawerOpenCenterInteractionMode) { 90 | MMDrawerOpenCenterInteractionModeNone, 91 | MMDrawerOpenCenterInteractionModeFull, 92 | MMDrawerOpenCenterInteractionModeNavigationBarOnly, 93 | }; 94 | 95 | @class MMDrawerController; 96 | typedef void (^MMDrawerControllerDrawerVisualStateBlock)(MMDrawerController * drawerController, MMDrawerSide drawerSide, CGFloat percentVisible); 97 | 98 | @interface MMDrawerController : UIViewController 99 | 100 | ///--------------------------------------- 101 | /// @name Accessing Drawer Container View Controller Properties 102 | ///--------------------------------------- 103 | 104 | /** 105 | The center view controller. 106 | 107 | This can only be set via the init methods, as well as the `setNewCenterViewController:...` methods. The size of this view controller will automatically be set to the size of the drawer container view controller, and it's position is modified from within this class. Do not modify the frame externally. 108 | */ 109 | @property (nonatomic, strong) UIViewController * centerViewController; 110 | 111 | /** 112 | The left drawer view controller. 113 | 114 | The size of this view controller is managed within this class, and is automatically set to the appropriate size based on the `maximumLeftDrawerWidth`. Do not modify the frame externally. 115 | */ 116 | @property (nonatomic, strong) UIViewController * leftDrawerViewController; 117 | 118 | /** 119 | The right drawer view controller. 120 | 121 | The size of this view controller is managed within this class, and is automatically set to the appropriate size based on the `maximumRightDrawerWidth`. Do not modify the frame externally. 122 | */ 123 | @property (nonatomic, strong) UIViewController * rightDrawerViewController; 124 | 125 | /** 126 | The maximum width of the `leftDrawerViewController`. 127 | 128 | By default, this is set to 280. If the `leftDrawerViewController` is nil, this property will return 0.0; 129 | */ 130 | @property (nonatomic, assign) CGFloat maximumLeftDrawerWidth; 131 | 132 | /** 133 | The maximum width of the `rightDrawerViewController`. 134 | 135 | By default, this is set to 280. If the `rightDrawerViewController` is nil, this property will return 0.0; 136 | 137 | */ 138 | @property (nonatomic, assign) CGFloat maximumRightDrawerWidth; 139 | 140 | /** 141 | The visible width of the `leftDrawerViewController`. 142 | 143 | Note this value can be greater than `maximumLeftDrawerWidth` during the full close animation when setting a new center view controller; 144 | */ 145 | @property (nonatomic, assign, readonly) CGFloat visibleLeftDrawerWidth; 146 | 147 | /** 148 | The visible width of the `rightDrawerViewController`. 149 | 150 | Note this value can be greater than `maximumRightDrawerWidth` during the full close animation when setting a new center view controller; 151 | */ 152 | @property (nonatomic, assign, readonly) CGFloat visibleRightDrawerWidth; 153 | 154 | /** 155 | The animation velocity of the open and close methods, measured in points per second. 156 | 157 | By default, this is set to 840 points per second (three times the default drawer width), meaning it takes 1/3 of a second for the `centerViewController` to open/close across the default drawer width. Note that there is a minimum .1 second duration for built in animations, to account for small distance animations. 158 | */ 159 | @property (nonatomic, assign) CGFloat animationVelocity; 160 | 161 | /** 162 | A boolean that determines whether or not the panning gesture will "hard-stop" at the maximum width for a given drawer side. 163 | 164 | By default, this value is set to YES. Enabling `shouldStretchDrawer` will give the pan a gradual asymptotic stopping point much like `UIScrollView` behaves. Note that if this value is set to YES, the `drawerVisualStateBlock` can be passed a `percentVisible` greater than 1.0, so be sure to handle that case appropriately. 165 | */ 166 | @property (nonatomic, assign) BOOL shouldStretchDrawer; 167 | 168 | /** 169 | The current open side of the drawer. 170 | 171 | Note this value will change as soon as a pan gesture opens a drawer, or when a open/close animation is finished. 172 | */ 173 | @property (nonatomic, assign, readonly) MMDrawerSide openSide; 174 | 175 | /** 176 | How a user is allowed to open a drawer using gestures. 177 | 178 | By default, this is set to `MMOpenDrawerGestureModeNone`. Note these gestures may affect user interaction with the `centerViewController`, so be sure to use appropriately. 179 | */ 180 | @property (nonatomic, assign) MMOpenDrawerGestureMode openDrawerGestureModeMask; 181 | 182 | /** 183 | How a user is allowed to close a drawer. 184 | 185 | By default, this is set to `MMCloseDrawerGestureModeNone`. Note these gestures may affect user interaction with the `centerViewController`, so be sure to use appropriately. 186 | */ 187 | @property (nonatomic, assign) MMCloseDrawerGestureMode closeDrawerGestureModeMask; 188 | 189 | /** 190 | The value determining if the user can interact with the `centerViewController` when a side drawer is open. 191 | 192 | By default, it is `MMDrawerOpenCenterInteractionModeNavigationBarOnly`, meaning that the user can only interact with the buttons on the `UINavigationBar`, if the center view controller is a `UINavigationController`. Otherwise, the user cannot interact with any other center view controller elements. 193 | */ 194 | @property (nonatomic, assign) MMDrawerOpenCenterInteractionMode centerHiddenInteractionMode; 195 | 196 | /** 197 | The flag determining if a shadow should be drawn off of `centerViewController` when a drawer is open. 198 | 199 | By default, this is set to YES. 200 | */ 201 | @property (nonatomic, assign) BOOL showsShadow; 202 | 203 | /** 204 | The flag determining if a custom background view should appear beneath the status bar, forcing the child content to be drawn lower than the status bar. This property is only available for > iOS 7.0 to take into account for the new behavior of the status bar. 205 | 206 | By default, this is set to NO. If running on < iOS 7.0, it will always return NO. 207 | */ 208 | @property (nonatomic, assign) BOOL showsStatusBarBackgroundView; 209 | 210 | /** 211 | The color of the status bar background view if `showsStatusBarBackgroundView` is set to YES. This value is ignored in < iOS 7.0. 212 | 213 | By default, this is set `[UIColor blackColor]`. 214 | */ 215 | @property (nonatomic, strong) UIColor * statusBarViewBackgroundColor; 216 | 217 | ///--------------------------------------- 218 | /// @name Initializing a `MMDrawerController` 219 | ///--------------------------------------- 220 | 221 | /** 222 | Creates and initializes an `MMDrawerController` object with the specified center view controller, left drawer view controller, and right drawer view controller. 223 | 224 | @param centerViewController The center view controller. This argument must not be `nil`. 225 | @param leftDrawerViewController The left drawer view controller. 226 | @param rightDrawerViewController The right drawer controller. 227 | 228 | @return The newly-initialized drawer container view controller. 229 | */ 230 | -(id)initWithCenterViewController:(UIViewController *)centerViewController leftDrawerViewController:(UIViewController *)leftDrawerViewController rightDrawerViewController:(UIViewController *)rightDrawerViewController; 231 | 232 | /** 233 | Creates and initializes an `MMDrawerController` object with the specified center view controller, left drawer view controller. 234 | 235 | @param centerViewController The center view controller. This argument must not be `nil`. 236 | @param leftDrawerViewController The left drawer view controller. 237 | 238 | @return The newly-initialized drawer container view controller. 239 | */ 240 | -(id)initWithCenterViewController:(UIViewController *)centerViewController leftDrawerViewController:(UIViewController *)leftDrawerViewController; 241 | 242 | /** 243 | Creates and initializes an `MMDrawerController` object with the specified center view controller, right drawer view controller. 244 | 245 | @param centerViewController The center view controller. This argument must not be `nil`. 246 | @param rightDrawerViewController The right drawer controller. 247 | 248 | @return The newly-initialized drawer container view controller. 249 | */ 250 | -(id)initWithCenterViewController:(UIViewController *)centerViewController rightDrawerViewController:(UIViewController *)rightDrawerViewController; 251 | 252 | ///--------------------------------------- 253 | /// @name Opening and Closing a Drawer 254 | ///--------------------------------------- 255 | 256 | /** 257 | Toggles the drawer open/closed based on the `drawer` passed in. 258 | 259 | Note that if you attempt to toggle a drawer closed while the other is open, nothing will happen. For example, if you pass in MMDrawerSideLeft, but the right drawer is open, nothing will happen. In addition, the completion block will be called with the finished flag set to NO. 260 | 261 | @param drawerSide The `MMDrawerSide` to toggle. This value cannot be `MMDrawerSideNone`. 262 | @param animated Determines whether the `drawer` should be toggle animated. 263 | @param completion The block that is called when the toggle is complete, or if no toggle took place at all. 264 | 265 | */ 266 | -(void)toggleDrawerSide:(MMDrawerSide)drawerSide animated:(BOOL)animated completion:(void(^)(BOOL finished))completion; 267 | 268 | /** 269 | Closes the open drawer. 270 | 271 | @param animated Determines whether the drawer side should be closed animated 272 | @param completion The block that is called when the close is complete 273 | 274 | */ 275 | -(void)closeDrawerAnimated:(BOOL)animated completion:(void(^)(BOOL finished))completion; 276 | 277 | /** 278 | Opens the `drawer` passed in. 279 | 280 | @param drawerSide The `MMDrawerSide` to open. This value cannot be `MMDrawerSideNone`. 281 | @param animated Determines whether the `drawer` should be open animated. 282 | @param completion The block that is called when the toggle is open. 283 | 284 | */ 285 | -(void)openDrawerSide:(MMDrawerSide)drawerSide animated:(BOOL)animated completion:(void(^)(BOOL finished))completion; 286 | 287 | ///--------------------------------------- 288 | /// @name Setting a new Center View Controller 289 | ///--------------------------------------- 290 | 291 | /** 292 | Sets the new `centerViewController`. 293 | 294 | This sets the view controller and will automatically adjust the frame based on the current state of the drawer controller. If `closeAnimated` is YES, it will immediately change the center view controller, and close the drawer from its current position. 295 | 296 | @param centerViewController The new `centerViewController`. 297 | @param closeAnimated Determines whether the drawer should be closed with an animation. 298 | @param completion The block called when the animation is finsihed. 299 | 300 | */ 301 | -(void)setCenterViewController:(UIViewController *)centerViewController withCloseAnimation:(BOOL)closeAnimated completion:(void(^)(BOOL finished))completion; 302 | 303 | /** 304 | Sets the new `centerViewController`. 305 | 306 | This sets the view controller and will automatically adjust the frame based on the current state of the drawer controller. If `closeFullAnimated` is YES, the current center view controller will animate off the screen, the new center view controller will then be set, followed by the drawer closing across the full width of the screen. 307 | 308 | @param newCenterViewController The new `centerViewController`. 309 | @param fullCloseAnimated Determines whether the drawer should be closed with an animation. 310 | @param completion The block called when the animation is finsihed. 311 | 312 | */ 313 | -(void)setCenterViewController:(UIViewController *)newCenterViewController withFullCloseAnimation:(BOOL)fullCloseAnimated completion:(void(^)(BOOL finished))completion; 314 | 315 | ///--------------------------------------- 316 | /// @name Animating the Width of a Drawer 317 | ///--------------------------------------- 318 | 319 | /** 320 | Sets the maximum width of the left drawer view controller. 321 | 322 | If the drawer is open, and `animated` is YES, it will animate the drawer frame as well as adjust the center view controller. If the drawer is not open, this change will take place immediately. 323 | 324 | @param width The new width of left drawer view controller. This must be greater than zero. 325 | @param animated Determines whether the drawer should be adjusted with an animation. 326 | @param completion The block called when the animation is finished. 327 | 328 | */ 329 | -(void)setMaximumLeftDrawerWidth:(CGFloat)width animated:(BOOL)animated completion:(void(^)(BOOL finished))completion; 330 | 331 | /** 332 | Sets the maximum width of the right drawer view controller. 333 | 334 | If the drawer is open, and `animated` is YES, it will animate the drawer frame as well as adjust the center view controller. If the drawer is not open, this change will take place immediately. 335 | 336 | @param width The new width of right drawer view controller. This must be greater than zero. 337 | @param animated Determines whether the drawer should be adjusted with an animation. 338 | @param completion The block called when the animation is finished. 339 | 340 | */ 341 | -(void)setMaximumRightDrawerWidth:(CGFloat)width animated:(BOOL)animated completion:(void(^)(BOOL finished))completion; 342 | 343 | ///--------------------------------------- 344 | /// @name Previewing a Drawer 345 | ///--------------------------------------- 346 | 347 | /** 348 | Bounce preview for the specified `drawerSide` a distance of 40 points. 349 | 350 | @param drawerSide The drawer to preview. This value cannot be `MMDrawerSideNone`. 351 | @param completion The block called when the animation is finsihed. 352 | 353 | */ 354 | -(void)bouncePreviewForDrawerSide:(MMDrawerSide)drawerSide completion:(void(^)(BOOL finished))completion; 355 | 356 | /** 357 | Bounce preview for the specified `drawerSide`. 358 | 359 | @param drawerSide The drawer side to preview. This value cannot be `MMDrawerSideNone`. 360 | @param distance The distance to bounce. 361 | @param completion The block called when the animation is finsihed. 362 | 363 | */ 364 | -(void)bouncePreviewForDrawerSide:(MMDrawerSide)drawerSide distance:(CGFloat)distance completion:(void(^)(BOOL finished))completion; 365 | 366 | ///--------------------------------------- 367 | /// @name Custom Drawer Animations 368 | ///--------------------------------------- 369 | 370 | /** 371 | Sets a callback to be called when a drawer visual state needs to be updated. 372 | 373 | This block is responsible for updating the drawer's view state, and the drawer controller will handle animating to that state from the current state. This block will be called when the drawer is opened or closed, as well when the user is panning the drawer. This block is not responsible for doing animations directly, but instead just updating the state of the properies (such as alpha, anchor point, transform, etc). Note that if `shouldStretchDrawer` is set to YES, it is possible for `percentVisible` to be greater than 1.0. If `shouldStretchDrawer` is set to NO, `percentVisible` will never be greater than 1.0. 374 | 375 | Note that when the drawer is finished opening or closing, the side drawer controller view will be reset with the following properies: 376 | 377 | - alpha: 1.0 378 | - transform: CATransform3DIdentity 379 | - anchorPoint: (0.5,0.5) 380 | 381 | @param drawerVisualStateBlock A block object to be called that allows the implementer to update visual state properties on the drawer. `percentVisible` represents the amount of the drawer space that is current visible, with drawer space being defined as the edge of the screen to the maxmimum drawer width. Note that you do have access to the drawerController, which will allow you to update things like the anchor point of the side drawer layer. 382 | */ 383 | -(void)setDrawerVisualStateBlock:(void(^)(MMDrawerController * drawerController, MMDrawerSide drawerSide, CGFloat percentVisible))drawerVisualStateBlock; 384 | 385 | ///--------------------------------------- 386 | /// @name Gesture Completion Handling 387 | ///--------------------------------------- 388 | 389 | /** 390 | Sets a callback to be called when a gesture has been completed. 391 | 392 | This block is called when a gesture action has been completed. You can query the `openSide` of the `drawerController` to determine what the new state of the drawer is. 393 | 394 | @param gestureCompletionBlock A block object to be called that allows the implementer be notified when a gesture action has been completed. 395 | */ 396 | -(void)setGestureCompletionBlock:(void(^)(MMDrawerController * drawerController, UIGestureRecognizer * gesture))gestureCompletionBlock; 397 | 398 | ///--------------------------------------- 399 | /// @name Custom Gesture Handler 400 | ///--------------------------------------- 401 | 402 | /** 403 | Sets a callback to be called to determine if a UIGestureRecognizer should recieve the given UITouch. 404 | 405 | This block provides a way to allow a gesture to be recognized with custom logic. For example, you may have a certain part of your view that should accept a pan gesture recognizer to open the drawer, but not another a part. If you return YES, the gesture is recognized and the appropriate action is taken. This provides similar support to how Facebook allows you to pan on the background view of the main table view, but not the content itself. You can inspect the `openSide` property of the `drawerController` to determine the current state of the drawer, and apply the appropriate logic within your block. 406 | 407 | Note that either `openDrawerGestureModeMask` must contain `MMOpenDrawerGestureModeCustom`, or `closeDrawerGestureModeMask` must contain `MMCloseDrawerGestureModeCustom` for this block to be consulted. 408 | 409 | @param gestureShouldRecognizeTouchBlock A block object to be called to determine if the given `touch` should be recognized by the given gesture. 410 | */ 411 | -(void)setGestureShouldRecognizeTouchBlock:(BOOL(^)(MMDrawerController * drawerController, UIGestureRecognizer * gesture, UITouch * touch))gestureShouldRecognizeTouchBlock; 412 | 413 | @end 414 | -------------------------------------------------------------------------------- /Examples/Advanced/JGScrollableTableViewCell Advanced/MMDrawerController/MMDrawerVisualState.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013 Mutual Mobile (http://mutualmobile.com/) 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | 22 | #import 23 | #import "MMDrawerController.h" 24 | 25 | /** 26 | `MMDrawerVisualState` provides prebuilt visual state update blocks that can be used in the callback block of `MMDrawerController` for drawer animation. 27 | */ 28 | 29 | @interface MMDrawerVisualState : NSObject 30 | 31 | /** 32 | Creates a slide and scale visual state block that gives an experience similar to Mailbox.app. It scales from 90% to 100%, and translates 50 pixels in the x direction. In addition, it also sets alpha from 0.0 to 1.0. 33 | 34 | @return The visual state block. 35 | */ 36 | +(MMDrawerControllerDrawerVisualStateBlock)slideAndScaleVisualStateBlock; 37 | 38 | /** 39 | Creates a slide visual state block that gives the user an experience that slides at the same speed of the center view controller during animation. This is equal to calling `parallaxVisualStateBlockWithParallaxFactor:` with a parallax factor of 1.0. 40 | 41 | @return The visual state block. 42 | */ 43 | +(MMDrawerControllerDrawerVisualStateBlock)slideVisualStateBlock; 44 | 45 | /** 46 | Creates a swinging door visual state block that gives the user an experience that animates the drawer in along the hinge. 47 | 48 | @return The visual state block. 49 | */ 50 | +(MMDrawerControllerDrawerVisualStateBlock)swingingDoorVisualStateBlock; 51 | 52 | /** 53 | Creates a parallax experience that slides the side drawer view controller at a different rate than the center view controller during animation. For every parallaxFactor of points moved by the center view controller, the side drawer view controller will move 1 point. Passing in 1.0 is the equivalent of a applying a sliding animation, while passing in MAX_FLOAT is the equivalent of having no animation at all. 54 | 55 | @param parallaxFactor The amount of parallax applied to the side drawer conroller. This value must be greater than 1.0. The closer the value is to 1.0, the faster the side drawer view controller will be parallaxing. 56 | 57 | @return The visual state block. 58 | */ 59 | +(MMDrawerControllerDrawerVisualStateBlock)parallaxVisualStateBlockWithParallaxFactor:(CGFloat)parallaxFactor; 60 | 61 | @end 62 | -------------------------------------------------------------------------------- /Examples/Advanced/JGScrollableTableViewCell Advanced/MMDrawerController/MMDrawerVisualState.m: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013 Mutual Mobile (http://mutualmobile.com/) 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | 22 | #import "MMDrawerVisualState.h" 23 | #import 24 | 25 | @implementation MMDrawerVisualState 26 | +(MMDrawerControllerDrawerVisualStateBlock)slideAndScaleVisualStateBlock{ 27 | MMDrawerControllerDrawerVisualStateBlock visualStateBlock = 28 | ^(MMDrawerController * drawerController, MMDrawerSide drawerSide, CGFloat percentVisible){ 29 | CGFloat minScale = .90; 30 | CGFloat scale = minScale + (percentVisible*(1.0-minScale)); 31 | CATransform3D scaleTransform = CATransform3DMakeScale(scale, scale, scale); 32 | 33 | CGFloat maxDistance = 50; 34 | CGFloat distance = maxDistance * percentVisible; 35 | CATransform3D translateTransform = CATransform3DIdentity; 36 | UIViewController * sideDrawerViewController; 37 | if(drawerSide == MMDrawerSideLeft) { 38 | sideDrawerViewController = drawerController.leftDrawerViewController; 39 | translateTransform = CATransform3DMakeTranslation((maxDistance-distance), 0.0, 0.0); 40 | } 41 | else if(drawerSide == MMDrawerSideRight){ 42 | sideDrawerViewController = drawerController.rightDrawerViewController; 43 | translateTransform = CATransform3DMakeTranslation(-(maxDistance-distance), 0.0, 0.0); 44 | } 45 | 46 | [sideDrawerViewController.view.layer setTransform:CATransform3DConcat(scaleTransform, translateTransform)]; 47 | [sideDrawerViewController.view setAlpha:percentVisible]; 48 | }; 49 | return visualStateBlock; 50 | } 51 | 52 | +(MMDrawerControllerDrawerVisualStateBlock)slideVisualStateBlock{ 53 | return [self parallaxVisualStateBlockWithParallaxFactor:1.0]; 54 | } 55 | 56 | 57 | +(MMDrawerControllerDrawerVisualStateBlock)swingingDoorVisualStateBlock{ 58 | MMDrawerControllerDrawerVisualStateBlock visualStateBlock = 59 | ^(MMDrawerController * drawerController, MMDrawerSide drawerSide, CGFloat percentVisible){ 60 | UIViewController * sideDrawerViewController; 61 | CGPoint anchorPoint; 62 | CGFloat maxDrawerWidth = 0.0; 63 | CGFloat xOffset; 64 | CGFloat angle = 0.0; 65 | 66 | if(drawerSide==MMDrawerSideLeft){ 67 | 68 | sideDrawerViewController = drawerController.leftDrawerViewController; 69 | anchorPoint = CGPointMake(1.0, .5); 70 | maxDrawerWidth = MAX(drawerController.maximumLeftDrawerWidth,drawerController.visibleLeftDrawerWidth); 71 | xOffset = -(maxDrawerWidth/2.0) + (maxDrawerWidth)*percentVisible; 72 | angle = -M_PI_2+(percentVisible*M_PI_2); 73 | } 74 | else { 75 | sideDrawerViewController = drawerController.rightDrawerViewController; 76 | anchorPoint = CGPointMake(0.0, .5); 77 | maxDrawerWidth = MAX(drawerController.maximumRightDrawerWidth,drawerController.visibleRightDrawerWidth); 78 | xOffset = (maxDrawerWidth/2.0) - (maxDrawerWidth)*percentVisible; 79 | angle = M_PI_2-(percentVisible*M_PI_2); 80 | } 81 | 82 | [sideDrawerViewController.view.layer setAnchorPoint:anchorPoint]; 83 | [sideDrawerViewController.view.layer setShouldRasterize:YES]; 84 | [sideDrawerViewController.view.layer setRasterizationScale:[[UIScreen mainScreen] scale]]; 85 | 86 | CATransform3D swingingDoorTransform = CATransform3DIdentity; 87 | if (percentVisible <= 1.f) { 88 | 89 | CATransform3D identity = CATransform3DIdentity; 90 | identity.m34 = -1.0/1000.0; 91 | CATransform3D rotateTransform = CATransform3DRotate(identity, angle, 0.0, 1.0, 0.0); 92 | 93 | CATransform3D translateTransform = CATransform3DMakeTranslation(xOffset, 0.0, 0.0); 94 | 95 | CATransform3D concatTransform = CATransform3DConcat(rotateTransform, translateTransform); 96 | 97 | swingingDoorTransform = concatTransform; 98 | } 99 | else{ 100 | CATransform3D overshootTransform = CATransform3DMakeScale(percentVisible, 1.f, 1.f); 101 | 102 | NSInteger scalingModifier = 1.f; 103 | if (drawerSide == MMDrawerSideRight) { 104 | scalingModifier = -1.f; 105 | } 106 | 107 | overshootTransform = CATransform3DTranslate(overshootTransform, scalingModifier*maxDrawerWidth/2, 0.f, 0.f); 108 | swingingDoorTransform = overshootTransform; 109 | } 110 | 111 | [sideDrawerViewController.view.layer setTransform:swingingDoorTransform]; 112 | }; 113 | return visualStateBlock; 114 | } 115 | 116 | +(MMDrawerControllerDrawerVisualStateBlock)parallaxVisualStateBlockWithParallaxFactor:(CGFloat)parallaxFactor{ 117 | MMDrawerControllerDrawerVisualStateBlock visualStateBlock = 118 | ^(MMDrawerController * drawerController, MMDrawerSide drawerSide, CGFloat percentVisible){ 119 | NSParameterAssert(parallaxFactor >= 1.0); 120 | CATransform3D transform = CATransform3DIdentity; 121 | UIViewController * sideDrawerViewController; 122 | if(drawerSide == MMDrawerSideLeft) { 123 | sideDrawerViewController = drawerController.leftDrawerViewController; 124 | CGFloat distance = MAX(drawerController.maximumLeftDrawerWidth,drawerController.visibleLeftDrawerWidth); 125 | if (percentVisible <= 1.f) { 126 | transform = CATransform3DMakeTranslation((-distance)/parallaxFactor+(distance*percentVisible/parallaxFactor), 0.0, 0.0); 127 | } 128 | else{ 129 | transform = CATransform3DMakeScale(percentVisible, 1.f, 1.f); 130 | transform = CATransform3DTranslate(transform, drawerController.maximumLeftDrawerWidth*(percentVisible-1.f)/2, 0.f, 0.f); 131 | } 132 | } 133 | else if(drawerSide == MMDrawerSideRight){ 134 | sideDrawerViewController = drawerController.rightDrawerViewController; 135 | CGFloat distance = MAX(drawerController.maximumRightDrawerWidth,drawerController.visibleRightDrawerWidth); 136 | if(percentVisible <= 1.f){ 137 | transform = CATransform3DMakeTranslation((distance)/parallaxFactor-(distance*percentVisible)/parallaxFactor, 0.0, 0.0); 138 | } 139 | else{ 140 | transform = CATransform3DMakeScale(percentVisible, 1.f, 1.f); 141 | transform = CATransform3DTranslate(transform, -drawerController.maximumRightDrawerWidth*(percentVisible-1.f)/2, 0.f, 0.f); 142 | } 143 | } 144 | 145 | [sideDrawerViewController.view.layer setTransform:transform]; 146 | }; 147 | return visualStateBlock; 148 | } 149 | 150 | @end 151 | -------------------------------------------------------------------------------- /Examples/Advanced/JGScrollableTableViewCell Advanced/MMDrawerController/UIViewController+MMDrawerController.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013 Mutual Mobile (http://mutualmobile.com/) 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | #import 22 | 23 | #import "MMDrawerController.h" 24 | 25 | /** 26 | A helper category on `UIViewController` that exposes the parent drawer controller, the visible side drawer frame, and a `mm_drawerWillAppear` method that is called when the drawer is about to appear. 27 | */ 28 | 29 | @interface UIViewController (MMDrawerController) 30 | 31 | ///--------------------------------------- 32 | /// @name Accessing Drawer View Controller Properties 33 | ///--------------------------------------- 34 | 35 | /** 36 | The `MMDrawerController` that the view controller is contained within. If the view controller is not contained within a `MMDrawerController`, this property is nil. Note that if the view controller is contained within a `UINavigationController`, that navigation controller is contained within a `MMDrawerController`, this property will return a refernce to the `MMDrawerController`, despite the fact that it is not the direct parent of the view controller. 37 | */ 38 | @property(nonatomic, strong, readonly) MMDrawerController *mm_drawerController; 39 | 40 | /** 41 | The visible rect of the side drawer view controller in the drawer controller coordinate space. If the view controller is not a drawer in a `MMDrawerController`, then this property returns `CGRectNull` 42 | */ 43 | @property(nonatomic, assign, readonly) CGRect mm_visibleDrawerFrame; 44 | 45 | @end 46 | -------------------------------------------------------------------------------- /Examples/Advanced/JGScrollableTableViewCell Advanced/MMDrawerController/UIViewController+MMDrawerController.m: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013 Mutual Mobile (http://mutualmobile.com/) 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | 22 | #import "UIViewController+MMDrawerController.h" 23 | 24 | @implementation UIViewController (MMDrawerController) 25 | 26 | 27 | -(MMDrawerController*)mm_drawerController{ 28 | UIViewController *parentViewController = self.parentViewController; 29 | while (parentViewController != nil) { 30 | if([parentViewController isKindOfClass:[MMDrawerController class]]){ 31 | return (MMDrawerController *)parentViewController; 32 | } 33 | parentViewController = parentViewController.parentViewController; 34 | } 35 | return nil; 36 | } 37 | 38 | -(CGRect)mm_visibleDrawerFrame{ 39 | if([self isEqual:self.mm_drawerController.leftDrawerViewController] || 40 | [self.navigationController isEqual:self.mm_drawerController.leftDrawerViewController]){ 41 | CGRect rect = self.mm_drawerController.view.bounds; 42 | rect.size.width = self.mm_drawerController.maximumLeftDrawerWidth; 43 | return rect; 44 | 45 | } 46 | else if([self isEqual:self.mm_drawerController.rightDrawerViewController] || 47 | [self.navigationController isEqual:self.mm_drawerController.rightDrawerViewController]){ 48 | CGRect rect = self.mm_drawerController.view.bounds; 49 | rect.size.width = self.mm_drawerController.maximumRightDrawerWidth; 50 | rect.origin.x = CGRectGetWidth(self.mm_drawerController.view.bounds)-rect.size.width; 51 | return rect; 52 | } 53 | else { 54 | return CGRectNull; 55 | } 56 | } 57 | 58 | @end 59 | -------------------------------------------------------------------------------- /Examples/Advanced/JGScrollableTableViewCell Advanced/en.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* Localized versions of Info.plist keys */ 2 | 3 | -------------------------------------------------------------------------------- /Examples/Advanced/JGScrollableTableViewCell Advanced/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // JGScrollableTableViewCell Advanced 4 | // 5 | // Created by Jonas Gessner on 21.12.13. 6 | // Copyright (c) 2013 Jonas Gessner. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | #import "JGAppDelegate.h" 12 | 13 | int main(int argc, char * argv[]) 14 | { 15 | @autoreleasepool { 16 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([JGAppDelegate class])); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Examples/Basic/JGScrollableTableViewCell Basic.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 6B3C710F1865BD16000F50B7 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 6B3C71001865BD16000F50B7 /* InfoPlist.strings */; }; 11 | 6B3C71101865BD16000F50B7 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 6B3C71021865BD16000F50B7 /* Images.xcassets */; }; 12 | 6B3C71111865BD16000F50B7 /* JGAppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 6B3C71041865BD16000F50B7 /* JGAppDelegate.m */; }; 13 | 6B3C71151865BD16000F50B7 /* JGTestViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 6B3C710D1865BD16000F50B7 /* JGTestViewController.m */; }; 14 | 6B3C71161865BD16000F50B7 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 6B3C710E1865BD16000F50B7 /* main.m */; }; 15 | 6B3C711D1865BD55000F50B7 /* JGScrollableTableViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 6B3C711A1865BD55000F50B7 /* JGScrollableTableViewCell.m */; }; 16 | 6B3C711E1865BD55000F50B7 /* JGScrollableTableViewCellAccessoryButton.m in Sources */ = {isa = PBXBuildFile; fileRef = 6B3C711C1865BD55000F50B7 /* JGScrollableTableViewCellAccessoryButton.m */; }; 17 | 6B3C71741865C24E000F50B7 /* JGExampleScrollableTableViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 6B3C71731865C24E000F50B7 /* JGExampleScrollableTableViewCell.m */; }; 18 | DD15FB8818269EE7009D5B06 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DD15FB8718269EE7009D5B06 /* Foundation.framework */; }; 19 | DD15FB8A18269EE7009D5B06 /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DD15FB8918269EE7009D5B06 /* CoreGraphics.framework */; }; 20 | DD15FB8C18269EE7009D5B06 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DD15FB8B18269EE7009D5B06 /* UIKit.framework */; }; 21 | /* End PBXBuildFile section */ 22 | 23 | /* Begin PBXFileReference section */ 24 | 6B3C71011865BD16000F50B7 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; 25 | 6B3C71021865BD16000F50B7 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; 26 | 6B3C71031865BD16000F50B7 /* JGAppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JGAppDelegate.h; sourceTree = ""; }; 27 | 6B3C71041865BD16000F50B7 /* JGAppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = JGAppDelegate.m; sourceTree = ""; }; 28 | 6B3C710A1865BD16000F50B7 /* JGScrollableTableViewCell Basic-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "JGScrollableTableViewCell Basic-Info.plist"; sourceTree = ""; }; 29 | 6B3C710B1865BD16000F50B7 /* JGScrollableTableViewCell Basic-Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "JGScrollableTableViewCell Basic-Prefix.pch"; sourceTree = ""; }; 30 | 6B3C710C1865BD16000F50B7 /* JGTestViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JGTestViewController.h; sourceTree = ""; }; 31 | 6B3C710D1865BD16000F50B7 /* JGTestViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = JGTestViewController.m; sourceTree = ""; }; 32 | 6B3C710E1865BD16000F50B7 /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 33 | 6B3C71191865BD55000F50B7 /* JGScrollableTableViewCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JGScrollableTableViewCell.h; sourceTree = ""; }; 34 | 6B3C711A1865BD55000F50B7 /* JGScrollableTableViewCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = JGScrollableTableViewCell.m; sourceTree = ""; }; 35 | 6B3C711B1865BD55000F50B7 /* JGScrollableTableViewCellAccessoryButton.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JGScrollableTableViewCellAccessoryButton.h; sourceTree = ""; }; 36 | 6B3C711C1865BD55000F50B7 /* JGScrollableTableViewCellAccessoryButton.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = JGScrollableTableViewCellAccessoryButton.m; sourceTree = ""; }; 37 | 6B3C71721865C24E000F50B7 /* JGExampleScrollableTableViewCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JGExampleScrollableTableViewCell.h; sourceTree = ""; }; 38 | 6B3C71731865C24E000F50B7 /* JGExampleScrollableTableViewCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = JGExampleScrollableTableViewCell.m; sourceTree = ""; }; 39 | DD15FB8418269EE7009D5B06 /* JGScrollableTableViewCell Basic.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "JGScrollableTableViewCell Basic.app"; sourceTree = BUILT_PRODUCTS_DIR; }; 40 | DD15FB8718269EE7009D5B06 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; 41 | DD15FB8918269EE7009D5B06 /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; }; 42 | DD15FB8B18269EE7009D5B06 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; }; 43 | /* End PBXFileReference section */ 44 | 45 | /* Begin PBXFrameworksBuildPhase section */ 46 | DD15FB8118269EE7009D5B06 /* Frameworks */ = { 47 | isa = PBXFrameworksBuildPhase; 48 | buildActionMask = 2147483647; 49 | files = ( 50 | DD15FB8A18269EE7009D5B06 /* CoreGraphics.framework in Frameworks */, 51 | DD15FB8C18269EE7009D5B06 /* UIKit.framework in Frameworks */, 52 | DD15FB8818269EE7009D5B06 /* Foundation.framework in Frameworks */, 53 | ); 54 | runOnlyForDeploymentPostprocessing = 0; 55 | }; 56 | /* End PBXFrameworksBuildPhase section */ 57 | 58 | /* Begin PBXGroup section */ 59 | 6B3C70FF1865BD16000F50B7 /* JGScrollableTableViewCell Basic */ = { 60 | isa = PBXGroup; 61 | children = ( 62 | 6B3C71031865BD16000F50B7 /* JGAppDelegate.h */, 63 | 6B3C71041865BD16000F50B7 /* JGAppDelegate.m */, 64 | 6B3C71721865C24E000F50B7 /* JGExampleScrollableTableViewCell.h */, 65 | 6B3C71731865C24E000F50B7 /* JGExampleScrollableTableViewCell.m */, 66 | 6B3C710C1865BD16000F50B7 /* JGTestViewController.h */, 67 | 6B3C710D1865BD16000F50B7 /* JGTestViewController.m */, 68 | 6B3C71171865BD24000F50B7 /* Supporting Files */, 69 | ); 70 | path = "JGScrollableTableViewCell Basic"; 71 | sourceTree = ""; 72 | }; 73 | 6B3C71171865BD24000F50B7 /* Supporting Files */ = { 74 | isa = PBXGroup; 75 | children = ( 76 | 6B3C71001865BD16000F50B7 /* InfoPlist.strings */, 77 | 6B3C71021865BD16000F50B7 /* Images.xcassets */, 78 | 6B3C710A1865BD16000F50B7 /* JGScrollableTableViewCell Basic-Info.plist */, 79 | 6B3C710B1865BD16000F50B7 /* JGScrollableTableViewCell Basic-Prefix.pch */, 80 | 6B3C710E1865BD16000F50B7 /* main.m */, 81 | ); 82 | name = "Supporting Files"; 83 | sourceTree = ""; 84 | }; 85 | 6B3C71181865BD55000F50B7 /* JGScrollableTableViewCell */ = { 86 | isa = PBXGroup; 87 | children = ( 88 | 6B3C71191865BD55000F50B7 /* JGScrollableTableViewCell.h */, 89 | 6B3C711A1865BD55000F50B7 /* JGScrollableTableViewCell.m */, 90 | 6B3C711B1865BD55000F50B7 /* JGScrollableTableViewCellAccessoryButton.h */, 91 | 6B3C711C1865BD55000F50B7 /* JGScrollableTableViewCellAccessoryButton.m */, 92 | ); 93 | name = JGScrollableTableViewCell; 94 | path = ../../JGScrollableTableViewCell; 95 | sourceTree = ""; 96 | }; 97 | DD15FB7B18269EE7009D5B06 = { 98 | isa = PBXGroup; 99 | children = ( 100 | 6B3C71181865BD55000F50B7 /* JGScrollableTableViewCell */, 101 | 6B3C70FF1865BD16000F50B7 /* JGScrollableTableViewCell Basic */, 102 | DD15FB8618269EE7009D5B06 /* Frameworks */, 103 | DD15FB8518269EE7009D5B06 /* Products */, 104 | ); 105 | sourceTree = ""; 106 | }; 107 | DD15FB8518269EE7009D5B06 /* Products */ = { 108 | isa = PBXGroup; 109 | children = ( 110 | DD15FB8418269EE7009D5B06 /* JGScrollableTableViewCell Basic.app */, 111 | ); 112 | name = Products; 113 | sourceTree = ""; 114 | }; 115 | DD15FB8618269EE7009D5B06 /* Frameworks */ = { 116 | isa = PBXGroup; 117 | children = ( 118 | DD15FB8718269EE7009D5B06 /* Foundation.framework */, 119 | DD15FB8918269EE7009D5B06 /* CoreGraphics.framework */, 120 | DD15FB8B18269EE7009D5B06 /* UIKit.framework */, 121 | ); 122 | name = Frameworks; 123 | sourceTree = ""; 124 | }; 125 | /* End PBXGroup section */ 126 | 127 | /* Begin PBXNativeTarget section */ 128 | DD15FB8318269EE7009D5B06 /* JGScrollableTableViewCell Basic */ = { 129 | isa = PBXNativeTarget; 130 | buildConfigurationList = DD15FBB018269EE8009D5B06 /* Build configuration list for PBXNativeTarget "JGScrollableTableViewCell Basic" */; 131 | buildPhases = ( 132 | DD15FB8018269EE7009D5B06 /* Sources */, 133 | DD15FB8118269EE7009D5B06 /* Frameworks */, 134 | DD15FB8218269EE7009D5B06 /* Resources */, 135 | ); 136 | buildRules = ( 137 | ); 138 | dependencies = ( 139 | ); 140 | name = "JGScrollableTableViewCell Basic"; 141 | productName = "JGScrollableTableViewCell Examples"; 142 | productReference = DD15FB8418269EE7009D5B06 /* JGScrollableTableViewCell Basic.app */; 143 | productType = "com.apple.product-type.application"; 144 | }; 145 | /* End PBXNativeTarget section */ 146 | 147 | /* Begin PBXProject section */ 148 | DD15FB7C18269EE7009D5B06 /* Project object */ = { 149 | isa = PBXProject; 150 | attributes = { 151 | CLASSPREFIX = JG; 152 | LastUpgradeCheck = 0510; 153 | ORGANIZATIONNAME = "Jonas Gessner"; 154 | }; 155 | buildConfigurationList = DD15FB7F18269EE7009D5B06 /* Build configuration list for PBXProject "JGScrollableTableViewCell Basic" */; 156 | compatibilityVersion = "Xcode 3.2"; 157 | developmentRegion = English; 158 | hasScannedForEncodings = 0; 159 | knownRegions = ( 160 | en, 161 | ); 162 | mainGroup = DD15FB7B18269EE7009D5B06; 163 | productRefGroup = DD15FB8518269EE7009D5B06 /* Products */; 164 | projectDirPath = ""; 165 | projectRoot = ""; 166 | targets = ( 167 | DD15FB8318269EE7009D5B06 /* JGScrollableTableViewCell Basic */, 168 | ); 169 | }; 170 | /* End PBXProject section */ 171 | 172 | /* Begin PBXResourcesBuildPhase section */ 173 | DD15FB8218269EE7009D5B06 /* Resources */ = { 174 | isa = PBXResourcesBuildPhase; 175 | buildActionMask = 2147483647; 176 | files = ( 177 | 6B3C710F1865BD16000F50B7 /* InfoPlist.strings in Resources */, 178 | 6B3C71101865BD16000F50B7 /* Images.xcassets in Resources */, 179 | ); 180 | runOnlyForDeploymentPostprocessing = 0; 181 | }; 182 | /* End PBXResourcesBuildPhase section */ 183 | 184 | /* Begin PBXSourcesBuildPhase section */ 185 | DD15FB8018269EE7009D5B06 /* Sources */ = { 186 | isa = PBXSourcesBuildPhase; 187 | buildActionMask = 2147483647; 188 | files = ( 189 | 6B3C71741865C24E000F50B7 /* JGExampleScrollableTableViewCell.m in Sources */, 190 | 6B3C71161865BD16000F50B7 /* main.m in Sources */, 191 | 6B3C711D1865BD55000F50B7 /* JGScrollableTableViewCell.m in Sources */, 192 | 6B3C71151865BD16000F50B7 /* JGTestViewController.m in Sources */, 193 | 6B3C711E1865BD55000F50B7 /* JGScrollableTableViewCellAccessoryButton.m in Sources */, 194 | 6B3C71111865BD16000F50B7 /* JGAppDelegate.m in Sources */, 195 | ); 196 | runOnlyForDeploymentPostprocessing = 0; 197 | }; 198 | /* End PBXSourcesBuildPhase section */ 199 | 200 | /* Begin PBXVariantGroup section */ 201 | 6B3C71001865BD16000F50B7 /* InfoPlist.strings */ = { 202 | isa = PBXVariantGroup; 203 | children = ( 204 | 6B3C71011865BD16000F50B7 /* en */, 205 | ); 206 | name = InfoPlist.strings; 207 | sourceTree = ""; 208 | }; 209 | /* End PBXVariantGroup section */ 210 | 211 | /* Begin XCBuildConfiguration section */ 212 | DD15FBAE18269EE8009D5B06 /* Debug */ = { 213 | isa = XCBuildConfiguration; 214 | buildSettings = { 215 | ALWAYS_SEARCH_USER_PATHS = NO; 216 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 217 | CLANG_CXX_LIBRARY = "libc++"; 218 | CLANG_ENABLE_MODULES = YES; 219 | CLANG_ENABLE_OBJC_ARC = YES; 220 | CLANG_WARN_BOOL_CONVERSION = YES; 221 | CLANG_WARN_CONSTANT_CONVERSION = 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_INT_CONVERSION = YES; 226 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 227 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 228 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 229 | COPY_PHASE_STRIP = NO; 230 | FRAMEWORK_SEARCH_PATHS = ""; 231 | GCC_C_LANGUAGE_STANDARD = gnu99; 232 | GCC_DYNAMIC_NO_PIC = NO; 233 | GCC_OPTIMIZATION_LEVEL = 0; 234 | GCC_PREPROCESSOR_DEFINITIONS = ( 235 | "DEBUG=1", 236 | "$(inherited)", 237 | ); 238 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 239 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 240 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 241 | GCC_WARN_UNDECLARED_SELECTOR = YES; 242 | GCC_WARN_UNINITIALIZED_AUTOS = YES; 243 | GCC_WARN_UNUSED_FUNCTION = YES; 244 | GCC_WARN_UNUSED_VARIABLE = YES; 245 | IPHONEOS_DEPLOYMENT_TARGET = 5.1.1; 246 | ONLY_ACTIVE_ARCH = YES; 247 | OTHER_LDFLAGS = ""; 248 | SDKROOT = iphoneos; 249 | TARGETED_DEVICE_FAMILY = "1,2"; 250 | }; 251 | name = Debug; 252 | }; 253 | DD15FBAF18269EE8009D5B06 /* Release */ = { 254 | isa = XCBuildConfiguration; 255 | buildSettings = { 256 | ALWAYS_SEARCH_USER_PATHS = NO; 257 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 258 | CLANG_CXX_LIBRARY = "libc++"; 259 | CLANG_ENABLE_MODULES = YES; 260 | CLANG_ENABLE_OBJC_ARC = YES; 261 | CLANG_WARN_BOOL_CONVERSION = YES; 262 | CLANG_WARN_CONSTANT_CONVERSION = YES; 263 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 264 | CLANG_WARN_EMPTY_BODY = YES; 265 | CLANG_WARN_ENUM_CONVERSION = YES; 266 | CLANG_WARN_INT_CONVERSION = YES; 267 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 268 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 269 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 270 | COPY_PHASE_STRIP = YES; 271 | ENABLE_NS_ASSERTIONS = NO; 272 | GCC_C_LANGUAGE_STANDARD = gnu99; 273 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 274 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 275 | GCC_WARN_UNDECLARED_SELECTOR = YES; 276 | GCC_WARN_UNINITIALIZED_AUTOS = YES; 277 | GCC_WARN_UNUSED_FUNCTION = YES; 278 | GCC_WARN_UNUSED_VARIABLE = YES; 279 | IPHONEOS_DEPLOYMENT_TARGET = 5.1.1; 280 | SDKROOT = iphoneos; 281 | TARGETED_DEVICE_FAMILY = "1,2"; 282 | VALIDATE_PRODUCT = YES; 283 | }; 284 | name = Release; 285 | }; 286 | DD15FBB118269EE8009D5B06 /* Debug */ = { 287 | isa = XCBuildConfiguration; 288 | buildSettings = { 289 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 290 | ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; 291 | FRAMEWORK_SEARCH_PATHS = ""; 292 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 293 | GCC_PREFIX_HEADER = "JGScrollableTableViewCell Basic/JGScrollableTableViewCell Basic-Prefix.pch"; 294 | GCC_WARN_UNUSED_LABEL = YES; 295 | GCC_WARN_UNUSED_PARAMETER = NO; 296 | INFOPLIST_FILE = "JGScrollableTableViewCell Basic/JGScrollableTableViewCell Basic-Info.plist"; 297 | IPHONEOS_DEPLOYMENT_TARGET = 5.1.1; 298 | OTHER_LDFLAGS = ""; 299 | PRODUCT_NAME = "JGScrollableTableViewCell Basic"; 300 | WRAPPER_EXTENSION = app; 301 | }; 302 | name = Debug; 303 | }; 304 | DD15FBB218269EE8009D5B06 /* Release */ = { 305 | isa = XCBuildConfiguration; 306 | buildSettings = { 307 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 308 | ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; 309 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 310 | GCC_PREFIX_HEADER = "JGScrollableTableViewCell Basic/JGScrollableTableViewCell Basic-Prefix.pch"; 311 | GCC_WARN_UNUSED_LABEL = YES; 312 | GCC_WARN_UNUSED_PARAMETER = NO; 313 | INFOPLIST_FILE = "JGScrollableTableViewCell Basic/JGScrollableTableViewCell Basic-Info.plist"; 314 | IPHONEOS_DEPLOYMENT_TARGET = 5.1.1; 315 | PRODUCT_NAME = "JGScrollableTableViewCell Basic"; 316 | WRAPPER_EXTENSION = app; 317 | }; 318 | name = Release; 319 | }; 320 | /* End XCBuildConfiguration section */ 321 | 322 | /* Begin XCConfigurationList section */ 323 | DD15FB7F18269EE7009D5B06 /* Build configuration list for PBXProject "JGScrollableTableViewCell Basic" */ = { 324 | isa = XCConfigurationList; 325 | buildConfigurations = ( 326 | DD15FBAE18269EE8009D5B06 /* Debug */, 327 | DD15FBAF18269EE8009D5B06 /* Release */, 328 | ); 329 | defaultConfigurationIsVisible = 0; 330 | defaultConfigurationName = Release; 331 | }; 332 | DD15FBB018269EE8009D5B06 /* Build configuration list for PBXNativeTarget "JGScrollableTableViewCell Basic" */ = { 333 | isa = XCConfigurationList; 334 | buildConfigurations = ( 335 | DD15FBB118269EE8009D5B06 /* Debug */, 336 | DD15FBB218269EE8009D5B06 /* Release */, 337 | ); 338 | defaultConfigurationIsVisible = 0; 339 | defaultConfigurationName = Release; 340 | }; 341 | /* End XCConfigurationList section */ 342 | }; 343 | rootObject = DD15FB7C18269EE7009D5B06 /* Project object */; 344 | } 345 | -------------------------------------------------------------------------------- /Examples/Basic/JGScrollableTableViewCell Basic.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Examples/Basic/JGScrollableTableViewCell Basic/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" : "ipad", 20 | "size" : "29x29", 21 | "scale" : "1x" 22 | }, 23 | { 24 | "idiom" : "ipad", 25 | "size" : "29x29", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "ipad", 30 | "size" : "40x40", 31 | "scale" : "1x" 32 | }, 33 | { 34 | "idiom" : "ipad", 35 | "size" : "40x40", 36 | "scale" : "2x" 37 | }, 38 | { 39 | "idiom" : "ipad", 40 | "size" : "76x76", 41 | "scale" : "1x" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "size" : "76x76", 46 | "scale" : "2x" 47 | } 48 | ], 49 | "info" : { 50 | "version" : 1, 51 | "author" : "xcode" 52 | } 53 | } -------------------------------------------------------------------------------- /Examples/Basic/JGScrollableTableViewCell Basic/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 | } -------------------------------------------------------------------------------- /Examples/Basic/JGScrollableTableViewCell Basic/JGAppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // JGAppDelegate.h 3 | // JGScrollableTableViewCell Examples 4 | // 5 | // Created by Jonas Gessner on 03.11.13. 6 | // Copyright (c) 2013 Jonas Gessner. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @class JGTestViewController; 12 | 13 | @interface JGAppDelegate : UIResponder 14 | 15 | @property (strong, nonatomic) UIWindow *window; 16 | 17 | @property (nonatomic, strong) JGTestViewController *mainViewController; 18 | @property (nonatomic, strong) UINavigationController *mainNavigationController; 19 | 20 | @end 21 | -------------------------------------------------------------------------------- /Examples/Basic/JGScrollableTableViewCell Basic/JGAppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // JGAppDelegate.m 3 | // JGScrollableTableViewCell Examples 4 | // 5 | // Created by Jonas Gessner on 03.11.13. 6 | // Copyright (c) 2013 Jonas Gessner. All rights reserved. 7 | // 8 | 9 | #import "JGAppDelegate.h" 10 | 11 | #import "JGTestViewController.h" 12 | 13 | @implementation JGAppDelegate 14 | 15 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions 16 | { 17 | self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; 18 | 19 | self.mainViewController = [[JGTestViewController alloc] initWithStyle:UITableViewStylePlain]; 20 | 21 | self.mainNavigationController = [[UINavigationController alloc] initWithRootViewController:self.mainViewController]; 22 | 23 | self.window.rootViewController = self.mainNavigationController; 24 | 25 | [self.window makeKeyAndVisible]; 26 | 27 | return YES; 28 | } 29 | 30 | - (void)applicationWillResignActive:(UIApplication *)application 31 | { 32 | // 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. 33 | // 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. 34 | } 35 | 36 | - (void)applicationDidEnterBackground:(UIApplication *)application 37 | { 38 | // 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. 39 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 40 | } 41 | 42 | - (void)applicationWillEnterForeground:(UIApplication *)application 43 | { 44 | // 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. 45 | } 46 | 47 | - (void)applicationDidBecomeActive:(UIApplication *)application 48 | { 49 | // 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. 50 | } 51 | 52 | - (void)applicationWillTerminate:(UIApplication *)application 53 | { 54 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 55 | } 56 | 57 | @end 58 | -------------------------------------------------------------------------------- /Examples/Basic/JGScrollableTableViewCell Basic/JGExampleScrollableTableViewCell.h: -------------------------------------------------------------------------------- 1 | // 2 | // JGExampleScrollableTableViewCell.h 3 | // JGScrollableTableViewCell Basic 4 | // 5 | // Created by Jonas Gessner on 21.12.13. 6 | // Copyright (c) 2013 Jonas Gessner. All rights reserved. 7 | // 8 | 9 | #import "JGScrollableTableViewCell.h" 10 | 11 | @interface JGExampleScrollableTableViewCell : JGScrollableTableViewCell 12 | 13 | - (void)setGrabberVisible:(BOOL)visible; 14 | 15 | @end 16 | -------------------------------------------------------------------------------- /Examples/Basic/JGScrollableTableViewCell Basic/JGExampleScrollableTableViewCell.m: -------------------------------------------------------------------------------- 1 | // 2 | // JGExampleScrollableTableViewCell.m 3 | // JGScrollableTableViewCell Basic 4 | // 5 | // Created by Jonas Gessner on 21.12.13. 6 | // Copyright (c) 2013 Jonas Gessner. All rights reserved. 7 | // 8 | 9 | #import "JGExampleScrollableTableViewCell.h" 10 | #import "JGScrollableTableViewCellAccessoryButton.h" 11 | 12 | @implementation JGExampleScrollableTableViewCell 13 | 14 | - (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier { 15 | self = [super initWithStyle:style reuseIdentifier:reuseIdentifier]; 16 | if (self) { 17 | [self setScrollViewBackgroundColor:[UIColor colorWithWhite:0.975f alpha:1.0f]]; 18 | self.contentView.backgroundColor = [UIColor grayColor]; 19 | 20 | JGScrollableTableViewCellAccessoryButton *actionView = [JGScrollableTableViewCellAccessoryButton button]; 21 | 22 | [actionView setButtonColor:[UIColor colorWithRed:0.975f green:0.0f blue:0.0f alpha:1.0f] forState:UIControlStateNormal]; 23 | [actionView setButtonColor:[UIColor colorWithRed:0.8f green:0.1f blue:0.1f alpha:1.0f] forState:UIControlStateHighlighted]; 24 | 25 | [actionView setTitle:@"Sample" forState:UIControlStateNormal]; 26 | 27 | CGFloat width = 80.0f; 28 | 29 | actionView.frame = CGRectMake(width, 0.0f, width, 0.0f); //width is the only frame parameter that needs to be set on the option view 30 | actionView.autoresizingMask = UIViewAutoresizingFlexibleHeight; 31 | 32 | 33 | JGScrollableTableViewCellAccessoryButton *moreView = [JGScrollableTableViewCellAccessoryButton button]; 34 | 35 | [moreView setButtonColor:[UIColor colorWithWhite:0.8f alpha:1.0f] forState:UIControlStateNormal]; 36 | [moreView setButtonColor:[UIColor colorWithWhite:0.65f alpha:1.0f] forState:UIControlStateHighlighted]; 37 | 38 | [moreView setTitle:@"Sample" forState:UIControlStateNormal]; 39 | 40 | moreView.frame = CGRectMake(0.0f, 0.0f, width, 0.0f); //width is the only frame parameter that needs to be set on the option view 41 | moreView.autoresizingMask = UIViewAutoresizingFlexibleHeight; 42 | 43 | 44 | UIView *optionView = [[UIView alloc] initWithFrame:CGRectMake(0.0f, 0.0f, width*2.0f, 0.0f)]; 45 | optionView.clipsToBounds = YES; 46 | 47 | [optionView addSubview:moreView]; 48 | [optionView addSubview:actionView]; 49 | 50 | [self setOptionView:optionView]; 51 | } 52 | return self; 53 | } 54 | 55 | - (void)setGrabberVisible:(BOOL)visible { 56 | if (visible) { 57 | UIView *grabber = [[UIView alloc] initWithFrame:(CGRect){CGPointZero, {30.0f, 40.0f}}]; 58 | 59 | UIView *dot1 = [[UIView alloc] initWithFrame:(CGRect){{15.0f, 7.5f}, {5.0f, 5.0f}}]; 60 | 61 | UIView *dot2 = [[UIView alloc] initWithFrame:(CGRect){{15.0f, 17.5f}, {5.0f, 5.0f}}]; 62 | 63 | UIView *dot3 = [[UIView alloc] initWithFrame:(CGRect){{15.0f, 27.5f}, {5.0f, 5.0f}}]; 64 | 65 | dot1.backgroundColor = [UIColor colorWithWhite:0.6f alpha:1.0f]; 66 | dot2.backgroundColor = [UIColor colorWithWhite:0.6f alpha:1.0f]; 67 | dot3.backgroundColor = [UIColor colorWithWhite:0.6f alpha:1.0f]; 68 | 69 | [grabber addSubview:dot1]; 70 | [grabber addSubview:dot2]; 71 | [grabber addSubview:dot3]; 72 | 73 | [self setGrabberView:grabber]; 74 | } 75 | else { 76 | [self setGrabberView:nil]; 77 | } 78 | } 79 | 80 | @end 81 | -------------------------------------------------------------------------------- /Examples/Basic/JGScrollableTableViewCell Basic/JGScrollableTableViewCell Basic-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleDisplayName 8 | ${PRODUCT_NAME} 9 | CFBundleExecutable 10 | ${EXECUTABLE_NAME} 11 | CFBundleIdentifier 12 | de.j-gessner.${PRODUCT_NAME:rfc1034identifier} 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | ${PRODUCT_NAME} 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | 1.0 25 | LSRequiresIPhoneOS 26 | 27 | UIRequiredDeviceCapabilities 28 | 29 | armv7 30 | 31 | 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 | -------------------------------------------------------------------------------- /Examples/Basic/JGScrollableTableViewCell Basic/JGScrollableTableViewCell Basic-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 | -------------------------------------------------------------------------------- /Examples/Basic/JGScrollableTableViewCell Basic/JGTestViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // JGTestViewController.h 3 | // JGScrollableTableViewCell Examples 4 | // 5 | // Created by Jonas Gessner on 03.11.13. 6 | // Copyright (c) 2013 Jonas Gessner. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface JGTestViewController : UITableViewController 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /Examples/Basic/JGScrollableTableViewCell Basic/JGTestViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // JGTestViewController.m 3 | // JGScrollableTableViewCell Examples 4 | // 5 | // Created by Jonas Gessner on 03.11.13. 6 | // Copyright (c) 2013 Jonas Gessner. All rights reserved. 7 | // 8 | 9 | #import "JGTestViewController.h" 10 | 11 | #import "JGExampleScrollableTableViewCell.h" 12 | 13 | #import "JGScrollableTableViewCellAccessoryButton.h" 14 | 15 | @interface JGTestViewController () { 16 | NSIndexPath *_openedIndexPath; 17 | } 18 | 19 | @end 20 | 21 | @implementation JGTestViewController 22 | 23 | - (instancetype)initWithStyle:(UITableViewStyle)style { 24 | self = [super initWithStyle:style]; 25 | if (self) { 26 | self.tableView.separatorStyle = UITableViewCellSeparatorStyleSingleLine; 27 | 28 | [self.tableView registerClass:[JGExampleScrollableTableViewCell class] forCellReuseIdentifier:@"ScrollCell"]; 29 | 30 | self.title = @"JGScrollableTableViewCell"; 31 | } 32 | return self; 33 | } 34 | 35 | #pragma mark - JGScrollableTableViewCellDelegate 36 | 37 | - (void)cellDidBeginScrolling:(JGScrollableTableViewCell *)cell { 38 | [JGScrollableTableViewCellManager closeAllCellsWithExceptionOf:cell stopAfterFirst:YES]; 39 | } 40 | 41 | - (void)cellDidScroll:(JGScrollableTableViewCell *)cell { 42 | [JGScrollableTableViewCellManager closeAllCellsWithExceptionOf:cell stopAfterFirst:YES]; 43 | } 44 | 45 | - (void)cellDidEndScrolling:(JGScrollableTableViewCell *)cell { 46 | if (cell.optionViewVisible) { 47 | _openedIndexPath = [self.tableView indexPathForCell:cell]; 48 | } 49 | else { 50 | _openedIndexPath = nil; 51 | } 52 | 53 | [JGScrollableTableViewCellManager closeAllCellsWithExceptionOf:cell stopAfterFirst:YES]; 54 | } 55 | 56 | #pragma mark - Table view data source 57 | 58 | - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { 59 | return 2; 60 | } 61 | 62 | - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { 63 | return 99; 64 | } 65 | 66 | - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath { 67 | return 70.0f; 68 | } 69 | 70 | - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { 71 | static NSString *const CellIdentifier = @"ScrollCell"; 72 | 73 | JGExampleScrollableTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath]; 74 | 75 | [cell setGrabberVisible:((indexPath.row % 3) == 0)]; 76 | 77 | cell.scrollDelegate = self; 78 | 79 | [cell setOptionViewVisible:[_openedIndexPath isEqual:indexPath]]; 80 | 81 | return cell; 82 | } 83 | 84 | - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { 85 | NSLog(@"Selected Index Path %@", indexPath); 86 | [tableView deselectRowAtIndexPath:indexPath animated:YES]; 87 | } 88 | 89 | @end 90 | -------------------------------------------------------------------------------- /Examples/Basic/JGScrollableTableViewCell Basic/en.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* Localized versions of Info.plist keys */ 2 | 3 | -------------------------------------------------------------------------------- /Examples/Basic/JGScrollableTableViewCell Basic/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // JGScrollableTableViewCell Examples 4 | // 5 | // Created by Jonas Gessner on 03.11.13. 6 | // Copyright (c) 2013 Jonas Gessner. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | #import "JGAppDelegate.h" 12 | 13 | int main(int argc, char * argv[]) 14 | { 15 | @autoreleasepool { 16 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([JGAppDelegate class])); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /JGScrollableTableViewCell.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |s| 2 | 3 | s.name = "JGScrollableTableViewCell" 4 | s.version = "1.1" 5 | s.summary = "A UITableViewCell subclass with a scrollable content view." 6 | s.description = <<-DESC 7 | JGScrollableTableViewCell is a simple and easy to use UITableViewCell subclass with a scrollable content view that exposes an accessory view when scrolled. The behavior is inspired by the iOS 7 mail app. 8 | DESC 9 | s.homepage = "https://github.com/JonasGessner/JGScrollableTableViewCell" 10 | s.license = { :type => "MIT", :file => "LICENSE.txt" } 11 | s.author = "Jonas Gessner" 12 | s.social_media_url = "http://twitter.com/JonasGessner" 13 | s.platform = :ios, "5.0" 14 | s.source = { :git => "https://github.com/JonasGessner/JGScrollableTableViewCell.git", :tag => "v1.1" } 15 | s.source_files = "JGScrollableTableViewCell/*.{h,m}" 16 | s.frameworks = "Foundation", "UIKit", "CoreGraphics" 17 | s.requires_arc = true 18 | 19 | end -------------------------------------------------------------------------------- /JGScrollableTableViewCell/JGScrollableTableViewCell.h: -------------------------------------------------------------------------------- 1 | // 2 | // JGScrollableTableViewCell.h 3 | // JGScrollableTableViewCell 4 | // 5 | // Created by Jonas Gessner on 03.11.13. 6 | // Copyright (c) 2013 Jonas Gessner. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | #define kJGScrollableTableViewCellVersion @"1.0.3" 12 | 13 | @class JGScrollableTableViewCell; 14 | 15 | @protocol JGScrollableTableViewCellDelegate 16 | 17 | @optional 18 | - (void)cellDidBeginScrolling:(JGScrollableTableViewCell *)cell; 19 | - (void)cellDidScroll:(JGScrollableTableViewCell *)cell; 20 | - (void)cellDidEndScrolling:(JGScrollableTableViewCell *)cell; 21 | 22 | @end 23 | 24 | 25 | /** 26 | Cell with scrollable content and an option view that is revealed when scrolling on the cell. 27 | */ 28 | @interface JGScrollableTableViewCell : UITableViewCell 29 | 30 | 31 | //Scroll view properties 32 | 33 | /** 34 | Insets the scroll view. Useful for displaying a border around the scroll area (when also setting \c contentView.backgroundColor) 35 | */ 36 | @property (nonatomic, assign) UIEdgeInsets scrollViewInsets; 37 | 38 | /** 39 | The scroll view used in the cell. Do not add any subviews to the scrollview or modify its frame, bounds, contentOffset or contentInset. 40 | */ 41 | @property (nonatomic, strong, readonly) UIScrollView *scrollView; 42 | 43 | 44 | /** 45 | Sets the background color of the visible scroll area. 46 | */ 47 | @property (nonatomic, strong) UIColor *scrollViewBackgroundColor; 48 | 49 | /** 50 | An instance conforming to the \c JGScrollableTableViewCellDelegate protocol. 51 | */ 52 | @property (nonatomic, weak) id scrollDelegate; 53 | 54 | 55 | /** 56 | @warning When the cell is selected or highlighted the scroll view won't be able to scroll. (This shouldn't be a problem anyway) 57 | @return If the user is currently dragging the scroll view or if the scroll view is decelerating. 58 | */ 59 | - (BOOL)isScrolling; 60 | 61 | 62 | //Opened sides 63 | 64 | /** 65 | @return The current visible state of the option view. 66 | */ 67 | @property (nonatomic, assign) BOOL optionViewVisible; 68 | 69 | 70 | /** 71 | Sets the current state of the option view with an optional animation of 0.3 seconds. 72 | */ 73 | - (void)setOptionViewVisible:(BOOL)optionViewVisible animated:(BOOL)animated; 74 | 75 | 76 | 77 | //Views 78 | 79 | /** 80 | The option view is set with the \c setOptionView:side: method. 81 | @return The option view. 82 | */ 83 | @property (nonatomic, strong) UIView *optionView; 84 | 85 | 86 | /** 87 | A view to use as a grabber for the scroll view. This view is static, so it won't be resized at all by the cell. If this view is \c nil then the entire area of the scroll view is scrollable, if this view is set then scrolling can only be performed on this view. 88 | */ 89 | @property (nonatomic, strong) UIView *grabberView; 90 | 91 | /** 92 | Adds a view to the scrolling area of the cell. 93 | @param view The view to add. 94 | */ 95 | - (void)addContentView:(UIView *)view; 96 | 97 | 98 | 99 | //Custom touch handling 100 | 101 | 102 | /** 103 | Invoked when the scroll view scrolls. Can be used to add custom behavior to the scroll view. 104 | */ 105 | @property (nonatomic, copy) void (^scrollViewDidScrollBlock)(JGScrollableTableViewCell *cell, UIScrollView *scrollView); 106 | 107 | 108 | @end 109 | 110 | 111 | 112 | /** 113 | Manage the state of all \c JGScrollableTableViewCells in a \c UITableView 114 | */ 115 | @interface JGScrollableTableViewCellManager : NSObject 116 | 117 | /** 118 | Closes all option views in the \c UITableView containing \c cell. 119 | @param cell The cell that should not be closed. 120 | @param stop A flag that can increase performance by stopping the enumeration of cells after the first cell with an opened option view has been found. Set this flag to \c YES when you have set up the \c JGScrollableTableViewCellDelegate to only allow one opened option view at a time. 121 | */ 122 | + (void)closeAllCellsWithExceptionOf:(JGScrollableTableViewCell *)cell stopAfterFirst:(BOOL)stop; 123 | 124 | @end 125 | -------------------------------------------------------------------------------- /JGScrollableTableViewCell/JGScrollableTableViewCell.m: -------------------------------------------------------------------------------- 1 | // 2 | // JGScrollableTableViewCell.m 3 | // JGScrollableTableViewCell 4 | // 5 | // Created by Jonas Gessner on 03.11.13. 6 | // Copyright (c) 2013 Jonas Gessner. All rights reserved. 7 | // 8 | 9 | #import "JGScrollableTableViewCell.h" 10 | 11 | @protocol JGTouchForwarder 12 | 13 | - (void)forwardTouchesBegan:(NSSet *)touches withEvent:(UIEvent *)event; 14 | - (void)forwardTouchesMoved:(NSSet *)touches withEvent:(UIEvent *)event; 15 | - (void)forwardTouchesEnded:(NSSet *)touches withEvent:(UIEvent *)event; 16 | - (void)forwardTouchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event; 17 | 18 | @end 19 | 20 | @protocol JGViewProvider 21 | 22 | - (UIView *)scrollViewCoverView; 23 | - (UITableView *)parentTableView; 24 | 25 | @end 26 | 27 | 28 | @interface JGScrollableTableViewCellScrollView : UIScrollView 29 | 30 | @property (nonatomic, weak) JGScrollableTableViewCell *parentCell; 31 | 32 | @end 33 | 34 | @implementation JGScrollableTableViewCellScrollView 35 | 36 | 37 | - (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event { 38 | UIView *hit = [super hitTest:point withEvent:event]; 39 | if (hit == self) { 40 | if (CGRectContainsPoint(self.parentCell.optionView.frame, [self convertPoint:point toView:self.parentCell])) { 41 | return [self.parentCell.optionView hitTest:[self convertPoint:point toView:self.parentCell.optionView] withEvent:event]; 42 | } 43 | else { 44 | return self; 45 | } 46 | } 47 | else { 48 | return hit; 49 | } 50 | } 51 | 52 | - (NSArray *)subviews { 53 | UIView *v = [self.parentCell scrollViewCoverView]; 54 | if (v) { 55 | return @[v]; 56 | } 57 | else { 58 | return nil; 59 | } 60 | } 61 | 62 | - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { 63 | [self.parentCell forwardTouchesBegan:touches withEvent:event]; 64 | [super touchesBegan:touches withEvent:event]; 65 | } 66 | 67 | - (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event { 68 | [self.parentCell forwardTouchesMoved:touches withEvent:event]; 69 | [super touchesMoved:touches withEvent:event]; 70 | } 71 | 72 | - (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event { 73 | [self.parentCell forwardTouchesEnded:touches withEvent:event]; 74 | [super touchesEnded:touches withEvent:event]; 75 | } 76 | 77 | - (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event { 78 | [self.parentCell forwardTouchesCancelled:touches withEvent:event]; 79 | [super touchesCancelled:touches withEvent:event]; 80 | } 81 | 82 | @end 83 | 84 | 85 | 86 | @interface JGScrollableTableViewCellManager () 87 | 88 | + (void)referenceCell:(JGScrollableTableViewCell *)cell inView:(UIView *)host; 89 | + (void)removeCellReference:(JGScrollableTableViewCell *)cell inView:(UIView *)host; 90 | 91 | + (NSSet *)allCellsInTableView:(UITableView *)host; 92 | 93 | 94 | @end 95 | 96 | @implementation JGScrollableTableViewCellManager 97 | 98 | static NSMutableDictionary *_refs; 99 | 100 | + (void)closeAllCellsWithExceptionOf:(JGScrollableTableViewCell *)cell stopAfterFirst:(BOOL)stop { 101 | UITableView *host = (UITableView *)cell.superview; 102 | NSSet *cells = [self allCellsInTableView:host]; 103 | 104 | for (JGScrollableTableViewCell *otherCell in cells) { 105 | if (otherCell != cell) { 106 | if (otherCell.isScrolling || otherCell.optionViewVisible) { 107 | [otherCell setOptionViewVisible:NO animated:YES]; 108 | if (stop) { 109 | break; 110 | } 111 | } 112 | } 113 | } 114 | } 115 | 116 | + (void)removeCellReference:(JGScrollableTableViewCell *)cell inView:(UIView *)host { 117 | if (!host) { 118 | return; 119 | } 120 | NSAssert([NSThread isMainThread], @"JGScrollableTableViewCellManager should only be used on the main thread"); 121 | 122 | NSValue *key = [NSValue valueWithNonretainedObject:host]; 123 | 124 | NSMutableSet *hostCells = _refs[key]; 125 | 126 | [hostCells removeObject:cell]; 127 | 128 | if (hostCells.count) { 129 | _refs[key] = hostCells; 130 | } 131 | else { 132 | [_refs removeObjectForKey:key]; 133 | } 134 | } 135 | 136 | + (void)referenceCell:(JGScrollableTableViewCell *)cell inView:(UIView *)host { 137 | if (!host) { 138 | return; 139 | } 140 | NSAssert([NSThread isMainThread], @"JGScrollableTableViewCellManager should only be used on the main thread"); 141 | 142 | if (!_refs) { 143 | _refs = [NSMutableDictionary dictionary]; 144 | } 145 | 146 | NSValue *key = [NSValue valueWithNonretainedObject:host]; 147 | 148 | NSMutableSet *hostCells = _refs[key]; 149 | if (!hostCells) { 150 | hostCells = [NSMutableSet set]; 151 | } 152 | 153 | [hostCells addObject:cell]; 154 | 155 | _refs[key] = hostCells; 156 | } 157 | 158 | + (NSSet *)allCellsInTableView:(UITableView *)host { 159 | NSAssert([NSThread isMainThread], @"JGScrollableTableViewCellManager should only be used on the main thread"); 160 | NSValue *key = [NSValue valueWithNonretainedObject:host]; 161 | return [_refs[key] copy]; 162 | } 163 | 164 | @end 165 | 166 | 167 | #define kJGScrollableTableViewCellAnimationDuration 0.3 168 | 169 | @interface JGScrollableTableViewCell () { 170 | BOOL _forceRelayout; 171 | BOOL _cancelCurrentForwardedGesture; 172 | 173 | BOOL _scrolling; 174 | BOOL _scrollingHasEnded; 175 | 176 | NSUInteger _ignoreScrollEvents; 177 | 178 | __weak UIView *_hostingView; 179 | } 180 | 181 | @property (nonatomic, strong, readonly) UIView *scrollViewCoverView; 182 | 183 | @end 184 | 185 | @implementation JGScrollableTableViewCell 186 | 187 | 188 | #pragma mark - Initialization 189 | 190 | - (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier { 191 | self = [super initWithStyle:style reuseIdentifier:reuseIdentifier]; 192 | if (self) { 193 | _scrollView = [[JGScrollableTableViewCellScrollView alloc] init]; 194 | _scrollView.backgroundColor = [UIColor clearColor]; 195 | _scrollView.showsHorizontalScrollIndicator = NO; 196 | ((JGScrollableTableViewCellScrollView *)_scrollView).parentCell = self; 197 | _scrollView.delegate = self; 198 | _scrollView.pagingEnabled = YES; 199 | _scrollView.scrollsToTop = NO; 200 | 201 | _scrollViewCoverView = [[UIView alloc] init]; 202 | [_scrollView addSubview:_scrollViewCoverView]; 203 | 204 | [self.contentView addSubview:_scrollView]; 205 | } 206 | return self; 207 | } 208 | 209 | #pragma mark - Delegates 210 | 211 | - (UITableView *)parentTableView { 212 | UIView *sup = self.superview; 213 | 214 | while (sup != nil && ![sup isKindOfClass:[UITableView class]]) { 215 | sup = sup.superview; 216 | } 217 | 218 | return (UITableView *)sup; 219 | } 220 | 221 | - (void)forwardTouchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { 222 | if (!self.isScrolling && !self.optionViewVisible) { 223 | if (self.grabberView && CGRectContainsPoint(self.grabberView.bounds, [touches.anyObject locationInView:self.grabberView])) { 224 | _cancelCurrentForwardedGesture = YES; 225 | } 226 | else { 227 | _cancelCurrentForwardedGesture = NO; 228 | [self touchesBegan:touches withEvent:event]; 229 | } 230 | } 231 | } 232 | 233 | - (void)forwardTouchesMoved:(NSSet *)touches withEvent:(UIEvent *)event { 234 | if (!self.isScrolling && !self.optionViewVisible && !_cancelCurrentForwardedGesture) { 235 | [self touchesCancelled:touches withEvent:event]; 236 | } 237 | } 238 | 239 | - (void)forwardTouchesEnded:(NSSet *)touches withEvent:(UIEvent *)event { 240 | if (!self.isScrolling && !self.optionViewVisible && !_cancelCurrentForwardedGesture) { 241 | [self touchesEnded:touches withEvent:event]; 242 | } 243 | else if (self.optionViewVisible) { 244 | __weak __typeof(self) weakSelf = self; 245 | 246 | [self setOptionViewVisible:NO animationDuration:kJGScrollableTableViewCellAnimationDuration completion:^{ 247 | if ([weakSelf.scrollDelegate respondsToSelector:@selector(cellDidEndScrolling:)]) { 248 | [weakSelf.scrollDelegate cellDidEndScrolling:weakSelf]; 249 | } 250 | }]; 251 | } 252 | } 253 | 254 | - (void)forwardTouchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event { 255 | if (!self.isScrolling && !self.optionViewVisible && !_cancelCurrentForwardedGesture) { 256 | [self touchesCancelled:touches withEvent:event]; 257 | } 258 | } 259 | 260 | - (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView { 261 | if (_ignoreScrollEvents) { 262 | return; 263 | } 264 | 265 | if (!_scrolling) { 266 | if (!_scrollingHasEnded && (self.selected || self.highlighted || (self.grabberView && !self.optionViewVisible && !CGRectContainsPoint(self.grabberView.bounds, [_scrollView.panGestureRecognizer locationInView:self.grabberView])))) { 267 | _scrollView.panGestureRecognizer.enabled = NO; 268 | _scrollView.panGestureRecognizer.enabled = YES; 269 | } 270 | else { 271 | _scrolling = YES; 272 | if ([self.scrollDelegate respondsToSelector:@selector(cellDidBeginScrolling:)]) { 273 | [self.scrollDelegate cellDidBeginScrolling:self]; 274 | } 275 | } 276 | } 277 | } 278 | 279 | - (void)scrollViewDidScroll:(UIScrollView *)__unused scrollView { 280 | if (_ignoreScrollEvents) { 281 | return; 282 | } 283 | 284 | if (_scrolling) { 285 | if ([self.scrollDelegate respondsToSelector:@selector(cellDidScroll:)]) { 286 | [self.scrollDelegate cellDidScroll:self]; 287 | } 288 | 289 | if (self.scrollViewDidScrollBlock) { 290 | self.scrollViewDidScrollBlock(self, _scrollView); 291 | } 292 | } 293 | } 294 | 295 | - (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate { 296 | if (_ignoreScrollEvents) { 297 | return; 298 | } 299 | 300 | _scrollingHasEnded = decelerate; 301 | _scrolling = NO; 302 | 303 | if (!decelerate) { 304 | _optionViewVisible = (_scrollView.contentOffset.x != 0.0f); 305 | 306 | if ([self.scrollDelegate respondsToSelector:@selector(cellDidEndScrolling:)]) { 307 | [self.scrollDelegate cellDidEndScrolling:self]; 308 | } 309 | } 310 | } 311 | 312 | - (void)scrollViewDidEndDecelerating:(UIScrollView *)__unused scrollView { 313 | if (_ignoreScrollEvents) { 314 | return; 315 | } 316 | 317 | _scrolling = NO; 318 | _scrollingHasEnded = NO; 319 | 320 | _optionViewVisible = (_scrollView.contentOffset.x != 0.0f); 321 | 322 | if ([self.scrollDelegate respondsToSelector:@selector(cellDidEndScrolling:)]) { 323 | [self.scrollDelegate cellDidEndScrolling:self]; 324 | } 325 | } 326 | 327 | 328 | #pragma mark - Overrides 329 | 330 | - (void)willMoveToSuperview:(UIView *)newSuperview { 331 | [super willMoveToSuperview:newSuperview]; 332 | 333 | if (newSuperview) { 334 | _hostingView = newSuperview; 335 | 336 | [JGScrollableTableViewCellManager referenceCell:self inView:_hostingView]; 337 | } 338 | else if (_hostingView) { 339 | [JGScrollableTableViewCellManager removeCellReference:self inView:_hostingView]; 340 | } 341 | } 342 | 343 | - (CGRect)contentBounds { 344 | return self.contentView.bounds; 345 | } 346 | 347 | - (void)layoutSubviews { 348 | [super layoutSubviews]; 349 | 350 | if (!self.isScrolling) { 351 | CGRect scrollViewFrame = UIEdgeInsetsInsetRect(self.contentBounds, self.scrollViewInsets); 352 | 353 | _ignoreScrollEvents++; 354 | 355 | _scrollView.frame = scrollViewFrame; 356 | 357 | _scrollView.contentSize = (CGSize){scrollViewFrame.size.width+self.optionView.frame.size.width, scrollViewFrame.size.height}; 358 | 359 | _scrollViewCoverView.frame = (CGRect){CGPointZero, scrollViewFrame.size}; 360 | 361 | if (self.grabberView) { 362 | CGSize grabberSize = self.grabberView.frame.size; 363 | 364 | self.grabberView.frame = (CGRect){{scrollViewFrame.size.width-grabberSize.width, (scrollViewFrame.size.height-grabberSize.height)/2.0f}, grabberSize}; 365 | } 366 | 367 | CGSize size = (CGSize){self.optionView.frame.size.width, scrollViewFrame.size.height}; 368 | 369 | self.optionView.frame = (CGRect){{CGRectGetMaxX(scrollViewFrame)-self.optionView.frame.size.width, 0.0f}, size}; 370 | 371 | 372 | _forceRelayout = YES; //enusres that next call is actually executed 373 | [self setOptionViewVisible:self.optionViewVisible]; //sets correct contentOffset 374 | _forceRelayout = NO; 375 | 376 | _ignoreScrollEvents--; 377 | } 378 | } 379 | 380 | #pragma mark - Getters 381 | 382 | - (BOOL)isScrolling { 383 | return (_scrolling || _scrollingHasEnded); 384 | } 385 | 386 | #pragma mark - Setters 387 | 388 | - (void)setOptionViewVisible:(BOOL)optionViewVisible animated:(BOOL)animated { 389 | [self setOptionViewVisible:optionViewVisible animationDuration:(animated ? kJGScrollableTableViewCellAnimationDuration : 0.0) completion:NULL]; 390 | } 391 | 392 | - (void)setOptionViewVisible:(BOOL)optionViewVisible animationDuration:(NSTimeInterval)duration completion:(void (^)(void))completion { 393 | if (!_forceRelayout && _optionViewVisible == optionViewVisible && !self.isScrolling) { 394 | return; 395 | } 396 | 397 | _scrolling = NO; 398 | _scrollingHasEnded = NO; 399 | 400 | _optionViewVisible = optionViewVisible; 401 | 402 | _ignoreScrollEvents++; 403 | 404 | CGPoint scrollDestination; 405 | 406 | scrollDestination = (CGPoint){(_optionViewVisible ? _scrollView.contentSize.width-1.0f : 0.0f), 0.0f}; 407 | 408 | [UIView animateWithDuration:duration delay:0.0 options:UIViewAnimationOptionCurveEaseOut | UIViewAnimationOptionBeginFromCurrentState animations:^{ 409 | [_scrollView.panGestureRecognizer setEnabled:NO]; 410 | [_scrollView scrollRectToVisible:(CGRect){scrollDestination, {1.0f, 1.0f}} animated:NO]; 411 | } completion:^(__unused BOOL finished) { 412 | [_scrollView.panGestureRecognizer setEnabled:YES]; 413 | _ignoreScrollEvents--; 414 | 415 | if (completion) { 416 | completion(); 417 | } 418 | }]; 419 | } 420 | 421 | - (void)setOptionViewVisible:(BOOL)optionViewVisible { 422 | [self setOptionViewVisible:optionViewVisible animated:NO]; 423 | } 424 | 425 | - (void)setGrabberView:(UIView *)grabberView { 426 | [self.grabberView removeFromSuperview]; 427 | 428 | _grabberView = grabberView; 429 | 430 | if (self.grabberView) { 431 | [_scrollView addSubview:self.grabberView]; 432 | 433 | [self setNeedsLayout]; 434 | [self layoutIfNeeded]; 435 | } 436 | } 437 | 438 | - (void)setOptionView:(UIView *)view { 439 | [self.optionView removeFromSuperview]; 440 | 441 | _optionView = view; 442 | 443 | [self.contentView insertSubview:self.optionView belowSubview:_scrollView]; 444 | 445 | [self setNeedsLayout]; 446 | [self layoutIfNeeded]; 447 | } 448 | 449 | - (void)setScrollViewBackgroundColor:(UIColor *)scrollViewBackgroundColor { 450 | _scrollViewBackgroundColor = scrollViewBackgroundColor; 451 | 452 | _scrollViewCoverView.backgroundColor = scrollViewBackgroundColor; 453 | self.backgroundColor = scrollViewBackgroundColor; 454 | } 455 | 456 | - (void)setScrollViewInsets:(UIEdgeInsets)scrollViewInsets { 457 | _scrollViewInsets = scrollViewInsets; 458 | 459 | [self setNeedsLayout]; 460 | [self layoutIfNeeded]; 461 | } 462 | 463 | - (void)addContentView:(UIView *)view { 464 | [_scrollViewCoverView addSubview:view]; 465 | } 466 | 467 | - (void)setHighlighted:(BOOL)highlighted animated:(BOOL)animated { 468 | void (^actions)(BOOL select) = ^(BOOL select) { 469 | _optionView.hidden = select; 470 | _scrollView.scrollEnabled = !select; 471 | }; 472 | 473 | if (highlighted) { 474 | actions(highlighted); 475 | } 476 | 477 | id previousBlock = [CATransaction completionBlock]; 478 | 479 | [CATransaction setCompletionBlock:^{ 480 | actions(highlighted); 481 | }]; 482 | 483 | [super setHighlighted:highlighted animated:animated]; 484 | 485 | self.backgroundColor = self.scrollViewBackgroundColor; 486 | 487 | [CATransaction setCompletionBlock:previousBlock]; 488 | } 489 | 490 | - (void)setSelected:(BOOL)selected animated:(BOOL)animated { 491 | void (^actions)(BOOL select) = ^(BOOL select) { 492 | _optionView.hidden = select; 493 | _scrollView.scrollEnabled = !select; 494 | }; 495 | 496 | if (selected) { 497 | actions(selected); 498 | } 499 | 500 | id previousBlock = [CATransaction completionBlock]; 501 | 502 | [CATransaction setCompletionBlock:^{ 503 | actions(selected); 504 | }]; 505 | 506 | [super setSelected:selected animated:animated]; 507 | 508 | self.backgroundColor = self.scrollViewBackgroundColor; 509 | 510 | [CATransaction setCompletionBlock:previousBlock]; 511 | } 512 | 513 | #pragma mark - Dealloc 514 | 515 | - (void)dealloc { 516 | [JGScrollableTableViewCellManager removeCellReference:self inView:_hostingView]; 517 | } 518 | 519 | @end 520 | -------------------------------------------------------------------------------- /JGScrollableTableViewCell/JGScrollableTableViewCellAccessoryButton.h: -------------------------------------------------------------------------------- 1 | // 2 | // JGScrollableTableViewCellAccessoryButton.h 3 | // JGScrollableTableViewCell 4 | // 5 | // Created by Jonas Gessner on 03.11.13. 6 | // Copyright (c) 2013 Jonas Gessner. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | 12 | /** 13 | An iOS 7 mail app inspired button. This class is \b not required when using \c JGScrollableTableViewCell. 14 | */ 15 | @interface JGScrollableTableViewCellAccessoryButton : UIButton 16 | 17 | /** 18 | Convenience method for +alloc -init. 19 | @warning Always initialize \c JGScrollableTableViewCellAccessoryButton using +alloc -init, or this convenience method. 20 | */ 21 | + (instancetype)button; 22 | 23 | 24 | /** 25 | Sets the button's background color for a given state. This is different from \c backgroundColor because \c backgroundColor is constant and doesn't change for different selection states. 26 | 27 | @discussion Internally this uses the \c -setBackgroundImage:forState: method of \c UIButton so that should not be set manually after calling this method. To display an image use the \c -setImage:forState: method 28 | 29 | @param buttonColor The color of the button. This color fills the entire rect of the button. 30 | @param state The state for which to set the button's color. 31 | */ 32 | - (void)setButtonColor:(UIColor *)buttonColor forState:(UIControlState)state; 33 | 34 | @end 35 | -------------------------------------------------------------------------------- /JGScrollableTableViewCell/JGScrollableTableViewCellAccessoryButton.m: -------------------------------------------------------------------------------- 1 | // 2 | // JGScrollableTableViewCellAccessoryButton.m 3 | // JGScrollableTableViewCell 4 | // 5 | // Created by Jonas Gessner on 03.11.13. 6 | // Copyright (c) 2013 Jonas Gessner. All rights reserved. 7 | // 8 | 9 | #import "JGScrollableTableViewCellAccessoryButton.h" 10 | 11 | @implementation JGScrollableTableViewCellAccessoryButton 12 | 13 | + (instancetype)button { 14 | return [[self alloc] init]; 15 | } 16 | 17 | - (instancetype)init { 18 | self = [super init]; 19 | if (self) { 20 | self.titleLabel.shadowColor = [UIColor clearColor]; 21 | self.titleLabel.shadowOffset = CGSizeZero; 22 | self.titleLabel.textColor = [UIColor whiteColor]; 23 | self.titleLabel.font = [UIFont fontWithName:@"HelveticaNeue-Medium" size:17.0f]; 24 | } 25 | return self; 26 | } 27 | 28 | - (void)setButtonColor:(UIColor *)buttonColor forState:(UIControlState)state { 29 | CGSize size = (CGSize){1.0f, 1.0f}; 30 | 31 | UIGraphicsBeginImageContextWithOptions(size, YES, 0.0f); 32 | 33 | [buttonColor setFill]; 34 | 35 | [[UIBezierPath bezierPathWithRect:(CGRect){CGPointZero, size}] fill]; 36 | 37 | UIImage *img = UIGraphicsGetImageFromCurrentImageContext(); 38 | 39 | UIGraphicsEndImageContext(); 40 | 41 | [self setBackgroundImage:img forState:state]; 42 | } 43 | 44 | @end 45 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Jonas Gessner 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | 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, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

JGScrollableTableViewCell

© 2013-2014 Jonas Gessner
2 | 3 | ---------------- 4 |
5 | 6 | JGScrollableTableViewCell is a simple and easy to use UITableViewCell subclass with a scrollable content view that exposes an accessory view when scrolled. The behavior is inspired by the iOS 7 mail app. 7 |
8 |
9 | Current Version: 1.1 10 |
11 |
12 |

13 | 14 | Requirements 15 | ============= 16 | • iOS 5 or higher
17 | • Built with ARC (If your Xcode project doesn't use ARC then set the `-fobjc-arc` compiler flag)
18 | • Foundation, UIKit and CoreGraphics frameworks
19 | 20 | 21 | Getting started 22 | ================= 23 | `JGScrollableTableViewCell` works just like any other `UITableViewCell`: Highlighting, selection, and `UITableViewDelegate` calls all work like normal. The only difference is that the cell's `contentView` is covered by a `UIScrollView` that basically becomes the new content view – which is scrollable. 24 | 25 | An "option view" can be assigned to the cell, which is placed behind the scroll view, making it possible to scroll on the cell to reveal the option view behind it. 26 | 27 | Note: You should always use custom subclasses of `JGScrollableTableViewCell` that add your custom content to the cell (ex labels & image views). 28 | 29 | 30 | Installation 31 | ================ 32 | CocoaPods:
33 | Add this to your `Podfile`: 34 | ``` 35 | pod 'JGScrollableTableViewCell', '1.1' 36 | ``` 37 | 38 | Add source files:
39 | Drag the JGScrollableTableViewCell folder into your project. 40 | 41 | After you have included JGScrollableTableViewCell in your project, simply `#import "JGScrollableTableViewCell.h"` and you are ready to go! 42 | 43 | Documentation 44 | =============== 45 | By default `JGScrollableTableViewCell` has an empty scroll area, and no option view. Here's a guide through the entire `JGScrollableTableViewCell` class: 46 | 47 | ####The scroll view 48 | 49 | ```objc 50 | - (UIScrollView *)scrollView; 51 | ``` 52 | Returns the scroll view used in the cell. Do not add any subviews to the scrollview or modify its frame, bounds, contentOffset or contentInset. 53 |
54 |
55 | 56 | ```objc 57 | - (void)setScrollViewInsets:(UIEdgeInsets)scrollViewInsets; 58 | ``` 59 | Insets the scroll view. Useful for displaying a border around the scroll area (when also setting `contentView.backgroundColor`) 60 |
61 |
62 | 63 | ```objc 64 | - (void)setScrollViewBackgroundColor:(UIColor *)scrollViewBackgroundColor; 65 | ``` 66 | Sets the background color of the scroll view. Equivalent to `contentView.backgroundColor` on a regular `UITableViewCell`. 67 |
68 |
69 | 70 | ```objc 71 | - (BOOL)isScrolling; 72 | ``` 73 | Returns `YES` if the user is currently dragging the scroll view or if the scroll view is decelerating. 74 |
75 |
76 | 77 | ```objc 78 | - (void)setGrabberView:(UIView *)grabberView; 79 | ``` 80 | Sets a view to use as a grabber for the scroll view. This view is static, so it won't be resized at all by the cell. If this view is \c nil then the entire area of the scroll view is scrollable, if this view is set then scrolling can only be performed on this view. 81 |
82 |
83 | 84 | ####The option view 85 | 86 | ```objc 87 | - (void)setOptionView:(UIView *)view ; 88 | ``` 89 | Sets a new option view & removes the old option view. The option view will be dynamically resized to fit the cell's size and the scroll view's insets. The only parameter of the option view's `frame` that is not changed is the width. The width should be set before passing the view to this method and should not be changed afterwards. 90 |
91 |
92 | 93 | ```objc 94 | - (UIView *)optionView; 95 | ``` 96 | Returns the current option view. 97 |
98 |
99 | 100 | ```objc 101 | - (void)setOptionViewVisible:(BOOL)optionViewVisible animated:(BOOL)animated; 102 | ``` 103 | Opens or closes the option view with an optional 0.3 second animation. 104 |
105 |
106 | 107 | ####Scrollable content 108 | 109 | ```objc 110 | - (void)addContentView:(UIView *)view; 111 | ``` 112 | You should at no point add a view to the cell's directly or to its `contentView`. Instead, pass views that should be displayed on the cell to this method. Views passed to this method will appear in the scrollable area of the cell. 113 |
114 |
115 | 116 | ###Delegate 117 | `JGScrollableTableViewCell` has a delegate that conforms to the `JGScrollableTableViewCellDelegate` protocol. It is used for handling scroll events. 118 | ```objc 119 | @property (nonatomic, weak) id scrollDelegate; 120 | ``` 121 |
122 |
123 | 124 | The `JGScrollableTableViewCellDelegate` protocol declares three optional (self explaining) methods: 125 | ```objc 126 | - (void)cellDidBeginScrolling:(JGScrollableTableViewCell *)cell; 127 | - (void)cellDidScroll:(JGScrollableTableViewCell *)cell; 128 | - (void)cellDidEndScrolling:(JGScrollableTableViewCell *)cell; 129 | ``` 130 | Ideally, your `UITableViewDelegate` should also be your `JGScrollableTableViewCellDelegate`. 131 |
132 |
133 | 134 | ##Custom Touch handling 135 | 136 | In some special cases custom touch handling may be needed (see `Advanced` example project). There are two blocks that can be used for customizing the scrolling behavior. 137 |
138 |
139 | 140 | ```objc 141 | @property (nonatomic, copy) void (^scrollViewDidScrollBlock)(JGScrollableTableViewCell *cell, UIScrollView *scrollView); 142 | ``` 143 | This block is invoked when the scroll view scrolls. Can be used to add custom behavior to the scroll view. 144 |
145 |
146 | 147 | ##Advanced usage 148 | 149 | ####Management of opened option views 150 | ```objc 151 | JGScrollableTableViewCellManager 152 | + (void)closeAllCellsWithExceptionOf:(JGScrollableTableViewCell *)cell stopAfterFirst:(BOOL)stop; 153 | ``` 154 | This closes all option views in the table view that contains `cell`. `stop` is a flag that can increase performance by stopping the enumeration of cells after the first cell with an opened option view has been found and closed. Set this flag to `YES` when you have set up a `JGScrollableTableViewCellDelegate` to only allow one opened option view at a time (like in the following example). 155 |
156 | 157 | Using this method call we can set up our table view to only allow one option view to be opened at at time: 158 | ```objc 159 | - (void)cellDidBeginScrolling:(JGScrollableTableViewCell *)cell { 160 | [JGScrollableTableViewCellManager closeAllCellsWithExceptionOf:cell stopAfterFirst:YES]; 161 | } 162 | 163 | - (void)cellDidScroll:(JGScrollableTableViewCell *)cell { 164 | [JGScrollableTableViewCellManager closeAllCellsWithExceptionOf:cell stopAfterFirst:YES]; 165 | } 166 | 167 | - (void)cellDidEndScrolling:(JGScrollableTableViewCell *)cell { 168 | [JGScrollableTableViewCellManager closeAllCellsWithExceptionOf:cell stopAfterFirst:YES]; 169 | } 170 | ``` 171 | 172 | 173 | ####Surviving UITableView's cell reuse 174 | Because `UITableView` reuses cells it is important to set the opened state of each cell when the `UITableViewDataSource` loads its data. To remember which cell was opened you can modify the `cellDidEndScrolling:` method to take note of the cell with the currently opened option view: 175 | 176 | 177 | ```objc 178 | - (void)cellDidEndScrolling:(JGScrollableTableViewCell *)cell { 179 | if (cell.optionViewVisible) { 180 | _openedIndexPath = [self.tableView indexPathForCell:cell]; 181 | } 182 | else { 183 | _openedIndexPath = nil; 184 | } 185 | 186 | [JGScrollableTableViewCellManager closeAllCellsWithExceptionOf:cell stopAfterFirst:YES]; 187 | } 188 | ``` 189 | (`_openedIndexPath` is an instance variable) 190 |
191 |
192 | 193 | The `tableView:cellForRowAtIndexPath:` method should contain this code to update each cell's `optionViewVisible` state: 194 | 195 | ```objc 196 | - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { 197 | static NSString *const CellIdentifier = @"ScrollCell"; 198 | 199 | JGScrollableTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath]; 200 | 201 | [cell setOptionViewVisible:[_openedIndexPath isEqual:indexPath]]; //this correctly sets the opened state of the cell's option view. 202 | 203 | return cell; 204 | } 205 | ``` 206 |
207 |
208 | 209 | Examples 210 | =========== 211 | There are two example projects located in the `Examples` folder. 212 | 213 | Credits 214 | ========== 215 | Created by Jonas Gessner. ©2013-2014 216 | 217 | Contribution 218 | =============== 219 | You are welcome to contribute to the project by forking the repo, modifying the code and opening issues or pull requests. 220 | 221 | License 222 | ============ 223 | Licensed under the MIT license. 224 | --------------------------------------------------------------------------------