├── .gitignore ├── Demo └── LLSlideMenuDemo │ ├── LLSlideMenuDemo.xcodeproj │ ├── project.pbxproj │ └── project.xcworkspace │ │ └── contents.xcworkspacedata │ └── LLSlideMenuDemo │ ├── AppDelegate.h │ ├── AppDelegate.m │ ├── Assets.xcassets │ ├── AppIcon.appiconset │ │ └── Contents.json │ ├── Contents.json │ ├── Head.imageset │ │ ├── Contents.json │ │ └── Head@2x.png │ ├── icon_menu.imageset │ │ ├── Contents.json │ │ └── icon_menu@2x.png │ └── icon_menu_highlight.imageset │ │ ├── Contents.json │ │ └── icon_menu_highlight@2x.png │ ├── Base.lproj │ ├── LaunchScreen.storyboard │ └── Main.storyboard │ ├── Info.plist │ ├── LLSlideMenu │ ├── LLAnimationManager.h │ ├── LLAnimationManager.m │ ├── LLCircleLayer.h │ ├── LLCircleLayer.m │ ├── LLDisplayLayer.h │ ├── LLDisplayLayer.m │ ├── LLSlideMenu.h │ ├── LLSlideMenu.m │ ├── UIView+SetRect.h │ └── UIView+SetRect.m │ ├── SecondController.h │ ├── SecondController.m │ ├── ViewController.h │ ├── ViewController.m │ └── main.m ├── LICENSE ├── LLSlideMenu.podspec ├── LLSlideMenu ├── LLAnimationManager.h ├── LLAnimationManager.m ├── LLCircleLayer.h ├── LLCircleLayer.m ├── LLDisplayLayer.h ├── LLDisplayLayer.m ├── LLSlideMenu.h ├── LLSlideMenu.m ├── UIView+SetRect.h └── UIView+SetRect.m ├── Preview └── LLSlideMenuPreview.gif └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | # 3 | build/ 4 | *.pbxuser 5 | !default.pbxuser 6 | *.mode1v3 7 | !default.mode1v3 8 | *.mode2v3 9 | !default.mode2v3 10 | *.perspectivev3 11 | !default.perspectivev3 12 | xcuserdata 13 | *.xccheckout 14 | *.moved-aside 15 | DerivedData 16 | *.hmap 17 | *.ipa 18 | *.xcuserstate 19 | 20 | # CocoaPods 21 | # 22 | # We recommend against adding the Pods directory to your .gitignore. However 23 | # you should judge for yourself, the pros and cons are mentioned at: 24 | # http://guides.cocoapods.org/using/using-cocoapods.html#should-i-ignore-the-pods-directory-in-source-control 25 | # 26 | #Pods/ 27 | -------------------------------------------------------------------------------- /Demo/LLSlideMenuDemo/LLSlideMenuDemo.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 844F159A1C0491E800B485F8 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 844F15991C0491E800B485F8 /* main.m */; }; 11 | 844F159D1C0491E800B485F8 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 844F159C1C0491E800B485F8 /* AppDelegate.m */; }; 12 | 844F15A01C0491E800B485F8 /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 844F159F1C0491E800B485F8 /* ViewController.m */; }; 13 | 844F15A31C0491E800B485F8 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 844F15A11C0491E800B485F8 /* Main.storyboard */; }; 14 | 844F15A51C0491E800B485F8 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 844F15A41C0491E800B485F8 /* Assets.xcassets */; }; 15 | 844F15A81C0491E800B485F8 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 844F15A61C0491E800B485F8 /* LaunchScreen.storyboard */; }; 16 | 844F15CD1C0492A500B485F8 /* LLAnimationManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 844F15C41C0492A500B485F8 /* LLAnimationManager.m */; }; 17 | 844F15CE1C0492A500B485F8 /* LLCircleLayer.m in Sources */ = {isa = PBXBuildFile; fileRef = 844F15C61C0492A500B485F8 /* LLCircleLayer.m */; }; 18 | 844F15CF1C0492A500B485F8 /* LLDisplayLayer.m in Sources */ = {isa = PBXBuildFile; fileRef = 844F15C81C0492A500B485F8 /* LLDisplayLayer.m */; }; 19 | 844F15D11C0492A500B485F8 /* UIView+SetRect.m in Sources */ = {isa = PBXBuildFile; fileRef = 844F15CC1C0492A500B485F8 /* UIView+SetRect.m */; }; 20 | 8454310A1C0D4ACE00A1AC5F /* LLSlideMenu.m in Sources */ = {isa = PBXBuildFile; fileRef = 845431091C0D4ACE00A1AC5F /* LLSlideMenu.m */; }; 21 | A48CDC7F1E62B0C500B37E21 /* SecondController.m in Sources */ = {isa = PBXBuildFile; fileRef = A48CDC7E1E62B0C500B37E21 /* SecondController.m */; }; 22 | /* End PBXBuildFile section */ 23 | 24 | /* Begin PBXFileReference section */ 25 | 844F15951C0491E800B485F8 /* LLSlideMenuDemo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = LLSlideMenuDemo.app; sourceTree = BUILT_PRODUCTS_DIR; }; 26 | 844F15991C0491E800B485F8 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 27 | 844F159B1C0491E800B485F8 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 28 | 844F159C1C0491E800B485F8 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; 29 | 844F159E1C0491E800B485F8 /* ViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = ""; }; 30 | 844F159F1C0491E800B485F8 /* ViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = ""; }; 31 | 844F15A21C0491E800B485F8 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 32 | 844F15A41C0491E800B485F8 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 33 | 844F15A71C0491E800B485F8 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 34 | 844F15A91C0491E800B485F8 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 35 | 844F15C31C0492A500B485F8 /* LLAnimationManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LLAnimationManager.h; sourceTree = ""; }; 36 | 844F15C41C0492A500B485F8 /* LLAnimationManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LLAnimationManager.m; sourceTree = ""; }; 37 | 844F15C51C0492A500B485F8 /* LLCircleLayer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LLCircleLayer.h; sourceTree = ""; }; 38 | 844F15C61C0492A500B485F8 /* LLCircleLayer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LLCircleLayer.m; sourceTree = ""; }; 39 | 844F15C71C0492A500B485F8 /* LLDisplayLayer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LLDisplayLayer.h; sourceTree = ""; }; 40 | 844F15C81C0492A500B485F8 /* LLDisplayLayer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LLDisplayLayer.m; sourceTree = ""; }; 41 | 844F15CB1C0492A500B485F8 /* UIView+SetRect.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIView+SetRect.h"; sourceTree = ""; }; 42 | 844F15CC1C0492A500B485F8 /* UIView+SetRect.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIView+SetRect.m"; sourceTree = ""; }; 43 | 845431081C0D4ACE00A1AC5F /* LLSlideMenu.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LLSlideMenu.h; sourceTree = ""; }; 44 | 845431091C0D4ACE00A1AC5F /* LLSlideMenu.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LLSlideMenu.m; sourceTree = ""; }; 45 | A48CDC7D1E62B0C500B37E21 /* SecondController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SecondController.h; sourceTree = ""; }; 46 | A48CDC7E1E62B0C500B37E21 /* SecondController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SecondController.m; sourceTree = ""; }; 47 | /* End PBXFileReference section */ 48 | 49 | /* Begin PBXFrameworksBuildPhase section */ 50 | 844F15921C0491E800B485F8 /* Frameworks */ = { 51 | isa = PBXFrameworksBuildPhase; 52 | buildActionMask = 2147483647; 53 | files = ( 54 | ); 55 | runOnlyForDeploymentPostprocessing = 0; 56 | }; 57 | /* End PBXFrameworksBuildPhase section */ 58 | 59 | /* Begin PBXGroup section */ 60 | 844F158C1C0491E800B485F8 = { 61 | isa = PBXGroup; 62 | children = ( 63 | 844F15971C0491E800B485F8 /* LLSlideMenuDemo */, 64 | 844F15961C0491E800B485F8 /* Products */, 65 | ); 66 | sourceTree = ""; 67 | }; 68 | 844F15961C0491E800B485F8 /* Products */ = { 69 | isa = PBXGroup; 70 | children = ( 71 | 844F15951C0491E800B485F8 /* LLSlideMenuDemo.app */, 72 | ); 73 | name = Products; 74 | sourceTree = ""; 75 | }; 76 | 844F15971C0491E800B485F8 /* LLSlideMenuDemo */ = { 77 | isa = PBXGroup; 78 | children = ( 79 | 844F15C21C0492A500B485F8 /* LLSlideMenu */, 80 | 844F159B1C0491E800B485F8 /* AppDelegate.h */, 81 | 844F159C1C0491E800B485F8 /* AppDelegate.m */, 82 | 844F159E1C0491E800B485F8 /* ViewController.h */, 83 | 844F159F1C0491E800B485F8 /* ViewController.m */, 84 | A48CDC7D1E62B0C500B37E21 /* SecondController.h */, 85 | A48CDC7E1E62B0C500B37E21 /* SecondController.m */, 86 | 844F15A11C0491E800B485F8 /* Main.storyboard */, 87 | 844F15A41C0491E800B485F8 /* Assets.xcassets */, 88 | 844F15A61C0491E800B485F8 /* LaunchScreen.storyboard */, 89 | 844F15A91C0491E800B485F8 /* Info.plist */, 90 | 844F15981C0491E800B485F8 /* Supporting Files */, 91 | ); 92 | path = LLSlideMenuDemo; 93 | sourceTree = ""; 94 | }; 95 | 844F15981C0491E800B485F8 /* Supporting Files */ = { 96 | isa = PBXGroup; 97 | children = ( 98 | 844F15991C0491E800B485F8 /* main.m */, 99 | ); 100 | name = "Supporting Files"; 101 | sourceTree = ""; 102 | }; 103 | 844F15C21C0492A500B485F8 /* LLSlideMenu */ = { 104 | isa = PBXGroup; 105 | children = ( 106 | 844F15C31C0492A500B485F8 /* LLAnimationManager.h */, 107 | 844F15C41C0492A500B485F8 /* LLAnimationManager.m */, 108 | 844F15C51C0492A500B485F8 /* LLCircleLayer.h */, 109 | 844F15C61C0492A500B485F8 /* LLCircleLayer.m */, 110 | 844F15C71C0492A500B485F8 /* LLDisplayLayer.h */, 111 | 844F15C81C0492A500B485F8 /* LLDisplayLayer.m */, 112 | 845431081C0D4ACE00A1AC5F /* LLSlideMenu.h */, 113 | 845431091C0D4ACE00A1AC5F /* LLSlideMenu.m */, 114 | 844F15CB1C0492A500B485F8 /* UIView+SetRect.h */, 115 | 844F15CC1C0492A500B485F8 /* UIView+SetRect.m */, 116 | ); 117 | path = LLSlideMenu; 118 | sourceTree = ""; 119 | }; 120 | /* End PBXGroup section */ 121 | 122 | /* Begin PBXNativeTarget section */ 123 | 844F15941C0491E800B485F8 /* LLSlideMenuDemo */ = { 124 | isa = PBXNativeTarget; 125 | buildConfigurationList = 844F15AC1C0491E800B485F8 /* Build configuration list for PBXNativeTarget "LLSlideMenuDemo" */; 126 | buildPhases = ( 127 | 844F15911C0491E800B485F8 /* Sources */, 128 | 844F15921C0491E800B485F8 /* Frameworks */, 129 | 844F15931C0491E800B485F8 /* Resources */, 130 | ); 131 | buildRules = ( 132 | ); 133 | dependencies = ( 134 | ); 135 | name = LLSlideMenuDemo; 136 | productName = LLSlideMenuDemo; 137 | productReference = 844F15951C0491E800B485F8 /* LLSlideMenuDemo.app */; 138 | productType = "com.apple.product-type.application"; 139 | }; 140 | /* End PBXNativeTarget section */ 141 | 142 | /* Begin PBXProject section */ 143 | 844F158D1C0491E800B485F8 /* Project object */ = { 144 | isa = PBXProject; 145 | attributes = { 146 | LastUpgradeCheck = 0700; 147 | ORGANIZATIONNAME = LiLei; 148 | TargetAttributes = { 149 | 844F15941C0491E800B485F8 = { 150 | CreatedOnToolsVersion = 7.0; 151 | DevelopmentTeam = P7KBGRKM57; 152 | }; 153 | }; 154 | }; 155 | buildConfigurationList = 844F15901C0491E800B485F8 /* Build configuration list for PBXProject "LLSlideMenuDemo" */; 156 | compatibilityVersion = "Xcode 3.2"; 157 | developmentRegion = English; 158 | hasScannedForEncodings = 0; 159 | knownRegions = ( 160 | en, 161 | Base, 162 | ); 163 | mainGroup = 844F158C1C0491E800B485F8; 164 | productRefGroup = 844F15961C0491E800B485F8 /* Products */; 165 | projectDirPath = ""; 166 | projectRoot = ""; 167 | targets = ( 168 | 844F15941C0491E800B485F8 /* LLSlideMenuDemo */, 169 | ); 170 | }; 171 | /* End PBXProject section */ 172 | 173 | /* Begin PBXResourcesBuildPhase section */ 174 | 844F15931C0491E800B485F8 /* Resources */ = { 175 | isa = PBXResourcesBuildPhase; 176 | buildActionMask = 2147483647; 177 | files = ( 178 | 844F15A81C0491E800B485F8 /* LaunchScreen.storyboard in Resources */, 179 | 844F15A51C0491E800B485F8 /* Assets.xcassets in Resources */, 180 | 844F15A31C0491E800B485F8 /* Main.storyboard in Resources */, 181 | ); 182 | runOnlyForDeploymentPostprocessing = 0; 183 | }; 184 | /* End PBXResourcesBuildPhase section */ 185 | 186 | /* Begin PBXSourcesBuildPhase section */ 187 | 844F15911C0491E800B485F8 /* Sources */ = { 188 | isa = PBXSourcesBuildPhase; 189 | buildActionMask = 2147483647; 190 | files = ( 191 | 844F15D11C0492A500B485F8 /* UIView+SetRect.m in Sources */, 192 | 844F15A01C0491E800B485F8 /* ViewController.m in Sources */, 193 | 844F159D1C0491E800B485F8 /* AppDelegate.m in Sources */, 194 | A48CDC7F1E62B0C500B37E21 /* SecondController.m in Sources */, 195 | 844F15CE1C0492A500B485F8 /* LLCircleLayer.m in Sources */, 196 | 844F15CD1C0492A500B485F8 /* LLAnimationManager.m in Sources */, 197 | 844F15CF1C0492A500B485F8 /* LLDisplayLayer.m in Sources */, 198 | 8454310A1C0D4ACE00A1AC5F /* LLSlideMenu.m in Sources */, 199 | 844F159A1C0491E800B485F8 /* main.m in Sources */, 200 | ); 201 | runOnlyForDeploymentPostprocessing = 0; 202 | }; 203 | /* End PBXSourcesBuildPhase section */ 204 | 205 | /* Begin PBXVariantGroup section */ 206 | 844F15A11C0491E800B485F8 /* Main.storyboard */ = { 207 | isa = PBXVariantGroup; 208 | children = ( 209 | 844F15A21C0491E800B485F8 /* Base */, 210 | ); 211 | name = Main.storyboard; 212 | sourceTree = ""; 213 | }; 214 | 844F15A61C0491E800B485F8 /* LaunchScreen.storyboard */ = { 215 | isa = PBXVariantGroup; 216 | children = ( 217 | 844F15A71C0491E800B485F8 /* Base */, 218 | ); 219 | name = LaunchScreen.storyboard; 220 | sourceTree = ""; 221 | }; 222 | /* End PBXVariantGroup section */ 223 | 224 | /* Begin XCBuildConfiguration section */ 225 | 844F15AA1C0491E800B485F8 /* Debug */ = { 226 | isa = XCBuildConfiguration; 227 | buildSettings = { 228 | ALWAYS_SEARCH_USER_PATHS = NO; 229 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 230 | CLANG_CXX_LIBRARY = "libc++"; 231 | CLANG_ENABLE_MODULES = YES; 232 | CLANG_ENABLE_OBJC_ARC = YES; 233 | CLANG_WARN_BOOL_CONVERSION = YES; 234 | CLANG_WARN_CONSTANT_CONVERSION = YES; 235 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 236 | CLANG_WARN_EMPTY_BODY = YES; 237 | CLANG_WARN_ENUM_CONVERSION = YES; 238 | CLANG_WARN_INT_CONVERSION = YES; 239 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 240 | CLANG_WARN_UNREACHABLE_CODE = YES; 241 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 242 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 243 | COPY_PHASE_STRIP = NO; 244 | DEBUG_INFORMATION_FORMAT = dwarf; 245 | ENABLE_STRICT_OBJC_MSGSEND = YES; 246 | ENABLE_TESTABILITY = YES; 247 | GCC_C_LANGUAGE_STANDARD = gnu99; 248 | GCC_DYNAMIC_NO_PIC = NO; 249 | GCC_NO_COMMON_BLOCKS = YES; 250 | GCC_OPTIMIZATION_LEVEL = 0; 251 | GCC_PREPROCESSOR_DEFINITIONS = ( 252 | "DEBUG=1", 253 | "$(inherited)", 254 | ); 255 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 256 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 257 | GCC_WARN_UNDECLARED_SELECTOR = YES; 258 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 259 | GCC_WARN_UNUSED_FUNCTION = YES; 260 | GCC_WARN_UNUSED_VARIABLE = YES; 261 | IPHONEOS_DEPLOYMENT_TARGET = 9.0; 262 | MTL_ENABLE_DEBUG_INFO = YES; 263 | ONLY_ACTIVE_ARCH = YES; 264 | SDKROOT = iphoneos; 265 | }; 266 | name = Debug; 267 | }; 268 | 844F15AB1C0491E800B485F8 /* Release */ = { 269 | isa = XCBuildConfiguration; 270 | buildSettings = { 271 | ALWAYS_SEARCH_USER_PATHS = NO; 272 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 273 | CLANG_CXX_LIBRARY = "libc++"; 274 | CLANG_ENABLE_MODULES = YES; 275 | CLANG_ENABLE_OBJC_ARC = YES; 276 | CLANG_WARN_BOOL_CONVERSION = YES; 277 | CLANG_WARN_CONSTANT_CONVERSION = YES; 278 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 279 | CLANG_WARN_EMPTY_BODY = YES; 280 | CLANG_WARN_ENUM_CONVERSION = YES; 281 | CLANG_WARN_INT_CONVERSION = YES; 282 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 283 | CLANG_WARN_UNREACHABLE_CODE = YES; 284 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 285 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 286 | COPY_PHASE_STRIP = NO; 287 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 288 | ENABLE_NS_ASSERTIONS = NO; 289 | ENABLE_STRICT_OBJC_MSGSEND = YES; 290 | GCC_C_LANGUAGE_STANDARD = gnu99; 291 | GCC_NO_COMMON_BLOCKS = YES; 292 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 293 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 294 | GCC_WARN_UNDECLARED_SELECTOR = YES; 295 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 296 | GCC_WARN_UNUSED_FUNCTION = YES; 297 | GCC_WARN_UNUSED_VARIABLE = YES; 298 | IPHONEOS_DEPLOYMENT_TARGET = 9.0; 299 | MTL_ENABLE_DEBUG_INFO = NO; 300 | SDKROOT = iphoneos; 301 | VALIDATE_PRODUCT = YES; 302 | }; 303 | name = Release; 304 | }; 305 | 844F15AD1C0491E800B485F8 /* Debug */ = { 306 | isa = XCBuildConfiguration; 307 | buildSettings = { 308 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 309 | INFOPLIST_FILE = LLSlideMenuDemo/Info.plist; 310 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 311 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 312 | PRODUCT_BUNDLE_IDENTIFIER = com.Lilei.LLSlideMenuDemo; 313 | PRODUCT_NAME = "$(TARGET_NAME)"; 314 | }; 315 | name = Debug; 316 | }; 317 | 844F15AE1C0491E800B485F8 /* Release */ = { 318 | isa = XCBuildConfiguration; 319 | buildSettings = { 320 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 321 | INFOPLIST_FILE = LLSlideMenuDemo/Info.plist; 322 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 323 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 324 | PRODUCT_BUNDLE_IDENTIFIER = com.Lilei.LLSlideMenuDemo; 325 | PRODUCT_NAME = "$(TARGET_NAME)"; 326 | }; 327 | name = Release; 328 | }; 329 | /* End XCBuildConfiguration section */ 330 | 331 | /* Begin XCConfigurationList section */ 332 | 844F15901C0491E800B485F8 /* Build configuration list for PBXProject "LLSlideMenuDemo" */ = { 333 | isa = XCConfigurationList; 334 | buildConfigurations = ( 335 | 844F15AA1C0491E800B485F8 /* Debug */, 336 | 844F15AB1C0491E800B485F8 /* Release */, 337 | ); 338 | defaultConfigurationIsVisible = 0; 339 | defaultConfigurationName = Release; 340 | }; 341 | 844F15AC1C0491E800B485F8 /* Build configuration list for PBXNativeTarget "LLSlideMenuDemo" */ = { 342 | isa = XCConfigurationList; 343 | buildConfigurations = ( 344 | 844F15AD1C0491E800B485F8 /* Debug */, 345 | 844F15AE1C0491E800B485F8 /* Release */, 346 | ); 347 | defaultConfigurationIsVisible = 0; 348 | defaultConfigurationName = Release; 349 | }; 350 | /* End XCConfigurationList section */ 351 | }; 352 | rootObject = 844F158D1C0491E800B485F8 /* Project object */; 353 | } 354 | -------------------------------------------------------------------------------- /Demo/LLSlideMenuDemo/LLSlideMenuDemo.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Demo/LLSlideMenuDemo/LLSlideMenuDemo/AppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.h 3 | // LLSlideMenuDemo 4 | // 5 | // Created by admin on 15/11/24. 6 | // Copyright © 2015年 LiLei. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface AppDelegate : UIResponder 12 | 13 | @property (strong, nonatomic) UIWindow *window; 14 | 15 | 16 | @end 17 | 18 | -------------------------------------------------------------------------------- /Demo/LLSlideMenuDemo/LLSlideMenuDemo/AppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.m 3 | // LLSlideMenuDemo 4 | // 5 | // Created by admin on 15/11/24. 6 | // Copyright © 2015年 LiLei. All rights reserved. 7 | // 8 | 9 | #import "AppDelegate.h" 10 | 11 | @interface AppDelegate () 12 | 13 | @end 14 | 15 | @implementation AppDelegate 16 | 17 | 18 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 19 | // Override point for customization after application launch. 20 | return YES; 21 | } 22 | 23 | - (void)applicationWillResignActive:(UIApplication *)application { 24 | // 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. 25 | // 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. 26 | } 27 | 28 | - (void)applicationDidEnterBackground:(UIApplication *)application { 29 | // 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. 30 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 31 | } 32 | 33 | - (void)applicationWillEnterForeground:(UIApplication *)application { 34 | // 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. 35 | } 36 | 37 | - (void)applicationDidBecomeActive:(UIApplication *)application { 38 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 39 | } 40 | 41 | - (void)applicationWillTerminate:(UIApplication *)application { 42 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 43 | } 44 | 45 | @end 46 | -------------------------------------------------------------------------------- /Demo/LLSlideMenuDemo/LLSlideMenuDemo/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "29x29", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "29x29", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "40x40", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "40x40", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "60x60", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "60x60", 31 | "scale" : "3x" 32 | } 33 | ], 34 | "info" : { 35 | "version" : 1, 36 | "author" : "xcode" 37 | } 38 | } -------------------------------------------------------------------------------- /Demo/LLSlideMenuDemo/LLSlideMenuDemo/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /Demo/LLSlideMenuDemo/LLSlideMenuDemo/Assets.xcassets/Head.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "Head@2x.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Demo/LLSlideMenuDemo/LLSlideMenuDemo/Assets.xcassets/Head.imageset/Head@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lilei644/LLSlideMenu/c3fd321747a8e0e30bc710025b8b1e7c070f97d0/Demo/LLSlideMenuDemo/LLSlideMenuDemo/Assets.xcassets/Head.imageset/Head@2x.png -------------------------------------------------------------------------------- /Demo/LLSlideMenuDemo/LLSlideMenuDemo/Assets.xcassets/icon_menu.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "icon_menu@2x.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Demo/LLSlideMenuDemo/LLSlideMenuDemo/Assets.xcassets/icon_menu.imageset/icon_menu@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lilei644/LLSlideMenu/c3fd321747a8e0e30bc710025b8b1e7c070f97d0/Demo/LLSlideMenuDemo/LLSlideMenuDemo/Assets.xcassets/icon_menu.imageset/icon_menu@2x.png -------------------------------------------------------------------------------- /Demo/LLSlideMenuDemo/LLSlideMenuDemo/Assets.xcassets/icon_menu_highlight.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "icon_menu_highlight@2x.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Demo/LLSlideMenuDemo/LLSlideMenuDemo/Assets.xcassets/icon_menu_highlight.imageset/icon_menu_highlight@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lilei644/LLSlideMenu/c3fd321747a8e0e30bc710025b8b1e7c070f97d0/Demo/LLSlideMenuDemo/LLSlideMenuDemo/Assets.xcassets/icon_menu_highlight.imageset/icon_menu_highlight@2x.png -------------------------------------------------------------------------------- /Demo/LLSlideMenuDemo/LLSlideMenuDemo/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /Demo/LLSlideMenuDemo/LLSlideMenuDemo/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | -------------------------------------------------------------------------------- /Demo/LLSlideMenuDemo/LLSlideMenuDemo/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIMainStoryboardFile 28 | Main 29 | UIRequiredDeviceCapabilities 30 | 31 | armv7 32 | 33 | UISupportedInterfaceOrientations 34 | 35 | UIInterfaceOrientationPortrait 36 | UIInterfaceOrientationLandscapeLeft 37 | UIInterfaceOrientationLandscapeRight 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /Demo/LLSlideMenuDemo/LLSlideMenuDemo/LLSlideMenu/LLAnimationManager.h: -------------------------------------------------------------------------------- 1 | // 2 | // AnimationManager.h 3 | // LLSideMenuDemo 4 | // 5 | // Created by admin on 15/11/22. 6 | // Copyright © 2015年 LiLei. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | #import "LLDisplayLayer.h" 12 | #import "LLCircleLayer.h" 13 | 14 | @class LLSlideMenu; 15 | @interface LLAnimationManager : NSObject 16 | 17 | // 是否正在动画 18 | @property (nonatomic, assign) BOOL isAnimating; 19 | 20 | @property (nonatomic, strong) LLDisplayLayer *layer; 21 | @property (nonatomic, strong) LLCircleLayer *circleLayer; 22 | 23 | @property (nonatomic, strong) LLSlideMenu *llSildeMenu; 24 | 25 | // 设置弹性 spring 26 | @property (nonatomic, assign) CGFloat springDamping; 27 | @property (nonatomic, assign) CGFloat springVelocity; 28 | @property (nonatomic, assign) NSInteger springNumOfFrames; 29 | 30 | 31 | // 开始动画 32 | - (void)startAnimate; 33 | 34 | // 结束动画 35 | - (void)endAnimate; 36 | 37 | @end 38 | -------------------------------------------------------------------------------- /Demo/LLSlideMenuDemo/LLSlideMenuDemo/LLSlideMenu/LLAnimationManager.m: -------------------------------------------------------------------------------- 1 | // 2 | // AnimationManager.m 3 | // LLSideMenuDemo 4 | // 5 | // Created by admin on 15/11/22. 6 | // Copyright © 2015年 LiLei. All rights reserved. 7 | // 8 | 9 | #import "LLAnimationManager.h" 10 | #import "LLSlideMenu.h" 11 | 12 | 13 | #define LL_SCREEN_HEIGHT [UIScreen mainScreen].bounds.size.height 14 | 15 | //===================== 16 | // anim key 17 | //===================== 18 | static NSString *ANIM_STARTBASE = @"startLLSlideBaseAnimate"; 19 | static NSString *ANIM_STARTSPRING = @"startLLSlideSpringAnimate"; 20 | static NSString *ANIM_STARTCIRCLE = @"startLLCircleAnimate"; 21 | static NSString *ANIM_ENDBASE = @"endLLSlideBaseAnimate"; 22 | static NSString *ANIM_ENDSPRING = @"endLLSlideSpringAnimate"; 23 | static NSString *ANIM_ENDCIRCLE = @"endLLCircleAnimate"; 24 | 25 | 26 | @class LLSlideMune; 27 | @implementation LLAnimationManager 28 | 29 | 30 | //===================== 31 | // 开始动画 32 | //===================== 33 | - (void)startAnimate{ 34 | // 判断是否滑动超出宽度 35 | if (_layer.distance >= _layer.bgWidth) { 36 | _layer.isAnimating = YES; 37 | CAKeyframeAnimation *animSpring = [self createSpringAnima:@"distance" duration:2.f usingSpringWithDamping:0.5 initialSpringVelocity:3 fromValue:@(0) toValue:@(_layer.bgWidth)]; 38 | [_layer addAnimation:animSpring forKey:ANIM_STARTSPRING]; 39 | CAKeyframeAnimation *animCircle = [self createBaseAnima:@"radius" duration:.8f fromValue:@(0) toValue:@(sqrt(_circleLayer.frame.size.width * _circleLayer.frame.size.width + _circleLayer.frame.size.height * _circleLayer.frame.size.width))]; 40 | [_circleLayer addAnimation:animCircle forKey:ANIM_STARTCIRCLE]; 41 | } else { 42 | CAKeyframeAnimation *animaBase = [self createBaseAnima:@"distance" duration:.2f fromValue:@(_layer.distance) toValue:@(_layer.bgWidth)]; 43 | [_layer addAnimation:animaBase forKey:ANIM_STARTBASE]; 44 | _layer.distance = _layer.bgWidth; 45 | } 46 | 47 | } 48 | 49 | 50 | //===================== 51 | // 结束动画 52 | //===================== 53 | - (void)endAnimate{ 54 | //==================================== 55 | // 判断如否distance则先变成圆弧在缩小圆弧 56 | //==================================== 57 | if (_layer.distance >= _layer.bgWidth) { 58 | // 关闭背景layer 59 | CAKeyframeAnimation *animBase = [self createBaseAnima:@"distance" duration:.3f fromValue:@(_layer.bgWidth) toValue:@(0)]; 60 | [_layer addAnimation:animBase forKey:ANIM_ENDBASE]; 61 | // 关闭contentView 62 | CAKeyframeAnimation *animCircle = [self createBaseAnima:@"radius" duration:.3f fromValue:@(_circleLayer.frame.size.width) toValue:@(0)]; 63 | [_circleLayer addAnimation:animCircle forKey:ANIM_ENDCIRCLE]; 64 | }else { 65 | _layer.isAnimating = NO; 66 | CAKeyframeAnimation *animSpring = [self createSpringAnima:@"distance" duration:1.5f usingSpringWithDamping:0.5 initialSpringVelocity:3 fromValue:@(_layer.distance) toValue:@(0)]; 67 | [_layer addAnimation:animSpring forKey:ANIM_ENDSPRING]; 68 | } 69 | 70 | } 71 | 72 | //===================== 73 | // 创建普通动画 74 | //===================== 75 | -(CAKeyframeAnimation *)createBaseAnima:(NSString *)keypath 76 | duration:(CFTimeInterval)duration 77 | fromValue:(id)fromValue 78 | toValue:(id)toValue{ 79 | NSMutableArray *values = [NSMutableArray arrayWithObjects:fromValue,toValue,nil]; 80 | 81 | CAKeyframeAnimation *anim = [CAKeyframeAnimation animationWithKeyPath:keypath]; 82 | anim.values = values; 83 | anim.duration = duration; 84 | anim.fillMode = kCAFillModeForwards; 85 | anim.removedOnCompletion = NO; 86 | anim.delegate = self; 87 | return anim; 88 | } 89 | 90 | //===================== 91 | // 创建弹性动画 92 | //===================== 93 | -(CAKeyframeAnimation *)createSpringAnima:(NSString *)keypath 94 | duration:(CFTimeInterval)duration 95 | usingSpringWithDamping:(CGFloat)damping 96 | initialSpringVelocity:(CGFloat)velocity 97 | fromValue:(id)fromValue 98 | toValue:(id)toValue{ 99 | 100 | // CGFloat dampingFactor = 20.0; 101 | // CGFloat velocityFactor = 15.0; 102 | NSMutableArray *values = [self springAnimationValues:fromValue toValue:toValue usingSpringWithDamping:damping * _springDamping initialSpringVelocity:velocity * _springVelocity duration:duration]; 103 | 104 | CAKeyframeAnimation *anim = [CAKeyframeAnimation animationWithKeyPath:keypath]; 105 | anim.values = values; 106 | anim.duration = duration; 107 | anim.fillMode = kCAFillModeForwards; 108 | anim.removedOnCompletion = NO; 109 | anim.delegate = self; 110 | return anim; 111 | } 112 | 113 | 114 | //===================== 115 | // 获取关键帧的属性值 116 | //===================== 117 | -(NSMutableArray *) springAnimationValues:(id)fromValue toValue:(id)toValue usingSpringWithDamping:(CGFloat)damping initialSpringVelocity:(CGFloat)velocity duration:(CGFloat)duration{ 118 | 119 | //关键帧 120 | NSMutableArray *values = [NSMutableArray arrayWithCapacity:_springNumOfFrames]; 121 | for (NSInteger i = 0; i < _springNumOfFrames; i++) { 122 | [values addObject:@(0.0)]; 123 | } 124 | //差值 125 | CGFloat diff = [toValue floatValue] - [fromValue floatValue]; 126 | 127 | for (NSInteger frame = 0; frame<_springNumOfFrames; frame++) { 128 | 129 | CGFloat x = (CGFloat)frame / (CGFloat)_springNumOfFrames; 130 | CGFloat value = [toValue floatValue] - diff * (pow(M_E, -damping * x) * cos(velocity * x)); // y = 1-e^{-5x} * cos(30x) 131 | 132 | values[frame] = @(value); 133 | } 134 | 135 | return values; 136 | } 137 | 138 | //===================== 139 | // 动画结束 140 | //===================== 141 | - (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag { 142 | if (flag) { 143 | if (anim == [_layer animationForKey:ANIM_STARTBASE]) { 144 | [_layer removeAllAnimations]; 145 | _layer.isAnimating = YES; 146 | _llSildeMenu.ll_isOpen = YES; 147 | CAKeyframeAnimation *animSpring = [self createSpringAnima:@"distance" duration:2.f usingSpringWithDamping:0.5 initialSpringVelocity:3 fromValue:@(0) toValue:@(_layer.bgWidth)]; 148 | [_layer addAnimation:animSpring forKey:ANIM_STARTSPRING]; 149 | CAKeyframeAnimation *animCircle = [self createBaseAnima:@"radius" duration:.8f fromValue:@(0) toValue:@(sqrt(_circleLayer.frame.size.width * _circleLayer.frame.size.width + _circleLayer.frame.size.height * _circleLayer.frame.size.width))]; 150 | [_circleLayer addAnimation:animCircle forKey:ANIM_STARTCIRCLE]; 151 | return; 152 | } 153 | if (anim == [_layer animationForKey:ANIM_STARTSPRING]) { 154 | _layer.distance = _layer.bgWidth; 155 | [_layer removeAllAnimations]; 156 | _llSildeMenu.ll_isOpen = YES; 157 | _isAnimating = NO; 158 | return; 159 | } 160 | if (anim == [_layer animationForKey:ANIM_ENDBASE]) { 161 | [_layer removeAllAnimations]; 162 | _layer.distance = _layer.bgWidth; 163 | _layer.isAnimating = NO; 164 | CAKeyframeAnimation *animSpring = [self createSpringAnima:@"distance" duration:2.f usingSpringWithDamping:0.5 initialSpringVelocity:3 fromValue:@(_layer.bgWidth) toValue:@(0)]; 165 | [_layer addAnimation:animSpring forKey:@"endLLSlideSpringAnimate"]; 166 | return; 167 | } 168 | if (anim == [_layer animationForKey:ANIM_ENDSPRING]) { 169 | _layer.distance = 0; 170 | _isAnimating = NO; 171 | [_layer removeAllAnimations]; 172 | _llSildeMenu.hidden = YES; 173 | _llSildeMenu.ll_isOpen = NO; 174 | return; 175 | } 176 | if (anim == [_circleLayer animationForKey:ANIM_ENDCIRCLE]) { 177 | _circleLayer.radius = 0; 178 | [_circleLayer removeAllAnimations]; 179 | return; 180 | } 181 | if (anim == [_circleLayer animationForKey:ANIM_STARTCIRCLE]) { 182 | _circleLayer.radius = sqrt(_circleLayer.frame.size.width * _circleLayer.frame.size.width + _circleLayer.frame.size.height * _circleLayer.frame.size.width); 183 | [_circleLayer removeAllAnimations]; 184 | return; 185 | } 186 | } else { 187 | // 解决由于页面跳转导致动画属性未关闭 188 | [_layer removeAllAnimations]; 189 | _llSildeMenu.ll_isOpen = !_llSildeMenu.ll_isOpen; 190 | _isAnimating = NO; 191 | } 192 | } 193 | 194 | 195 | @end 196 | -------------------------------------------------------------------------------- /Demo/LLSlideMenuDemo/LLSlideMenuDemo/LLSlideMenu/LLCircleLayer.h: -------------------------------------------------------------------------------- 1 | // 2 | // LLCircleLayer.h 3 | // LLSlideMenuDemo 4 | // 5 | // Created by admin on 15/11/24. 6 | // Copyright © 2015年 LiLei. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | 12 | @interface LLCircleLayer : CALayer 13 | 14 | // radius 15 | @property (nonatomic, assign) CGFloat radius; 16 | 17 | // center 18 | @property (nonatomic, assign) CGPoint center; 19 | 20 | @end 21 | -------------------------------------------------------------------------------- /Demo/LLSlideMenuDemo/LLSlideMenuDemo/LLSlideMenu/LLCircleLayer.m: -------------------------------------------------------------------------------- 1 | // 2 | // LLCircleLayer.m 3 | // LLSlideMenuDemo 4 | // 5 | // Created by admin on 15/11/24. 6 | // Copyright © 2015年 LiLei. All rights reserved. 7 | // 8 | 9 | #import "LLCircleLayer.h" 10 | 11 | @implementation LLCircleLayer 12 | 13 | //===================== 14 | // 初始化 15 | //===================== 16 | - (instancetype)init { 17 | if (self = [super init]) { 18 | // 默认属性 19 | _radius = 0; 20 | _center = CGPointMake(0, 0); 21 | } 22 | return self; 23 | } 24 | 25 | - (instancetype)initWithLayer:(LLCircleLayer *)layer { 26 | self = [super initWithLayer:layer]; 27 | if (self) { 28 | self.radius = layer.radius; 29 | self.center = layer.center; 30 | } 31 | return self; 32 | } 33 | 34 | //===================== 35 | // Draw 36 | //===================== 37 | - (void)drawInContext:(CGContextRef)ctx { 38 | UIBezierPath *bezier = [UIBezierPath bezierPathWithArcCenter:_center radius:_radius startAngle:0 endAngle:2 * M_PI clockwise:YES]; 39 | [bezier closePath]; 40 | CGContextAddPath(ctx, bezier.CGPath); 41 | CGContextSetFillColorWithColor(ctx, [UIColor whiteColor].CGColor); 42 | CGContextFillPath(ctx); 43 | } 44 | 45 | 46 | //===================== 47 | // 关键帧动画key 48 | //===================== 49 | +(BOOL)needsDisplayForKey:(NSString *)key{ 50 | if ([key isEqual:@"radius"]) { 51 | return YES; 52 | } 53 | return [super needsDisplayForKey:key]; 54 | } 55 | 56 | @end 57 | -------------------------------------------------------------------------------- /Demo/LLSlideMenuDemo/LLSlideMenuDemo/LLSlideMenu/LLDisplayLayer.h: -------------------------------------------------------------------------------- 1 | // 2 | // LLDisplayLayer.h 3 | // LLSideMenuDemo 4 | // 5 | // Created by admin on 15/11/20. 6 | // Copyright © 2015年 LiLei. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | 12 | @interface LLDisplayLayer : CALayer 13 | 14 | // layer的width 15 | @property (nonatomic, assign) CGFloat bgWidth; 16 | 17 | // 背景色 18 | @property (nonatomic, strong) UIColor *menuBgColor; 19 | 20 | // 间距 21 | @property (nonatomic, assign) CGFloat distance; 22 | 23 | // 是否正在动画 24 | @property (nonatomic, assign) BOOL isAnimating; 25 | 26 | // 贝塞尔曲线 27 | @property (nonatomic, strong) UIBezierPath *bezier; 28 | 29 | @end 30 | -------------------------------------------------------------------------------- /Demo/LLSlideMenuDemo/LLSlideMenuDemo/LLSlideMenu/LLDisplayLayer.m: -------------------------------------------------------------------------------- 1 | // 2 | // LLDisplayLayer.m 3 | // LLSideMenuDemo 4 | // 5 | // Created by admin on 15/11/20. 6 | // Copyright © 2015年 LiLei. All rights reserved. 7 | // 8 | 9 | #import "LLDisplayLayer.h" 10 | 11 | #define LL_SCREEN_HEIGHT [UIScreen mainScreen].bounds.size.height 12 | 13 | @implementation LLDisplayLayer 14 | 15 | //===================== 16 | // 初始化 17 | //===================== 18 | - (instancetype)init { 19 | if (self = [super init]) { 20 | // 默认属性 21 | _menuBgColor = [UIColor whiteColor]; 22 | _isAnimating = NO; 23 | } 24 | return self; 25 | } 26 | 27 | - (instancetype)initWithLayer:(LLDisplayLayer *)layer { 28 | self = [super initWithLayer:layer]; 29 | if (self) { 30 | self.bgWidth = layer.bgWidth; 31 | self.menuBgColor = layer.menuBgColor; 32 | self.distance = layer.distance; 33 | self.isAnimating = layer.isAnimating; 34 | self.bezier = layer.bezier; 35 | } 36 | return self; 37 | } 38 | 39 | 40 | //===================== 41 | // 关键帧动画key 42 | //===================== 43 | +(BOOL)needsDisplayForKey:(NSString *)key{ 44 | if ([key isEqual:@"distance"]) { 45 | return YES; 46 | } 47 | return [super needsDisplayForKey:key]; 48 | } 49 | 50 | 51 | //===================== 52 | // Draw 53 | //===================== 54 | - (void)drawInContext:(CGContextRef)ctx { 55 | _bezier = [UIBezierPath bezierPath]; 56 | CGPoint pointStart,pointA,pointB,pointEnd; 57 | 58 | // 区分曲线路径 59 | if (_isAnimating) { 60 | pointStart = CGPointMake(_distance, 0); 61 | pointA = CGPointMake(_bgWidth, LL_SCREEN_HEIGHT/4); 62 | pointB = CGPointMake(_bgWidth, LL_SCREEN_HEIGHT * 3/4); 63 | pointEnd = CGPointMake(_distance, LL_SCREEN_HEIGHT); 64 | [_bezier moveToPoint:CGPointMake(0, 0)]; 65 | [_bezier addLineToPoint:pointStart]; 66 | [_bezier addCurveToPoint:pointEnd controlPoint1:pointA controlPoint2:pointB]; 67 | [_bezier addLineToPoint:CGPointMake(0, LL_SCREEN_HEIGHT)]; 68 | } else { 69 | pointStart = CGPointMake(0, 0); 70 | pointA = CGPointMake(_distance, LL_SCREEN_HEIGHT/4); 71 | pointB = CGPointMake(_distance, LL_SCREEN_HEIGHT * 3/4); 72 | pointEnd = CGPointMake(0, LL_SCREEN_HEIGHT); 73 | [_bezier moveToPoint:pointStart]; 74 | [_bezier addCurveToPoint:pointEnd controlPoint1:pointA controlPoint2:pointB]; 75 | } 76 | 77 | 78 | [_bezier closePath]; 79 | CGContextAddPath(ctx, _bezier.CGPath); 80 | CGContextSetFillColorWithColor(ctx, _menuBgColor.CGColor); 81 | CGContextFillPath(ctx); 82 | } 83 | 84 | 85 | @end 86 | -------------------------------------------------------------------------------- /Demo/LLSlideMenuDemo/LLSlideMenuDemo/LLSlideMenu/LLSlideMenu.h: -------------------------------------------------------------------------------- 1 | // 2 | // LLSideMune.h 3 | // LLSideMenuDemo 4 | // 5 | // Created by admin on 15/11/20. 6 | // Copyright © 2015年 LiLei. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "LLAnimationManager.h" 11 | 12 | 13 | @interface LLSlideMenu : UIView 14 | 15 | // MenuView width 16 | @property (nonatomic, assign) CGFloat ll_menuWidth; 17 | // slide distance 18 | @property (nonatomic, assign) CGFloat ll_distance; 19 | // background color 20 | @property (nonatomic, strong) UIColor *ll_menuBackgroundColor; 21 | // background image 22 | @property (nonatomic, strong) UIImage *ll_menuBackgroundImage; 23 | 24 | // animate manager 25 | @property (nonatomic, strong) LLAnimationManager *ll_animManager; 26 | 27 | // 菜单是否打开 28 | @property (nonatomic, assign) BOOL ll_isOpen; 29 | 30 | // 是否正在动画 31 | @property (nonatomic, assign) BOOL ll_isAnimating; 32 | 33 | // 设置弹性 spring 34 | @property (nonatomic, assign) CGFloat ll_springDamping; // 阻力 35 | @property (nonatomic, assign) CGFloat ll_springVelocity; // 速度 36 | @property (nonatomic, assign) NSInteger ll_springFramesNum; // 关键帧个数 37 | 38 | 39 | // close menu 40 | - (void)ll_closeSlideMenu; 41 | 42 | // open menu 43 | - (void)ll_openSlideMenu; 44 | 45 | 46 | @end 47 | -------------------------------------------------------------------------------- /Demo/LLSlideMenuDemo/LLSlideMenuDemo/LLSlideMenu/LLSlideMenu.m: -------------------------------------------------------------------------------- 1 | // 2 | // LLSideMune.m 3 | // LLSideMenuDemo 4 | // 5 | // Created by admin on 15/11/20. 6 | // Copyright © 2015年 LiLei. All rights reserved. 7 | // 8 | 9 | #import "LLSlideMenu.h" 10 | #import "UIView+SetRect.h" 11 | #import "LLDisplayLayer.h" 12 | #import "LLCircleLayer.h" 13 | 14 | #define LL_SCREEN_WIDTH [UIScreen mainScreen].bounds.size.width 15 | #define LL_SCREEN_HEIGHT [UIScreen mainScreen].bounds.size.height 16 | 17 | @interface LLSlideMenu () 18 | 19 | // 菜单显示的View 20 | @property (nonatomic, strong) UIView *displayView; 21 | 22 | // 点击关闭菜单的Button 23 | @property (nonatomic, strong) UIButton *closeMenuButton; 24 | 25 | // displayLayer 26 | @property (nonatomic, strong) LLDisplayLayer *displayLayer; 27 | 28 | // circleLayer 29 | @property (nonatomic, strong) LLCircleLayer *circleLayer; 30 | 31 | @property (nonatomic, assign) BOOL isOverLoad; 32 | 33 | @end 34 | 35 | @implementation LLSlideMenu 36 | 37 | 38 | //===================== 39 | // 初始化 40 | //===================== 41 | - (instancetype)init { 42 | self = [super initWithFrame:CGRectMake(0, 0, LL_SCREEN_WIDTH, LL_SCREEN_HEIGHT)]; 43 | if (self) { 44 | [self initSetUpView]; 45 | } 46 | return self; 47 | } 48 | 49 | 50 | //===================== 51 | // 初始化界面 52 | //===================== 53 | - (void)initSetUpView { 54 | _isOverLoad = NO; 55 | [self.layer addSublayer:self.displayLayer]; 56 | _displayView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 0, LL_SCREEN_HEIGHT)]; 57 | // _displayView.backgroundColor = [UIColor greenColor]; 58 | [self addSubview:self.displayView]; 59 | 60 | // 隐藏 61 | self.hidden = YES; 62 | // 动画管理 63 | _ll_animManager = [LLAnimationManager new]; 64 | _ll_animManager.isAnimating = NO; 65 | _ll_animManager.llSildeMenu = self; 66 | _ll_animManager.circleLayer = _circleLayer; 67 | _ll_isOpen = NO; 68 | 69 | // 设置默认弹力大小和关键帧数量 70 | _ll_animManager.springDamping = 20.f; 71 | _ll_animManager.springVelocity = 15.f; 72 | _ll_animManager.springNumOfFrames = 60; 73 | } 74 | 75 | 76 | 77 | //===================== 78 | // 设置View宽度 79 | //===================== 80 | - (void)setLl_menuWidth:(CGFloat)ll_menuWidth { 81 | _ll_menuWidth = ll_menuWidth; 82 | _displayView.width = ll_menuWidth; 83 | self.closeMenuButton.x = ll_menuWidth; 84 | self.closeMenuButton.width = LL_SCREEN_WIDTH - ll_menuWidth; 85 | self.displayLayer.bgWidth = ll_menuWidth; 86 | 87 | _ll_animManager.layer = _displayLayer; 88 | _ll_animManager.circleLayer = self.circleLayer; 89 | } 90 | 91 | //===================== 92 | // 设置滑动偏移距离 93 | //===================== 94 | - (void)setLl_distance:(CGFloat)ll_distance { 95 | _ll_distance = ll_distance; 96 | self.hidden = NO; 97 | self.displayLayer.distance = ll_distance; 98 | _displayLayer.isAnimating = NO; 99 | _ll_animManager.isAnimating = NO; 100 | [_displayLayer setNeedsDisplay]; 101 | } 102 | 103 | //================================= 104 | // 添加menu背景色 105 | //================================= 106 | - (void)setLl_menuBackgroundColor:(UIColor *)ll_menuBackgroundColor { 107 | _ll_menuBackgroundColor = ll_menuBackgroundColor; 108 | _displayLayer.menuBgColor = ll_menuBackgroundColor; 109 | } 110 | 111 | //================================= 112 | // 添加menu背景图片 113 | //================================= 114 | - (void)setLl_menuBackgroundImage:(UIImage *)ll_menuBackgroundImage { 115 | _ll_menuBackgroundImage = ll_menuBackgroundImage; 116 | _displayLayer.menuBgColor = [UIColor colorWithPatternImage:ll_menuBackgroundImage]; 117 | } 118 | 119 | //================================= 120 | // 设置弹力程度和关键帧数量 121 | //================================= 122 | - (void)setLl_springDamping:(CGFloat)ll_springDamping { 123 | _ll_springDamping = ll_springDamping; 124 | _ll_animManager.springDamping = ll_springDamping; 125 | } 126 | 127 | - (void)setLl_springVelocity:(CGFloat)ll_springVelocity { 128 | _ll_springVelocity = ll_springVelocity; 129 | _ll_animManager.springVelocity = ll_springVelocity; 130 | } 131 | 132 | - (void)setLl_springFramesNum:(NSInteger)ll_springFramesNum { 133 | _ll_springFramesNum = ll_springFramesNum; 134 | _ll_animManager.springNumOfFrames = ll_springFramesNum; 135 | } 136 | 137 | 138 | //===================== 139 | // 关闭菜单 140 | //===================== 141 | - (void)ll_closeSlideMenu { 142 | if (_ll_animManager.isAnimating) { 143 | return; 144 | } 145 | _displayLayer.isAnimating = YES; 146 | _closeMenuButton.hidden = YES; 147 | _ll_animManager.isAnimating = YES; 148 | [_ll_animManager endAnimate]; 149 | } 150 | 151 | //===================== 152 | // 打开菜单 153 | //===================== 154 | - (void)ll_openSlideMenu { 155 | if (_ll_animManager.isAnimating) { 156 | return; 157 | } 158 | self.hidden = NO; 159 | _displayLayer.isAnimating = NO; 160 | _closeMenuButton.hidden = NO; 161 | _ll_animManager.isAnimating = YES; 162 | [_ll_animManager startAnimate]; 163 | } 164 | 165 | //================================= 166 | // 添加View直接添加到displayView上 167 | //================================= 168 | - (void)addSubview:(UIView *)view { 169 | if (view != _displayView && view != _closeMenuButton) { 170 | [_displayView addSubview:view]; 171 | } else { 172 | [super addSubview:view]; 173 | } 174 | } 175 | 176 | - (BOOL)ll_isAnimating { 177 | return _ll_animManager.isAnimating; 178 | } 179 | 180 | 181 | #pragma 懒加载 182 | - (UIButton *)closeMenuButton { 183 | if (!_closeMenuButton) { 184 | _closeMenuButton = [[UIButton alloc] initWithFrame:CGRectMake(0, 0, LL_SCREEN_WIDTH, LL_SCREEN_HEIGHT)]; 185 | _closeMenuButton.hidden = YES; 186 | [_closeMenuButton addTarget:self action:@selector(ll_closeSlideMenu) forControlEvents:UIControlEventTouchUpInside]; 187 | [self addSubview:_closeMenuButton]; 188 | } 189 | return _closeMenuButton; 190 | } 191 | 192 | - (LLDisplayLayer *)displayLayer { 193 | if (!_displayLayer) { 194 | _displayLayer = [[LLDisplayLayer alloc] init]; 195 | [_displayLayer setFrame:CGRectMake(0, 0, LL_SCREEN_WIDTH, LL_SCREEN_HEIGHT)]; 196 | // layer添加阴影 197 | _displayLayer.shadowOffset = CGSizeMake(0, 3); 198 | _displayLayer.shadowRadius = 5.0; 199 | _displayLayer.shadowColor = [UIColor blackColor].CGColor; 200 | _displayLayer.shadowOpacity = 0.6; 201 | } 202 | return _displayLayer; 203 | } 204 | 205 | - (LLCircleLayer *)circleLayer { 206 | if (!_circleLayer) { 207 | _circleLayer = [[LLCircleLayer alloc] init]; 208 | [_circleLayer setFrame:CGRectMake(0, 0, _displayView.width, _displayView.height)]; 209 | _circleLayer.radius = 0; 210 | _circleLayer.center = _displayView.center; 211 | _displayView.layer.mask = self.circleLayer; 212 | } 213 | return _circleLayer; 214 | } 215 | 216 | @end 217 | -------------------------------------------------------------------------------- /Demo/LLSlideMenuDemo/LLSlideMenuDemo/LLSlideMenu/UIView+SetRect.h: -------------------------------------------------------------------------------- 1 | // 2 | // UIView+SetRect.h 3 | // TestPch 4 | // 5 | // Created by admin on 15/11/14. 6 | // Copyright (c) 2015年 LiLei. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | #define Width [UIScreen mainScreen].bounds.size.width 12 | #define Height [UIScreen mainScreen].bounds.size.height 13 | 14 | @interface UIView (SetRect) 15 | 16 | // Frame 17 | @property (nonatomic) CGPoint viewOrigin; 18 | @property (nonatomic) CGSize viewSize; 19 | 20 | // Frame Origin 21 | @property (nonatomic) CGFloat x; 22 | @property (nonatomic) CGFloat y; 23 | 24 | // Frame Size 25 | @property (nonatomic) CGFloat width; 26 | @property (nonatomic) CGFloat height; 27 | 28 | // Frame Borders 29 | @property (nonatomic) CGFloat top; 30 | @property (nonatomic) CGFloat left; 31 | @property (nonatomic) CGFloat bottom; 32 | @property (nonatomic) CGFloat right; 33 | 34 | // Center Point 35 | #if !IS_IOS_DEVICE 36 | @property (nonatomic) CGPoint center; 37 | #endif 38 | @property (nonatomic) CGFloat centerX; 39 | @property (nonatomic) CGFloat centerY; 40 | 41 | // Middle Point 42 | @property (nonatomic, readonly) CGPoint middlePoint; 43 | @property (nonatomic, readonly) CGFloat middleX; 44 | @property (nonatomic, readonly) CGFloat middleY; 45 | @end 46 | -------------------------------------------------------------------------------- /Demo/LLSlideMenuDemo/LLSlideMenuDemo/LLSlideMenu/UIView+SetRect.m: -------------------------------------------------------------------------------- 1 | // 2 | // UIView+SetRect.m 3 | // TestPch 4 | // 5 | // Created by admin on 15/11/14. 6 | // Copyright (c) 2015年 LiLei. All rights reserved. 7 | // 8 | 9 | #import "UIView+SetRect.h" 10 | 11 | @implementation UIView (SetRect) 12 | 13 | #pragma mark Frame 14 | 15 | - (CGPoint)viewOrigin 16 | { 17 | return self.frame.origin; 18 | } 19 | 20 | - (void)setViewOrigin:(CGPoint)newOrigin 21 | { 22 | CGRect newFrame = self.frame; 23 | newFrame.origin = newOrigin; 24 | self.frame = newFrame; 25 | } 26 | 27 | - (CGSize)viewSize 28 | { 29 | return self.frame.size; 30 | } 31 | 32 | - (void)setViewSize:(CGSize)newSize 33 | { 34 | CGRect newFrame = self.frame; 35 | newFrame.size = newSize; 36 | self.frame = newFrame; 37 | } 38 | 39 | 40 | #pragma mark Frame Origin 41 | 42 | - (CGFloat)x 43 | { 44 | return self.frame.origin.x; 45 | } 46 | 47 | - (void)setX:(CGFloat)newX 48 | { 49 | CGRect newFrame = self.frame; 50 | newFrame.origin.x = newX; 51 | self.frame = newFrame; 52 | } 53 | 54 | - (CGFloat)y 55 | { 56 | return self.frame.origin.y; 57 | } 58 | 59 | - (void)setY:(CGFloat)newY 60 | { 61 | CGRect newFrame = self.frame; 62 | newFrame.origin.y = newY; 63 | self.frame = newFrame; 64 | } 65 | 66 | 67 | #pragma mark Frame Size 68 | 69 | - (CGFloat)height 70 | { 71 | return self.frame.size.height; 72 | } 73 | 74 | - (void)setHeight:(CGFloat)newHeight 75 | { 76 | CGRect newFrame = self.frame; 77 | newFrame.size.height = newHeight; 78 | self.frame = newFrame; 79 | } 80 | 81 | - (CGFloat)width 82 | { 83 | return self.frame.size.width; 84 | } 85 | 86 | - (void)setWidth:(CGFloat)newWidth 87 | { 88 | CGRect newFrame = self.frame; 89 | newFrame.size.width = newWidth; 90 | self.frame = newFrame; 91 | } 92 | 93 | 94 | #pragma mark Frame Borders 95 | 96 | - (CGFloat)left 97 | { 98 | return self.x; 99 | } 100 | 101 | - (void)setLeft:(CGFloat)left 102 | { 103 | self.x = left; 104 | } 105 | 106 | - (CGFloat)right 107 | { 108 | return self.frame.origin.x + self.frame.size.width; 109 | } 110 | 111 | - (void)setRight:(CGFloat)right 112 | { 113 | self.x = right - self.width; 114 | } 115 | 116 | - (CGFloat)top 117 | { 118 | return self.y; 119 | } 120 | 121 | - (void)setTop:(CGFloat)top 122 | { 123 | self.y = top; 124 | } 125 | 126 | - (CGFloat)bottom 127 | { 128 | return self.frame.origin.y + self.frame.size.height; 129 | } 130 | 131 | - (void)setBottom:(CGFloat)bottom 132 | { 133 | self.y = bottom - self.height; 134 | } 135 | 136 | 137 | #pragma mark Center Point 138 | 139 | #if !IS_IOS_DEVICE 140 | - (CGPoint)center 141 | { 142 | return CGPointMake(self.left + self.middleX, self.top + self.middleY); 143 | } 144 | 145 | - (void)setCenter:(CGPoint)newCenter 146 | { 147 | self.left = newCenter.x - self.middleX; 148 | self.top = newCenter.y - self.middleY; 149 | } 150 | #endif 151 | 152 | - (CGFloat)centerX 153 | { 154 | return self.center.x; 155 | } 156 | 157 | - (void)setCenterX:(CGFloat)newCenterX 158 | { 159 | self.center = CGPointMake(newCenterX, self.center.y); 160 | } 161 | 162 | - (CGFloat)centerY 163 | { 164 | return self.center.y; 165 | } 166 | 167 | - (void)setCenterY:(CGFloat)newCenterY 168 | { 169 | self.center = CGPointMake(self.center.x, newCenterY); 170 | } 171 | 172 | 173 | #pragma mark Middle Point 174 | 175 | - (CGPoint)middlePoint 176 | { 177 | return CGPointMake(self.middleX, self.middleY); 178 | } 179 | 180 | - (CGFloat)middleX 181 | { 182 | return self.width / 2; 183 | } 184 | 185 | - (CGFloat)middleY 186 | { 187 | return self.height / 2; 188 | } 189 | 190 | @end 191 | -------------------------------------------------------------------------------- /Demo/LLSlideMenuDemo/LLSlideMenuDemo/SecondController.h: -------------------------------------------------------------------------------- 1 | // 2 | // SecondController.h 3 | // LLSlideMenuDemo 4 | // 5 | // Created by lilei on 2017/2/26. 6 | // Copyright © 2017年 LiLei. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface SecondController : UIViewController 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /Demo/LLSlideMenuDemo/LLSlideMenuDemo/SecondController.m: -------------------------------------------------------------------------------- 1 | // 2 | // SecondController.m 3 | // LLSlideMenuDemo 4 | // 5 | // Created by lilei on 2017/2/26. 6 | // Copyright © 2017年 LiLei. All rights reserved. 7 | // 8 | 9 | #import "SecondController.h" 10 | 11 | @interface SecondController () 12 | 13 | @end 14 | 15 | @implementation SecondController 16 | 17 | - (void)viewDidLoad { 18 | [super viewDidLoad]; 19 | } 20 | 21 | 22 | - (IBAction)dismissAction:(id)sender { 23 | [self dismissViewControllerAnimated:YES completion:nil]; 24 | } 25 | 26 | 27 | @end 28 | -------------------------------------------------------------------------------- /Demo/LLSlideMenuDemo/LLSlideMenuDemo/ViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.h 3 | // LLSlideMenuDemo 4 | // 5 | // Created by admin on 15/11/24. 6 | // Copyright © 2015年 LiLei. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface ViewController : UIViewController 12 | 13 | 14 | @end 15 | 16 | -------------------------------------------------------------------------------- /Demo/LLSlideMenuDemo/LLSlideMenuDemo/ViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.m 3 | // LLSlideMenuDemo 4 | // 5 | // Created by admin on 15/11/24. 6 | // Copyright © 2015年 LiLei. All rights reserved. 7 | // 8 | 9 | #import "ViewController.h" 10 | #import "LLSlideMenu.h" 11 | 12 | @interface ViewController () 13 | 14 | @property (nonatomic, strong) LLSlideMenu *slideMenu; 15 | 16 | // 全屏侧滑手势 17 | @property (nonatomic, strong) UIPanGestureRecognizer *leftSwipe; 18 | @property (nonatomic, strong) UIPercentDrivenInteractiveTransition *percent; 19 | 20 | @end 21 | 22 | @implementation ViewController 23 | 24 | - (void)viewDidLoad { 25 | [super viewDidLoad]; 26 | 27 | // 初始化 28 | _slideMenu = [[LLSlideMenu alloc] init]; 29 | [self.view addSubview:_slideMenu]; 30 | // 设置菜单宽度 31 | _slideMenu.ll_menuWidth = 200.f; 32 | // 设置菜单背景色 33 | _slideMenu.ll_menuBackgroundColor = [UIColor redColor]; 34 | // 设置弹力和速度, 默认的是20,15,60 35 | _slideMenu.ll_springDamping = 20; // 阻力 36 | _slideMenu.ll_springVelocity = 15; // 速度 37 | _slideMenu.ll_springFramesNum = 60; // 关键帧数量 38 | 39 | 40 | 41 | //=================== 42 | // Menu添加子View 43 | //=================== 44 | UIImageView *img = [[UIImageView alloc] initWithFrame:CGRectMake(50, 40, 80, 80)]; 45 | [img setImage:[UIImage imageNamed:@"Head"]]; 46 | 47 | // 添加触摸事件 48 | img.userInteractionEnabled = YES; 49 | [img addGestureRecognizer:[[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(imageViewTapAction)]]; 50 | [_slideMenu addSubview:img]; 51 | 52 | 53 | 54 | UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(30, 140, 150, 400)]; 55 | label.text = @"这是第一行菜单\n\n这是第二行菜单\n\n这是第三行菜单\n\n这是第四行菜单\n\n这是第五行菜单\n\n这是第六行菜单\n\n这是第七行菜单\n\n这是第八行菜单\n\n这是第九行菜单\n\n这是第十行菜单"; 56 | [label setTextColor:[UIColor whiteColor]]; 57 | [label setNumberOfLines:0]; 58 | [_slideMenu addSubview:label]; 59 | 60 | 61 | //=================== 62 | // 添加全屏侧滑手势 63 | //=================== 64 | self.leftSwipe = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(swipeLeftHandle:)]; 65 | self.leftSwipe.maximumNumberOfTouches = 1; 66 | [self.view addGestureRecognizer:_leftSwipe]; 67 | } 68 | 69 | 70 | 71 | //=================== 72 | // 全屏侧滑手势监听 73 | //=================== 74 | - (void)swipeLeftHandle:(UIScreenEdgePanGestureRecognizer *)recognizer { 75 | // 如果菜单已打开则禁止滑动 76 | if (_slideMenu.ll_isOpen || _slideMenu.ll_isAnimating) { 77 | return; 78 | } 79 | // 计算手指滑的物理距离(滑了多远,与起始位置无关) 80 | CGFloat progress = [recognizer translationInView:self.view].x / (self.view.bounds.size.width * 1.0); 81 | // 把这个百分比限制在 0~1 之间 82 | progress = MIN(1.0, MAX(0.0, progress)); 83 | 84 | // 当手势刚刚开始,我们创建一个 UIPercentDrivenInteractiveTransition 对象 85 | if (recognizer.state == UIGestureRecognizerStateBegan) { 86 | self.percent = [[UIPercentDrivenInteractiveTransition alloc] init]; 87 | } else if (recognizer.state == UIGestureRecognizerStateChanged) { 88 | 89 | // 当手慢慢划入时,我们把总体手势划入的进度告诉 UIPercentDrivenInteractiveTransition 对象。 90 | [self.percent updateInteractiveTransition:progress]; 91 | _slideMenu.ll_distance = [recognizer translationInView:self.view].x; 92 | 93 | } else if (recognizer.state == UIGestureRecognizerStateCancelled || recognizer.state == UIGestureRecognizerStateEnded) { 94 | // 当手势结束,我们根据用户的手势进度来判断过渡是应该完成还是取消并相应的调用 finishInteractiveTransition 或者 cancelInteractiveTransition 方法. 95 | if (progress > 0.4) { 96 | [self.percent finishInteractiveTransition]; 97 | [_slideMenu ll_openSlideMenu]; 98 | }else{ 99 | [self.percent cancelInteractiveTransition]; 100 | [_slideMenu ll_closeSlideMenu]; 101 | } 102 | self.percent = nil; 103 | } 104 | } 105 | 106 | 107 | //=================== 108 | // 按钮监听 109 | //=================== 110 | - (IBAction)openLLSlideMenuAction:(id)sender { 111 | //=================== 112 | // 打开菜单 113 | //=================== 114 | if (_slideMenu.ll_isOpen) { 115 | [_slideMenu ll_closeSlideMenu]; 116 | } else { 117 | [_slideMenu ll_openSlideMenu]; 118 | } 119 | } 120 | 121 | 122 | //=================== 123 | // 点击图片监听事件 124 | //=================== 125 | - (void)imageViewTapAction { 126 | [self performSegueWithIdentifier:@"ToSecondController" sender:self]; 127 | } 128 | 129 | 130 | @end 131 | -------------------------------------------------------------------------------- /Demo/LLSlideMenuDemo/LLSlideMenuDemo/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // LLSlideMenuDemo 4 | // 5 | // Created by admin on 15/11/24. 6 | // Copyright © 2015年 LiLei. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "AppDelegate.h" 11 | 12 | int main(int argc, char * argv[]) { 13 | @autoreleasepool { 14 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Li Lei 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /LLSlideMenu.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |s| 2 | s.name = "LLSlideMenu" 3 | s.version = "1.0.6" 4 | s.summary = "This is a way to create a spring slide menu use LLSlideMenu." 5 | s.homepage = "https://github.com/lilei644/LLSlideMenu" 6 | s.license = { :type => "MIT", :file => "LICENSE" } 7 | s.author = { "lilei644" => "1793161563@qq.com" } 8 | s.platform = :ios, "6.0" 9 | s.source = { :git => "https://github.com/lilei644/LLSlideMenu.git", :tag => s.version.to_s } 10 | s.requires_arc = true 11 | s.source_files = "LLSlideMenu/*.{h,m}" 12 | s.framework = "UIKit","QuartzCore" 13 | end 14 | -------------------------------------------------------------------------------- /LLSlideMenu/LLAnimationManager.h: -------------------------------------------------------------------------------- 1 | // 2 | // AnimationManager.h 3 | // LLSideMenuDemo 4 | // 5 | // Created by admin on 15/11/22. 6 | // Copyright © 2015年 LiLei. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | #import "LLDisplayLayer.h" 12 | #import "LLCircleLayer.h" 13 | 14 | @class LLSlideMenu; 15 | @interface LLAnimationManager : NSObject 16 | 17 | // 是否正在动画 18 | @property (nonatomic, assign) BOOL isAnimating; 19 | 20 | @property (nonatomic, strong) LLDisplayLayer *layer; 21 | @property (nonatomic, strong) LLCircleLayer *circleLayer; 22 | 23 | @property (nonatomic, strong) LLSlideMenu *llSildeMenu; 24 | 25 | // 设置弹性 spring 26 | @property (nonatomic, assign) CGFloat springDamping; 27 | @property (nonatomic, assign) CGFloat springVelocity; 28 | @property (nonatomic, assign) NSInteger springNumOfFrames; 29 | 30 | 31 | // 开始动画 32 | - (void)startAnimate; 33 | 34 | // 结束动画 35 | - (void)endAnimate; 36 | 37 | @end 38 | -------------------------------------------------------------------------------- /LLSlideMenu/LLAnimationManager.m: -------------------------------------------------------------------------------- 1 | // 2 | // AnimationManager.m 3 | // LLSideMenuDemo 4 | // 5 | // Created by admin on 15/11/22. 6 | // Copyright © 2015年 LiLei. All rights reserved. 7 | // 8 | 9 | #import "LLAnimationManager.h" 10 | #import "LLSlideMenu.h" 11 | 12 | 13 | #define LL_SCREEN_HEIGHT [UIScreen mainScreen].bounds.size.height 14 | 15 | //===================== 16 | // anim key 17 | //===================== 18 | static NSString *ANIM_STARTBASE = @"startLLSlideBaseAnimate"; 19 | static NSString *ANIM_STARTSPRING = @"startLLSlideSpringAnimate"; 20 | static NSString *ANIM_STARTCIRCLE = @"startLLCircleAnimate"; 21 | static NSString *ANIM_ENDBASE = @"endLLSlideBaseAnimate"; 22 | static NSString *ANIM_ENDSPRING = @"endLLSlideSpringAnimate"; 23 | static NSString *ANIM_ENDCIRCLE = @"endLLCircleAnimate"; 24 | 25 | 26 | @class LLSlideMune; 27 | @implementation LLAnimationManager 28 | 29 | 30 | //===================== 31 | // 开始动画 32 | //===================== 33 | - (void)startAnimate{ 34 | // 判断是否滑动超出宽度 35 | if (_layer.distance >= _layer.bgWidth) { 36 | _layer.isAnimating = YES; 37 | CAKeyframeAnimation *animSpring = [self createSpringAnima:@"distance" duration:2.f usingSpringWithDamping:0.5 initialSpringVelocity:3 fromValue:@(0) toValue:@(_layer.bgWidth)]; 38 | [_layer addAnimation:animSpring forKey:ANIM_STARTSPRING]; 39 | CAKeyframeAnimation *animCircle = [self createBaseAnima:@"radius" duration:.8f fromValue:@(0) toValue:@(sqrt(_circleLayer.frame.size.width * _circleLayer.frame.size.width + _circleLayer.frame.size.height * _circleLayer.frame.size.width))]; 40 | [_circleLayer addAnimation:animCircle forKey:ANIM_STARTCIRCLE]; 41 | } else { 42 | CAKeyframeAnimation *animaBase = [self createBaseAnima:@"distance" duration:.2f fromValue:@(_layer.distance) toValue:@(_layer.bgWidth)]; 43 | [_layer addAnimation:animaBase forKey:ANIM_STARTBASE]; 44 | _layer.distance = _layer.bgWidth; 45 | } 46 | 47 | } 48 | 49 | 50 | //===================== 51 | // 结束动画 52 | //===================== 53 | - (void)endAnimate{ 54 | //==================================== 55 | // 判断如否distance则先变成圆弧在缩小圆弧 56 | //==================================== 57 | if (_layer.distance >= _layer.bgWidth) { 58 | // 关闭背景layer 59 | CAKeyframeAnimation *animBase = [self createBaseAnima:@"distance" duration:.3f fromValue:@(_layer.bgWidth) toValue:@(0)]; 60 | [_layer addAnimation:animBase forKey:ANIM_ENDBASE]; 61 | // 关闭contentView 62 | CAKeyframeAnimation *animCircle = [self createBaseAnima:@"radius" duration:.3f fromValue:@(_circleLayer.frame.size.width) toValue:@(0)]; 63 | [_circleLayer addAnimation:animCircle forKey:ANIM_ENDCIRCLE]; 64 | }else { 65 | _layer.isAnimating = NO; 66 | CAKeyframeAnimation *animSpring = [self createSpringAnima:@"distance" duration:1.5f usingSpringWithDamping:0.5 initialSpringVelocity:3 fromValue:@(_layer.distance) toValue:@(0)]; 67 | [_layer addAnimation:animSpring forKey:ANIM_ENDSPRING]; 68 | } 69 | 70 | } 71 | 72 | //===================== 73 | // 创建普通动画 74 | //===================== 75 | -(CAKeyframeAnimation *)createBaseAnima:(NSString *)keypath 76 | duration:(CFTimeInterval)duration 77 | fromValue:(id)fromValue 78 | toValue:(id)toValue{ 79 | NSMutableArray *values = [NSMutableArray arrayWithObjects:fromValue,toValue,nil]; 80 | 81 | CAKeyframeAnimation *anim = [CAKeyframeAnimation animationWithKeyPath:keypath]; 82 | anim.values = values; 83 | anim.duration = duration; 84 | anim.fillMode = kCAFillModeForwards; 85 | anim.removedOnCompletion = NO; 86 | anim.delegate = self; 87 | return anim; 88 | } 89 | 90 | //===================== 91 | // 创建弹性动画 92 | //===================== 93 | -(CAKeyframeAnimation *)createSpringAnima:(NSString *)keypath 94 | duration:(CFTimeInterval)duration 95 | usingSpringWithDamping:(CGFloat)damping 96 | initialSpringVelocity:(CGFloat)velocity 97 | fromValue:(id)fromValue 98 | toValue:(id)toValue{ 99 | 100 | // CGFloat dampingFactor = 20.0; 101 | // CGFloat velocityFactor = 15.0; 102 | NSMutableArray *values = [self springAnimationValues:fromValue toValue:toValue usingSpringWithDamping:damping * _springDamping initialSpringVelocity:velocity * _springVelocity duration:duration]; 103 | 104 | CAKeyframeAnimation *anim = [CAKeyframeAnimation animationWithKeyPath:keypath]; 105 | anim.values = values; 106 | anim.duration = duration; 107 | anim.fillMode = kCAFillModeForwards; 108 | anim.removedOnCompletion = NO; 109 | anim.delegate = self; 110 | return anim; 111 | } 112 | 113 | 114 | //===================== 115 | // 获取关键帧的属性值 116 | //===================== 117 | -(NSMutableArray *) springAnimationValues:(id)fromValue toValue:(id)toValue usingSpringWithDamping:(CGFloat)damping initialSpringVelocity:(CGFloat)velocity duration:(CGFloat)duration{ 118 | 119 | //关键帧 120 | NSMutableArray *values = [NSMutableArray arrayWithCapacity:_springNumOfFrames]; 121 | for (NSInteger i = 0; i < _springNumOfFrames; i++) { 122 | [values addObject:@(0.0)]; 123 | } 124 | //差值 125 | CGFloat diff = [toValue floatValue] - [fromValue floatValue]; 126 | 127 | for (NSInteger frame = 0; frame<_springNumOfFrames; frame++) { 128 | 129 | CGFloat x = (CGFloat)frame / (CGFloat)_springNumOfFrames; 130 | CGFloat value = [toValue floatValue] - diff * (pow(M_E, -damping * x) * cos(velocity * x)); // y = 1-e^{-5x} * cos(30x) 131 | 132 | values[frame] = @(value); 133 | } 134 | 135 | return values; 136 | } 137 | 138 | //===================== 139 | // 动画结束 140 | //===================== 141 | - (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag { 142 | if (flag) { 143 | if (anim == [_layer animationForKey:ANIM_STARTBASE]) { 144 | [_layer removeAllAnimations]; 145 | _layer.isAnimating = YES; 146 | _llSildeMenu.ll_isOpen = YES; 147 | CAKeyframeAnimation *animSpring = [self createSpringAnima:@"distance" duration:2.f usingSpringWithDamping:0.5 initialSpringVelocity:3 fromValue:@(0) toValue:@(_layer.bgWidth)]; 148 | [_layer addAnimation:animSpring forKey:ANIM_STARTSPRING]; 149 | CAKeyframeAnimation *animCircle = [self createBaseAnima:@"radius" duration:.8f fromValue:@(0) toValue:@(sqrt(_circleLayer.frame.size.width * _circleLayer.frame.size.width + _circleLayer.frame.size.height * _circleLayer.frame.size.width))]; 150 | [_circleLayer addAnimation:animCircle forKey:ANIM_STARTCIRCLE]; 151 | return; 152 | } 153 | if (anim == [_layer animationForKey:ANIM_STARTSPRING]) { 154 | _layer.distance = _layer.bgWidth; 155 | [_layer removeAllAnimations]; 156 | _llSildeMenu.ll_isOpen = YES; 157 | _isAnimating = NO; 158 | return; 159 | } 160 | if (anim == [_layer animationForKey:ANIM_ENDBASE]) { 161 | [_layer removeAllAnimations]; 162 | _layer.distance = _layer.bgWidth; 163 | _layer.isAnimating = NO; 164 | CAKeyframeAnimation *animSpring = [self createSpringAnima:@"distance" duration:2.f usingSpringWithDamping:0.5 initialSpringVelocity:3 fromValue:@(_layer.bgWidth) toValue:@(0)]; 165 | [_layer addAnimation:animSpring forKey:@"endLLSlideSpringAnimate"]; 166 | return; 167 | } 168 | if (anim == [_layer animationForKey:ANIM_ENDSPRING]) { 169 | _layer.distance = 0; 170 | _isAnimating = NO; 171 | [_layer removeAllAnimations]; 172 | _llSildeMenu.hidden = YES; 173 | _llSildeMenu.ll_isOpen = NO; 174 | return; 175 | } 176 | if (anim == [_circleLayer animationForKey:ANIM_ENDCIRCLE]) { 177 | _circleLayer.radius = 0; 178 | [_circleLayer removeAllAnimations]; 179 | return; 180 | } 181 | if (anim == [_circleLayer animationForKey:ANIM_STARTCIRCLE]) { 182 | _circleLayer.radius = sqrt(_circleLayer.frame.size.width * _circleLayer.frame.size.width + _circleLayer.frame.size.height * _circleLayer.frame.size.width); 183 | [_circleLayer removeAllAnimations]; 184 | return; 185 | } 186 | } else { 187 | // 解决由于页面跳转导致动画属性未关闭 188 | [_layer removeAllAnimations]; 189 | _llSildeMenu.ll_isOpen = !_llSildeMenu.ll_isOpen; 190 | _isAnimating = NO; 191 | } 192 | } 193 | 194 | 195 | @end 196 | -------------------------------------------------------------------------------- /LLSlideMenu/LLCircleLayer.h: -------------------------------------------------------------------------------- 1 | // 2 | // LLCircleLayer.h 3 | // LLSlideMenuDemo 4 | // 5 | // Created by admin on 15/11/24. 6 | // Copyright © 2015年 LiLei. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | 12 | @interface LLCircleLayer : CALayer 13 | 14 | // radius 15 | @property (nonatomic, assign) CGFloat radius; 16 | 17 | // center 18 | @property (nonatomic, assign) CGPoint center; 19 | 20 | @end 21 | -------------------------------------------------------------------------------- /LLSlideMenu/LLCircleLayer.m: -------------------------------------------------------------------------------- 1 | // 2 | // LLCircleLayer.m 3 | // LLSlideMenuDemo 4 | // 5 | // Created by admin on 15/11/24. 6 | // Copyright © 2015年 LiLei. All rights reserved. 7 | // 8 | 9 | #import "LLCircleLayer.h" 10 | 11 | @implementation LLCircleLayer 12 | 13 | //===================== 14 | // 初始化 15 | //===================== 16 | - (instancetype)init { 17 | if (self = [super init]) { 18 | // 默认属性 19 | _radius = 0; 20 | _center = CGPointMake(0, 0); 21 | } 22 | return self; 23 | } 24 | 25 | - (instancetype)initWithLayer:(LLCircleLayer *)layer { 26 | self = [super initWithLayer:layer]; 27 | if (self) { 28 | self.radius = layer.radius; 29 | self.center = layer.center; 30 | } 31 | return self; 32 | } 33 | 34 | //===================== 35 | // Draw 36 | //===================== 37 | - (void)drawInContext:(CGContextRef)ctx { 38 | UIBezierPath *bezier = [UIBezierPath bezierPathWithArcCenter:_center radius:_radius startAngle:0 endAngle:2 * M_PI clockwise:YES]; 39 | [bezier closePath]; 40 | CGContextAddPath(ctx, bezier.CGPath); 41 | CGContextSetFillColorWithColor(ctx, [UIColor whiteColor].CGColor); 42 | CGContextFillPath(ctx); 43 | } 44 | 45 | 46 | //===================== 47 | // 关键帧动画key 48 | //===================== 49 | +(BOOL)needsDisplayForKey:(NSString *)key{ 50 | if ([key isEqual:@"radius"]) { 51 | return YES; 52 | } 53 | return [super needsDisplayForKey:key]; 54 | } 55 | 56 | @end 57 | -------------------------------------------------------------------------------- /LLSlideMenu/LLDisplayLayer.h: -------------------------------------------------------------------------------- 1 | // 2 | // LLDisplayLayer.h 3 | // LLSideMenuDemo 4 | // 5 | // Created by admin on 15/11/20. 6 | // Copyright © 2015年 LiLei. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | 12 | @interface LLDisplayLayer : CALayer 13 | 14 | // layer的width 15 | @property (nonatomic, assign) CGFloat bgWidth; 16 | 17 | // 背景色 18 | @property (nonatomic, strong) UIColor *menuBgColor; 19 | 20 | // 间距 21 | @property (nonatomic, assign) CGFloat distance; 22 | 23 | // 是否正在动画 24 | @property (nonatomic, assign) BOOL isAnimating; 25 | 26 | // 贝塞尔曲线 27 | @property (nonatomic, strong) UIBezierPath *bezier; 28 | 29 | @end 30 | -------------------------------------------------------------------------------- /LLSlideMenu/LLDisplayLayer.m: -------------------------------------------------------------------------------- 1 | // 2 | // LLDisplayLayer.m 3 | // LLSideMenuDemo 4 | // 5 | // Created by admin on 15/11/20. 6 | // Copyright © 2015年 LiLei. All rights reserved. 7 | // 8 | 9 | #import "LLDisplayLayer.h" 10 | 11 | #define LL_SCREEN_HEIGHT [UIScreen mainScreen].bounds.size.height 12 | 13 | @implementation LLDisplayLayer 14 | 15 | //===================== 16 | // 初始化 17 | //===================== 18 | - (instancetype)init { 19 | if (self = [super init]) { 20 | // 默认属性 21 | _menuBgColor = [UIColor whiteColor]; 22 | _isAnimating = NO; 23 | } 24 | return self; 25 | } 26 | 27 | - (instancetype)initWithLayer:(LLDisplayLayer *)layer { 28 | self = [super initWithLayer:layer]; 29 | if (self) { 30 | self.bgWidth = layer.bgWidth; 31 | self.menuBgColor = layer.menuBgColor; 32 | self.distance = layer.distance; 33 | self.isAnimating = layer.isAnimating; 34 | self.bezier = layer.bezier; 35 | } 36 | return self; 37 | } 38 | 39 | 40 | //===================== 41 | // 关键帧动画key 42 | //===================== 43 | +(BOOL)needsDisplayForKey:(NSString *)key{ 44 | if ([key isEqual:@"distance"]) { 45 | return YES; 46 | } 47 | return [super needsDisplayForKey:key]; 48 | } 49 | 50 | 51 | //===================== 52 | // Draw 53 | //===================== 54 | - (void)drawInContext:(CGContextRef)ctx { 55 | _bezier = [UIBezierPath bezierPath]; 56 | CGPoint pointStart,pointA,pointB,pointEnd; 57 | 58 | // 区分曲线路径 59 | if (_isAnimating) { 60 | pointStart = CGPointMake(_distance, 0); 61 | pointA = CGPointMake(_bgWidth, LL_SCREEN_HEIGHT/4); 62 | pointB = CGPointMake(_bgWidth, LL_SCREEN_HEIGHT * 3/4); 63 | pointEnd = CGPointMake(_distance, LL_SCREEN_HEIGHT); 64 | [_bezier moveToPoint:CGPointMake(0, 0)]; 65 | [_bezier addLineToPoint:pointStart]; 66 | [_bezier addCurveToPoint:pointEnd controlPoint1:pointA controlPoint2:pointB]; 67 | [_bezier addLineToPoint:CGPointMake(0, LL_SCREEN_HEIGHT)]; 68 | } else { 69 | pointStart = CGPointMake(0, 0); 70 | pointA = CGPointMake(_distance, LL_SCREEN_HEIGHT/4); 71 | pointB = CGPointMake(_distance, LL_SCREEN_HEIGHT * 3/4); 72 | pointEnd = CGPointMake(0, LL_SCREEN_HEIGHT); 73 | [_bezier moveToPoint:pointStart]; 74 | [_bezier addCurveToPoint:pointEnd controlPoint1:pointA controlPoint2:pointB]; 75 | } 76 | 77 | 78 | [_bezier closePath]; 79 | CGContextAddPath(ctx, _bezier.CGPath); 80 | CGContextSetFillColorWithColor(ctx, _menuBgColor.CGColor); 81 | CGContextFillPath(ctx); 82 | } 83 | 84 | 85 | @end 86 | -------------------------------------------------------------------------------- /LLSlideMenu/LLSlideMenu.h: -------------------------------------------------------------------------------- 1 | // 2 | // LLSideMune.h 3 | // LLSideMenuDemo 4 | // 5 | // Created by admin on 15/11/20. 6 | // Copyright © 2015年 LiLei. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "LLAnimationManager.h" 11 | 12 | 13 | @interface LLSlideMenu : UIView 14 | 15 | // MenuView width 16 | @property (nonatomic, assign) CGFloat ll_menuWidth; 17 | // slide distance 18 | @property (nonatomic, assign) CGFloat ll_distance; 19 | // background color 20 | @property (nonatomic, strong) UIColor *ll_menuBackgroundColor; 21 | // background image 22 | @property (nonatomic, strong) UIImage *ll_menuBackgroundImage; 23 | 24 | // animate manager 25 | @property (nonatomic, strong) LLAnimationManager *ll_animManager; 26 | 27 | // 菜单是否打开 28 | @property (nonatomic, assign) BOOL ll_isOpen; 29 | 30 | // 是否正在动画 31 | @property (nonatomic, assign) BOOL ll_isAnimating; 32 | 33 | // 设置弹性 spring 34 | @property (nonatomic, assign) CGFloat ll_springDamping; // 阻力 35 | @property (nonatomic, assign) CGFloat ll_springVelocity; // 速度 36 | @property (nonatomic, assign) NSInteger ll_springFramesNum; // 关键帧个数 37 | 38 | 39 | // close menu 40 | - (void)ll_closeSlideMenu; 41 | 42 | // open menu 43 | - (void)ll_openSlideMenu; 44 | 45 | 46 | @end 47 | -------------------------------------------------------------------------------- /LLSlideMenu/LLSlideMenu.m: -------------------------------------------------------------------------------- 1 | // 2 | // LLSideMune.m 3 | // LLSideMenuDemo 4 | // 5 | // Created by admin on 15/11/20. 6 | // Copyright © 2015年 LiLei. All rights reserved. 7 | // 8 | 9 | #import "LLSlideMenu.h" 10 | #import "UIView+SetRect.h" 11 | #import "LLDisplayLayer.h" 12 | #import "LLCircleLayer.h" 13 | 14 | #define LL_SCREEN_WIDTH [UIScreen mainScreen].bounds.size.width 15 | #define LL_SCREEN_HEIGHT [UIScreen mainScreen].bounds.size.height 16 | 17 | @interface LLSlideMenu () 18 | 19 | // 菜单显示的View 20 | @property (nonatomic, strong) UIView *displayView; 21 | 22 | // 点击关闭菜单的Button 23 | @property (nonatomic, strong) UIButton *closeMenuButton; 24 | 25 | // displayLayer 26 | @property (nonatomic, strong) LLDisplayLayer *displayLayer; 27 | 28 | // circleLayer 29 | @property (nonatomic, strong) LLCircleLayer *circleLayer; 30 | 31 | @property (nonatomic, assign) BOOL isOverLoad; 32 | 33 | @end 34 | 35 | @implementation LLSlideMenu 36 | 37 | 38 | //===================== 39 | // 初始化 40 | //===================== 41 | - (instancetype)init { 42 | self = [super initWithFrame:CGRectMake(0, 0, LL_SCREEN_WIDTH, LL_SCREEN_HEIGHT)]; 43 | if (self) { 44 | [self initSetUpView]; 45 | } 46 | return self; 47 | } 48 | 49 | 50 | //===================== 51 | // 初始化界面 52 | //===================== 53 | - (void)initSetUpView { 54 | _isOverLoad = NO; 55 | [self.layer addSublayer:self.displayLayer]; 56 | _displayView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 0, LL_SCREEN_HEIGHT)]; 57 | // _displayView.backgroundColor = [UIColor greenColor]; 58 | [self addSubview:self.displayView]; 59 | 60 | // 隐藏 61 | self.hidden = YES; 62 | // 动画管理 63 | _ll_animManager = [LLAnimationManager new]; 64 | _ll_animManager.isAnimating = NO; 65 | _ll_animManager.llSildeMenu = self; 66 | _ll_animManager.circleLayer = _circleLayer; 67 | _ll_isOpen = NO; 68 | 69 | // 设置默认弹力大小和关键帧数量 70 | _ll_animManager.springDamping = 20.f; 71 | _ll_animManager.springVelocity = 15.f; 72 | _ll_animManager.springNumOfFrames = 60; 73 | } 74 | 75 | 76 | 77 | //===================== 78 | // 设置View宽度 79 | //===================== 80 | - (void)setLl_menuWidth:(CGFloat)ll_menuWidth { 81 | _ll_menuWidth = ll_menuWidth; 82 | _displayView.width = ll_menuWidth; 83 | self.closeMenuButton.x = ll_menuWidth; 84 | self.closeMenuButton.width = LL_SCREEN_WIDTH - ll_menuWidth; 85 | self.displayLayer.bgWidth = ll_menuWidth; 86 | 87 | _ll_animManager.layer = _displayLayer; 88 | _ll_animManager.circleLayer = self.circleLayer; 89 | } 90 | 91 | //===================== 92 | // 设置滑动偏移距离 93 | //===================== 94 | - (void)setLl_distance:(CGFloat)ll_distance { 95 | _ll_distance = ll_distance; 96 | self.hidden = NO; 97 | self.displayLayer.distance = ll_distance; 98 | _displayLayer.isAnimating = NO; 99 | _ll_animManager.isAnimating = NO; 100 | [_displayLayer setNeedsDisplay]; 101 | } 102 | 103 | //================================= 104 | // 添加menu背景色 105 | //================================= 106 | - (void)setLl_menuBackgroundColor:(UIColor *)ll_menuBackgroundColor { 107 | _ll_menuBackgroundColor = ll_menuBackgroundColor; 108 | _displayLayer.menuBgColor = ll_menuBackgroundColor; 109 | } 110 | 111 | //================================= 112 | // 添加menu背景图片 113 | //================================= 114 | - (void)setLl_menuBackgroundImage:(UIImage *)ll_menuBackgroundImage { 115 | _ll_menuBackgroundImage = ll_menuBackgroundImage; 116 | _displayLayer.menuBgColor = [UIColor colorWithPatternImage:ll_menuBackgroundImage]; 117 | } 118 | 119 | //================================= 120 | // 设置弹力程度和关键帧数量 121 | //================================= 122 | - (void)setLl_springDamping:(CGFloat)ll_springDamping { 123 | _ll_springDamping = ll_springDamping; 124 | _ll_animManager.springDamping = ll_springDamping; 125 | } 126 | 127 | - (void)setLl_springVelocity:(CGFloat)ll_springVelocity { 128 | _ll_springVelocity = ll_springVelocity; 129 | _ll_animManager.springVelocity = ll_springVelocity; 130 | } 131 | 132 | - (void)setLl_springFramesNum:(NSInteger)ll_springFramesNum { 133 | _ll_springFramesNum = ll_springFramesNum; 134 | _ll_animManager.springNumOfFrames = ll_springFramesNum; 135 | } 136 | 137 | 138 | //===================== 139 | // 关闭菜单 140 | //===================== 141 | - (void)ll_closeSlideMenu { 142 | if (_ll_animManager.isAnimating) { 143 | return; 144 | } 145 | _displayLayer.isAnimating = YES; 146 | _closeMenuButton.hidden = YES; 147 | _ll_animManager.isAnimating = YES; 148 | [_ll_animManager endAnimate]; 149 | } 150 | 151 | //===================== 152 | // 打开菜单 153 | //===================== 154 | - (void)ll_openSlideMenu { 155 | if (_ll_animManager.isAnimating) { 156 | return; 157 | } 158 | self.hidden = NO; 159 | _displayLayer.isAnimating = NO; 160 | _closeMenuButton.hidden = NO; 161 | _ll_animManager.isAnimating = YES; 162 | [_ll_animManager startAnimate]; 163 | } 164 | 165 | //================================= 166 | // 添加View直接添加到displayView上 167 | //================================= 168 | - (void)addSubview:(UIView *)view { 169 | if (view != _displayView && view != _closeMenuButton) { 170 | [_displayView addSubview:view]; 171 | } else { 172 | [super addSubview:view]; 173 | } 174 | } 175 | 176 | - (BOOL)ll_isAnimating { 177 | return _ll_animManager.isAnimating; 178 | } 179 | 180 | 181 | #pragma 懒加载 182 | - (UIButton *)closeMenuButton { 183 | if (!_closeMenuButton) { 184 | _closeMenuButton = [[UIButton alloc] initWithFrame:CGRectMake(0, 0, LL_SCREEN_WIDTH, LL_SCREEN_HEIGHT)]; 185 | _closeMenuButton.hidden = YES; 186 | [_closeMenuButton addTarget:self action:@selector(ll_closeSlideMenu) forControlEvents:UIControlEventTouchUpInside]; 187 | [self addSubview:_closeMenuButton]; 188 | } 189 | return _closeMenuButton; 190 | } 191 | 192 | - (LLDisplayLayer *)displayLayer { 193 | if (!_displayLayer) { 194 | _displayLayer = [[LLDisplayLayer alloc] init]; 195 | [_displayLayer setFrame:CGRectMake(0, 0, LL_SCREEN_WIDTH, LL_SCREEN_HEIGHT)]; 196 | // layer添加阴影 197 | _displayLayer.shadowOffset = CGSizeMake(0, 3); 198 | _displayLayer.shadowRadius = 5.0; 199 | _displayLayer.shadowColor = [UIColor blackColor].CGColor; 200 | _displayLayer.shadowOpacity = 0.6; 201 | } 202 | return _displayLayer; 203 | } 204 | 205 | - (LLCircleLayer *)circleLayer { 206 | if (!_circleLayer) { 207 | _circleLayer = [[LLCircleLayer alloc] init]; 208 | [_circleLayer setFrame:CGRectMake(0, 0, _displayView.width, _displayView.height)]; 209 | _circleLayer.radius = 0; 210 | _circleLayer.center = _displayView.center; 211 | _displayView.layer.mask = self.circleLayer; 212 | } 213 | return _circleLayer; 214 | } 215 | 216 | @end 217 | -------------------------------------------------------------------------------- /LLSlideMenu/UIView+SetRect.h: -------------------------------------------------------------------------------- 1 | // 2 | // UIView+SetRect.h 3 | // TestPch 4 | // 5 | // Created by admin on 15/11/14. 6 | // Copyright (c) 2015年 LiLei. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | #define Width [UIScreen mainScreen].bounds.size.width 12 | #define Height [UIScreen mainScreen].bounds.size.height 13 | 14 | @interface UIView (SetRect) 15 | 16 | // Frame 17 | @property (nonatomic) CGPoint viewOrigin; 18 | @property (nonatomic) CGSize viewSize; 19 | 20 | // Frame Origin 21 | @property (nonatomic) CGFloat x; 22 | @property (nonatomic) CGFloat y; 23 | 24 | // Frame Size 25 | @property (nonatomic) CGFloat width; 26 | @property (nonatomic) CGFloat height; 27 | 28 | // Frame Borders 29 | @property (nonatomic) CGFloat top; 30 | @property (nonatomic) CGFloat left; 31 | @property (nonatomic) CGFloat bottom; 32 | @property (nonatomic) CGFloat right; 33 | 34 | // Center Point 35 | #if !IS_IOS_DEVICE 36 | @property (nonatomic) CGPoint center; 37 | #endif 38 | @property (nonatomic) CGFloat centerX; 39 | @property (nonatomic) CGFloat centerY; 40 | 41 | // Middle Point 42 | @property (nonatomic, readonly) CGPoint middlePoint; 43 | @property (nonatomic, readonly) CGFloat middleX; 44 | @property (nonatomic, readonly) CGFloat middleY; 45 | @end 46 | -------------------------------------------------------------------------------- /LLSlideMenu/UIView+SetRect.m: -------------------------------------------------------------------------------- 1 | // 2 | // UIView+SetRect.m 3 | // TestPch 4 | // 5 | // Created by admin on 15/11/14. 6 | // Copyright (c) 2015年 LiLei. All rights reserved. 7 | // 8 | 9 | #import "UIView+SetRect.h" 10 | 11 | @implementation UIView (SetRect) 12 | 13 | #pragma mark Frame 14 | 15 | - (CGPoint)viewOrigin 16 | { 17 | return self.frame.origin; 18 | } 19 | 20 | - (void)setViewOrigin:(CGPoint)newOrigin 21 | { 22 | CGRect newFrame = self.frame; 23 | newFrame.origin = newOrigin; 24 | self.frame = newFrame; 25 | } 26 | 27 | - (CGSize)viewSize 28 | { 29 | return self.frame.size; 30 | } 31 | 32 | - (void)setViewSize:(CGSize)newSize 33 | { 34 | CGRect newFrame = self.frame; 35 | newFrame.size = newSize; 36 | self.frame = newFrame; 37 | } 38 | 39 | 40 | #pragma mark Frame Origin 41 | 42 | - (CGFloat)x 43 | { 44 | return self.frame.origin.x; 45 | } 46 | 47 | - (void)setX:(CGFloat)newX 48 | { 49 | CGRect newFrame = self.frame; 50 | newFrame.origin.x = newX; 51 | self.frame = newFrame; 52 | } 53 | 54 | - (CGFloat)y 55 | { 56 | return self.frame.origin.y; 57 | } 58 | 59 | - (void)setY:(CGFloat)newY 60 | { 61 | CGRect newFrame = self.frame; 62 | newFrame.origin.y = newY; 63 | self.frame = newFrame; 64 | } 65 | 66 | 67 | #pragma mark Frame Size 68 | 69 | - (CGFloat)height 70 | { 71 | return self.frame.size.height; 72 | } 73 | 74 | - (void)setHeight:(CGFloat)newHeight 75 | { 76 | CGRect newFrame = self.frame; 77 | newFrame.size.height = newHeight; 78 | self.frame = newFrame; 79 | } 80 | 81 | - (CGFloat)width 82 | { 83 | return self.frame.size.width; 84 | } 85 | 86 | - (void)setWidth:(CGFloat)newWidth 87 | { 88 | CGRect newFrame = self.frame; 89 | newFrame.size.width = newWidth; 90 | self.frame = newFrame; 91 | } 92 | 93 | 94 | #pragma mark Frame Borders 95 | 96 | - (CGFloat)left 97 | { 98 | return self.x; 99 | } 100 | 101 | - (void)setLeft:(CGFloat)left 102 | { 103 | self.x = left; 104 | } 105 | 106 | - (CGFloat)right 107 | { 108 | return self.frame.origin.x + self.frame.size.width; 109 | } 110 | 111 | - (void)setRight:(CGFloat)right 112 | { 113 | self.x = right - self.width; 114 | } 115 | 116 | - (CGFloat)top 117 | { 118 | return self.y; 119 | } 120 | 121 | - (void)setTop:(CGFloat)top 122 | { 123 | self.y = top; 124 | } 125 | 126 | - (CGFloat)bottom 127 | { 128 | return self.frame.origin.y + self.frame.size.height; 129 | } 130 | 131 | - (void)setBottom:(CGFloat)bottom 132 | { 133 | self.y = bottom - self.height; 134 | } 135 | 136 | 137 | #pragma mark Center Point 138 | 139 | #if !IS_IOS_DEVICE 140 | - (CGPoint)center 141 | { 142 | return CGPointMake(self.left + self.middleX, self.top + self.middleY); 143 | } 144 | 145 | - (void)setCenter:(CGPoint)newCenter 146 | { 147 | self.left = newCenter.x - self.middleX; 148 | self.top = newCenter.y - self.middleY; 149 | } 150 | #endif 151 | 152 | - (CGFloat)centerX 153 | { 154 | return self.center.x; 155 | } 156 | 157 | - (void)setCenterX:(CGFloat)newCenterX 158 | { 159 | self.center = CGPointMake(newCenterX, self.center.y); 160 | } 161 | 162 | - (CGFloat)centerY 163 | { 164 | return self.center.y; 165 | } 166 | 167 | - (void)setCenterY:(CGFloat)newCenterY 168 | { 169 | self.center = CGPointMake(self.center.x, newCenterY); 170 | } 171 | 172 | 173 | #pragma mark Middle Point 174 | 175 | - (CGPoint)middlePoint 176 | { 177 | return CGPointMake(self.middleX, self.middleY); 178 | } 179 | 180 | - (CGFloat)middleX 181 | { 182 | return self.width / 2; 183 | } 184 | 185 | - (CGFloat)middleY 186 | { 187 | return self.height / 2; 188 | } 189 | 190 | @end 191 | -------------------------------------------------------------------------------- /Preview/LLSlideMenuPreview.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lilei644/LLSlideMenu/c3fd321747a8e0e30bc710025b8b1e7c070f97d0/Preview/LLSlideMenuPreview.gif -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # LLSlideMenu 2 | This is a spring slide menu for iOS apps
3 | 一个弹性侧滑菜单

4 | 弹性动画原理借鉴该[项目](https://github.com/KittenYang/KYAnimatedPageControl)中阻尼函数实现 5 | 6 | ## Preview  预览 7 | ![image](https://github.com/lilei644/LLSlideMenu/blob/master/Preview/LLSlideMenuPreview.gif) 8 | 9 | ## Installation  安装 10 | * pod 11 | ``` 12 | 1.pod 'LLSlideMenu', '~> 1.0.6' 13 | 2.pod install // 若获取失败请重新 pod setup 14 | 3.#import "LLSlideMenu.h" 15 | ``` 16 | * Common 17 | ``` 18 | 1.Add "LLSlideMenu" files to your Project // 直接导入“LLSlideMenu”文件夹到项目中 19 | 2.#import "LLSlideMenu.h" 20 | ``` 21 | 22 | ## Usage  用法 23 | * Init  初始化 24 | ``` 25 | LLSlideMune *slideMenu = [[LLSlideMune alloc] init]; 26 | [self.view addSubview:slideMenu]; 27 | ``` 28 | * Base Property  基本属性 29 | ``` 30 | // 设置菜单宽度 menu width 31 | _slideMenu.ll_menuWidth = 200.f; 32 | 33 | // 设置菜单背景色 background color 34 | _slideMenu.ll_menuBackgroundColor = [UIColor redColor]; 35 | 36 | // 设置菜单背景图片 background image 37 | _slideMenu.ll_menuBackgroundImage = [UIImage imageNamed:@"image"]; 38 | 39 | // 背景色或背景图片设置一个即可 background image and color select one 40 | 41 | ``` 42 | * Open or Close  打开或关闭 43 | ``` 44 | [_slideMenu ll_openSlideMenu]; // 打开 open 45 | [_slideMenu ll_closeSlideMenu]; // 关闭 close 46 | ``` 47 | * GestureRecognizer  手势监听 48 | ``` 49 | _slideMenu.ll_distance = 100.f; // 拉伸距离 pulling distance 50 | ``` 51 | * Spring and Frames  弹力和关键帧设置(可控制弹性效果和时间) 52 | ``` 53 | _slideMenu.ll_springDamping = 20; // 阻力 54 | _slideMenu.ll_springVelocity = 15; // 速度 55 | _slideMenu.ll_springFramesNum = 60; // 关键帧数量 56 | ``` 57 | 58 | ## Requirements  版本要求 59 | IOS 6.0 Above 60 | 61 | ## License 62 | LLSlideMenu is provided under the MIT license. See LICENSE file for details. 63 | --------------------------------------------------------------------------------