美文网首页
关于侧滑菜单的一些见解(仿QQ)

关于侧滑菜单的一些见解(仿QQ)

作者: winsonGali | 来源:发表于2019-04-17 15:39 被阅读0次

    QQ在这两天升级了新版本,侧滑出来的左侧菜单居然变成了全屏……
    上个版本还是占屏80%的呢,而我也是参照上个版本80%屏而模仿的,以上。

    效果图:
    Apr-17-2019 16-28-13.gif
    视图层级

    在Container上分别添加Main、Left、Right为子控制器;
    在Container的视图上分别添加Main、Left、Right的视图;
    将Main视图置于最上层,隐藏Left、Right视图;


    视图层级.png
    显示左侧菜单

    从左往右划,显示左视图,想对平移,Main视图逐渐渐变暗,滑至指定处停止。

    显示右侧菜单

    从右往左划,显示右视图,想对平移,Main视图逐渐渐变暗,滑至指定处停止。

    关键代码
    
    - (void)panGuesture:(UIPanGestureRecognizer *)panGuesture {
        CGPoint point = [panGuesture locationInView:panGuesture.view];
        
        if (panGuesture.state == UIGestureRecognizerStateBegan) {
            NSLog(@"Pan start");
            startPoint = point;
            
        } else if (panGuesture.state == UIGestureRecognizerStateChanged) {
            //启动左视图侧滑
            if (startPoint.x < RIGHT_GAP) {
                _leftSideVC.view.hidden = NO;
                CGFloat offset = point.x - startPoint.x;
                CGFloat originX = _mainVC.view.frame.origin.x;
                CGFloat finalOriginX = offset + originX;
                CGFloat maxOffSet = [UIScreen mainScreen].bounds.size.width - RIGHT_GAP;
                finalOriginX = finalOriginX < 0 ? 0 : finalOriginX;
                finalOriginX = finalOriginX > maxOffSet ? maxOffSet : finalOriginX;
                if (finalOriginX >= 0 && finalOriginX <= maxOffSet) {
                    _mainVC.view.frame = CGRectMake(finalOriginX, 0, CGRectGetWidth(_mainVC.view.bounds), CGRectGetHeight(_mainVC.view.bounds));
                    CGFloat sss = finalOriginX * (_leftSideVC.view.frame.size.width / 2.00) / maxOffSet;
                    _leftSideVC.view.frame = CGRectMake(- _leftSideVC.view.frame.size.width / 2.00 + sss, 0, _leftSideVC.view.frame.size.width, _leftSideVC.view.frame.size.height);
                }
                _mainVC.alphaView.alpha = 1.00 * finalOriginX / maxOffSet;
            }
            //启动右视图侧滑
            if (startPoint.x > _mainVC.view.bounds.size.width - RIGHT_GAP) {
                _rightSideVC.view.hidden = NO;
                CGFloat offset = point.x - startPoint.x;
                CGFloat originX = _mainVC.view.frame.origin.x;
                CGFloat finalOriginX = offset + originX;
                if (finalOriginX >= - (_mainVC.view.bounds.size.width - RIGHT_GAP) && finalOriginX <= 0) {
                    _mainVC.view.frame = CGRectMake(finalOriginX, 0, CGRectGetWidth(_mainVC.view.bounds), CGRectGetHeight(_mainVC.view.bounds));
                    CGFloat sss = -finalOriginX * (_rightSideVC.view.frame.size.width / 2.00) / (_rightSideVC.view.bounds.size.width - RIGHT_GAP);
                    _rightSideVC.view.frame = CGRectMake(_rightSideVC.view.frame.size.width / 2.00 - sss, 0, _rightSideVC.view.frame.size.width, _rightSideVC.view.frame.size.height);
                }
                _mainVC.alphaView.alpha = 1.00 * finalOriginX / (RIGHT_GAP - _mainVC.view.bounds.size.width);
            }
        } else if (panGuesture.state == UIGestureRecognizerStateEnded) {
            NSLog(@"Pan ended");
            CGPoint speedPoint = [panGuesture velocityInView:panGuesture.view];
            //启动左视图侧滑
            if (startPoint.x < RIGHT_GAP) {
                //滑动速度大于500,类似于swip手势,直接就显示左视图。
                if (speedPoint.x > 500) {
                    [self showLeftSideVC];
                } else if (speedPoint.x < -500) {
                    [self hideLeftSideVC];
                } else {
                    NSLog(@"Pan ended showAlpha = %.2f", _mainVC.alphaView.alpha);
                    CGFloat maxOffSet = [UIScreen mainScreen].bounds.size.width - RIGHT_GAP;
                    CGFloat originX = _mainVC.view.frame.origin.x;
                    if (originX < maxOffSet / 2.00) {
                        [self hideLeftSideVC];
                    } else {
                        [self showLeftSideVC];
                    }
                }
            }
            //启动右视图侧滑
            if (startPoint.x > _mainVC.view.bounds.size.width - RIGHT_GAP) {
                //滑动速度大于500,类似于swip手势,直接就显示右视图。
                if (speedPoint.x > 500) {
                    [self hideRightSideVC];
                } else if (speedPoint.x < -500) {
                    [self showRightSideVC];
                } else {
                    CGFloat maxOffSet = [UIScreen mainScreen].bounds.size.width - RIGHT_GAP;
                    CGFloat originX = _mainVC.view.frame.origin.x;
                    if (originX < - maxOffSet / 2.00) {
                        [self showRightSideVC];
                    } else {
                        [self hideRightSideVC];
                    }
                }
            }
             startPoint = CGPointMake(0, 0);
        } else if (panGuesture.state == UIGestureRecognizerStateCancelled) {
            NSLog(@"Pan Canceled");
        } else if (panGuesture.state == UIGestureRecognizerStateFailed) {
            NSLog(@"Pan failed");
        } else if (panGuesture.state == UIGestureRecognizerStatePossible) {
            NSLog(@"Pan possible");
        } else if (panGuesture.state == UIGestureRecognizerStateRecognized) {
            NSLog(@"Pan recognized");
        }
    }
    
    
    - (void)showLeftSideVC {
        NSLog(@"showLeftSideVC");
        _leftSideVC.view.hidden = NO;
        CGFloat maxOffSet = [UIScreen mainScreen].bounds.size.width - RIGHT_GAP;
        __weak typeof(self) weakSelf = self;
        [UIView animateWithDuration:0.2 animations:^{
            weakSelf.mainVC.alphaView.alpha = 1;
            weakSelf.mainVC.view.frame = CGRectMake(maxOffSet, 0, weakSelf.mainVC.view.bounds.size.width, weakSelf.mainVC.view.bounds.size.height);
            weakSelf.leftSideVC.view.frame = CGRectMake(0, 0, weakSelf.leftSideVC.view.frame.size.width, weakSelf.leftSideVC.view.frame.size.height);
        } completion:^(BOOL finished) {
            
        }];
    }
    
    - (void)hideLeftSideVC {
        NSLog(@"hideLeftSideVC");
        __weak typeof(self) weakSelf = self;
        [UIView animateWithDuration:0.2 animations:^{
            weakSelf.mainVC.alphaView.alpha = 0;
            weakSelf.mainVC.view.frame = CGRectMake(0, 0, weakSelf.mainVC.view.bounds.size.width, weakSelf.mainVC.view.bounds.size.height);
            weakSelf.leftSideVC.view.frame = CGRectMake(- weakSelf.leftSideVC.view.frame.size.width / 2.00, 0, weakSelf.leftSideVC.view.frame.size.width, weakSelf.leftSideVC.view.frame.size.height);
        } completion:^(BOOL finished) {
            weakSelf.leftSideVC.view.hidden = YES;
        }];
    }
    
    
    - (void)showRightSideVC {
        NSLog(@"showRightSideVC");
        _rightSideVC.view.hidden = NO;
        CGFloat maxOffSet = [UIScreen mainScreen].bounds.size.width - RIGHT_GAP;
        __weak typeof(self) weakSelf = self;
        [UIView animateWithDuration:0.2 animations:^{
            weakSelf.mainVC.alphaView.alpha = 1;
            weakSelf.mainVC.view.frame = CGRectMake(-maxOffSet, 0, weakSelf.mainVC.view.bounds.size.width, weakSelf.mainVC.view.bounds.size.height);
            weakSelf.rightSideVC.view.frame = CGRectMake(0, 0, weakSelf.rightSideVC.view.frame.size.width, weakSelf.rightSideVC.view.frame.size.height);
        } completion:^(BOOL finished) {
            
        }];
    }
    
    - (void)hideRightSideVC {
        NSLog(@"hideRightSideVC");
        __weak typeof(self) weakSelf = self;
        [UIView animateWithDuration:0.2 animations:^{
            weakSelf.mainVC.alphaView.alpha = 0;
            weakSelf.mainVC.view.frame = CGRectMake(0, 0, weakSelf.mainVC.view.bounds.size.width, weakSelf.mainVC.view.bounds.size.height);
            weakSelf.rightSideVC.view.frame = CGRectMake(weakSelf.rightSideVC.view.frame.size.width / 2.00, 0, weakSelf.rightSideVC.view.frame.size.width, weakSelf.leftSideVC.view.frame.size.height);
        } completion:^(BOOL finished) {
            weakSelf.rightSideVC.view.hidden = YES;
        }];
    }
    
    
    一些问题

    1、手势由于添加在_mainVC.view上,如果这个_mainVC为导航控制器,则需要处理滑动手势问题,只有在根目录才可以从左往右划打开左菜单:

    - (void)doPan:(UIPanGestureRecognizer *)pan {    
       //只有mainNVC回到根目录的时候,才可以打开侧滑菜单。
       if (_mainNVC.viewControllers.count > 1) {
           return;
       }
    }
    

    2、假设_mainVC.view上有个tableView,tableViewCell可以左滑删除,此情况下不能打开右菜单,目前未能想到比较好的解决方案;

    3、假设只有左菜单,_mainVC.view上有个tableView,tableViewCell可以左滑删除,由于pan手势直接加在_mainVC.view上,所以会造成pan手势与tableViewCell左滑删除手势冲突,导致左滑不能调起tableViewCell的侧滑事件,此时可通过手势代理相关设置来解决此办法,以下是我的解决办法:

    - (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch {
        //当手势起始触摸点超过屏幕右侧一半的时候,忽略pan手势。
        CGPoint point = [touch locationInView:touch.view];
        if (point.x > _mainNVC.view.bounds.size.width / 2.0) {
            return NO;
        }
        return YES;
    }
    

    4、关于左菜单点击事件引起的跳转问题,左菜单点击任何一个按钮,不能在左菜单的导航控制器推出,应由_mainVC的导航控制器推出,此处涉及到一个事件传递,以下是我的解决办法:
    左侧菜单是一个LeftNVC,rootVC是LeftVC,主菜单是MainNVC。
    a. 给LeftNVC设置一个MainNVC的属性:

    #import <UIKit/UIKit.h>
    #import "MainNVC.h"
    @interface LeftNVC : UINavigationController
    @property (strong, nonatomic) MainNVC *mainNVC;
    @end
    

    b.在ContainerVC里面将_mainNVC赋值给_leftNVC:

    _leftNVC.mainNVC = _mainNVC;
    

    c.在_leftVC里面用关联对象_mainNVC去推新页面:

    #import "LeftVC.h"
    #import "LeftNVC.h"
    #import "CacheVC.h"
    
    @interface LeftVC ()
    @property (strong, nonatomic) LeftNVC *selfNVC;
    @end
    
    @implementation LeftVC
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        self.selfNVC = (LeftNVC *)self.navigationController;
    }
    
    - (IBAction)gotoCacheVC:(id)sender {
        //恢复原状
        if (self.selfNVC.mainNVC.tapToDissmissBlock) {
            self.selfNVC.mainNVC.tapToDissmissBlock();
        }
        //在mainNVC推出新页面
        CacheVC *vc = [[UIStoryboard storyboardWithName:@"Main" bundle:nil] instantiateViewControllerWithIdentifier:@"CacheVC"];
        [self.selfNVC.mainNVC pushViewController:vc animated:YES];
    }
    

    Demo

    相关文章

      网友评论

          本文标题:关于侧滑菜单的一些见解(仿QQ)

          本文链接:https://www.haomeiwen.com/subject/apcnwqtx.html