前两天在cocochina看到《用Swift制作一个漂亮的汉堡按钮过渡动画》,于是用Xamarin.iOS再次实现了一下,基本效果都出来了,但对圆形CGPath坐标解析不是很完美,没法连接在一起。编程思路可以参考以下的文章地址,而C#源码就直接贴出来了。
中文地址:《用Swift制作一个漂亮的汉堡按钮过渡动画》
3.接下来,需要利用路径来绘制图层了
4.在设置动画响应之前,先实现CAShapeLayer的Extention扩展方法,用来实现执行动画:
5.为Button添加动画响应:
中文地址:《用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);