iOS开发-抽屉效果

抽屉效果以前比较多,现在看到的比较少,手Q和今日头条现在侧边滑动通过抽屉的方式实现,关于第三方的抽屉效果有很多,稍微对比了一下实用效果比较实用的有两个RESideMenu和MMDrawerController,网上也有人推荐PPRevealSideViewController单就星星的数量的数量而言和上面两个都不一个层级的,慎重选择。本文先来看一下两者的效果,然后介绍抽屉效果实现原理.

RESideMenu

RESlideMenu.gif

代码实现起来也很简单:

    UINavigationController *navigationController = [[UINavigationController alloc] initWithRootViewController:[[DEMOFirstViewController alloc] init]];
    DEMOLeftMenuViewController *leftMenuViewController = [[DEMOLeftMenuViewController alloc] init];
    DEMORightMenuViewController *rightMenuViewController = [[DEMORightMenuViewController alloc] init];
    
    RESideMenu *sideMenuViewController = [[RESideMenu alloc] initWithContentViewController:navigationController
                                                                    leftMenuViewController:leftMenuViewController
                                                                   rightMenuViewController:rightMenuViewController];
    sideMenuViewController.backgroundImage = [UIImage imageNamed:@"Stars"];
    sideMenuViewController.menuPreferredStatusBarStyle = 1; // UIStatusBarStyleLightContent
    sideMenuViewController.delegate = self;
    sideMenuViewController.contentViewShadowColor = [UIColor blackColor];
    sideMenuViewController.contentViewShadowOffset = CGSizeMake(0, 0);
    sideMenuViewController.contentViewShadowOpacity = 0.6;
    sideMenuViewController.contentViewShadowRadius = 12;
    sideMenuViewController.contentViewShadowEnabled = YES;
    self.window.rootViewController = sideMenuViewController;

抽屉的动画效果,通过内容控制器和抽屉控制器,统一的由RESlideMenu进行管理,RESlideMenu同样是继承自UIViewController,支持iOS6.0及其以上手机版本.

MMDrawerController

MMControllers.gif

单从Demo的效果看,作者还是挺用心的,很赞,基本上你需要的效果都能实现,用来做今日头条的抽屉效果毫无压力。主体实现代码:

    UIViewController * leftSideDrawerViewController = [[MMExampleLeftSideDrawerViewController alloc] init];

    UIViewController * centerViewController = [[MMExampleCenterTableViewController alloc] init];
    
    UIViewController * rightSideDrawerViewController = [[MMExampleRightSideDrawerViewController alloc] init];
    
    UINavigationController * navigationController = [[MMNavigationController alloc] initWithRootViewController:centerViewController];
    [navigationController setRestorationIdentifier:@"MMExampleCenterNavigationControllerRestorationKey"];
    UINavigationController * rightSideNavController = [[MMNavigationController alloc] initWithRootViewController:rightSideDrawerViewController];
    [rightSideNavController setRestorationIdentifier:@"MMExampleRightNavigationControllerRestorationKey"];
    UINavigationController * leftSideNavController = [[MMNavigationController alloc] initWithRootViewController:leftSideDrawerViewController];
    [leftSideNavController setRestorationIdentifier:@"MMExampleLeftNavigationControllerRestorationKey"];
    self.drawerController = [[MMDrawerController alloc]
                        initWithCenterViewController:navigationController
                        leftDrawerViewController:leftSideNavController
                        rightDrawerViewController:rightSideNavController];
    [self.drawerController setShowsShadow:NO];
    [self.drawerController setRestorationIdentifier:@"MMDrawer"];
    [self.drawerController setMaximumRightDrawerWidth:200.0];
    [self.drawerController setOpenDrawerGestureModeMask:MMOpenDrawerGestureModeAll];
    [self.drawerController setCloseDrawerGestureModeMask:MMCloseDrawerGestureModeAll];
    //http://www.jianshu.com/users/24da48b2ddb3/latest_articles
    [self.drawerController
     setDrawerVisualStateBlock:^(MMDrawerController *drawerController, MMDrawerSide drawerSide, CGFloat percentVisible) {
         MMDrawerControllerDrawerVisualStateBlock block;
         block = [[MMExampleDrawerVisualStateManager sharedManager]
                  drawerVisualStateBlockForDrawerSide:drawerSide];
         if(block){
             block(drawerController, drawerSide, percentVisible);
         }
     }];
    self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];

    UIColor * tintColor = [UIColor colorWithRed:29.0/255.0
                                          green:173.0/255.0
                                           blue:234.0/255.0
                                          alpha:1.0];
    [self.window setTintColor:tintColor];
    [self.window setRootViewController:self.drawerController];

    return YES;

从最上面的三行代码我们可以看到抽屉效果实现的逻辑是内容和侧边栏的一个集中控制器,关于详细的API可以在文中的底部的参考地址详细研究~

模拟实现

如果实际开发中无论是从时间还是从效果上看建议选择以上两者中的一种实现,以下只是作为抽屉效果的一点探索,与大家分享,只适用于学习:

抽屉模拟.gif

效果很简单,实现起来很简单,这里UITableView展示内容,背景视图当做左右抽屉,偷了懒,UITableView拖动通过UIPanGestureRecognizer实现的,有人是通过UIResponder中的touchesMoved和touchesEnded实现计算偏移量的代码:

    UITouch *touch = [touches anyObject];
    CGPoint currentPoint = [touch locationInView:self.tableView];
    CGPoint prePoint = [touch previousLocationInView:self.tableView];

对于UITableView是无效的,内容如果是单纯的UIView是可以通过以上代码实现抽屉效果的;
数据初始化代码:

    for (NSInteger i=0;i<5; i++) {
        [self.dataSource addObject:[NSString stringWithFormat:@"FlyElephant-%ld",i]];
    }
    
    [self.view addSubview:self.leftDrawer];
    UIPanGestureRecognizer *panGesture=[[UIPanGestureRecognizer alloc]initWithTarget:self action:@selector(panGestureRecognizer:)];
    [self.tableView addGestureRecognizer:panGesture];
    [self.view addSubview:self.tableView];

宏定义:

#define SCREENWIDTH [[UIScreen mainScreen] bounds].size.width
#define SCREENHEIGHT [[UIScreen mainScreen] bounds].size.height
#define MAXYOFFSET   200
#define ENDRIGHTX    200
#define ENDLEFTX    -200</code></pre>
手势控制`panGestureRecognizer:(UIPanGestureRecognizer *)panGesture`代码:
CGPoint transition = [panGesture translationInView:self.tableView];
self.tableView.frame=[self panGestureOffset:transition.x];
[panGesture setTranslation:CGPointZero inView:self.tableView];
//拖动手势结束
if (panGesture.state==UIGestureRecognizerStateEnded) {
    CGFloat originX =self.tableView.frame.origin.x;
    CGFloat offsetX=0;
    //大于屏幕的一半进入新的位置
    if (originX > SCREENWIDTH*0.5) {
        offsetX=ENDRIGHTX-originX;
    }else if(originX < SCREENWIDTH*0.5 && originX > -SCREENWIDTH*0.5){
        //小于屏幕的一半,大于屏幕负一半的时候,则恢复到初始状态
        offsetX=offsetX-originX;
    }else if (originX<-SCREENWIDTH*0.5){
        offsetX=ENDLEFTX-originX;
    }
    
    [UIView animateWithDuration:0.5 animations:^{
        self.tableView.frame=[self panGestureOffset:offsetX];
    }];
}
偏移量`panGestureOffset:(CGFloat)offsetX`控制:  
offsetX=self.tableView.frame.origin.x+offsetX;
CGFloat offsetY = offsetX/SCREENWIDTH * MAXYOFFSET;
//如果需要设置右边的抽屉,参数为负数,需要取绝对值
CGFloat scale = (SCREENHEIGHT-fabs(2*offsetY))/SCREENHEIGHT;
CGFloat height = SCREENHEIGHT*scale;
CGFloat width  = SCREENWIDTH;
CGFloat x = offsetX;
CGFloat y = (SCREENHEIGHT- height)* 0.5;

return CGRectMake(x, y, width, height);
### 参考链接
[RESideMenu](https://github.com/romaonthego/RESideMenu)
[MMDrawerController](https://github.com/mutualmobile/MMDrawerController)
[FEDrawerMenu](https://github.com/SmallElephant/FEDrawerMenu/tree/master)

推荐阅读更多精彩内容

  • 这是第二次感受写作收获,与第一次最大的不同是:开始喜欢接受生活中每天有写作的习惯了。 总结写作给自己生活带来的变化...
    唐薇阅读 46评论 2 2
  • bootstrap是最受欢迎的HTML、CSS、JS的框架,用于开发响应式布局,移动设备优先的项目是为所有开发者、...
    Man僵小鱼阅读 36评论 0 1
  • 水源西起腾云, 东下马颈白岒。 直奔乌山罐下, 蒋军氹中修行。 迂迴砖塘镇内, 岂只蓬壁生輝。 砖塘萝卜脆嫩, 皇...
    喷泉阅读 114评论 1 5
  • 文/金错刀频道 祥燎(微信公众号:金错刀) 在里约,曾有这样一家餐厅,它坐落在破烂的社区里,却有世界级大厨坐镇,有...
    温迪139阅读 118评论 0 0