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

2024-06-03 06:08
文章标签 简单 实现 导航 侧边

本文主要是介绍一个很简单的侧边栏导航实现,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

一个简单的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 controllerUIView *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 transformnav.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进行转换来实现,等同一个导航栏,只是这个导航栏需要我们自己去实现。了解了各种细节,到了具体的动画效果等等就需要我们自己去尝试如果做到更好的效果了。

这篇关于一个很简单的侧边栏导航实现的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1026188

相关文章

使用Sentinel自定义返回和实现区分来源方式

《使用Sentinel自定义返回和实现区分来源方式》:本文主要介绍使用Sentinel自定义返回和实现区分来源方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录Sentinel自定义返回和实现区分来源1. 自定义错误返回2. 实现区分来源总结Sentinel自定

Mysql表的简单操作(基本技能)

《Mysql表的简单操作(基本技能)》在数据库中,表的操作主要包括表的创建、查看、修改、删除等,了解如何操作这些表是数据库管理和开发的基本技能,本文给大家介绍Mysql表的简单操作,感兴趣的朋友一起看... 目录3.1 创建表 3.2 查看表结构3.3 修改表3.4 实践案例:修改表在数据库中,表的操作主要

Java实现时间与字符串互相转换详解

《Java实现时间与字符串互相转换详解》这篇文章主要为大家详细介绍了Java中实现时间与字符串互相转换的相关方法,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录一、日期格式化为字符串(一)使用预定义格式(二)自定义格式二、字符串解析为日期(一)解析ISO格式字符串(二)解析自定义

opencv图像处理之指纹验证的实现

《opencv图像处理之指纹验证的实现》本文主要介绍了opencv图像处理之指纹验证的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学... 目录一、简介二、具体案例实现1. 图像显示函数2. 指纹验证函数3. 主函数4、运行结果三、总结一、

Springboot处理跨域的实现方式(附Demo)

《Springboot处理跨域的实现方式(附Demo)》:本文主要介绍Springboot处理跨域的实现方式(附Demo),具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不... 目录Springboot处理跨域的方式1. 基本知识2. @CrossOrigin3. 全局跨域设置4.

Spring Boot 3.4.3 基于 Spring WebFlux 实现 SSE 功能(代码示例)

《SpringBoot3.4.3基于SpringWebFlux实现SSE功能(代码示例)》SpringBoot3.4.3结合SpringWebFlux实现SSE功能,为实时数据推送提供... 目录1. SSE 简介1.1 什么是 SSE?1.2 SSE 的优点1.3 适用场景2. Spring WebFlu

基于SpringBoot实现文件秒传功能

《基于SpringBoot实现文件秒传功能》在开发Web应用时,文件上传是一个常见需求,然而,当用户需要上传大文件或相同文件多次时,会造成带宽浪费和服务器存储冗余,此时可以使用文件秒传技术通过识别重复... 目录前言文件秒传原理代码实现1. 创建项目基础结构2. 创建上传存储代码3. 创建Result类4.

SpringBoot日志配置SLF4J和Logback的方法实现

《SpringBoot日志配置SLF4J和Logback的方法实现》日志记录是不可或缺的一部分,本文主要介绍了SpringBoot日志配置SLF4J和Logback的方法实现,文中通过示例代码介绍的非... 目录一、前言二、案例一:初识日志三、案例二:使用Lombok输出日志四、案例三:配置Logback一

Python如何使用__slots__实现节省内存和性能优化

《Python如何使用__slots__实现节省内存和性能优化》你有想过,一个小小的__slots__能让你的Python类内存消耗直接减半吗,没错,今天咱们要聊的就是这个让人眼前一亮的技巧,感兴趣的... 目录背景:内存吃得满满的类__slots__:你的内存管理小助手举个大概的例子:看看效果如何?1.

Python+PyQt5实现多屏幕协同播放功能

《Python+PyQt5实现多屏幕协同播放功能》在现代会议展示、数字广告、展览展示等场景中,多屏幕协同播放已成为刚需,下面我们就来看看如何利用Python和PyQt5开发一套功能强大的跨屏播控系统吧... 目录一、项目概述:突破传统播放限制二、核心技术解析2.1 多屏管理机制2.2 播放引擎设计2.3 专