iOS基于UITableView实现多层展开与收起

本文实例为大家分享了bleView多层展开与收起的具体代码,供大家参考,具体内容如下

规则要求:

  • tableview 有多层,类似于xcode文件目录的层级关系,每一个最开始展示的层姑且称之为根目录吧,并且,每个根目录下的层数不定。
  • 与文件目录类似,每个目录下可以有不同层级的目录同时展开,但是同一层次中只有一层是展开的,即要展开B层次的某一层,则需要收起B层次所有其他的层级。
  • 最底层是一个个文件,不能再展开(这里在业务逻辑上用处是:跳转到不同的页面)。

想法:

  • 整个界面是一个tableview,层级关系用cell中的label的位置展现,而tableview的数据源是一个一维数组_resultArray,其中,展开是在特定位置插入数据,收起是在特定位置删除数据。
  • 每一层目录存储着下一层的引用,就是包含了下一层的全部数据。展开该层的时候就是将下一层的数据加入_resultArray,收起该层时,是将该层的所有下层的数据从_resultArray中删除。

数据存储

//每个目录的数据结构如下:
@interface OpenTest : NSObject
@property (copy, nonatomic) NSString *title;  //非首层展示的标题
@property (assign, nonatomic) NSInteger level; //决定偏移量大小
@property (copy, nonatomic) NSString *openUrl; //最后一层跳转的规则
@property (copy, nonatomic) NSMutableArray *detailArray; //下一层的数据
@property (assign, nonatomic) BOOL isOpen;    //是否要展开
@property (copy, nonatomic) NSString *imageName; //首层的图片
@end

其中,因为detailArray中存储的对象也应该是OpenTest, 所以需要在OpenTest.m中借助MJExtension (在github上下载并加入到项目中)进行显式转化。

#import "OpenTest.h"
@implementation OpenTest
- (instancetype)init {
  self = [super init];
  if (self) {
    [OpenTest mj_setupObjectClassInArray:^NSDictionary *{
      return @{
           @"detailArray" : [OpenTest class]
           };
    }];
  }
  return self;
}
@end

数据处理

开始建造源数据dataArray,该数组可明确层级关系,并且得到展示数组_resultArray。建造过程如下:

- (void)initData {
_dataArray = [NSMutableArray new];
_resultArray = [NSMutableArray new];

NSMutableArray *secondArray1 = [NSMutableArray new];
NSMutableArray *threeArray1 = [NSMutableArray new];
NSMutableArray *fourArray1 = [NSMutableArray new];

NSArray *FirstTitleArray = @[@"FirstTitle1", @"FirstTitle2", @"FirstTitle3", @"FirstTitle4", @"FirstTitle5", @"FirstTitle6", @"FirstTitle7", @"FirstTitle8", @"FirstTitle9", @"FirstTitle10"];
NSArray *SecondTitleArray = @[@"SecondTitle1", @"SecondTitle2", @"SecondTitle3"];
NSArray *ThreeTitleArray = @[@"ThreeTitle1", @"ThreeTitle2", @"ThreeTitle3", @"ThreeTitle4"];
NSArray *FourTitleArray = @[@"FourTitle1", @"FourTitle2", @"FourTitle3"];
NSArray *imageArray = @[@"scroller1", @"scroller2", @"scroller3", @"scroller4", @"scroller5", @"scroller6", @"scroller7", @"scroller8", @"scroller9", @"scroller10"];

//第四层数据
for (int i = 0; i < FourTitleArray.count; i++) {
  OpenTest *model = [[OpenTest alloc] init];
  model.title = FourTitleArray[i];
  model.level = 3;
  model.isOpen = NO;

  [fourArray1 addObject:model];
}

//第三层数据
for (int i = 0; i < ThreeTitleArray.count; i++) {
  OpenTest *model = [[OpenTest alloc] init];
  model.title = ThreeTitleArray[i];
  model.level = 2;
  model.isOpen = NO;
  model.detailArray = fourArray1;

  [threeArray1 addObject:model];
}

//第二层数据
for (int i = 0; i < SecondTitleArray.count; i++) {
  OpenTest *model = [[OpenTest alloc] init];
  model.title = SecondTitleArray[i];
  model.level = 1;
  model.isOpen = NO;
  model.detailArray = [threeArray1 mutableCopy];

  [secondArray1 addObject:model];
}

//第一层数据
for (int i = 0; i < FirstTitleArray.count; i++) {
  OpenTest *model = [[OpenTest alloc] init];
  model.title = FirstTitleArray[i];
  model.level = 0;
  model.isOpen = NO;
  model.detailArray = [secondArray1 mutableCopy];
  model.imageName = imageArray[i];

  [_dataArray addObject:model];
}

//处理源数据,获得展示数组_resultArray
[self dealWithDataArray:_dataArray];
}

/**
 将源数据数组处理成要展示的一维数组,最开始是展示首层的所有的数据
 @param dataArray 源数据数组
 */
- (void)dealWithDataArray:(NSMutableArray *)dataArray {
for (OpenTest *model in dataArray) {
  [_resultArray addObject:model];

  if (model.isOpen && model.detailArray.count > 0) {
    [self dealWithDataArray:model.detailArray];
  }
}
}

当首层没有展开数据时,点击首层展开第二层数据,比较容易实现,即在_resultArray添加下一层数据。添加数据方法如下:

/**
 在指定位置插入要展示的数据
 @param dataArray 数据数组
 @param row    需要插入的数组下标
 */
- (void)addObjectWithDataArray:(NSMutableArray *)dataArray row:(NSInteger)row {
for (int i = 0; i < dataArray.count; i++) {
  OpenTest *model = dataArray[i];
  model.isOpen = NO;
  [_resultArray insertObject:model atIndex:row];
  row += 1;
}
}

收起方法实现如下:

/**
 删除要收起的数据
 @param dataArray 数据
 @param count   统计删除数据的个数
 @return 删除数据的个数
 */
- (CGFloat)deleteObjectWithDataArray:(NSMutableArray *)dataArray count:(NSInteger)count {
for (OpenTest *model in dataArray) {
  count += 1;

  if (model.isOpen && model.detailArray.count > 0) {
    count = [self deleteObjectWithDataArray:model.detailArray count:count];
  }

  model.isOpen = NO;

  [_resultArray removeObject:model];
}

return count;
}

在已经展开的时候点击另外一个目录,要先收起再展开。因为每个层次只有一个目录是展开的,所以收起的时候,只需要跟同层次的目录数据比较,如果是已经打开的,则删除打开目录的所有子层。方法如下:

/**
 与点击同一层的数据比较,然后删除要收起的数据和插入要展开的数据
 @param model 点击的cell对应的model
 @param row  点击的在tableview的indexPath.row,也对应_resultArray的下标
 */
- (void)compareSameLevelWithModel:(OpenTest *)model row:(NSInteger)row {
NSInteger count = 0;
NSInteger index = 0;  //需要收起的起始位置
//如果直接用_resultArray,在for循环为完成之前,_resultArray会发生改变,使程序崩溃。
NSMutableArray *copyArray = [_resultArray mutableCopy];

for (int i = 0; i < copyArray.count; i++) {
  OpenTest *openModel = copyArray[i];
  if (openModel.level == model.level) {
    //同一个层次的比较
    if (openModel.isOpen) {
      //删除openModel所有的下一层
      count = [self deleteObjectWithDataArray:openModel.detailArray count:count];
      index = i;
      openModel.isOpen = NO;
      break;
    }
  }
}

//插入的位置在删除的位置的后面,则需要减去删除的数量。
if (row > index && row > count) {
  row -= count;
}

[self addObjectWithDataArray:model.detailArray row:row + 1];
}

界面

系统的tableviewcell 无法修改textLabel的位置,需要修改偏移量有两种方法。
1、继承UITableViewCell, 然后重写父类的方法 - layoutSubviews, 在该方法中修改textLabel的frame。
2、在cell.contentView 中添加一个label。

我这里使用的是第二种方法:

UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"Cell"];

  if (cell == nil) {
    cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"Cell"];
    cell.selectionStyle = UITableViewCellSelectionStyleNone;

    UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(15, 0, UI_SCREEN_WIDTH / 2, 32)];
    label.font = [UIFont systemFontOfSize:14];
    label.tag = LabelTag;

    [cell.contentView addSubview:label];
  }

  for (UIView *view in cell.contentView.subviews) {
    if (view.tag == LabelTag) {
      ((UILabel *)view).text = model.title;
      ((UILabel *)view).frame = CGRectMake(15 + (model.level - 1) * SpaceWidth , 0, UI_SCREEN_WIDTH / 2, 32);
    }
  }

最后在didSelectRowAtIndexPath方法中实现点击展开与收起,方法如下:

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
NSInteger row = indexPath.row;
OpenTest *model = _resultArray[row];

if (model.isOpen) {
  //原来是展开的,现在要收起,则删除model.detailArray存储的数据
  [self deleteObjectWithDataArray:model.detailArray count:0];
}
else {
  if (model.detailArray.count > 0) {
    //原来是收起的,现在要展开,则需要将同层次展开的收起,然后再展开
    [self compareSameLevelWithModel:model row:row];
  }
  else {
    //点击的是最后一层数据,跳转到别的界面
    NSLog(@"最后一层");
  }
}

model.isOpen = !model.isOpen;

//滑动到屏幕顶部
for (int i = 0; i < _resultArray.count; i++) {
  OpenTest *openModel = _resultArray[i];

  if (openModel.isOpen && openModel.level == 0) {
    //将点击的cell滑动到屏幕顶部
    NSIndexPath *selectedPath = [NSIndexPath indexPathForRow:i inSection:0];
    [tableView scrollToRowAtIndexPath:selectedPath atScrollPosition:UITableViewScrollPositionTop animated:YES];
  }
}

[tableView reloadData];
}

效果

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • iOS中Cell的Section展开和收起的示例代码

    整理文档,搜刮出一个iOS中Cell的Section展开和收起的示例代码,稍微整理精简一下做下分享. 首先,先上图,让大家看看效果 相信大家对于TableViewd数据的设置都熟悉,这方面就不多说的,重点的还是来看: 1.如何实现cell的Section的展开和收起的效果 - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { [self.tableView des

  • iOS中的二级菜单及Cell的展开收起示例

    最近又做了一个项目,涉及到二级菜单及cell的展开收起,这是我所做过的第三个项目中做这个功能了,我当然不能把公司的项目界面show出来,所以我重新创建一个工程,数据都写的是固定的数据.作为总结,记录实现过程,及要注意的一些点:如进来默认选中第一行,数据优化等. 先看看我们实现的效果: 基本UI布局思路: 1.将view分为左右两部分,左,右分别是一个tableView 2.点击左边的cell时候,刷新右边的数据 需要注意及处理的点有: 1.默认进来界面显示左边选中第一行,及对应右边的数据 2.每

  • iOS基于UITableView实现多层展开与收起

    本文实例为大家分享了bleView多层展开与收起的具体代码,供大家参考,具体内容如下 规则要求: tableview 有多层,类似于xcode文件目录的层级关系,每一个最开始展示的层姑且称之为根目录吧,并且,每个根目录下的层数不定. 与文件目录类似,每个目录下可以有不同层级的目录同时展开,但是同一层次中只有一层是展开的,即要展开B层次的某一层,则需要收起B层次所有其他的层级. 最底层是一个个文件,不能再展开(这里在业务逻辑上用处是:跳转到不同的页面). 想法: 整个界面是一个tableview,

  • 原生JS仿QQ阅读点击展开、收起效果

    使用JS技术实现QQ阅读类似的点击展开.收起效果,具体内容如下 一.定义展开函数showdiv(),实现点击"全文"按钮,全文展开. 1.点击展开函数,需要将触发点击事件的按钮作为参数传入 2.通过传入的按钮,查找其父元素,将其父元素设置为隐藏. 3.将紧跟其父元素之后的元素设置为显示. 二.定义收起函数hidediv(),实现点击"收起全文"按钮,全文内容隐藏. 1.点击收起函数,需要将触发点击事件的按钮作为参数传入 2.通过传入的按钮,查找其父元素,将其父元素设

  • ios实现UITableView之间圆角和间隙

    ios实现UITableView之间圆角和间隙效果,上图 实现UITableView 之间的圆角和间隙 废话不多说,直接上代码 第一步 去除系统默认tableview分割线 [self.homeView.tableOrder setSeparatorStyle:UITableViewCellSeparatorStyleNone]; 第二步 //cell自定义 -(id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSStr

  • IOS中UITableView滚动到指定位置

    方法很简单: - (void)scrollToRowAtIndexPath:(NSIndexPath *)indexPath atScrollPosition:(UITableViewScrollPosition)scrollPosition animated:(BOOL)animated 有些需要注意的地方: 如果在reloadData后需要立即获取tableview的cell.高度,或者需要滚动tableview,那么,直接在reloadData后执行代码是有可能出问题的. reloadDa

  • iOS中UITableview错位的问题怎么修复

    问题描述: 问题1:当一个navigation导航进入到UITabBarController   TabBar里面有多个页面,页面下有tableView,当我进入Tableview的时候,上面两行table给挡住了,当我点击进去返回回来又没有可以看得见了,发现table向上的是44PT刚好是一个top bar 的位置.(但是从这个页面的父页面push到这个页面还是被挡住了,但是我从这个页面的子页面pop出来又不会被挡住) 问题2: 做UISearchBar,UISearchDisplayCont

  • iOS实现UITableView左滑删除复制即用功能

    开发项目时候需要用到tableview左滑删除,就研究了一下,话不多说直接上代码 //设Cell可编辑 - (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath { return YES; } //设置删除按钮 -(UITableViewCellEditingStyle)tableView:(UITableView *)tableView editingStyleForRow

  • ios基于MJRefresh实现上拉刷新和下拉加载动画效果

    本文介绍了ios基于MJRefresh实现上拉刷新和下拉加载动画效果,分享给大家,具体如下: 目录 1. 头部刷新动画 2.尾部刷新动画 头部刷新动画 #import <MJRefresh/MJRefresh.h> @interface HZNormalHeader : MJRefreshGifHeader @end #import "HZNormalHeader.h" @implementation HZNormalHeader #pragma mark - 重写父类的方

  • 详细整理iOS中UITableView的性能优化

    一.介绍 iOS开发中,UITableView可能是平时我们打交道最多的UI控件之一,其重要性不言而喻.Android也是如此,Android中的ListView和UITableView是相同功能的一个控件,但是iOS的UITableView更为强大一点,原因就不说了,如果你学过Android就知道iOS中的UITableView使用起来是非常简单的,这也是峰哥喜欢iOS胜过Android的原因之一.今天研究的内容就是UITableView的优化. 开始之前,你能说出几种UITableView的

  • 使用C#语言实现的查询条件界面展开和收起功能

    先简单说一下应用场景,现有一个C#客户端的查询界面,上方放置查询条件,下方放置查询结果.因为实际情况中查询条件可能占了好几行的位置,所以希望在界面上默认只保留一行最主要的查询条件,并在右侧有一个"展开/收起"功能. 收起时界面: 展开时界面: 最开始我的实现方式是这样的,在界面上放置一个LinkLabel,设置此LinkLabel的鼠标单击事件,调整查询条件所在Panel的Height属性. C#代码如下: using System; using System.Collections.

随机推荐