基本了解
首先我们先简单的来看下实现的效果,在侧滑栏出来的时候是会有下面的波浪的效果的
首先我们先分步骤来进行规划
- 1、点击切换按钮添加一个模糊的背景
- 2、点击按钮的时候会从左滑入一个菜单栏
- 3、让view动起来其实就是进行多次绘制(动画基于绘制)可以用贝塞尔曲线来进行绘制
- 4、通过两个辅助的view,求出它们的差值,因为两个view如果弹簧的摩擦不一样,动画不一样就可以了 获取一组动态的数据
- 5、利用CADisplayLink进行获取这一组动态的值
- 6、然后我们就是去添加按钮,如果想让按钮梯度的进入界面,那么我们就要给按钮去添加动画其实就是去修改其的frame,然后以动画的形式给展示出来
具体案例
首先我们来看一下viewController,其实在里面我们只有按钮的点击方法和侧滑栏的初始化方法
@interface ViewController ()
@property (nonatomic ,strong)slideMenuView *menuView;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.menuView = [[slideMenuView alloc]initWithTitle:@[@"1",@"2",@"3",@"4",@"5"]];
}
- (IBAction)switchAction:(id)sender {
[self.menuView switchAction];
}
接下来我们再来看下侧滑栏组件的声明和实现
slideMenuView的声明
#import <UIKit/UIKit.h>
@interface slideMenuView : UIView
//进行初始化方法
-(id)initWithTitle:(NSArray *)btnTitles;
//进行切换动画
-(void)switchAction;
@end
相应的实现,我们也来看下我们声明的变量,要使用的
//按钮的高度
#define menuBtnHeight 40
//按钮直接的空隙
#define buttonSpace 30
#import "slideMenuView.h"
#import "slideMenuBtn.h"
@implementation slideMenuView
{
//模糊效果视图
UIVisualEffectView * blurView;
//两个辅助的view
UIView * helperSideView;
UIView * helperCenterView;
//keyWindow
UIWindow * keyWindow;
//是否进行切换了,就是外面按钮点击的次数的效果
BOOL switched;
//两个点的异同
CGFloat diff;
//侧滑栏颜色
UIColor * menuColor;
//定时器
CADisplayLink * displayLink;
}
下面我们再来看下初始化方法
-(id)initWithTitle:(NSArray *)btnTitles
{
self = [super init];
if (self) {
//获取menuColor
menuColor = [UIColor colorWithRed:0 green:0.722 blue:1 alpha:1];
//保存keyWindow
keyWindow = [UIApplication sharedApplication].keyWindow;
//背景模糊的view
blurView = [[UIVisualEffectView alloc]initWithEffect:[UIBlurEffect effectWithStyle:UIBlurEffectStyleDark]];
blurView.frame = keyWindow.frame;
blurView.alpha = 0.5;
//设置frame
self.frame = CGRectMake(-CGRectGetWidth(keyWindow.frame)/2, 0, CGRectGetWidth(keyWindow.frame)/2, CGRectGetHeight(keyWindow.frame));
//设置背景颜色为clearColor
self.backgroundColor = [UIColor clearColor];
#pragma mark - 添加我们的辅助的view,来帮我们做出波浪的效果,使两个赋值的view进行弹簧效果来改变frame
//创建view
helperSideView = [[UIView alloc]initWithFrame:CGRectMake(-40, 0, 40, 40)];
// helperSideView.backgroundColor = [UIColor redColor];
helperCenterView = [[UIView alloc]initWithFrame:CGRectMake(-40,CGRectGetHeight(keyWindow.bounds)/2-20,40,40)];
// helperCenterView.backgroundColor = [UIColor orangeColor];
//添加的两个辅助的view
[keyWindow addSubview:helperSideView];
[keyWindow addSubview:helperCenterView];
//插入我们的view
[keyWindow insertSubview:self belowSubview:helperSideView];
//添加手势,可以进行手势的点击来进行使背景模式view消失
UITapGestureRecognizer * tap = [[UITapGestureRecognizer alloc]initWithTarget:self action:@selector(dismissView)];
[blurView addGestureRecognizer:tap];
//添加按钮
[self addBtnTitles:btnTitles];
}
return self;
}
然后就是暴露给外面进行切换的方法,也就是左侧滑栏划入和滑出的
-(void)switchAction
{
if(!switched)
{
//1、添加模糊背景
[keyWindow insertSubview:blurView belowSubview:self];
//2、添加划入的动画
[UIView animateWithDuration:.3 animations:^{
//原本菜单栏是在外部的,现在赋值将bounds给了frame之后,就可以将这个菜单栏给移动进来了
self.frame = self.bounds;
}];
//参数3是弹簧的阻力,参数4就是初始化的速度,参数5就是动画选项从当前位置
[UIView animateWithDuration:.7 delay:0 usingSpringWithDamping:.5 initialSpringVelocity:.9 options:UIViewAnimationOptionBeginFromCurrentState animations:^{
//设置helperSideView的Center
self->helperSideView.center = CGPointMake(self->keyWindow.center.x, CGRectGetHeight(self->helperSideView.bounds)/2);
} completion:^(BOOL finished) {
}];
//设置弹簧效果
[UIView animateWithDuration:.7 delay:0 usingSpringWithDamping:.8 initialSpringVelocity:.2 options:UIViewAnimationOptionBeginFromCurrentState animations:^{
self->helperCenterView.center = self->keyWindow.center;
} completion:^(BOOL finished) {
//动画完成之后我们去移除定时器
[self removeDisplayLink];
}];
//获取辅助view的x差异值,然后去做波浪效果
[self getDiff];
//给按钮添加动画效果
[self addBtnAnima];
switched= YES;
}else
{
[self dismissView];
}
}
下面是侧滑栏滑出的方法
//dismiss的view
-(void)dismissView
{
//设置Switched为NO
switched = NO;
[UIView animateWithDuration:0.5 animations:^{
//设置self的Frame
self.frame = CGRectMake(-(CGRectGetWidth(self->keyWindow.frame)/2+menuBlankWidth), 0, CGRectGetWidth(self->keyWindow.frame)/2+menuBlankWidth, CGRectGetHeight(self->keyWindow.frame));
//然后我们去设置辅助的view
self->helperSideView.center = CGPointMake(-20, 20);
self->helperCenterView.center = CGPointMake(-20, CGRectGetHeight(self->keyWindow.bounds)/2);
self->blurView.alpha = 0;
} completion:^(BOOL finished) {
//移除这个遮盖的view
[self->blurView removeFromSuperview];
//设置透明度为0.5
self->blurView.alpha = 0.5;
}];
}
再来看下我们获取两个辅助view的x的差值的方法
//获取两个辅助的view的x的坐标的插值
-(void)getDiff
{
if(!displayLink)
{
displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(displayLinkAction:)];
[displayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes];
}
}
定时器会定时调用的方法
//调用定时器的方法
-(void)displayLinkAction:(CADisplayLink *)displayLink
{
CALayer * layer1 = helperSideView.layer.presentationLayer;
CALayer * layer2 = helperCenterView.layer.presentationLayer;
//在动画当中我们可以通过valueForKeyPath去获取值
CGRect r1 = [[layer1 valueForKeyPath:@"frame"] CGRectValue];
CGRect r2 = [[layer2 valueForKeyPath:@"frame"] CGRectValue];
//这就是那两个辅助的view的x的差距
diff = r1.origin.x - r2.origin.x;
NSLog(@"%f",diff);
//接下来我们就需要用这个数据,在drawRect方法中利用贝塞尔曲线进行绘制
[self setNeedsDisplay];
}
移除定时器的方法,当我们的两个辅助view的弹簧动画结束的时候,就说明两者的x差值已经为0了,所以我们就可以将定时器给移除了
-(void)removeDisplayLink
{
[displayLink invalidate];
displayLink = nil;
}
还有就是绘方法drawRect方法,有了这个我们就可以画出波浪效果了
- (void)drawRect:(CGRect)rect
{
//波浪效果我们可以去使用贝塞尔曲线去画,先画一个矩形
//画出贝塞尔曲线
UIBezierPath * path = [UIBezierPath bezierPath];
//移动开始点到(0,0)的位置
[path moveToPoint:CGPointMake(0, 0)];
//画线到我们的尾节点
[path addLineToPoint:CGPointMake(CGRectGetWidth(keyWindow.frame)/2,0)];
//第一个参数为结束的节点,第二个参数为控制节点
//下面的控制点先加diff之后,再去返回
[path addQuadCurveToPoint:CGPointMake(CGRectGetWidth(keyWindow.frame)/2,CGRectGetHeight(keyWindow.frame)) controlPoint:CGPointMake(CGRectGetWidth(keyWindow.frame)/2+diff, CGRectGetHeight(keyWindow.frame)/2)];
//然后再往左边画线
[path addLineToPoint:CGPointMake(0, CGRectGetHeight(keyWindow.frame))];
//然后闭合曲线
[path closePath];
//获取上下文
CGContextRef context = UIGraphicsGetCurrentContext();
//然后我们去添加路径
CGContextAddPath(context, path.CGPath);
//设置绘制要使用的颜色
[menuColor set];
//使用非零圈数规则在当前路径中绘制区域
CGContextFillPath(context);
}
最后就是添加按钮的方法和添加按钮动画的方法,其中添加按钮的方法是在我们初始化的方法当中就被调用了,添加按钮动画的方法则是在我们点击切换按钮的时候会调用的switchAction方法当中被调用的
-(void)addBtnTitles:(NSArray *)titles
{
//计算上下的间隔
CGFloat space = (CGRectGetHeight(keyWindow.bounds)-titles.count*menuBtnHeight-(titles.count-1)*buttonSpace)/2;
for (int i=0; i<titles.count; i++) {
slideMenuBtn * btn = [[slideMenuBtn alloc]initWithTitle:titles[i]];
btn.center = CGPointMake(CGRectGetWidth(keyWindow.bounds)/4,space+menuBtnHeight*i+buttonSpace*i);
btn.bounds = CGRectMake(0,0,CGRectGetWidth(keyWindow.bounds)/2-20*2,menuBtnHeight);
[self addSubview:btn];
}
}
-(void)addBtnAnima
{
//遍历子视图
for (int i=0; i<self.subviews.count; i++) {
UIView * btn = self.subviews[i];
btn.transform = CGAffineTransformMakeTranslation(-100, 0);
[UIView animateWithDuration:.7 delay:(i*(.2/self.subviews.count)) usingSpringWithDamping:.6 initialSpringVelocity:0 options:UIViewAnimationOptionBeginFromCurrentState animations:^{
//恢复为原来的frame
btn.transform = CGAffineTransformIdentity;
} completion:nil];
}
}
关于按钮的声明如下所示
@interface slideMenuBtn : UIView
-(id)initWithTitle:(NSString *)title;
@property (nonatomic ,copy)void(^btnClickBlock)(void);
@end
具体实现
#import "slideMenuBtn.h"
@implementation slideMenuBtn
{
NSString * btnTitle;
}
//根据title,进行初始化
-(id)initWithTitle:(NSString *)title
{
self = [super init];
if (self) {
btnTitle = title;
}
return self;
}
-(void)drawRect:(CGRect)rect
{
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextAddRect(context, rect);
//初始化方法
UIColor * color = [UIColor colorWithRed:0 green:0.722 blue:1 alpha:1];
//https://www.jianshu.com/p/ae622c4ab7f4
UIBezierPath * path = [UIBezierPath bezierPathWithRoundedRect:CGRectInset(rect, 1, 1) cornerRadius:rect.size.height/2];
//设置描边的颜色
[[UIColor whiteColor]setStroke];
//设置填充的颜色
[color setFill];
path.lineWidth = 1;
//设置描边和填充
[path fill];
//圆角Path
[path stroke];
/**
NSMutableParagraphStyle是带属性的文本段落属性,用于控制段落有关属性(行间距,文本缩进等等)
https://blog.csdn.net/sw_gegewu/article/details/51399485
*/
NSMutableParagraphStyle *style = [[NSMutableParagraphStyle defaultParagraphStyle] mutableCopy];
//设置文本居中处理
style.alignment = NSTextAlignmentCenter;
/*
这个属性的值是一个NSParagraphStyle对象。使用此属性将多个属性应用到文本范围。如果不指定此属性,则该字符串将使用默认的段落属性,如NSParagraphStyle的defaultversihstyle方法返回的那样
*/
NSDictionary *attr = @{NSParagraphStyleAttributeName:style,
NSFontAttributeName:[UIFont systemFontOfSize:24.0f],
NSForegroundColorAttributeName:[UIColor whiteColor]
};
//根据属性来获取文字的size
CGSize size = [btnTitle sizeWithAttributes:attr];
CGRect r = CGRectMake(rect.origin.x, rect.origin.y+(rect.size.height-size.height)/2.0, rect.size.width, size.height);
[btnTitle drawInRect:r withAttributes:attr];
}
-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
self.btnClickBlock();
}
@end