ios-侧滑栏的动画制作

基本了解

首先我们先简单的来看下实现的效果,在侧滑栏出来的时候是会有下面的波浪的效果的

这里写图片描述
这里写图片描述

首先我们先分步骤来进行规划

  • 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
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值