转自http://blog.csdn.net/cocoarannie/article/details/12589439

左右侧栏已经是当前APP最流行的布局,很多客户端软件都使用了左右侧栏,例如网易新闻,人人网,Weico等等。

这篇博客以当前网易新闻客户端的模式为例仿写了一个左右侧栏架构实现。

先看一下Demo的实现效果

  

实现主要思路以及细节:

视图控制器有三个视图按不同层次排列,最上层的是主要显示视图_mainContentView,下面的为左右侧栏视图;

点击左侧栏不同按钮压入不同的主视图控制器;

在显示侧栏时点击视图空白区域闭合,利用tap手势;

拖动主页面根据不同的方向和位置进行移动和缩放, 利用pan手势;

向右拖显示左侧栏,向左拖显示右侧栏;

首先,点击左侧栏时,左侧栏将点击的数据模型传给分栏控制器,让其更改主视图内容

模型:

[cpp] view plaincopy
  1. @interface ClassModel : NSObject
  2. @property (strong, nonatomic) NSString *title;
  3. @property (strong, nonatomic) NSString *className;
  4. @property (strong, nonatomic) NSString *contentText;
  5. @property (strong, nonatomic) NSString *imageName;
  6. + (id)classModelWithTitle:(NSString *)title className:(NSString *)className contentText:(NSString *)text andImageName:(NSString *)imageName;
  7. @end

一个工厂方法,模型存入不同选择下的不同视图控制器的具体内容。

以新闻页为例:

ClassModel *newscm = [ClassModel classModelWithTitle:@"新闻" className:@"NewsViewController" contentText:@"新闻视图内容" andImageName:@"sidebar_nav_news"];

来看主视图切换不同界面的方法:

[cpp] view plaincopy
  1. - (void)showContentControllerWithModel:(ClassModel *)model
  2. {
  3. [self closeSideBar];
  4. UIViewController *controller = _controllersDict[model.className];
  5. if (!controller)
  6. {
  7. Class c = NSClassFromString(model.className);
  8. HRViewController *vc = [[c alloc] init];
  9. controller = [[UINavigationController alloc] initWithRootViewController:vc];
  10. vc.contentText = model.contentText;
  11. [_controllersDict setObject:controller forKey:model.className];
  12. }
  13. if (_mainContentView.subviews.count > 0)
  14. {
  15. UIView *view = [_mainContentView.subviews firstObject];
  16. [view removeFromSuperview];
  17. }
  18. controller.view.frame = _mainContentView.frame;
  19. [_mainContentView addSubview:controller.view];
  20. }

中间省略了配置导航栏的内容。

这个方法中又一个[_mainContentView.subviews firstObject]方法,这个方法是IOS7才有的,按以前习惯写[0]也好。

实现视图移动动画主要是通过仿射变换来实现的,也有的例子通过操作frame,看习惯了。

一个根据方向返回仿射变换值的私有方法

[cpp] view plaincopy
  1. - (CGAffineTransform)transformWithDirection:(RMoveDirection)direction
  2. {
  3. CGFloat translateX = 0;
  4. switch (direction) {
  5. case RMoveDirectionLeft:
  6. translateX = -RContentOffset;
  7. break;
  8. case RMoveDirectionRight:
  9. translateX = RContentOffset;
  10. break;
  11. default:
  12. break;
  13. }
  14. MyLog(@"%.2f",translateX);
  15. CGAffineTransform transT = CGAffineTransformMakeTranslation(translateX, 0);
  16. CGAffineTransform scaleT = CGAffineTransformMakeScale(1.0, RContentScale);
  17. CGAffineTransform conT = CGAffineTransformConcat(transT, scaleT);
  18. return conT;
  19. }

一个根据方向配置页面的阴影效果的私有方法

[cpp] view plaincopy
  1. - (void)configureViewShadowWithDirection:(RMoveDirection)direction
  2. {
  3. CGFloat shadowW;
  4. switch (direction)
  5. {
  6. case RMoveDirectionLeft:
  7. shadowW = 2.0f;
  8. break;
  9. case RMoveDirectionRight:
  10. shadowW = -2.0f;
  11. break;
  12. default:
  13. break;
  14. }
  15. _mainContentView.layer.shadowOffset = CGSizeMake(shadowW, 1.0);
  16. _mainContentView.layer.shadowColor = [UIColor blackColor].CGColor;
  17. _mainContentView.layer.shadowOpacity = 0.8f;
  18. }

点击导航栏左侧按钮的展开侧栏方法(右侧类似)

[cpp] view plaincopy
  1. - (void)leftItemClick
  2. {
  3. CGAffineTransform conT = [self transformWithDirection:RMoveDirectionRight];
  4. [self.view sendSubviewToBack:_rightSideView];
  5. [self configureViewShadowWithDirection:RMoveDirectionRight];
  6. [UIView animateWithDuration:ROpenDuration
  7. animations:^{
  8. _mainContentView.transform = conT;
  9. }
  10. completion:^(BOOL finished) {
  11. _tapGestureRec.enabled = YES;
  12. }];
  13. }

关闭侧栏返回主页面的方法

[cpp] view plaincopy
  1. - (void)closeSideBar
  2. {
  3. CGAffineTransform oriT = CGAffineTransformIdentity;
  4. [UIView animateWithDuration:RCloseDuration
  5. animations:^{
  6. _mainContentView.transform = oriT;
  7. }
  8. completion:^(BOOL finished) {
  9. _tapGestureRec.enabled = NO;
  10. }];
  11. }

最后是最重要的,手势拖动的实现方法

[cpp] view plaincopy
  1. - (void)moveViewWithGesture:(UIPanGestureRecognizer *)panGes
  2. {
  3. static CGFloat currentTranslateX;
  4. if (panGes.state == UIGestureRecognizerStateBegan)
  5. {
  6. currentTranslateX = _mainContentView.transform.tx;
  7. }
  8. if (panGes.state == UIGestureRecognizerStateChanged)
  9. {
  10. CGFloat transX = [panGes translationInView:_mainContentView].x;
  11. transX = transX + currentTranslateX;
  12. CGFloat sca;
  13. if (transX > 0)
  14. {
  15. [self.view sendSubviewToBack:_rightSideView];
  16. [self configureViewShadowWithDirection:RMoveDirectionRight];
  17. if (_mainContentView.frame.origin.x < RContentOffset)
  18. {
  19. sca = 1 - (_mainContentView.frame.origin.x/RContentOffset) * (1-RContentScale);
  20. }
  21. else
  22. {
  23. sca = RContentScale;
  24. }
  25. }
  26. else    //transX < 0
  27. {
  28. [self.view sendSubviewToBack:_leftSideView];
  29. [self configureViewShadowWithDirection:RMoveDirectionLeft];
  30. if (_mainContentView.frame.origin.x > -RContentOffset)
  31. {
  32. sca = 1 - (-_mainContentView.frame.origin.x/RContentOffset) * (1-RContentScale);
  33. }
  34. else
  35. {
  36. sca = RContentScale;
  37. }
  38. }
  39. CGAffineTransform transS = CGAffineTransformMakeScale(1.0, sca);
  40. CGAffineTransform transT = CGAffineTransformMakeTranslation(transX, 0);
  41. CGAffineTransform conT = CGAffineTransformConcat(transT, transS);
  42. _mainContentView.transform = conT;
  43. }
  44. else if (panGes.state == UIGestureRecognizerStateEnded)
  45. {
  46. CGFloat panX = [panGes translationInView:_mainContentView].x;
  47. CGFloat finalX = currentTranslateX + panX;
  48. if (finalX > RJudgeOffset)
  49. {
  50. CGAffineTransform conT = [self transformWithDirection:RMoveDirectionRight];
  51. [UIView beginAnimations:nil context:nil];
  52. _mainContentView.transform = conT;
  53. [UIView commitAnimations];
  54. _tapGestureRec.enabled = YES;
  55. return;
  56. }
  57. if (finalX < -RJudgeOffset)
  58. {
  59. CGAffineTransform conT = [self transformWithDirection:RMoveDirectionLeft];
  60. [UIView beginAnimations:nil context:nil];
  61. _mainContentView.transform = conT;
  62. [UIView commitAnimations];
  63. _tapGestureRec.enabled = YES;
  64. return;
  65. }
  66. else
  67. {
  68. CGAffineTransform oriT = CGAffineTransformIdentity;
  69. [UIView beginAnimations:nil context:nil];
  70. _mainContentView.transform = oriT;
  71. [UIView commitAnimations];
  72. _tapGestureRec.enabled = NO;
  73. }
  74. }
  75. }

根据不同的状态设置不同状态下的仿射变换值,来赋给_mainContentView,在到达临界状态后,该视图的缩放不再进行。

当拖动手势结束后,根据拖动的位置,来确定视图如何进行移动。

这个例子主要的代码都贴在了上面,这里的素材是随便取的。现在的网易客户端的侧栏背景为深色视图,左侧栏栏目跟这个demo差不多。

Demo源码:点击打开链接

IOS仿网易新闻客户端左右侧栏相关推荐

  1. ios仿网易新闻客户端

    新闻类型采用UICollectionView, 新闻内容切换使用UICollectionView 具体内容的显示使用UITableView,达到复用的目的. 根据上次点击获取数据的时间间隔来判断是否需 ...

  2. IOS开发——仿网易新闻客户端

    IOS开发--仿网易新闻客户端 本文没有内容,传个资源 衔接地址:http://download.csdn.net/detail/u012881779/7152281 左侧导航部分: 新闻版块 订阅版 ...

  3. Android 开源框架ViewPageIndicator 和 ViewPager 仿网易新闻客户端Tab标签

     转载请注明出处:http://blog.csdn.net/xiaanming/article/details/10766053 之前用JakeWharton的开源框架ActionBarSherl ...

  4. android分类功能,Android 仿网易新闻客户端分类排序功能

    先来看看网易新闻客户端以及自己实现的效果图,效果当然还是网易的好 gridviewsort.gif 如何实现拖拽一个Item 用WindowManager添加一个ImageView,并且将这个Imag ...

  5. Android高仿网易新闻客户端之动态添加标签

    承接上一篇文章:Android高仿网易新闻客户端之首页,今天来实现动态添加标签效果. 动态标签页是一个流式布局,实现了宽度自动换行高度自动分配的功能,代码如下: FlowLayout.java pac ...

  6. 仿网易新闻客户端UI界面小Demo

    图一 图二 图三 仿网易新闻客户端UI界面Demo 图一:新闻模块 UI界面:点击下方按钮,显示相应内容的页面信息 图三:点击新闻模块上方按钮显示体育,娱乐,科技等页面,相应的内容. 所涉及的知识点: ...

  7. android 仿网易标签切换,Android 仿网易新闻客户端Tab标签

    Android 开源框架ViewPageIndicator和ViewPager仿网易新闻客户端Tab标签 http://blog.csdn.net/xiaanming/article/details/ ...

  8. 仿网易新闻客户端的上面的tab和下面的功能条

    2019独角兽企业重金招聘Python工程师标准>>> 仿网易新闻客户端的上面的tab和下面的功能条 package com.and.netease; import com.and. ...

  9. android 仿网易新闻客户端源码都有

    原文:android 仿网易新闻客户端源码都有 android 仿网易新闻服务端源码 源代码下载地址: http://www.zuidaima.com/share/1550463560944640.h ...

最新文章

  1. 产业丨一文读懂人工智能产业链,未来10年2000亿美元市场
  2. 把libreoffice集成到网页中_Python3.7.3安装教程并集成Sublime Text3
  3. EPSON 利用CCD图像处理包标定工具坐标系
  4. 2019.7.29学习整理python
  5. visio保存后公式变形_固体力学中的变形分析
  6. c语言unicode编码转ascii码,编码转换(ASCII和Unicode、Unicode和中文相互转换)
  7. vue 引入qunee_在vue项目中怎么使用qunee拓扑图插件,怎么正确的使用 graphEditor ?求帮助...
  8. 公众号二维码怎么生成
  9. python 矩阵化为最简阶梯型
  10. 微信分账:分账接收方列表格式错误
  11. 历数国内外知名制作人~~制作人发展趋势
  12. tools:ignore=“MissingConstraints“属性
  13. js实现520倒计时
  14. 深入理解微服务Maven父子项目构造以及项目打包部署
  15. 微信小程序生成海报无法保存到相册
  16. 如何使用 Unity制作微信小游戏,微信小游戏制作方案 最新完整详细教程来袭【持续更新】
  17. Android 自定义控件基础:measure过程
  18. 关于 SAP Spartacus 重定向部分外部 url 到后台系统的问题
  19. 嵌入式研发精英培养计划课程体系-曹国辉-专题视频课程
  20. 全面认识快照(Snapshot)技术

热门文章

  1. cocos creator 3.x使用tween缓动接口和贝塞尔曲线实现简易抛物线
  2. 蓝桥杯练习——不定方程解法2021.2.23
  3. Kafka的结构、特点和原理(细节)
  4. PostgreSQL/pgsql自动添加分区子表
  5. 法拉第未来在美国开始卖地了 900英亩报价4000万美元
  6. element el-autocomplete 自动完成的理解
  7. Siamese+LSTM+Distance(abs)
  8. 联阳(ITE) IT6251FN QFN64 LVDS显示端口 多媒体转换器芯片
  9. MySQL查询性能优化七种武器之链路追踪
  10. Camtasia音画同步剪辑教程