用Xamarin.iOS制作一个漂亮的汉堡按钮过渡动画

    前两天在cocochina看到《用Swift制作一个漂亮的汉堡按钮过渡动画》,于是用Xamarin.iOS再次实现了一下,基本效果都出来了,但对圆形CGPath坐标解析不是很完美,没法连接在一起。编程思路可以参考以下的文章地址,而C#源码就直接贴出来了。
中文地址:《用Swift制作一个漂亮的汉堡按钮过渡动画》

原网地址: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;
		}

    3.接下来,需要利用路径来绘制图层了

<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);
		}

    4.在设置动画响应之前,先实现CAShapeLayer的Extention扩展方法,用来实现执行动画:

<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));
		}
	}

    5.为Button添加动画响应:
<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);



  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值