一个很简单的侧边栏导航实现

一个简单的sidebar的小例子。

在实习的项目中,有一个重要的UI部分就是一个sidebar.之前一直很想自己能够重构这一部分,但是无论是参考项目代码还是到GitHub上的代码,我都感觉到很吃力。因为大部分设计到很多跳转,让我一下子很难理清楚。五一的时候重新整理了下写代码的思路,觉得要自己先理清楚基本的过程,写每一个方法前都要明白这个方法要达到什么样的功能。今天花了一上午参考了一个GitHub的样例写了一个小小sidebar,在这里记录一下这个过程,也相当于做一个笔记。

—-分割线—-

在手机APP上,我们可以看到许多侧边栏的应用。侧边栏不仅仅可以起到一个导航的作用,也可以用来展示一些相关的信息。我们可以看到像网易新闻,QQ等APP的侧边栏功能非常强大。而我认为,使用侧边栏相对于一般的顶部导航栏更为美观,也能省去视图的一部分空间占用。

首先,我们要认识到,侧边栏是一个视图。我们通过在当前视图的导航按钮或者通过手势来调出侧边栏。因此,首先要认识一下手势。

在UIKit中,有专门的手势识别器。这里我仅仅记录一下在侧边栏调用中要使用的滑动手势swip。其他的手势我认为也是大同小异,只需要自己写一个demo就能很好的理解。

下面这个方法展示了一个如何去添加一个手势到视图

- (void)addSwipToView:(UIView *)view withDirection:(UISwipeGestureRecognizerDirection)direction {
    UISwipeGestureRecognizer *swip = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(handleSwip:)];
    swip.direction = direction;
    [view addGestureRecognizer:swip];
}

UISwipeGestureRecognizer 这个代表了这是一个扫动的手势识别。direction是手势滑动的方向。

接下来,有了手势,我们就需要去写一个方法来处理手势将会处罚的动作

- (void)handleSwip:(UISwipeGestureRecognizer *)recognizer
{
    /*!
     *  @brief  处理滑动手势
     *
     *  @since 1.0
     */

    //判断滑动的方向
    //当手势是向右滑动的时候
    if (recognizer.direction == UISwipeGestureRecognizerDirectionRight) {
        CGFloat height = self.view.frame.size.height;
        CGFloat width = self.view.frame.size.width;
        UINavigationController *newNv =  self.childViewControllers[_currentIndex];
        [UIView animateWithDuration:kAnimateDuration animations:^{
            /*!
             *  @brief  当向右侧滑动的时候,左侧边栏将要出现
             *          方法是计算左侧边栏和当前视图的高度比例,计算得出cover和侧边栏不同的高度。
             *
             *  @since 1.0
             */
            // animation of View controller
            UIView *view = newNv.view;
            // 添加cover视图
            [self addCoverToView:view];
            CGFloat scale = leftMenuH / height;
            CGAffineTransform scaleTransf = CGAffineTransformMakeScale(scale, scale);
            CGFloat targetX = leftMenuW - width * (1 - scale) / 2;
            CGFloat targetY = leftMenuY - height * (1 - scale) / 2;
            CGAffineTransform transf = CGAffineTransformTranslate(scaleTransf, targetX / scale, targetY / scale);
            view.transform = transf;
            // animation of left menu
            _leftMenu.transform = CGAffineTransformMakeTranslation(leftMenuW, 0);
        }];

    }

    //滑动收拾向左
    if (recognizer.direction == UISwipeGestureRecognizerDirectionLeft) {
        /*!
         *  @brief  当向左滑动的时候,左侧边栏将隐藏
         *          放是 将cover视图上父视图上去掉
         *
         *  @since 1.0
         */
        [_cover removeFromSuperview];
        UINavigationController *nav = self.childViewControllers[_currentIndex];
        [UIView animateWithDuration:kAnimateDuration animations:^{
            // clear transform
            nav.view.transform = CGAffineTransformIdentity;
            _leftMenu.transform = CGAffineTransformIdentity;
        }];

    }

}
```
可以看到,我们将会判断手势滑动的方向,根据左右,我们分别调用不同的行为。如果行为方法非常复杂,用另外的函数进行封装。


在我们简单的看过这两个方法来了解了基本的手势处理后,接下来就是我们去看看如何去设计一个侧边栏了。

首先我们自定义一个类,这里我并没有是使用interface build,所以可能效果上不是特别好。

首先我的想法是建立一个View,加入不同的组件,然后代理模式使这个视图和主视图进行关联。
在这个View中,我仅仅添加了三个button来做演示。

代码如下

//
// CSBLeftView.m
// SideBarDemo
//
// Created by 陈思博 on 5/5/15.
// Copyright (c) 2015 csb. All rights reserved.
//

import “CSBLeftView.h”

@interface CSBLeftView()

{
UIButton *selectButton;
}

@end

@implementation CSBLeftView

/*!
* @brief 左视图的构建思路
向视图中添加所需要的组件或者子视图
为子视图或者组件添加所需要的事件
当当前视图的按钮或者子视图发生跳转活其他的时候,通过协议进行定义相关的方法
*
* @since 1.0
*/

  • (id)initWithFrame:(CGRect)frame
    {
    /*!

    • @brief 根据传进来的frame进行设置初始化
      *
    • @since 1.0
      */
      self = [super initWithFrame:frame];
      if(self)
      {
      self.backgroundColor = [UIColor clearColor];

      UIColor *color = [[UIColor alloc] initWithRed:187.0/255.0 green:67.0/255.0 blue:71.0/255.0 alpha:1];
      [self addButtonByIcon:@”sidebar_nav_reading” tile:@”阅读” andTintColor:color];

      color = [[UIColor alloc] initWithRed:187.0/255.0 green:114.0/255.0 blue:72.0/255.0 alpha:1];
      [self addButtonByIcon:@”sidebar_nav_photo” tile:@”照片” andTintColor:color];

      color = [[UIColor alloc] initWithRed:67.0/255.0 green:125.0/255.0 blue:187.0/255.0 alpha:1];
      [self addButtonByIcon:@”sidebar_nav_news” tile:@”新闻” andTintColor:color];

    }

    return self;

}

  • (void)setDelegate:(id)delegate{
    _delegate = delegate;
    selectButton = self.subviews[0];
    selectButton.selected = YES;
    }

  • (void)addButtonByIcon:(NSString )iconName tile:(NSString )title andTintColor:(UIColor *)color
    {
    /*!

    • @brief 根绝给定的图标和标题创建添加一个Button
      *
    • @since 1.0
      */
      UIButton *button = [[UIButton alloc] init];
      [self addSubview:button];
      [button setTitle:title forState:UIControlStateNormal];
      [button setTintColor:[UIColor whiteColor]];
      [button setImage:[UIImage imageNamed:iconName] forState:UIControlStateNormal];
      [button setAdjustsImageWhenHighlighted:NO];
      // 点击事件
      [button addTarget:self action:@selector(buttonAction:) forControlEvents:UIControlEventTouchUpInside];
      // 按钮内部向左对齐
      button.contentHorizontalAlignment = UIControlContentHorizontalAlignmentLeft;
      // 标题
      button.titleEdgeInsets = UIEdgeInsetsMake(0, 10, 0, 0);
      button.contentEdgeInsets = UIEdgeInsetsMake(0, 30, 0, 0);

}

  • (void)buttonAction:(UIButton *)button{
    /*!

    • @brief 按钮点击事件处理
      *
    • @since 1.0
      */
      if ([self.delegate respondsToSelector:@selector(leftMenu:ChangeViewControllerFrom:to:)]) {
      [self.delegate leftMenu:self ChangeViewControllerFrom:selectButton.tag to:button.tag];
      }
      selectButton.selected = NO;
      button.selected = YES;
      selectButton = button;
      }
  • (void)layoutSubviews{
    /*!

    • @brief 根据当前的子视图来排列
      *
    • @since 1.0
      */
      [super layoutSubviews];

    NSArray *buttons = self.subviews;
    NSInteger count = buttons.count;
    CGFloat btnW = self.bounds.size.width;
    CGFloat btnH = self.bounds.size.height / count;
    for (int i = 0; i < count; i++) {
    UIButton *button = buttons[i];
    CGRect frame = CGRectMake(0, btnH * i, btnW, btnH);
    button.tag = i;
    button.frame = frame;
    }
    }

@end


其余部分没有什么太多好说的,但有一个地方特别要注意。我们要想到,怎么才能在点击按钮后跳转到另外的视图呢?自然我们想到要为点击事件添加一个处理方法。而这个方法却并不在这里实现,而是要在主视图的类里面实现。为什么呢?因为我们要在主视图里面进行控制器的变换。


接下来我们把注意力放到主视图里。
通过思考,我们可以明白,侧边栏视图实际是作为主视图的一个子视图存在的,通过手势或者按钮来唤醒。也就是说,如果要调出侧边栏,我们必须通过计算视图之间的位置关系。在初始化的时候,我们将侧边栏作为子视图添加到主视图中,同时设置侧边栏的位置。
  • (void)viewDidLoad {
    [super viewDidLoad];
    /*!

    • @brief 初始左侧视图,将视图添加到子视图中,添加子视图控制器
      *
      *
      *
    • @since 1.0
      */

    _currentIndex = 0;
    _BGImageView = [[UIImageView alloc] initWithFrame:self.view.bounds];
    _BGImageView.image = [UIImage imageNamed:@”sidebar_bg”];
    [self.view addSubview:_BGImageView];

    CSBLeftView *leftView = [[CSBLeftView alloc] initWithFrame:CGRectMake(-leftMenuW, leftMenuY, leftMenuW, leftMenuH)];
    [self.view addSubview:leftView];
    _leftMenu = leftView;
    leftView.delegate =self;

    [self addChildNvController:@”阅读”];
    [self addChildNvController:@”图片”];
    [self addChildNvController:@”新闻”];

    UINavigationController *newNv = self.childViewControllers[0];
    [self.view addSubview:newNv.view];

}


注意到,我们初始化侧边栏的时候将他初始的位置X设置为他的宽度的负数。这样就刚好使侧边栏在主视图外面。

然后我们要为每个按钮要添加相匹配的控制器。这样在点击按钮后,根据按钮的index来调用相关的控制器。
  • (void)addChildNvController:(NSString *)title
    {
    /*!

    • @brief 添加一个子控制器
      *
    • @since 1.0
      */
      UIViewController *vc = [[ReadViewController alloc] init];
      vc.view.backgroundColor = [UIColor colorWithRed:220.0/255.0 green:220.0/255.0 blue:220.0/255.0 alpha:1.0];
      // UINavigationController属于容器,最少需要一个RootController,Title是设置在容器中的Controller上
      UINavigationController *newNv = [[UINavigationController alloc] initWithRootViewController:vc];
      newNv.navigationBar.barTintColor = [UIColor colorWithRed:168.0/255.0 green:20.0/255.0 blue:4.0/255.0 alpha:1.0];
      // title color
      NSDictionary *attris = @{NSForegroundColorAttributeName:[UIColor whiteColor],NSFontAttributeName:[UIFont boldSystemFontOfSize:18.0]};
      newNv.navigationBar.titleTextAttributes = attris;
      newNv.navigationBar.tintColor = [UIColor whiteColor];
      // left barItem
      UIBarButtonItem *left = [[UIBarButtonItem alloc] initWithImage:[UIImage imageNamed:@”top_navigation_menuicon.png”] style:UIBarButtonItemStylePlain target:self action:@selector(leftButtonAction)];
      vc.navigationItem.leftBarButtonItem = left;
      vc.title = title;
      // add gesture
      [self addSwipToView:vc.view withDirection:UISwipeGestureRecognizerDirectionRight];

    [self addChildViewController:newNv];


在完成这一切后,通过代理方法,使按钮点击的时候,跳转到另一个控制器。

  • (void)leftMenu:(UIView *)leftMenu ChangeViewControllerFrom:(NSInteger)fromIndex to:(NSInteger)index
    {
    /*!

    • @brief 代理方法
      根据index来变换视图
      *
    • @since 1.0
      */

    //讲当前的子控制器的视图从父视图中移除
    UINavigationController *oldNv = self.childViewControllers[_currentIndex];
    CGAffineTransform transform = oldNv.view.transform;
    [oldNv.view removeFromSuperview];

    //添加子视图控制和相关的视图到当前视图中
    _currentIndex = index;
    UINavigationController *newNv = self.childViewControllers[_currentIndex];
    transform = newNv.view.transform;
    [self.view addSubview:newNv.view];

    [UIView animateWithDuration:kAnimateDuration animations:^{
    newNv.view.transform = CGAffineTransformIdentity;
    _leftMenu.transform = CGAffineTransformIdentity;
    }];
    [_cover removeFromSuperview];
    }
    “`

这是最后的效果图。

这里写图片描述

总结

因为在IOS自带的UI中并没有实现侧边栏,所以网上有很多人通过不同的方法来实现了侧边栏。虽然在具体的实现上面有所不同,但是总体来说主要是通过不同的controller进行转换来实现,等同一个导航栏,只是这个导航栏需要我们自己去实现。了解了各种细节,到了具体的动画效果等等就需要我们自己去尝试如果做到更好的效果了。

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值