How to build a nice Hamburger Button transition in Swift
来源:互联网 发布:写轮眼美瞳软件下载 编辑:程序博客网 时间:2024/06/04 19:28
Hamburger buttons may have become somewhat of a cliché in interface designlately, but when I came across a particularlynice transition of ahamburger button on dribbble, I had to try and recreate it in code.
Here’s the original shot by the CreativeDash team:
You’ll notice how the top and bottom strokes of the hamburger form a X, whilethe middle one morphs into an outline. I knew this effect could be recreatedusingCAShapeLayer
, but first I had to create a CGPath
for each of the threestrokes.
The short, straight strokes can be figured out manually:
let shortStroke: CGPath = { let path = CGPathCreateMutable() CGPathMoveToPoint(path, nil, 2, 2) CGPathAddLineToPoint(path, nil, 28, 2) return path}()
For the middle stroke however, I created a path in Sketch that starts from themiddle and seamlessly transitions into the outline.
I then exported this path as an SVG file and imported it into the oldPaintCode 1, which converted the file into a code snippet that created aUIBezierPath
. I then rewrote said piece of code into the followinginstructions that create the desired CGPath
object:
let outline: CGPath = { let 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}()
There probably is a library that allows you to load CGPath
s straight from SVGfiles, but for a short path like this one, having it in code is not a big deal.
In my UIButton
subclass, I added three CAShapeLayer
properties and set thetheir paths accordingly:
self.top.path = shortStrokeself.middle.path = outlineself.bottom.path = shortStroke
Then I styled all three of them like so:
layer.fillColor = nillayer.strokeColor = UIColor.whiteColor().CGColorlayer.lineWidth = 4layer.miterLimit = 4layer.lineCap = kCALineCapRoundlayer.masksToBounds = true
In order to calculate their bounds correctly, I needed take the size of thestroke into account. Thankfully,CGPathCreateCopyByStrokingPath
will create apath that follows the outlines of the original stroke, therefore its boundingbox will then fully contain the contents of theCAShapeLayer
:
let boundingPath = CGPathCreateCopyByStrokingPath(layer.path, nil, 4, kCGLineCapRound, kCGLineJoinMiter, 4)layer.bounds = CGPathGetPathBoundingBox(boundingPath)
Since the outer strokes will later rotate around their right-most point, I hadto set theiranchorPoint
accordingly when layouting their layers:
self.top.anchorPoint = CGPointMake(28.0 / 30.0, 0.5)self.top.position = CGPointMake(40, 18)self.middle.position = CGPointMake(27, 27)self.middle.strokeStart = hamburgerStrokeStartself.middle.strokeEnd = hamburgerStrokeEndself.bottom.anchorPoint = CGPointMake(28.0 / 30.0, 0.5)self.bottom.position = CGPointMake(40, 36)
Now, when the button changes state, it should animate the three strokes to theirnew positions. Once again, the two outer strokes are easy. For the top stroke, Ifirst moved it inward by 4 points to keep in centered, then rotated it bynegative 45 degrees to form one half of the X:
var transform = CATransform3DIdentitytransform = CATransform3DTranslate(transform, -4, 0, 0)transform = CATransform3DRotate(transform, -M_PI_4, 0, 0, 1)let animation = CABasicAnimation(keyPath: "transform")animation.fromValue = NSValue(CATransform3D: CATransform3DIdentity)animation.toValue = NSValue(CATransform3D: transform)animation.timingFunction = CAMediaTimingFunction(controlPoints: 0.25, -0.8, 0.75, 1.85)animation.beginTime = CACurrentMediaTime() + 0.25self.top.addAnimation(animation, forKey: "transform")self.top.transform = transform
(The bottom stroke is left as an exercise to the reader.)
Again, the middle stroke is a little trickier. In order to achieve the desiredeffect, I needed to animate theCAShapeLayer
’s strokeStart
and strokeEnd
properties separately.
First I had to figure out the correct values for the two properties in the twostates. Note that the even in its hamburger state, the stroke does not start at0. By having the path extend slightly beyond the left edge of the outer strokes,we can achieve a nice anticipation effect when applying the timing functionlater:
let menuStrokeStart: CGFloat = 0.325let menuStrokeEnd: CGFloat = 0.9let hamburgerStrokeStart: CGFloat = 0.028let hamburgerStrokeEnd: CGFloat = 0.111
Now we only need to create the animations and add them to the layer:
let strokeStart = CABasicAnimation(keyPath: "strokeStart")strokeStart.fromValue = hamburgerStrokeStartstrokeStart.toValue = menuStrokeStartstrokeStart.duration = 0.5strokeStart.timingFunction = CAMediaTimingFunction(controlPoints: 0.25, -0.4, 0.5, 1)self.middle.addAnimation(strokeStart, forKey: "strokeStart")self.middle.strokeStart = menuStrokeStartlet strokeEnd = CABasicAnimation(keyPath: "strokeEnd")strokeEnd.fromValue = hamburgerStrokeEndstrokeEnd.toValue = menuStrokeEndstrokeEnd.duration = 0.6strokeEnd.timingFunction = CAMediaTimingFunction(controlPoints: 0.25, -0.4, 0.5, 1)self.middle.addAnimation(strokeEnd, forKey: "strokeEnd")self.middle.strokeEnd = menuStrokeEnd
Putting everything together, the result looks something like this:
- How to build a nice Hamburger Button transition in Swift
- How to build a nice Hamburger Button transition in Swift(做动画可以参考下)
- How to Animate the Up-Button to Hamburger like in Gmail
- How to Create a Slick and Clean Button in Photoshop
- how to build a cgal program in vs2010
- how to build a cgal program in qt under ubuntu
- how to build a smallest binay in ARMv7 device?
- How To Build a Yacc
- How To Build a Yacc
- How to "Build" a Computer
- How to Build a PREEMPT_RTkernel
- How to Build a House
- How To Build A Pipeline
- How to add a button to PreferenceScreen
- How to import a C library in Swift using the Swift Package Manager
- How to build libDTS in the MSYS?
- How to build libA52 in the MSY?
- How to build giflib in the MSYS?
- 简单SQL优化小记
- Alamofire-Swift Networking网络库
- 《C语言及程序设计初步》第35讲实践项目
- table td tr th的含义(参考百度知道)
- c#初学-多线程中lock用法的经典实例
- How to build a nice Hamburger Button transition in Swift
- UICollectionView(纯代码方式)实现带上下拉刷新的瀑布流式
- 读《重新定义公司》谷歌是如何运营的 How Google Works
- Python 网络编程基础
- Genymotion 安装教程
- Docker实战(十二):Docker安装ElasticSearch集群环境
- 认识函数式接口
- [LA] Centering a data set
- cf#339-A. Link/Cut Tree-水