原网地址:How to build a nice Hamburger Button transition in Swift
这个效果主要使用了CAShapeLayer根据CGPath路径来构建图层,用CABasicAnimation来控制动画效果,如果不是经常使用图形核心编程技术的还真短期实现不了,所以iOS图形图像及动画结合的核心编程还是挺重要的,许多漂亮的特效少不了使用。接下来看如何实现这个特效:
1.既然是个按钮,首先继承UIButton控件来编写,并且定义相关属性
public class HamburgerButton:UIButton { private float menuStrokeStart = 0.325f; private float menuStrokeEnd = 0.9f; private float hamburgerStrokeStart = 0.028f; private float hamburgerStrokeEnd = 0.111f; private CAShapeLayer top; private CAShapeLayer middle; private CAShapeLayer bottom; public HamburgerButton (RectangleF frame) : base (frame) { top = new CAShapeLayer (); middle = new CAShapeLayer (); bottom = new CAShapeLayer (); this.LoadView (); }<pre name="code" class="csharp"><span style="white-space:pre"> </span>}
2.根据效果图显示,需要编制三条图形路径,其中两条是直线路径,而中间一条是圆形路径。画图形路径,作者使用了一个工具Sketch,它可以用图形的方式绘制出图形的运动轨迹从而导出相连坐标点,那是相当的重要,否则你得使用几何公式图形来生成这个路径了。
第1、3条直线CGPath:
private CGPath ShortStroke () { var path = new CGPath (); path.MoveToPoint (2f, 2f); path.AddLineToPoint (28f, 2f); return path; }第2条中间部分CGPath:
private CGPath OutLine () { var path = new CGPath (); path.MoveToPoint (10f, 27f); path.AddCurveToPoint (12.00f, 27.00f, 28.02f, 27.00f, 40f, 27f); path.AddCurveToPoint (55.92f, 27.00f, 50.47f, 2.00f, 27f, 2f); path.AddCurveToPoint (13.16f, 2.00f, 2.00f, 13.16f, 2f, 27f); path.AddCurveToPoint (2.00f, 40.84f, 13.16f, 52.00f, 27f, 52f); path.AddCurveToPoint (40.84f, 52.00f, 52.00f, 40.84f, 52f, 27f); path.AddCurveToPoint (52.00f, 13.16f, 42.39f, 2.00f, 27f, 2f); path.AddCurveToPoint (13.16f, 2.00f, 2.00f, 13.16f, 2f, 27f); return path; }
<span style="white-space:pre"> </span>private void LoadView () { //实例化图形 this.top.Path = this.ShortStroke (); this.middle.Path = this.OutLine (); this.bottom.Path = this.ShortStroke (); //为图形设置属性,样式,并添加到layer中 CAShapeLayer[] layers = new CAShapeLayer[] { this.top, this.middle, this.bottom }; foreach (var layer in layers) { layer.FillColor = UIColor.Clear.CGColor; layer.MasksToBounds = true; layer.LineWidth = 4; layer.MiterLimit = 4; layer.StrokeColor = UIColor.White.CGColor; layer.LineCap = new NSString (CGLineCap.Round.ToString ()); var strokingPath = layer.Path.CopyByStrokingPath (4, CGLineCap.Round, CGLineJoin.Miter, 4); layer.Bounds = strokingPath.PathBoundingBox; var dic = new NSDictionary ( new NSString ("strokeStart"), new NSNull (), new NSString ("strokeEnd"), new NSNull (), new NSString ("transform"), new NSNull () ); layer.Actions = dic; this.Layer.AddSublayer (layer); } //布置位置 this.top.AnchorPoint = new PointF (28.0f / 30.0f, 0.5f); this.top.Position = new PointF (40, 18); this.middle.Position = new PointF (27, 27); this.middle.StrokeStart = this.hamburgerStrokeStart; this.middle.StrokeEnd = this.hamburgerStrokeEnd; this.bottom.AnchorPoint = new PointF (28.0f / 30.0f, 0.5f); this.bottom.Position = new PointF (40, 36); }
<span style="white-space:pre"> </span>public static class Extentsion { public static void OCB_ApplyAnimation (this CAShapeLayer layer, CABasicAnimation animation) { var copy = animation.Copy () as CABasicAnimation; if (copy.From == null) { copy.From = layer.PresentationLayer.ValueForKeyPath (new NSString (copy.KeyPath)); } layer.AddAnimation (copy, copy.KeyPath); layer.SetValueForKey (copy.To, new NSString (copy.KeyPath)); } }
<span style="white-space:pre"> </span>public bool ShowsMenu { get { return this.Selected; } set { this.Selected = value; //第1条中间动画控制 var strokeStart = CABasicAnimation.FromKeyPath ("strokeStart"); var strokeEnd = CABasicAnimation.FromKeyPath ("strokeEnd"); if (this.Selected) { var obj = menuStrokeStart as object; strokeStart.To = NSObject.FromObject (obj); strokeStart.Duration = 0.5; strokeStart.TimingFunction = new CAMediaTimingFunction (0.25f, -0.4f, 0.5f, 1f); var obj2 = menuStrokeEnd as object; strokeEnd.To = NSObject.FromObject (obj2); strokeEnd.Duration = 0.6; strokeEnd.TimingFunction = new CAMediaTimingFunction (0.25f, -0.4f, 0.5f, 1f); } else { var obj = hamburgerStrokeStart as object; strokeStart.To = NSObject.FromObject (obj); strokeStart.Duration = 0.5; strokeStart.TimingFunction = new CAMediaTimingFunction (0.25f, 0f, 0.5f, 1.2f); strokeStart.BeginTime = CABasicAnimation.CurrentMediaTime () + 0.1; strokeStart.FillMode = CAFillMode.Backwards.ToString (); var obj2 = hamburgerStrokeEnd as object; strokeEnd.To = NSObject.FromObject (obj2); strokeEnd.Duration = 0.6; strokeEnd.TimingFunction = new CAMediaTimingFunction (0.25f, 0.3f, 0.5f, 0.9f); } this.middle.OCB_ApplyAnimation (strokeStart); this.middle.OCB_ApplyAnimation (strokeEnd); //第1、3直线动画控制 var topTransform = CABasicAnimation.FromKeyPath ("transform"); topTransform.TimingFunction = new CAMediaTimingFunction (0.5f, -0.8f, 0.5f, 1.85f); topTransform.Duration = 0.4; topTransform.FillMode = CAFillMode.Backwards.ToString (); var bottomTransform = topTransform.Copy () as CABasicAnimation; if (this.Selected) { var translation = CATransform3D.MakeTranslation (-4, 0, 0); var value1 = translation.Rotate (-0.7853975f, 0f, 0f, 1f); topTransform.To = NSValue.FromObject (value1 as object); topTransform.BeginTime = CABasicAnimation.CurrentMediaTime () + 0.25; var value2 = translation.Rotate (0.7853975f, 0f, 0f, 1f); bottomTransform.To = NSValue.FromObject (value2 as object); bottomTransform.BeginTime = CABasicAnimation.CurrentMediaTime () + 0.25; } else { topTransform.To = NSValue.FromObject (CATransform3D.Identity as object); topTransform.BeginTime = CABasicAnimation.CurrentMediaTime () + 0.05; bottomTransform.To = NSValue.FromObject (CATransform3D.Identity as object); bottomTransform.BeginTime = CABasicAnimation.CurrentMediaTime () + 0.05; } this.top.OCB_ApplyAnimation (topTransform); this.bottom.OCB_ApplyAnimation (bottomTransform); } }6 .最后,组合代码完成调用。
HamburgerButton hamButton = new HamburgerButton (new RectangleF (133,133,54,54)); hamButton.TouchUpInside += delegate { hamButton.ShowsMenu = !hamButton.ShowsMenu; }; this.View.AddSubview (hamButton);