以前Android写过一个demo,是波浪效果,应该是仿照百度外卖的那个头像效果。突然想拿Xamarin来试试,利用我的脑洞终于给弄出来了,不知道方法是不是合理。先贴出来展示一下吧.
实际效果是波浪在动,头像以及文字也在上下动,给人一种效果就是头像和文字是漂在波浪上面的。
下面来说一下实现吧:
首先将看见的这个view分成三个view,分别实现。第一个view就是波浪view,即如下图
就是类似一个正弦曲线的这么一个效果。
第二个就是头像加文字的view,把她单独出来做一个view目的就是可以单独控制它的动画来配合波浪的动画。
下面首先来看看波浪view的画法:新建一个View继承UIView。通过Draw方法来画线,想要实现波浪效果让它动起来,最少要画两个屏幕长度的波浪,这个大家应该能理解吧。
还有一点注意的就是我没有找到直接画正弦的方法,最后是用贝塞尔曲线拼接而成的。
看下代码:
using System;
using System.Drawing;
using CoreGraphics;
using UIKit;
namespace RuilaiGrow.iOS
{
/// <summary>
/// 波浪View
/// by ge
/// </summary>
public class WaveView : UIView
{
//正线曲线,顶部,中线,底部Y轴高度
private nfloat _waveTop = 50f;
private nfloat _waveHalfHei = 65f;
private nfloat _waveBottom = 80f;
public WaveView()
{
this.Frame = new RectangleF(0, 0, (float)(UIScreen.MainScreen.Bounds.Width * 2), (float)_waveBottom);
this.BackgroundColor = UIColor.White;
}
//划线
public override void Draw(CoreGraphics.CGRect rect)
{
base.Draw(rect);
//本View上半部分红色背景矩形
CGContext contextBac = UIGraphics.GetCurrentContext();
contextBac.SetFillColor(MvxTouchColor.DeepRed.CGColor);
contextBac.AddRect(new RectangleF(0, 0, (float)(UIScreen.MainScreen.Bounds.Width * 2), (float)_waveHalfHei));
contextBac.FillPath();
//屏幕的宽度
nfloat viewWid = UIScreen.MainScreen.Bounds.Width;
//利用贝塞尔曲线完成正弦波浪曲线
//分成两次画出正弦曲线
for (int i = 0; i < 2; i++)
{
nfloat startX = i * viewWid;
//左半边弧
CGContext contextLeft = UIGraphics.GetCurrentContext();
contextLeft.SetLineWidth(1f);
contextLeft.SetStrokeColor(MvxTouchColor.DeepRed.CGColor);
contextLeft.MoveTo(startX, _waveHalfHei);
contextLeft.AddCurveToPoint(startX, _waveHalfHei,
(startX + viewWid / 4), _waveTop,
(startX + viewWid / 2), _waveHalfHei);
contextLeft.SetFillColor(MvxTouchColor.White.CGColor);
contextLeft.DrawPath(CGPathDrawingMode.EOFill);
//右半边弧
CGContext contextRight = UIGraphics.GetCurrentContext();
contextRight.SetLineWidth(1f);
contextRight.SetStrokeColor(MvxTouchColor.DeepRed.CGColor);
contextRight.MoveTo((startX + viewWid / 2), _waveHalfHei);
contextRight.AddCurveToPoint((startX + viewWid / 2),
_waveHalfHei, (startX + (System.nfloat)(viewWid * 0.75)),
_waveBottom, startX + viewWid, _waveHalfHei);
contextRight.SetFillColor(MvxTouchColor.AlphaDeepRed.CGColor);
contextRight.DrawPath(CGPathDrawingMode.EOFill);
}
}
}
}
上面我将整个类都贴出来,因为没啥难度,不过有一点要注意的,就是在波浪下凹处仔细看会有一点透明白色的区域,这个区域我是将右半边弧度用了一个透明颜色填充了。大家可以自己换。
头像和文字的view我就不占用这里的地方了,没什么要注意的。
下面直接贴出来整体的View的类:
注:
1.代码中最下面部分就是波浪view与头像view的动画。波浪view还好说,就是一个循环左移的动画,头像的动画的轨迹分析一下:一个正弦函数移动,应该是先向下移动一个驼峰距离,在向上移动两个驼峰距离,在向下移动一个驼峰距离。这是一个完整的周期,下一个动画循环这个周期即可。
2.代码中使用了一种控件间的约束方式,大家可以换成自己的,如果需要的话可以私聊我。
3.动画属性我设置的是匀速的,即UIViewAnimationOptions.CurveLinear这里,相关属性大家查ios原生的就可以查到。
4.本人水平有限,可能用的都是特别简单的东西来实现这个功能,可能多走了些弯路或者有更好的实现方式,希望大家可以多多提建议,共同进步。
using System;
using System.Drawing;
using CoreGraphics;
using RuilaiGrow.iOS.Common;
using UIKit;
namespace RuilaiGrow.iOS
{
/// <summary>
/// 里程碑模块主页顶部头像View
/// by ge
/// </summary>
public class UserHeaderView : UIView
{
private nfloat _waveBottom = 80f;
private nfloat _waveTopHei = 7f;
//波浪条
private WaveView _waveView = null;
public WaveView WaveView {
get {
if (_waveView == null) {
_waveView = new WaveView();
}
return _waveView;
}
}
//头像,名字,年龄布局
private UIView _headerView = null;
public UIView HeaderView {
get {
if (_headerView == null) {
_headerView = new UIView();
_headerView.AddSubview(HeaderImg);
_headerView.AddSubview(NameText);
_headerView.AddSubview(AgeText);
}
return _headerView;
}
}
//头像
private UIButton _headerImg = null;
public UIButton HeaderImg {
get {
if (_headerImg == null) {
_headerImg = new UIButton();
_headerImg.SetImage(UIImage.FromFile("icon_child_head.png"), UIControlState.Normal);
}
return _headerImg;
}
}
//名字
private UILabel _nameText = null;
public UILabel NameText {
get {
if (_nameText == null) {
_nameText = new UILabel();
_nameText.Text = "小贝";
_nameText.TextAlignment = UITextAlignment.Left;
_nameText.TextColor = MvxTouchColor.White;
_nameText.Font = UIFont.SystemFontOfSize(18f);
return _nameText;
}
return _nameText;
}
}
//月龄
private UILabel _ageText = null;
public UILabel AgeText {
get {
if (_ageText == null)
{
_ageText = new UILabel();
_ageText.Text = "43个月";
_ageText.TextAlignment = UITextAlignment.Left;
_ageText.TextColor = MvxTouchColor.GraySix;
_ageText.Font = UIFont.SystemFontOfSize(13f);
}
return _ageText;
}
}
/// <summary>
/// 构造函数
/// 设定背景色与frame
/// </summary>
public UserHeaderView()
{
this.Frame = new RectangleF(0, 0, (float)UIScreen.MainScreen.Bounds.Width, (float)_waveBottom);
this.BackgroundColor = UIColor.White;
}
//提交view
public override void LayoutSubviews()
{
base.LayoutSubviews();
nfloat textMarTop = 6f;
nfloat marBottom = 6f;
this.AddSubview(WaveView);
this.AddSubview(HeaderView);
//一种aotolayout方式进行view约束
this.ConstrainLayout(() =>
HeaderView.Frame.Bottom == this.Frame.Bottom - marBottom
&& HeaderView.Frame.Width == this.Frame.Width
&& HeaderView.Frame.Height == this.Frame.Height - marBottom
);
HeaderView.ConstrainLayout(() =>
HeaderImg.Frame.Bottom == HeaderView.Frame.Bottom
&& HeaderImg.Frame.GetCenterX() == HeaderView.Frame.GetCenterX()
&& NameText.Frame.Top == HeaderImg.Frame.Top + textMarTop
&& NameText.Frame.Left == HeaderImg.Frame.Right + textMarTop
&& NameText.Frame.Right == HeaderView.Frame.Right
&& AgeText.Frame.Top == NameText.Frame.Bottom
&& AgeText.Frame.Left == HeaderImg.Frame.Right + textMarTop
&& AgeText.Frame.Right == HeaderView.Frame.Right
);
//开启动画
startAnimation(UIScreen.MainScreen.Bounds.Width);
}
/// <summary>
/// 开始波浪动画 动画结束监听中重启本方法
/// </summary>
/// <param name="leftMove">Left move.</param>
public void startAnimation(nfloat leftMove)
{
startHeadAnimation(_waveTopHei, true);
var waveFrame = WaveView.Frame;
waveFrame.X = WaveView.Frame.X - leftMove;
UIView.Animate(12d, 0d, UIViewAnimationOptions.CurveLinear, () => WaveView.Frame = waveFrame,
() =>
{
waveFrame.X = WaveView.Frame.X + leftMove;
WaveView.Frame = waveFrame;
/** 动画完成监听 **/
startAnimation(leftMove);
});
}
/// <summary>
/// 头像初次view动画
/// </summary>
/// <param name="waveTopHei">Wave top hei.</param>
public void startHeadAnimation(nfloat waveTopHei, bool isFirst)
{
var headerFrame = HeaderView.Frame;
headerFrame.Y = HeaderView.Frame.Y + waveTopHei;
UIView.Animate(3d, 0d, UIViewAnimationOptions.CurveLinear, () => HeaderView.Frame = headerFrame,
() =>
{
waveTopHei = waveTopHei * 2;
waveTopHei = -waveTopHei;
if (!isFirst)
return;
/** 动画完成监听 **/
startHeadAnimationAgain(waveTopHei);
});
}
/// <summary>
/// 头像view动画
/// </summary>
/// <param name="waveTopHei">Wave top hei.</param>
public void startHeadAnimationAgain(nfloat waveTopHei)
{
var headerFrame = HeaderView.Frame;
headerFrame.Y = HeaderView.Frame.Y + waveTopHei;
UIView.Animate(6d, 0d, UIViewAnimationOptions.CurveLinear, () => HeaderView.Frame = headerFrame,
() =>
{
waveTopHei = waveTopHei / 2;
waveTopHei = -waveTopHei;
/** 动画完成监听 **/
startHeadAnimation(waveTopHei, false);
});
}
}
}