Instantly share code, notes, and snippets.
Created
July 14, 2014 08:43
-
Star
(0)
0
You must be signed in to star a gist -
Fork
(0)
0
You must be signed in to fork a gist
-
Save jhildensperger/eb1d4bb274a2c85a437e to your computer and use it in GitHub Desktop.
An old fashioned Objective-C implementation of this awesome swift hamburger button https://github.com/robb/hamburger-button
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// MenuButton.m | |
// | |
// Created by James Hildensperger on 7/13/14. | |
// Copyright (c) 2014 Zymurgical. All rights reserved. | |
// | |
#import "MenuButton.h" | |
static CGFloat menuStrokeStart = 0.325; | |
static CGFloat menuStrokeEnd = 0.9; | |
static CGFloat hamburgerStrokeStart = 0.028; | |
static CGFloat hamburgerStrokeEnd = 0.111; | |
@interface MenuButton () | |
@property (nonatomic) CAShapeLayer *topShapeLayer; | |
@property (nonatomic) CAShapeLayer *middleShapeLayer; | |
@property (nonatomic) CAShapeLayer *bottomShapeLayer; | |
@end | |
@implementation MenuButton | |
- (id)initWithFrame:(CGRect)frame { | |
if (self = [super initWithFrame:frame]) { | |
self.backgroundColor = [UIColor blackColor]; | |
self.layer.cornerRadius = frame.size.width/2; | |
for (CAShapeLayer *layer in @[self.topShapeLayer, self.middleShapeLayer, self.bottomShapeLayer]) { | |
layer.fillColor = nil; | |
layer.strokeColor = [UIColor whiteColor].CGColor; | |
layer.lineWidth = 4; | |
layer.miterLimit = 4; | |
layer.lineCap = kCALineCapRound; | |
layer.masksToBounds = true; | |
CGPathRef strokingPath = CGPathCreateCopyByStrokingPath(layer.path, nil, 4, kCGLineCapRound, kCGLineJoinMiter, 4); | |
layer.bounds = CGPathGetPathBoundingBox(strokingPath); | |
layer.actions = @{@"strokeStart": [NSNull null], @"strokeEnd": [NSNull null], @"transform": [NSNull null]}; | |
[self.layer addSublayer:layer]; | |
} | |
} | |
return self; | |
} | |
- (void)setShowsMenu:(BOOL)showsMenu { | |
if (_showsMenu != showsMenu) { | |
_showsMenu = showsMenu; | |
CABasicAnimation *strokeStart = [CABasicAnimation animationWithKeyPath:@"strokeStart"]; | |
CABasicAnimation *strokeEnd = [CABasicAnimation animationWithKeyPath:@"strokeEnd"]; | |
if (showsMenu) { | |
self.middleShapeLayer.strokeStart = menuStrokeStart; | |
strokeStart.fromValue = @(hamburgerStrokeStart); | |
strokeStart.duration = 0.5; | |
strokeStart.timingFunction = [CAMediaTimingFunction functionWithControlPoints:0.25 :-0.4 :0.5 :1]; | |
self.middleShapeLayer.strokeEnd = menuStrokeEnd; | |
strokeEnd.fromValue = @(hamburgerStrokeEnd); | |
strokeEnd.duration = 0.6; | |
strokeEnd.timingFunction = [CAMediaTimingFunction functionWithControlPoints:0.25 :-0.4 :0.5 :1]; | |
} else { | |
self.middleShapeLayer.strokeStart = hamburgerStrokeStart; | |
strokeStart.fromValue = @(menuStrokeStart); | |
strokeStart.duration = 0.5; | |
strokeStart.timingFunction = [CAMediaTimingFunction functionWithControlPoints:0.25 :0 :0.5 :1.2]; | |
strokeStart.beginTime = CACurrentMediaTime() + 0.1; | |
strokeStart.fillMode = kCAFillModeBackwards; | |
self.middleShapeLayer.strokeEnd = hamburgerStrokeEnd; | |
strokeEnd.fromValue = @(menuStrokeEnd); | |
strokeEnd.duration = 0.6; | |
strokeEnd.timingFunction = [CAMediaTimingFunction functionWithControlPoints:0.25 :0.3 :0.5 :.9]; | |
} | |
[self.middleShapeLayer addAnimation:strokeStart forKey:@"strokeStart"]; | |
[self.middleShapeLayer addAnimation:strokeEnd forKey:@"strokeEnd"]; | |
CABasicAnimation *topTransform = [CABasicAnimation animationWithKeyPath:@"transform"]; | |
topTransform.timingFunction = [CAMediaTimingFunction functionWithControlPoints:0.5 :-0.8 :0.5 :1.85]; | |
topTransform.duration = 0.4; | |
topTransform.fillMode = kCAFillModeBackwards; | |
CABasicAnimation *bottomTransform = [topTransform copy]; | |
CATransform3D translation = CATransform3DMakeTranslation(-4, 0, 0); | |
if (showsMenu) { | |
self.topShapeLayer.transform = CATransform3DRotate(translation, -0.7853975, 0, 0, 1); | |
topTransform.fromValue = valueWith3DTransform(CATransform3DIdentity); | |
topTransform.beginTime = CACurrentMediaTime() + 0.25; | |
self.bottomShapeLayer.transform = CATransform3DRotate(translation, 0.7853975, 0, 0, 1); | |
bottomTransform.fromValue = valueWith3DTransform(CATransform3DIdentity); | |
bottomTransform.beginTime = CACurrentMediaTime() + 0.25; | |
} else { | |
self.topShapeLayer.transform = CATransform3DIdentity; | |
topTransform.fromValue = valueWith3DTransform(CATransform3DRotate(translation, -0.7853975, 0, 0, 1)); | |
topTransform.beginTime = CACurrentMediaTime() + 0.05; | |
self.bottomShapeLayer.transform = CATransform3DIdentity; | |
bottomTransform.fromValue = valueWith3DTransform(CATransform3DRotate(translation, 0.7853975, 0, 0, 1)); | |
bottomTransform.beginTime = CACurrentMediaTime() + 0.05; | |
} | |
[self.topShapeLayer addAnimation:topTransform forKey:@"topTransform"]; | |
[self.bottomShapeLayer addAnimation:bottomTransform forKey:@"bottomTransform"]; | |
} | |
} | |
- (CAShapeLayer *)topShapeLayer { | |
if (!_topShapeLayer) { | |
_topShapeLayer = [CAShapeLayer layer]; | |
_topShapeLayer.path = linePath(); | |
_topShapeLayer.anchorPoint = CGPointMake(28.0 / 30.0, 0.5); | |
_topShapeLayer.position = CGPointMake(40, 18); | |
} | |
return _topShapeLayer; | |
} | |
- (CAShapeLayer *)middleShapeLayer { | |
if (!_middleShapeLayer) { | |
_middleShapeLayer = [CAShapeLayer layer]; | |
_middleShapeLayer.path = outlinePath(); | |
_middleShapeLayer.position = CGPointMake(27, 27); | |
_middleShapeLayer.strokeStart = hamburgerStrokeStart; | |
_middleShapeLayer.strokeEnd = hamburgerStrokeEnd; | |
} | |
return _middleShapeLayer; | |
} | |
- (CAShapeLayer *)bottomShapeLayer { | |
if (!_bottomShapeLayer) { | |
_bottomShapeLayer = [CAShapeLayer layer]; | |
_bottomShapeLayer.path = linePath(); | |
_bottomShapeLayer.anchorPoint = CGPointMake(28.0 / 30.0, 0.5); | |
_bottomShapeLayer.position = CGPointMake(40, 36); | |
} | |
return _bottomShapeLayer; | |
} | |
CGPathRef linePath () { | |
CGMutablePathRef path = CGPathCreateMutable(); | |
CGPathMoveToPoint(path, nil, 2, 2); | |
CGPathAddLineToPoint(path, nil, 28, 2); | |
return path; | |
} | |
CGPathRef outlinePath () { | |
CGMutablePathRef path = CGPathCreateMutable(); | |
CGPathMoveToPoint(path, nil, 10, 27); | |
CGPathAddCurveToPoint(path, nil, 12.00, 27.00, 28.02, 27.00, 40, 27); | |
CGPathAddCurveToPoint(path, nil, 55.92, 27.00, 50.47, 2.00, 27, 2); | |
CGPathAddCurveToPoint(path, nil, 13.16, 2.00, 2.00, 13.16, 2, 27); | |
CGPathAddCurveToPoint(path, nil, 2.00, 40.84, 13.16, 52.00, 27, 52); | |
CGPathAddCurveToPoint(path, nil, 40.84, 52.00, 52.00, 40.84, 52, 27); | |
CGPathAddCurveToPoint(path, nil, 52.00, 13.16, 42.39, 2.00, 27, 2); | |
CGPathAddCurveToPoint(path, nil, 13.16, 2.00, 2.00, 13.16, 2, 27); | |
return path; | |
} | |
NSValue *valueWith3DTransform(CATransform3D transform) { | |
return [NSValue valueWithCATransform3D:transform]; | |
} | |
@end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment