Skip to content

Instantly share code, notes, and snippets.

@jhildensperger
Created July 14, 2014 08:43
Show Gist options
  • Save jhildensperger/eb1d4bb274a2c85a437e to your computer and use it in GitHub Desktop.
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
//
// 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