仿QQ左侧滑动菜单的实现

冲动


最近接手的一个老项目有个侧滑菜单的需求,这个老项目的侧滑菜单用了网络上的一个第三方库,但是这个库竟然没有处理好导航条的显示,以至于项目中很多需要导航条展示信息的视图都通过添加subView的方式模仿了导航条。这简直是暴殄天物啊,放着真正的导航栏那强大的作用不用,浪费了多少时间和精力啊,都是青春啊,开发的前路上早已挖了无数的坑等着身心疲惫的我们往里跳啊。对此深恶痛绝的我,早就下了不杀此贼誓不罢休的决心。好吧,最近看三国看的有点多了。

暗渡陈仓


我在无所不能的GitHub上搜索侧滑菜单项目,试验了各种,发觉没有能很好的符合当前项目需求。我们当前的项目需求是跟手机QQ左侧滑动菜单一样的效果,有个默认的主视图,然后其他视图的展示都是在这个视图的基础上push出来的。当然把这些项目改造出这种功能其实也不难,但是左侧菜单的视差效果都跟QQ相差甚远,唯一相似的就是RESideMenu这个比较有名的项目,但是它的视差效果是菜单在展示过程中由比较大然后缩放到实际大小,感觉不如QQ的效果好,怎奈我当时心情比骄傲浮躁,改了个参数也没调出想要的效果来,于是一怒之下决定自己写一个。事后,发现有个Swift版的特别炫酷,完全模仿的QQ,点这里查看
Swift版手机QQ侧滑菜单

大哭,此大神还有相应的教程,如果各位看官不想听我接下来的实现思路又能看懂Swift的可以瞬移到此大神的博客了。

思路


不送..... 呜呜呜......
先看我寒碜的效果
本人的手机QQ侧滑菜单

实在忍不了,换了小图了

哎,炫酷没法比啊。

iOS5之后每个UIViewController都可以轻松当一个容器了,可以同时管理许多其他的UIViewController了。

addChildViewController:
willMoveToParentViewController: 
didMoveToParentViewController:

拿去,不谢!具体怎么写一个容器不在本文的讨论重点,还请大家自行谷歌。好吧,我是在用红杏翻墙 软广取消,红杏被郭嘉干掉后我的钱也追不回来了

现在的话我有两个子视图控制器了,把他们的view分别放在了我自定义的menuViewContainer和contentViewContainer这两个View里。OK,基础已打好。
理所应当,我在contentViewCcontainer上添加一个滑动手势,滑动时移动视图的位置就行了。那怎么确定手指在滑动时相应的视图应该滑动到哪呢?滑动到哪里结束呢?怎么确定同时改缩放到什么程度呢?
这些问题我本来想一一说讲一遍,发现自己太啰嗦,又不知道该怎么讲,所以还是直接上代码吧。
- (void)panGestureRecognizer:(UIScreenEdgePanGestureRecognizer *)recognizer{

    CGPoint point = [recognizer translationInView:self.view];

    if(recognizer.state == UIGestureRecognizerStateBegan) {
       [self updateContentViewShadow];
    }else if(recognizer.state == UIGestureRecognizerStateChanged){

       CGFloat menuVisibleWidth = self.view.bounds.size.width-self.realContentViewVisibleWidth;
       CGFloat delta = self.menuHidden ? point.x/menuVisibleWidth : (menuVisibleWidth+point.x)/menuVisibleWidth;
       CGFloat scale = 1-(1-MinScaleContentView)*delta;
       CGFloat menuScale = MinScaleMenuView + (1-MinScaleMenuView)*delta;

       if(self.menuHidden){
          //以内容视图最小缩放为界限
          if(scale < MinScaleContentView){//A
            self.contentViewContainer.transform = CGAffineTransformMakeTranslation(menuVisibleWidth, 0);
            self.contentViewContainer.transform = CGAffineTransformScale(self.contentViewContainer.transform,MinScaleContentView,MinScaleContentView);
            self.contentViewScale = MinScaleContentView;
            self.menuViewContainer.transform = CGAffineTransformMakeScale(1, 1);
            self.menuViewContainer.transform = CGAffineTransformTranslate(self.menuViewContainer.transform, 0, 0);
           }else{//大于最小界限又分大于等于1和小于1两种情况
             if(scale < 1){//B
                self.contentViewContainer.transform = CGAffineTransformMakeTranslation(point.x, 0);
                self.contentViewContainer.transform = CGAffineTransformScale(self.contentViewContainer.transform,scale, scale);
                self.contentViewScale = scale;
                self.menuViewContainer.transform = CGAffineTransformMakeScale(menuScale, menuScale);
                self.menuViewContainer.transform = CGAffineTransformTranslate(self.menuViewContainer.transform, -MoveDistanceMenuView *(1-delta), 0);
             }else{//C
                self.contentViewContainer.transform = CGAffineTransformMakeTranslation(0, 0);
                self.contentViewContainer.transform = CGAffineTransformScale(self.contentViewContainer.transform,1, 1);
                self.contentViewScale = 1;
                self.menuViewContainer.transform = CGAffineTransformMakeScale(MinScaleMenuView, MinScaleMenuView);
                self.menuViewContainer.transform = CGAffineTransformTranslate(self.menuViewContainer.transform, -MoveDistanceMenuView, 0);
            }
        }          
    }else{         
        if(scale > 1){//D
            self.contentViewContainer.transform = CGAffineTransformMakeTranslation(0, 0);
            self.contentViewContainer.transform = CGAffineTransformScale(self.contentViewContainer.transform,1,1);
            self.contentViewScale = 1;
            self.menuViewContainer.transform = CGAffineTransformMakeScale(MinScaleMenuView, MinScaleMenuView);
            self.menuViewContainer.transform = CGAffineTransformTranslate(self.menuViewContainer.transform, -MoveDistanceMenuView, 0);
        }else{
            if(scale>MinScaleContentView){//E
                self.contentViewContainer.transform = CGAffineTransformMakeTranslation(point.x+menuVisibleWidth, 0);
                self.contentViewContainer.transform = CGAffineTransformScale(self.contentViewContainer.transform,scale, scale);
                self.contentViewScale = scale;
                self.menuViewContainer.transform = CGAffineTransformMakeScale(menuScale, menuScale);
                self.menuViewContainer.transform = CGAffineTransformTranslate(self.menuViewContainer.transform, -MoveDistanceMenuView * (1-delta), 0);
            }else{//F
                self.contentViewContainer.transform =CGAffineTransformMakeTranslation(self.view.bounds.size.width-self.realContentViewVisibleWidth, 0);
                self.contentViewContainer.transform = CGAffineTransformScale(self.contentViewContainer.transform,MinScaleContentView, MinScaleContentView);
                self.contentViewScale = MinScaleContentView;
                self.menuViewContainer.transform = CGAffineTransformMakeScale(1, 1);
                self.menuViewContainer.transform = CGAffineTransformTranslate(self.menuViewContainer.transform, 0, 0);
            }
        }
    }        
  }else if(recognizer.state == UIGestureRecognizerStateEnded){
    [self showMenu:(self.contentViewContainer.frame.origin.x > self.view.bounds.size.width/2)];
  }
}

这样的结尾是不是太不负责任了???
好吧,我提醒大家,整段代码的精华全在delta的计算,视图滑动后的最终状态是由menuVisibleWidth和MinSacleContentView来决定。根据当前手势滑动的距离占menuVisibleWidth的百分比来计算滑动动作完成度,然后根据这个值来移动和缩放相应的视图。

最后,上我的项目地址,如果大家不嫌弃,请给star,如果有问题,烦请留言。感激涕零


标题:仿QQ左侧滑动菜单的实现
作者:yuyedaidao
地址:http://mooncake.wang/articles/2018/11/09/1573699411029.html