// 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(); } }]; }