先贴上链接:https://github.com/chenzhicheng163/TableViewDemo.git
本文末尾也有。
效果如下。
主要功能有:分组,展开收起,cell在不同组之间移动。红点代表有更新请求。header右边的数字代表该科目下的更新人数和总人数。
具体用到了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