第二十五篇、抽屉效果的核心代码

// pan 拽 手势处理
- (IBAction)panGesture:(UIPanGestureRecognizer *)sender
{
   
  // 如果是刚按下的状态,则记住,mainView的起始x
  if (UIGestureRecognizerStateBegan == sender.state) {
    _startX = self.mainView.frame.origin.x;
  }
   
   
  // 平移拖动的距离
  CGPoint delta = [sender translationInView:_mainView];
   
  CGRect frame = self.mainView.frame;
   
  // 计算新的x值,并做健壮性判断
  kEndX = _startX + delta.x;
   
  // 1,限制最大拖动范围
   
  if (kEndX >= kLeftWidth) {
    kEndX = kLeftWidth;
  }
  if (kEndX <= - kRightWidth) {
    kEndX = - kRightWidth;
  }
  // 2,由于 左view和右view在重叠,所以要隐藏其中的一个
  if (kEndX > 0) {
    // NSLog(@"--调用频率相当高--");
    _rightView.hidden = YES;
    _leftView.hidden = NO;
  } else {
    _rightView.hidden = NO;
    _leftView.hidden = YES;
  }
   
   
  if (UIGestureRecognizerStateEnded == sender.state) {
     
    // 手势结束的时候,需进行robust判断
     
    // 2,分析end松手时候,的位置x,决定展开到什么程度
/*
    // 2.1 如果只向右拖了一点点,小于 1/2 的左view的宽度,则归0
    if (kEndX < 0.5*kLeftWidth && kEndX >= 0) {
      kEndX = 0;
    }else if (kEndX >= 0.5*kLeftWidth && kEndX <= kLeftWidth) {
      // 2.2 如果向右拖一大半了,大于 1/2 的左view的宽度,虽然还没到位,也可以认为是到位了
      kEndX = kLeftWidth;
    }else if (kEndX > - 0.5*kRightWidth && kEndX <= 0) {
      // 2.3 如果只向左拖了一点点,小于 1/2 的右view的宽度,则归0
      kEndX = 0;
    }else if (kEndX <= - 0.5*kRightWidth) {
      // 2.4 如果向左拖一大半了,大于 1/2 的右view的宽度,虽然还没到位,也可以认为是到位了
      kEndX = - kRightWidth;
    }
*/
     
     
    // 第2种判断方式
    // 起始为0,delta.x大于0 代表向右滑动
    if (_startX == 0 && delta.x >0) {
      kEndX = kLeftWidth;
    }else if (_startX == 0 && delta.x < 0){
      // 起始为0,delta.x小于0 代表向左滑动
      kEndX = - kRightWidth;
    }else if (_startX == kLeftWidth && delta.x < 0){
      // 起始为kLeftWidth,delta.x小于0 代表向左滑动
      kEndX =0;
    }else if (_startX == - kRightWidth && delta.x > 0){
      // 起始为- kRightWidth,delta.x大于0 代表向右滑动
      kEndX = 0;
    }
     
 
     
  }
   
  // 最后,才设置mainView的新的frame
  [UIView animateWithDuration:0.2 animations:^{
        self.mainView.frame=frame;
  }];
   
   
   
  // 最后,为mainView所在的图层 添加阴影效果
  [self addShadowFormainViewWithEndX:kEndX];
   
}
 
// 自定义方法,为mainView所在的图层 添加阴影效果 (调用频率相当高)
- (void)addShadowFormainViewWithEndX:(CGFloat)endX
{
  // 1,点击工程,加号,导入第3方框架 #import <QuartzCore/QuartzCore.h>
   
  // 2,拿到mainView所在的图层,设置阴影 参数
  
  // NSLog(@"调用频率很高---");
  _mainView.layer.shadowColor = [UIColor blackColor].CGColor;
  _mainView.layer.shadowOpacity = 0.5;
  if (endX >= 0) {
    _mainView.layer.shadowOffset = CGSizeMake(-5, 0);
  } else {
    _mainView.layer.shadowOffset = CGSizeMake(5, 0);
  }
   
}
 
// 单击按钮,也一样可以展开 左右侧边栏
- (IBAction)btnClick:(UIButton *)sender
{
  // 定义一个临时变量
  CGFloat startX = _mainView.frame.origin.x;
   
   
  // 先为mainView所在的图层 添加阴影效果
  [self addShadowFormainViewWithEndX:sender.tag == 1?1:-1];
   
   
  // 定义一个临时变量
  CGFloat tempEndX = 0;
  // 左边的按钮被单击
  if (1 == sender.tag) {

    // 隐藏右半边
    _leftView.hidden = NO;
    _rightView.hidden = YES;
     
    if (startX == 0) {
      tempEndX = kLeftWidth;
    }else if (startX == kLeftWidth){
      tempEndX = 0;
    }
  } else {
    // 单击右边按钮, 隐藏左半边
    _leftView.hidden = YES;
    _rightView.hidden = NO;
    if (startX == 0) {
      tempEndX = - kRightWidth;
    }else if (startX == - kRightWidth){
      tempEndX = 0;
    }
  }
  // 最后才设置mainView的x,调用抽取出来的公共代码,设置mainView的x,参数是endX
  [self setmainViewX:tempEndX];
   
   
 
}
 
 
// 抽取出来的公共代码,设置mainView的x,参数是endX
- (void)setmainViewX:(CGFloat)endX
{
  CGRect frame = self.mainView.frame;
  frame.origin.x = endX;
  [UIView animateWithDuration:0.2 animations:^{
    self.mainView.frame=frame;
  }];
   
}

三个view提供给外面的属性都是weak

动画效果用CATransform3DMakeScale

第二种实现方式:

.h

#define rScreenHeight [UIScreen mainScreen].bounds.size.height
#define rScreenWidth [UIScreen mainScreen].bounds.size.width

#define rOffSet rScreenWidth / 4

#import <UIKit/UIKit.h>

@interface RLSideslipController : UIViewController

/*
 *初始化方法
 *main      最中间的视图控制器
 *left      向右滑动时的视图控制器
 *
 */
- (instancetype)initWithMainViewController:(UIViewController *)main
                        leftViewController:(UIViewController *)left;

@end

.m

#define rMainFrame CGRectMake(0,0,rScreenWidth,rScreenHeight)
#define rLeftFrame CGRectMake(-rOffSet,0,rScreenWidth,rScreenHeight)

#define rVelocityRatio rOffSet / (rScreenWidth - rOffSet)

#import "RLSideslipController.h"

@interface RLSideslipController ()

@property (nonatomic, strong) UIViewController * mainVC;
@property (nonatomic, strong) UIViewController * leftVC;
@property (nonatomic, strong) UIView * maskView;
//左侧边缘手势
@property (nonatomic, strong) UIScreenEdgePanGestureRecognizer * edge;
//右滑手势
@property (nonatomic, strong) UIPanGestureRecognizer * pan;
//点击手势
@property (nonatomic, strong) UITapGestureRecognizer * tap;

@end

#pragma mark - initalizer
- (instancetype)initWithMainViewController:(UIViewController *)main leftViewController:(UIViewController *)left {
    if (self = [super init]) {
        self.mainVC = main;
        self.leftVC = left;
    }
    return self;
}

#pragma mark - load
- (void)viewDidLoad {
    [super viewDidLoad];
    //设置两个视图的frame
    self.mainVC.view.frame = rMainFrame;
    self.leftVC.view.frame = rLeftFrame;
    //将两个视图添加进来,并且main在上层
    [self.view addSubview:self.leftVC.view];
    [self.view addSubview:self.mainVC.view];
    //初始添加侧滑手势
    [self.view addGestureRecognizer:self.edge];
}

#pragma mark - Getters
- (UIScreenEdgePanGestureRecognizer *)edge {
    if (!_edge) {
        _edge = [[UIScreenEdgePanGestureRecognizer alloc] initWithTarget:self action:@selector(respondsToEdge:)];
        _edge.edges = UIRectEdgeLeft;//设置只有在左侧边缘滑动时才响应
    }
    return _edge;
}

- (UIPanGestureRecognizer *)pan {
    if (!_pan) {
        _pan = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(respondsToPan:)];
    }
    return _pan;
}

- (UITapGestureRecognizer *)tap {
    if (!_tap) {
        _tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(respondsToTap:)];
    }
    return _tap;
}

- (UIView *)maskView {
    if (!_maskView) {
        _maskView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, rScreenWidth, rScreenHeight)];
        _maskView.backgroundColor = [UIColor clearColor];
    }
    return _maskView;
}

#pragma mark - responds
- (void)respondsToEdge:(UIScreenEdgePanGestureRecognizer *)sender {
    CGPoint transformPoint = [sender translationInView:self.view];
    switch (sender.state) {
        case UIGestureRecognizerStateBegan: {
            [self.maskView addGestureRecognizer:self.tap];
            [self.mainVC.view addSubview:self.maskView];
        }
            break;
        case UIGestureRecognizerStateChanged: {
            //不等式rScreenWidth * 3 / 2 - rOffSet >= mainCenterX >= rScreenWidth / 2
            CGFloat mainCenterX = MIN(rScreenWidth * 3 / 2 - rOffSet, MAX(rScreenWidth / 2, rScreenWidth / 2 + transformPoint.x));
            //不等式rScreenWidth / 2 >= mainCenterX >= rScreenWidth / 2 - rOffSet
            CGFloat leftCenterX = MIN(rScreenWidth / 2, MAX(rScreenWidth / 2 - rOffSet, rScreenWidth / 2 - rOffSet + transformPoint.x * rVelocityRatio));
            self.mainVC.view.center = CGPointMake(mainCenterX, rScreenHeight / 2);
            self.leftVC.view.center = CGPointMake(leftCenterX, rScreenHeight / 2);
        }
            break;
        case UIGestureRecognizerStateEnded: {
            CGFloat mainCurrentCenterX = self.mainVC.view.center.x;//当前主视图的位置
            CGFloat allOffSet = rScreenWidth - rOffSet;//主视图应该移动的偏移量
            CGFloat ratio = (mainCurrentCenterX - rScreenWidth / 2) / allOffSet;//当前完成了得百分比
            __weak typeof(self)weakSelf = self;
            if (ratio >= 0.6) {
                //完成显示的剩余动画
                [self showWithRatio:ratio completion:^{
                    [weakSelf.view removeGestureRecognizer:weakSelf.edge];
                    [weakSelf.mainVC.view addGestureRecognizer:weakSelf.pan];
                }];
            }else {
                //完成消失的剩余动画
                [self dismissWithRatio:ratio completion:^{
                    [weakSelf.maskView removeGestureRecognizer:weakSelf.tap];
                    [weakSelf.maskView removeFromSuperview];
                }];
            }
        }
            break;
        default:
            break;
    }
}

- (void)respondsToPan:(UIPanGestureRecognizer *)sender {
    CGPoint transformPoint = [sender translationInView:self.view];
    //如果向右滑动,则直接返回
    if (transformPoint.x > 0) {
        return;
    }
    switch (sender.state) {
        case UIGestureRecognizerStateChanged: {
            //不等式rScreenWidth * 3 / 2 - rOffSet >= mainCenterX >= rScreenWidth / 2
            CGFloat mainCenterX = MIN(rScreenWidth * 3 / 2 - rOffSet, MAX(rScreenWidth / 2, rScreenWidth * 3 / 2 - rOffSet + transformPoint.x));
            //不等式rScreenWidth / 2 >= mainCenterX >= rScreenWidth / 2 - rOffSet
            CGFloat leftCenterX = MIN(rScreenWidth / 2, MAX(rScreenWidth / 2 - rOffSet, rScreenWidth / 2 + transformPoint.x * rVelocityRatio));
            self.mainVC.view.center = CGPointMake(mainCenterX, rScreenHeight / 2);
            self.leftVC.view.center = CGPointMake(leftCenterX, rScreenHeight / 2);
        }
            break;
        case UIGestureRecognizerStateEnded: {
            CGFloat mainCurrentCenterX = self.mainVC.view.center.x;//当前主视图的位置
            CGFloat allOffSet = rScreenWidth - rOffSet;//主视图应该移动的偏移量
            CGFloat ratio = (mainCurrentCenterX - rScreenWidth / 2) / allOffSet;//当前完成了得百分比
            if (ratio < 0.4) {
                __weak typeof(self)weakSelf = self;
                [self dismissWithRatio:ratio completion:^{
                    [weakSelf.view addGestureRecognizer:weakSelf.edge];
                    [weakSelf.mainVC.view removeGestureRecognizer:weakSelf.pan];
                    [weakSelf.maskView removeGestureRecognizer:weakSelf.tap];
                    [weakSelf.maskView removeFromSuperview];
                }];
            }else {
                [self showWithRatio:1 - ratio completion:nil];
            }
        }
            break;
        default:
            break;
    }
}

- (void)respondsToTap:(UITapGestureRecognizer *)sender {
    __weak typeof(self)weakSelf = self;
    [self dismissWithRatio:1 completion:^{
        [weakSelf.view addGestureRecognizer:weakSelf.edge];
        [weakSelf.mainVC.view removeGestureRecognizer:weakSelf.pan];
        [weakSelf.maskView removeGestureRecognizer:weakSelf.tap];
        [weakSelf.maskView removeFromSuperview];
    }];
}

#pragma mark - show or dismiss
- (void)showWithRatio:(CGFloat)ratio completion:(void(^)(void))comple  {
    __weak typeof(self)weakSelf = self;
    [UIView animateWithDuration:1 * (1 - ratio) animations:^{
        weakSelf.mainVC.view.center = CGPointMake(rScreenWidth * 3 / 2 - rOffSet, rScreenHeight / 2);
        weakSelf.leftVC.view.center = CGPointMake(rScreenWidth / 2, rScreenHeight / 2);
    } completion:^(BOOL finished) {
        if (comple) {
            comple();
        }
    }];
}

- (void)dismissWithRatio:(CGFloat)ratio completion:(void(^)(void))comple {
    __weak typeof(self)weakSelf = self;
    [UIView animateWithDuration:1 * ratio animations:^{
        weakSelf.mainVC.view.center = CGPointMake(rScreenWidth / 2, rScreenHeight / 2);
        weakSelf.leftVC.view.center = CGPointMake(rScreenWidth / 2 - rOffSet, rScreenHeight / 2);
    } completion:^(BOOL finished) {
        if (comple) {
            comple();
        }
    }];
}
原文地址:https://www.cnblogs.com/HJQ2016/p/5870082.html