├── .gitignore ├── Hamburger Button.xcodeproj └── project.pbxproj ├── Hamburger Button ├── AppDelegate.swift ├── HamburgerButton.swift ├── Images.xcassets │ ├── AppIcon.appiconset │ │ └── Contents.json │ └── LaunchImage.launchimage │ │ └── Contents.json ├── Info.plist └── ViewController.swift └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | # Mac OS X 2 | *.DS_Store 3 | 4 | # Xcode 5 | *.pbxuser 6 | *.mode1v3 7 | *.mode2v3 8 | *.perspectivev3 9 | project.xcworkspace/ 10 | xcuserdata/ 11 | 12 | # Generated files 13 | *.o 14 | *.pyc 15 | *.hi 16 | 17 | #Python modules 18 | MANIFEST 19 | dist/ 20 | build/ 21 | 22 | # Backup files 23 | *~.nib 24 | \#*# 25 | .#* 26 | 27 | -------------------------------------------------------------------------------- /Hamburger Button.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 5451838819647E0A005B583F /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5451838719647E0A005B583F /* AppDelegate.swift */; }; 11 | 5451838A19647E0A005B583F /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5451838919647E0A005B583F /* ViewController.swift */; }; 12 | 5451838F19647E0A005B583F /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 5451838E19647E0A005B583F /* Images.xcassets */; }; 13 | 545183A919647E3F005B583F /* HamburgerButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 545183A819647E3F005B583F /* HamburgerButton.swift */; }; 14 | 545183AB19647E9E005B583F /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 545183AA19647E9E005B583F /* QuartzCore.framework */; }; 15 | 545183AD196480E9005B583F /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 545183AC196480E9005B583F /* CoreGraphics.framework */; }; 16 | /* End PBXBuildFile section */ 17 | 18 | /* Begin PBXFileReference section */ 19 | 5451838219647E0A005B583F /* Hamburger Button.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "Hamburger Button.app"; sourceTree = BUILT_PRODUCTS_DIR; }; 20 | 5451838619647E0A005B583F /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 21 | 5451838719647E0A005B583F /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 22 | 5451838919647E0A005B583F /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; 23 | 5451838E19647E0A005B583F /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; 24 | 545183A819647E3F005B583F /* HamburgerButton.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HamburgerButton.swift; sourceTree = ""; }; 25 | 545183AA19647E9E005B583F /* QuartzCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuartzCore.framework; path = System/Library/Frameworks/QuartzCore.framework; sourceTree = SDKROOT; }; 26 | 545183AC196480E9005B583F /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; }; 27 | /* End PBXFileReference section */ 28 | 29 | /* Begin PBXFrameworksBuildPhase section */ 30 | 5451837F19647E0A005B583F /* Frameworks */ = { 31 | isa = PBXFrameworksBuildPhase; 32 | buildActionMask = 2147483647; 33 | files = ( 34 | 545183AB19647E9E005B583F /* QuartzCore.framework in Frameworks */, 35 | 545183AD196480E9005B583F /* CoreGraphics.framework in Frameworks */, 36 | ); 37 | runOnlyForDeploymentPostprocessing = 0; 38 | }; 39 | /* End PBXFrameworksBuildPhase section */ 40 | 41 | /* Begin PBXGroup section */ 42 | 5451837919647E0A005B583F = { 43 | isa = PBXGroup; 44 | children = ( 45 | 5451838419647E0A005B583F /* Hamburger Button */, 46 | 545183AE196480EF005B583F /* Frameworks */, 47 | 5451838319647E0A005B583F /* Products */, 48 | ); 49 | sourceTree = ""; 50 | }; 51 | 5451838319647E0A005B583F /* Products */ = { 52 | isa = PBXGroup; 53 | children = ( 54 | 5451838219647E0A005B583F /* Hamburger Button.app */, 55 | ); 56 | name = Products; 57 | sourceTree = ""; 58 | }; 59 | 5451838419647E0A005B583F /* Hamburger Button */ = { 60 | isa = PBXGroup; 61 | children = ( 62 | 5451838719647E0A005B583F /* AppDelegate.swift */, 63 | 5451838919647E0A005B583F /* ViewController.swift */, 64 | 545183A819647E3F005B583F /* HamburgerButton.swift */, 65 | 5451838E19647E0A005B583F /* Images.xcassets */, 66 | 5451838519647E0A005B583F /* Supporting Files */, 67 | ); 68 | path = "Hamburger Button"; 69 | sourceTree = ""; 70 | }; 71 | 5451838519647E0A005B583F /* Supporting Files */ = { 72 | isa = PBXGroup; 73 | children = ( 74 | 5451838619647E0A005B583F /* Info.plist */, 75 | ); 76 | name = "Supporting Files"; 77 | sourceTree = ""; 78 | }; 79 | 545183AE196480EF005B583F /* Frameworks */ = { 80 | isa = PBXGroup; 81 | children = ( 82 | 545183AC196480E9005B583F /* CoreGraphics.framework */, 83 | 545183AA19647E9E005B583F /* QuartzCore.framework */, 84 | ); 85 | name = Frameworks; 86 | sourceTree = ""; 87 | }; 88 | /* End PBXGroup section */ 89 | 90 | /* Begin PBXNativeTarget section */ 91 | 5451838119647E0A005B583F /* Hamburger Button */ = { 92 | isa = PBXNativeTarget; 93 | buildConfigurationList = 5451839E19647E0A005B583F /* Build configuration list for PBXNativeTarget "Hamburger Button" */; 94 | buildPhases = ( 95 | 5451837E19647E0A005B583F /* Sources */, 96 | 5451837F19647E0A005B583F /* Frameworks */, 97 | 5451838019647E0A005B583F /* Resources */, 98 | ); 99 | buildRules = ( 100 | ); 101 | dependencies = ( 102 | ); 103 | name = "Hamburger Button"; 104 | productName = "Hamburger Button"; 105 | productReference = 5451838219647E0A005B583F /* Hamburger Button.app */; 106 | productType = "com.apple.product-type.application"; 107 | }; 108 | /* End PBXNativeTarget section */ 109 | 110 | /* Begin PBXProject section */ 111 | 5451837A19647E0A005B583F /* Project object */ = { 112 | isa = PBXProject; 113 | attributes = { 114 | LastSwiftMigration = 0710; 115 | LastSwiftUpdateCheck = 0710; 116 | LastUpgradeCheck = 0800; 117 | ORGANIZATIONNAME = "Robert Böhnke"; 118 | TargetAttributes = { 119 | 5451838119647E0A005B583F = { 120 | CreatedOnToolsVersion = 6.0; 121 | LastSwiftMigration = 0800; 122 | }; 123 | }; 124 | }; 125 | buildConfigurationList = 5451837D19647E0A005B583F /* Build configuration list for PBXProject "Hamburger Button" */; 126 | compatibilityVersion = "Xcode 3.2"; 127 | developmentRegion = English; 128 | hasScannedForEncodings = 0; 129 | knownRegions = ( 130 | en, 131 | Base, 132 | ); 133 | mainGroup = 5451837919647E0A005B583F; 134 | productRefGroup = 5451838319647E0A005B583F /* Products */; 135 | projectDirPath = ""; 136 | projectRoot = ""; 137 | targets = ( 138 | 5451838119647E0A005B583F /* Hamburger Button */, 139 | ); 140 | }; 141 | /* End PBXProject section */ 142 | 143 | /* Begin PBXResourcesBuildPhase section */ 144 | 5451838019647E0A005B583F /* Resources */ = { 145 | isa = PBXResourcesBuildPhase; 146 | buildActionMask = 2147483647; 147 | files = ( 148 | 5451838F19647E0A005B583F /* Images.xcassets in Resources */, 149 | ); 150 | runOnlyForDeploymentPostprocessing = 0; 151 | }; 152 | /* End PBXResourcesBuildPhase section */ 153 | 154 | /* Begin PBXSourcesBuildPhase section */ 155 | 5451837E19647E0A005B583F /* Sources */ = { 156 | isa = PBXSourcesBuildPhase; 157 | buildActionMask = 2147483647; 158 | files = ( 159 | 5451838A19647E0A005B583F /* ViewController.swift in Sources */, 160 | 545183A919647E3F005B583F /* HamburgerButton.swift in Sources */, 161 | 5451838819647E0A005B583F /* AppDelegate.swift in Sources */, 162 | ); 163 | runOnlyForDeploymentPostprocessing = 0; 164 | }; 165 | /* End PBXSourcesBuildPhase section */ 166 | 167 | /* Begin XCBuildConfiguration section */ 168 | 5451839C19647E0A005B583F /* Debug */ = { 169 | isa = XCBuildConfiguration; 170 | buildSettings = { 171 | ALWAYS_SEARCH_USER_PATHS = NO; 172 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 173 | CLANG_CXX_LIBRARY = "libc++"; 174 | CLANG_ENABLE_MODULES = YES; 175 | CLANG_ENABLE_OBJC_ARC = YES; 176 | CLANG_WARN_BOOL_CONVERSION = YES; 177 | CLANG_WARN_CONSTANT_CONVERSION = YES; 178 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 179 | CLANG_WARN_EMPTY_BODY = YES; 180 | CLANG_WARN_ENUM_CONVERSION = YES; 181 | CLANG_WARN_INFINITE_RECURSION = YES; 182 | CLANG_WARN_INT_CONVERSION = YES; 183 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 184 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 185 | CLANG_WARN_UNREACHABLE_CODE = YES; 186 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 187 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 188 | COPY_PHASE_STRIP = NO; 189 | ENABLE_STRICT_OBJC_MSGSEND = YES; 190 | ENABLE_TESTABILITY = YES; 191 | GCC_C_LANGUAGE_STANDARD = gnu99; 192 | GCC_DYNAMIC_NO_PIC = NO; 193 | GCC_NO_COMMON_BLOCKS = YES; 194 | GCC_OPTIMIZATION_LEVEL = 0; 195 | GCC_PREPROCESSOR_DEFINITIONS = ( 196 | "DEBUG=1", 197 | "$(inherited)", 198 | ); 199 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 200 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 201 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 202 | GCC_WARN_UNDECLARED_SELECTOR = YES; 203 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 204 | GCC_WARN_UNUSED_FUNCTION = YES; 205 | GCC_WARN_UNUSED_VARIABLE = YES; 206 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 207 | METAL_ENABLE_DEBUG_INFO = YES; 208 | ONLY_ACTIVE_ARCH = YES; 209 | SDKROOT = iphoneos; 210 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 211 | }; 212 | name = Debug; 213 | }; 214 | 5451839D19647E0A005B583F /* Release */ = { 215 | isa = XCBuildConfiguration; 216 | buildSettings = { 217 | ALWAYS_SEARCH_USER_PATHS = NO; 218 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 219 | CLANG_CXX_LIBRARY = "libc++"; 220 | CLANG_ENABLE_MODULES = YES; 221 | CLANG_ENABLE_OBJC_ARC = YES; 222 | CLANG_WARN_BOOL_CONVERSION = YES; 223 | CLANG_WARN_CONSTANT_CONVERSION = YES; 224 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 225 | CLANG_WARN_EMPTY_BODY = YES; 226 | CLANG_WARN_ENUM_CONVERSION = YES; 227 | CLANG_WARN_INFINITE_RECURSION = YES; 228 | CLANG_WARN_INT_CONVERSION = YES; 229 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 230 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 231 | CLANG_WARN_UNREACHABLE_CODE = YES; 232 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 233 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 234 | COPY_PHASE_STRIP = YES; 235 | ENABLE_NS_ASSERTIONS = NO; 236 | ENABLE_STRICT_OBJC_MSGSEND = YES; 237 | GCC_C_LANGUAGE_STANDARD = gnu99; 238 | GCC_NO_COMMON_BLOCKS = YES; 239 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 240 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 241 | GCC_WARN_UNDECLARED_SELECTOR = YES; 242 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 243 | GCC_WARN_UNUSED_FUNCTION = YES; 244 | GCC_WARN_UNUSED_VARIABLE = YES; 245 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 246 | METAL_ENABLE_DEBUG_INFO = NO; 247 | SDKROOT = iphoneos; 248 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 249 | VALIDATE_PRODUCT = YES; 250 | }; 251 | name = Release; 252 | }; 253 | 5451839F19647E0A005B583F /* Debug */ = { 254 | isa = XCBuildConfiguration; 255 | buildSettings = { 256 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 257 | ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; 258 | INFOPLIST_FILE = "Hamburger Button/Info.plist"; 259 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 260 | PRODUCT_BUNDLE_IDENTIFIER = "com.robertboehnke.${PRODUCT_NAME:rfc1034identifier}"; 261 | PRODUCT_NAME = "Hamburger Button"; 262 | SWIFT_VERSION = 3.0; 263 | }; 264 | name = Debug; 265 | }; 266 | 545183A019647E0A005B583F /* Release */ = { 267 | isa = XCBuildConfiguration; 268 | buildSettings = { 269 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 270 | ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; 271 | INFOPLIST_FILE = "Hamburger Button/Info.plist"; 272 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 273 | PRODUCT_BUNDLE_IDENTIFIER = "com.robertboehnke.${PRODUCT_NAME:rfc1034identifier}"; 274 | PRODUCT_NAME = "Hamburger Button"; 275 | SWIFT_VERSION = 3.0; 276 | }; 277 | name = Release; 278 | }; 279 | /* End XCBuildConfiguration section */ 280 | 281 | /* Begin XCConfigurationList section */ 282 | 5451837D19647E0A005B583F /* Build configuration list for PBXProject "Hamburger Button" */ = { 283 | isa = XCConfigurationList; 284 | buildConfigurations = ( 285 | 5451839C19647E0A005B583F /* Debug */, 286 | 5451839D19647E0A005B583F /* Release */, 287 | ); 288 | defaultConfigurationIsVisible = 0; 289 | defaultConfigurationName = Release; 290 | }; 291 | 5451839E19647E0A005B583F /* Build configuration list for PBXNativeTarget "Hamburger Button" */ = { 292 | isa = XCConfigurationList; 293 | buildConfigurations = ( 294 | 5451839F19647E0A005B583F /* Debug */, 295 | 545183A019647E0A005B583F /* Release */, 296 | ); 297 | defaultConfigurationIsVisible = 0; 298 | defaultConfigurationName = Release; 299 | }; 300 | /* End XCConfigurationList section */ 301 | }; 302 | rootObject = 5451837A19647E0A005B583F /* Project object */; 303 | } 304 | -------------------------------------------------------------------------------- /Hamburger Button/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // Hamburger Button 4 | // 5 | // Created by Robert Böhnke on 02/07/14. 6 | // Copyright (c) 2014 Robert Böhnke. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | @UIApplicationMain 12 | class AppDelegate: UIResponder, UIApplicationDelegate { 13 | var window: UIWindow? 14 | 15 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool { 16 | self.window = UIWindow(frame: UIScreen.main.bounds) 17 | self.window!.backgroundColor = UIColor.white 18 | self.window!.rootViewController = ViewController() 19 | self.window!.makeKeyAndVisible() 20 | return true 21 | } 22 | } 23 | 24 | -------------------------------------------------------------------------------- /Hamburger Button/HamburgerButton.swift: -------------------------------------------------------------------------------- 1 | // 2 | // HamburgerButton.swift 3 | // Hamburger Button 4 | // 5 | // Created by Robert Böhnke on 02/07/14. 6 | // Copyright (c) 2014 Robert Böhnke. All rights reserved. 7 | // 8 | 9 | import CoreGraphics 10 | import QuartzCore 11 | import UIKit 12 | 13 | class HamburgerButton : UIButton { 14 | let shortStroke: CGPath = { 15 | let path = CGMutablePath() 16 | path.move(to: CGPoint(x: 2, y: 2)) 17 | path.addLine(to: CGPoint(x: 28, y:2)) 18 | 19 | return path 20 | }() 21 | 22 | let outline: CGPath = { 23 | let path = CGMutablePath() 24 | path.move(to: CGPoint(x: 10, y: 27)) 25 | path.addCurve(to: CGPoint(x: 40, y: 27), control1: CGPoint(x: 12, y: 27), control2: CGPoint(x: 28.02, y: 27)) 26 | path.addCurve(to: CGPoint(x: 27, y: 02), control1: CGPoint(x: 55.92, y: 27), control2: CGPoint(x: 50.47, y: 2)) 27 | path.addCurve(to: CGPoint(x: 2, y: 27), control1: CGPoint(x: 13.16, y: 2), control2: CGPoint(x: 2, y: 13.16)) 28 | path.addCurve(to: CGPoint(x: 27, y: 52), control1: CGPoint(x: 2, y: 40.84), control2: CGPoint(x: 13.16, y: 52)) 29 | path.addCurve(to: CGPoint(x: 52, y: 27), control1: CGPoint(x: 40.84, y: 52), control2: CGPoint(x: 52, y: 40.84)) 30 | path.addCurve(to: CGPoint(x: 27, y: 2), control1: CGPoint(x: 52, y: 13.16), control2: CGPoint(x: 42.39, y: 2)) 31 | path.addCurve(to: CGPoint(x: 2, y: 27), control1: CGPoint(x: 13.16, y: 2), control2: CGPoint(x: 2, y: 13.16)) 32 | 33 | return path 34 | }() 35 | 36 | let menuStrokeStart: CGFloat = 0.325 37 | let menuStrokeEnd: CGFloat = 0.9 38 | 39 | let hamburgerStrokeStart: CGFloat = 0.028 40 | let hamburgerStrokeEnd: CGFloat = 0.111 41 | 42 | required init?(coder aDecoder: NSCoder) { 43 | super.init(coder: aDecoder) 44 | } 45 | 46 | override init(frame: CGRect) { 47 | super.init(frame: frame) 48 | 49 | self.top.path = shortStroke 50 | self.middle.path = outline 51 | self.bottom.path = shortStroke 52 | 53 | for layer in [ self.top, self.middle, self.bottom ] { 54 | layer?.fillColor = nil 55 | layer?.strokeColor = UIColor.white.cgColor 56 | layer?.lineWidth = 4 57 | layer?.miterLimit = 4 58 | layer?.lineCap = kCALineCapRound 59 | layer?.masksToBounds = true 60 | 61 | let strokingPath = CGPath(__byStroking: (layer?.path!)!, transform: nil, lineWidth: 4, lineCap: .round, lineJoin: .miter, miterLimit: 4) 62 | 63 | layer?.bounds = (strokingPath?.boundingBoxOfPath)! 64 | 65 | layer?.actions = [ 66 | "strokeStart": NSNull(), 67 | "strokeEnd": NSNull(), 68 | "transform": NSNull() 69 | ] 70 | 71 | self.layer.addSublayer(layer!) 72 | } 73 | 74 | self.top.anchorPoint = CGPoint(x: 28.0 / 30.0, y: 0.5) 75 | self.top.position = CGPoint(x: 40, y: 18) 76 | 77 | self.middle.position = CGPoint(x: 27, y: 27) 78 | self.middle.strokeStart = hamburgerStrokeStart 79 | self.middle.strokeEnd = hamburgerStrokeEnd 80 | 81 | self.bottom.anchorPoint = CGPoint(x: 28.0 / 30.0, y: 0.5) 82 | self.bottom.position = CGPoint(x: 40, y: 36) 83 | } 84 | 85 | var showsMenu: Bool = false { 86 | didSet { 87 | let strokeStart = CABasicAnimation(keyPath: "strokeStart") 88 | let strokeEnd = CABasicAnimation(keyPath: "strokeEnd") 89 | 90 | if self.showsMenu { 91 | strokeStart.toValue = menuStrokeStart 92 | strokeStart.duration = 0.5 93 | strokeStart.timingFunction = CAMediaTimingFunction(controlPoints: 0.25, -0.4, 0.5, 1) 94 | 95 | strokeEnd.toValue = menuStrokeEnd 96 | strokeEnd.duration = 0.6 97 | strokeEnd.timingFunction = CAMediaTimingFunction(controlPoints: 0.25, -0.4, 0.5, 1) 98 | } else { 99 | strokeStart.toValue = hamburgerStrokeStart 100 | strokeStart.duration = 0.5 101 | strokeStart.timingFunction = CAMediaTimingFunction(controlPoints: 0.25, 0, 0.5, 1.2) 102 | strokeStart.beginTime = CACurrentMediaTime() + 0.1 103 | strokeStart.fillMode = kCAFillModeBackwards 104 | 105 | strokeEnd.toValue = hamburgerStrokeEnd 106 | strokeEnd.duration = 0.6 107 | strokeEnd.timingFunction = CAMediaTimingFunction(controlPoints: 0.25, 0.3, 0.5, 0.9) 108 | } 109 | 110 | self.middle.ocb_applyAnimation(strokeStart) 111 | self.middle.ocb_applyAnimation(strokeEnd) 112 | 113 | let topTransform = CABasicAnimation(keyPath: "transform") 114 | topTransform.timingFunction = CAMediaTimingFunction(controlPoints: 0.5, -0.8, 0.5, 1.85) 115 | topTransform.duration = 0.4 116 | topTransform.fillMode = kCAFillModeBackwards 117 | 118 | let bottomTransform = topTransform.copy() as! CABasicAnimation 119 | 120 | if self.showsMenu { 121 | let translation = CATransform3DMakeTranslation(-4, 0, 0) 122 | 123 | topTransform.toValue = NSValue(caTransform3D: CATransform3DRotate(translation, -0.7853975, 0, 0, 1)) 124 | topTransform.beginTime = CACurrentMediaTime() + 0.25 125 | 126 | bottomTransform.toValue = NSValue(caTransform3D: CATransform3DRotate(translation, 0.7853975, 0, 0, 1)) 127 | bottomTransform.beginTime = CACurrentMediaTime() + 0.25 128 | } else { 129 | topTransform.toValue = NSValue(caTransform3D: CATransform3DIdentity) 130 | topTransform.beginTime = CACurrentMediaTime() + 0.05 131 | 132 | bottomTransform.toValue = NSValue(caTransform3D: CATransform3DIdentity) 133 | bottomTransform.beginTime = CACurrentMediaTime() + 0.05 134 | } 135 | 136 | self.top.ocb_applyAnimation(topTransform) 137 | self.bottom.ocb_applyAnimation(bottomTransform) 138 | } 139 | } 140 | 141 | var top: CAShapeLayer! = CAShapeLayer() 142 | var bottom: CAShapeLayer! = CAShapeLayer() 143 | var middle: CAShapeLayer! = CAShapeLayer() 144 | } 145 | 146 | extension CALayer { 147 | func ocb_applyAnimation(_ animation: CABasicAnimation) { 148 | let copy = animation.copy() as! CABasicAnimation 149 | 150 | if copy.fromValue == nil { 151 | copy.fromValue = self.presentation()!.value(forKeyPath: copy.keyPath!) 152 | } 153 | 154 | self.add(copy, forKey: copy.keyPath) 155 | self.setValue(copy.toValue, forKeyPath:copy.keyPath!) 156 | } 157 | } 158 | -------------------------------------------------------------------------------- /Hamburger Button/Images.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "29x29", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "40x40", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "60x60", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "60x60", 21 | "scale" : "3x" 22 | } 23 | ], 24 | "info" : { 25 | "version" : 1, 26 | "author" : "xcode" 27 | } 28 | } -------------------------------------------------------------------------------- /Hamburger Button/Images.xcassets/LaunchImage.launchimage/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "orientation" : "portrait", 5 | "idiom" : "iphone", 6 | "extent" : "full-screen", 7 | "minimum-system-version" : "7.0", 8 | "scale" : "2x" 9 | }, 10 | { 11 | "orientation" : "portrait", 12 | "idiom" : "iphone", 13 | "subtype" : "retina4", 14 | "extent" : "full-screen", 15 | "minimum-system-version" : "7.0", 16 | "scale" : "2x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /Hamburger Button/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 | UIRequiredDeviceCapabilities 26 | 27 | armv7 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /Hamburger Button/ViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.swift 3 | // Hamburger Button 4 | // 5 | // Created by Robert Böhnke on 02/07/14. 6 | // Copyright (c) 2014 Robert Böhnke. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class ViewController: UIViewController { 12 | var button: HamburgerButton! = nil 13 | 14 | override func viewDidLoad() { 15 | super.viewDidLoad() 16 | self.view.backgroundColor = UIColor(red: 38.0 / 255, green: 151.0 / 255, blue: 68.0 / 255, alpha: 1) 17 | 18 | self.button = HamburgerButton(frame: CGRect(x: 133, y: 133, width: 54, height: 54)) 19 | self.button.addTarget(self, action: #selector(ViewController.toggle(_:)), for:.touchUpInside) 20 | 21 | self.view.addSubview(button) 22 | } 23 | 24 | override var preferredStatusBarStyle : UIStatusBarStyle { 25 | return .lightContent 26 | } 27 | 28 | func toggle(_ sender: AnyObject!) { 29 | self.button.showsMenu = !self.button.showsMenu 30 | } 31 | } 32 | 33 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # A Hamburger button transition 2 | 3 |

4 | 5 |

6 | 7 | I wrote a [blog post](http://robb.is/working-on/a-hamburger-button-transition/) 8 | on how this works. Inspired by a [dribbble shot by 9 | Creativedash](https://dribbble.com/shots/1623679-Open-Close). 10 | --------------------------------------------------------------------------------