美文网首页
关于侧滑菜单的一些见解(仿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)

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

  • Flutter-抽屉效果只能拖拽

    flutter_new类似日历的抽屉 仿QQ侧滑菜单

  • 仿QQ侧滑菜单

    一. 实例说明 二. 关键技术    使用自定义类 QQMenu 类中的构造方法,View类中的 onMeasur...

  • Android仿qq侧滑菜单

    我们经常能看到各种app中都有应用侧滑菜单(SlidingMenu),效果很好的一种显示方式,今天我就向大家展示可...

  • iOS仿QQ侧滑菜单

    最近看到QQ上的侧滑菜单特有意思,就自己试着去实现了一下,看上去效果相差不大。最后的效果图: 首先,需要自定义一个...

  • Android仿QQ侧滑菜单

    先上效果图: GIF图有点模糊,源码已上传Github:Android仿QQ侧滑菜单 整体思路: 自定义ItemV...

  • iOS仿QQ侧滑菜单

    第一次在简书上写文章。 今天我要记录下如何拿UITabBarController做QQ侧滑菜单效果: 首先要了解U...

  • DrawerLayout

    Android DrawerLayout 高仿QQ5.2双向侧滑菜单 DrawerLayout的使用 直接将Dra...

  • Android FrameLayout+ViewDragHelp

    大家好!这篇介绍FrameLayout+ViewDragHelper实现QQ7.1.0侧滑菜单,在QQ侧滑菜单上我...

  • 仿QQ侧滑菜单(详细注释)

    滑动菜单: HorizontalScrollView实现侧滑 先定义一个MyScrollView类,继承与Hori...

网友评论

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

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