UITableView分组操作

先贴上链接:https://github.com/chenzhicheng163/TableViewDemo.git
本文末尾也有。

效果如下。

主要功能有:分组,展开收起,cell在不同组之间移动。红点代表有更新请求。header右边的数字代表该科目下的更新人数和总人数。

demo.gif

具体用到了2个第三方:Masonry和SWTableViewCell
Masonry和SWTableViewCell可以使用Cocoapods加载第三方库,也可以直接到github上去下载源文件放到工程中去

在SWTableViewCell中我做了一些修改,给NSMutableArray(SWUtilityButtons)增加了一个方法

#pragma mark -added
- (void)sw_addUtilityButtonWithColor:(UIColor *)color title:(NSString *)title font:(UIFont *)font
{
    UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
    button.backgroundColor = color;
    [button setTitle:title forState:UIControlStateNormal];
    [button setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
    if (font) {
        [button.titleLabel setFont:font];
    }
    [self addObject:button];
}


还有一个工具类:NSString的类目NSString+Helpers

下面简单说一下实现思路,使用2个数组来存放header的model和cell的model

header的model有一个bool值表示该组展开还是收起,当收起的时候该组的cell个数返回0;

存放cell的model的数组里面存放的是一个一个的数组,每一个数组就对应每一个分组下的cell的model;

先贴上model的代码:
StudentCellModel.h


#import <Foundation/Foundation.h>

#import <UIKit/UIKit.h>

@interface StudentCellModel : NSObject

@property (nonatomic, strong) NSString *headerImageUrl;

@property (nonatomic, strong) NSString *nameString;

@property (nonatomic, strong) NSString *classType;

@property (nonatomic, strong) NSString *subjectType;

@property (nonatomic, strong) NSString *sourceType;

@property (nonatomic, strong) NSString *trainTimeString;

@property (nonatomic, strong) NSString *phone;

@property (nonatomic, assign) BOOL hasRegard;//是否已经关心

@property (nonatomic, assign) BOOL hasNewRequest;//是否有请求更新

@property (nonatomic, assign) CGFloat cellHeight;

@end

StudentCellModel.m


#import "StudentCellModel.h"

@implementation StudentCellModel

@synthesize cellHeight = _cellHeight;

- (instancetype)init{

      self = [super init];

      if (self) {

      }

      return self;

}

- (CGFloat)cellHeight{

    return 62.0f;

}

- (void)setCellHeight:(CGFloat)cellHeight{

    if (cellHeight > 0.0f) {

    _cellHeight = cellHeight;

    }else{

    _cellHeight = 62.0f;

    }
}

@end

StudentHeaderViewModel.h


#import<Foundation/Foundation.h>

@interface StudentHeaderViewModel : NSObject

@property (nonatomic, strong) NSString *titleString;//标题

@property (nonatomic, strong) NSString *totalStudentString;

@property (nonatomic, strong) NSString *totalNewRequestStudentString;

@property (nonatomic, assign) BOOL isExpand;

@property (nonatomic, assign) BOOL hasNewRequest;

@property (nonatomic, assign) CGFloat headerViewHeight;

@end

StudentHeaderViewModel.m


#import "StudentCellModel.h"

@implementation StudentCellModel

@synthesize cellHeight = _cellHeight;

- (instancetype)init{

    self = [super init];

    if (self) {

    }

    return self;

}

- (CGFloat)cellHeight{

    return 62.0f;

}

- (void)setCellHeight:(CGFloat)cellHeight{

    if (cellHeight > 0.0f) {

    _cellHeight = cellHeight;

    }else{

    _cellHeight = 62.0f;
    }
}

@end

接下来就是ViewController

重点就在以下几个方法:

- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section;
- (void)tapGestureRecognizerAction:(UITapGestureRecognizer *)tap;
- (void)swipeableTableViewCell:(SWTableViewCell *)cell didTriggerLeftUtilityButtonWithIndex:(NSInteger)index;
- (void)swipeableTableViewCell:(SWTableViewCell *)cell didTriggerRightUtilityButtonWithIndex:(NSInteger)index;

ViewController.m文件

@implementation ViewController

#pragma mark -data

- (void)reloadData{

   [self.tableHeaderViewModelArray removeAllObjects];

   [self.tableSeactionModelArray removeAllObjects];

   //headerViewModel

   NSArray *headerViewTitleArray = @[@"新增学员",@"科目一学员",@"科目二学员",@"科目三学员",@"科目四学员",@"已结业学员"];

   for (int i = 0; i < headerViewTitleArray.count; i++) {

      NSString *titleString = [headerViewTitleArray objectAtIndex:i];

      StudentHeaderViewModel *model = [[StudentHeaderViewModel alloc]init];

      model.titleString = titleString;

      model.totalStudentString = [NSString stringWithFormat:@"%d",3 + i];

      model.totalNewRequestStudentString = [NSString stringWithFormat:@"%d",i + 1];

      model.isExpand = NO;

      model.hasNewRequest = YES;

      model.headerViewHeight = 50.0f;

      [self.tableHeaderViewModelArray addObject:model];

   }

   //cellModel

   NSString *nameFormat = @"张文静";

   NSString *phone = @"110";

   for (int i = 0; i < headerViewTitleArray.count; i++) {

      StudentHeaderViewModel *model = [self.tableHeaderViewModelArray objectAtIndex:i];

      NSMutableArray *modelArray = [[NSMutableArray alloc]init];

      for (int j = 0; j < [model.totalStudentString intValue]; j++) {

         StudentCellModel *cellModel = [[StudentCellModel alloc]init];

         cellModel.headerImageUrl = @"";

         cellModel.nameString = [NSString stringWithFormat:@"%@%d,%d",nameFormat,i,j];

         cellModel.classType = @"";

         cellModel.sourceType = @"";

         cellModel.subjectType = [SubjectTypeArray objectAtIndex:i];

         cellModel.trainTimeString = [NSString stringWithFormat:@"已学%d次,累计%d次",i,j];

         cellModel.phone = phone;

         cellModel.cellHeight = 62.0f;

         if (j < i + 1) {

            cellModel.hasNewRequest = YES;

         }else{

            cellModel.hasNewRequest  = NO;

         }

         [modelArray addObject:cellModel];

      }

      [self.tableSeactionModelArray addObject:modelArray];

   }

   [self.tableView reloadData];

}

#pragma mark -UITableViewDelegate,UITableViewDataSource

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView{

   return self.tableHeaderViewModelArray.count;

}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{

   StudentHeaderViewModel *headerViewModel = [self.tableHeaderViewModelArray objectAtIndex:section];

   if (headerViewModel.isExpand) {

      return [[self.tableSeactionModelArray objectAtIndex:section] count];

   }else{

      return 0;

   }

}

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{

   StudentCellModel *cellModel = [[self.tableSeactionModelArray objectAtIndex:indexPath.section] objectAtIndex:indexPath.row];

   return cellModel.cellHeight;

}

- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section{

   StudentHeaderViewModel *headerViewModel = [self.tableHeaderViewModelArray objectAtIndex:section];

   return headerViewModel.headerViewHeight;

}

- (CGFloat)tableView:(UITableView *)tableView heightForFooterInSection:(NSInteger)section{

   return 0.01f;

}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{

   StudentCell *cell = [self.tableView dequeueReusableCellWithIdentifier:StudentCellId];

   StudentCellModel *cellModel = [[self.tableSeactionModelArray objectAtIndex:indexPath.section] objectAtIndex:indexPath.row];

   [cell initWithModel:cellModel];

   [cell setLeftUtilityButtons:[self leftButtonsWithIndex:indexPath.section] WithButtonWidth:52.0f];

   [cell setRightUtilityButtons:[self rightButtonsWithCellModel:cellModel] WithButtonWidth:100.0f];

   cell.delegate = self;

   return cell;

}

- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section{

   StudentHeaderView *headerView  = [[StudentHeaderView alloc]initWithFrame:CGRectZero];

   StudentHeaderViewModel *headerViewModel = [self.tableHeaderViewModelArray objectAtIndex:section];

   [headerView initWithModel:headerViewModel];

   // 如果处于展开状态,则图标进行90度的旋转
   
   if (headerView.model.isExpand) {

      headerView.arrowImageView.transform = CGAffineTransformMakeRotation(M_PI / 2);

   }else{

      headerView.arrowImageView.transform = CGAffineTransformMakeRotation(0);

   }

   //添加手势

   UITapGestureRecognizer *tapGestureRecognizer = [[UITapGestureRecognizer alloc]initWithTarget:self action:@selector(tapGestureRecognizerAction:)];

   [headerView addGestureRecognizer:tapGestureRecognizer];

   return headerView;

}

- (UIView *)tableView:(UITableView *)tableView viewForFooterInSection:(NSInteger)section{

   return nil;

}

-(void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath{

   if ([tableView respondsToSelector:@selector(setSeparatorInset:)]) {

      [tableView setSeparatorInset:UIEdgeInsetsZero];

   }

   if ([tableView respondsToSelector:@selector(setLayoutMargins:)]) {

      [tableView setLayoutMargins:UIEdgeInsetsZero];

   }

   if ([cell respondsToSelector:@selector(setLayoutMargins:)]) {

      [cell setLayoutMargins:UIEdgeInsetsZero];

   }

}

#pragma mark -action

- (void)tapGestureRecognizerAction:(UITapGestureRecognizer *)tap{

   StudentHeaderView *headerView = (StudentHeaderView *)[tap view];

   headerView.model.isExpand = !headerView.model.isExpand;

   //重载当前section

   NSInteger index = [self.tableHeaderViewModelArray indexOfObject:headerView.model];

   [self.tableView reloadSections:[NSIndexSet indexSetWithIndex:index] withRowAnimation:UITableViewRowAnimationAutomatic];

}

#pragma mark -other

- (NSArray *)leftButtonsWithIndex:(NSInteger)index{

   UIFont *font = [UIFont systemFontOfSize:12];

      NSMutableArray *leftUtilityButtons = [NSMutableArray new];

   NSArray *titleArray = @[@"科一",@"科二",@"科三",@"科四",@"结业"];

   for (NSInteger i = 0 ; i < titleArray.count; i++) {

      UIColor *color = [UIColor colorWithRed:0.78f green:0.78f blue:0.8f alpha:1.0];

      // 例如:现在的状态是科目一,那么按理下一个状态该是科目二 ,所以把科目二的按钮颜色和其他的颜色做区分

      if (i == index) {

         color = [UIColor colorWithRed:1.0f green:0.231f blue:0.188 alpha:1.0f];

      }

      if (index != 0) {

         if (i == index - 1) {

         //当前状态的颜色

         color = [UIColor orangeColor];

         }

      }

   [leftUtilityButtons sw_addUtilityButtonWithColor:color title:[titleArray objectAtIndex:i] font:font];

   }

return leftUtilityButtons;

}

- (NSArray *)rightButtonsWithCellModel:(StudentCellModel *)model{

   NSString *string = @"特别关心";

   UIColor *color = [UIColor colorWithRed:1.0f green:0.231f blue:0.188 alpha:1.0f];

   //如果状态是"已经关心",那么显示的就是"取消关心"

   if (model.hasRegard) {
   
      string = @"取消关心";

      color = [UIColor colorWithRed:0.78f green:0.78f blue:0.8f alpha:1.0];

   }

   NSMutableArray *rightUtilityButtons = [NSMutableArray new];

   [rightUtilityButtons sw_addUtilityButtonWithColor:color title:string font:[UIFont systemFontOfSize:12]];

   return rightUtilityButtons;

}

- (void)swipeableTableViewCell:(SWTableViewCell *)cell didTriggerLeftUtilityButtonWithIndex:(NSInteger)index{

   StudentCell *studentCell = (StudentCell *)cell;

   NSIndexPath *indexPath = [self.tableView indexPathForCell:studentCell];

   //选择的是当前所处的状态,弹出提示框并返回

   if (index + 1 == indexPath.section) {

      NSArray *titleArray = @[@"科一",@"科二",@"科三",@"科四",@"结业"];

      NSString *state = [titleArray objectAtIndex:index];

      UIAlertView *alertView = [[UIAlertView alloc]initWithTitle:@"错误" message:[NSString stringWithFormat:@"当前状态已经是%@了!",state] delegate:nil cancelButtonTitle:@"确定" otherButtonTitles: nil];

      [alertView show];

      return;

   }

   StudentCellModel *cellModel = [[self.tableSeactionModelArray objectAtIndex:indexPath.section] objectAtIndex:indexPath.row];

   //将model的科目类型设置为对应的科目

   cellModel.subjectType = [SubjectTypeArray objectAtIndex:index + 1];

   //修改状态之后就将将hasNewRequest置为NO

   if (cellModel.hasNewRequest) {

      cellModel.hasNewRequest = NO;

      StudentHeaderViewModel *headerViewModel = [self.tableHeaderViewModelArray objectAtIndex:indexPath.section];

      headerViewModel.totalNewRequestStudentString = [NSString stringWithFormat:@"%d",([headerViewModel.totalNewRequestStudentString intValue] - 1)];

   }

   //将cellModle从原来的一组中移除

   NSMutableArray *sectionCellModelArray = [self.tableSeactionModelArray objectAtIndex:index + 1];

   [sectionCellModelArray addObject:cellModel];

   //把cellModel加入到新的一组中

   NSMutableArray *sectionCellModelArray2 = [self.tableSeactionModelArray objectAtIndex:indexPath.section];

   [sectionCellModelArray2 removeObject:cellModel];

   //重新计算每一组的学员数量

   for (int i = 0; i < self.tableHeaderViewModelArray.count; i++) {

      //只对要重载的2组重新计算学员数量

      if (i == index + 1 || i == indexPath.section) {

         StudentHeaderViewModel *headerViewModel = [self.tableHeaderViewModelArray objectAtIndex:i];

         NSString *string = [NSString stringWithFormat:@"%ld",[[self.tableSeactionModelArray objectAtIndex:i] count]];

         headerViewModel.totalStudentString = string;

         //如果该组的总人数为0,那么将该组收起来

         if ([string intValue] <= 0) {

            headerViewModel.isExpand = NO;

         }

      }

   }

   //重载当前cell所在的组,以及cell将要进入的新的一组的数据

   NSMutableIndexSet *mutableIndexSet = [[NSMutableIndexSet alloc]init];

   [mutableIndexSet addIndex:indexPath.section];

   [mutableIndexSet addIndex:index + 1];

   [self.tableView reloadSections:[mutableIndexSet copy] withRowAnimation:UITableViewRowAnimationAutomatic];

}

- (void)swipeableTableViewCell:(SWTableViewCell *)cell didTriggerRightUtilityButtonWithIndex:(NSInteger)index{

   if (index == 0) {

      //将选中的cell的关注状态取反,然后重载该cell

      StudentCell *studentCell = (StudentCell *)cell;

      studentCell.model.hasRegard = !studentCell.model.hasRegard;

      NSIndexPath *indexPath = [self.tableView indexPathForCell:studentCell];

      [self.tableView reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationAutomatic];

   }

}
@end

第一次写,写得很不好,望各位看官们多多见谅哈。
sorry,迟来的链接:https://github.com/chenzhicheng163/TableViewDemo.git

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,258评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,335评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,225评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,126评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,140评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,098评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,018评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,857评论 0 273
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,298评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,518评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,678评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,400评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,993评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,638评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,801评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,661评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,558评论 2 352

推荐阅读更多精彩内容