前言
UITableView作为UIKit中最重要的一个组件,应用还是很广泛很灵活的,它的特性用来实现分组列表再合适不过。可折叠分组列表最典型的是好友列表,是一个二级目录,点击每一个分组都会展开或折叠一个好友列表。
这里使用TableView的section header作为分组一级目录,每个section的cell作为二级目录。section header里面放的是一个自定义的UIButton,button的图片设置为折叠指示箭头,button的文字为分组名称,为这个button定义代理,点击按钮时通知刷新对应的section的数据即可展开或者折叠该分组。
自定义section header头部按钮以及协议
头部按钮的制作主要有三点:一个是定义点击后的通知代理,二是调整按钮图片和文字的frame成左图右文字的样式(通过contentRect代理回调来调整),三是点击后按钮图片的旋转动画(tranform旋转90)。
//
// SectionHeaderView.h
// JXHDemo
//
// Created by 919575700@qq.com on 10/23/15.
// Copyright (c) 2015 Jiangxh. All rights reserved.
// section头部视图,是一个buntton
#import <UIKit/UIKit.h>
@class SectionHeaderView;
/**
* 自定义协议
*/
@protocol SectionHeaderDelegate <NSObject>
//点击了section header
- (void)sectionDidClicked:(SectionHeaderView *)sender;
@end
@interface SectionHeaderView : UIButton
/**
* 记录是否已经展开
*/
@property (nonatomic)BOOL isOpen;
/**
* 协议
*/
@property (nonatomic, weak) id<SectionHeaderDelegate>delegate;
@end
//
// SectionHeaderView.m
// JXHDemo
//
// Created by 919575700@qq.com on 10/23/15.
// Copyright (c) 2015 Jiangxh. All rights reserved.
//
#define sectionMargin 10
#define sectionIconSize 20
#import "SectionHeaderView.h"
@interface SectionHeaderView()
@end
@implementation SectionHeaderView
- (id)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
//设置按钮属性
[self setButton];
}
return self;
}
/**
* 设置按钮属性
*/
- (void)setButton {
_isOpen = NO;
//背景色
self.backgroundColor = [UIColor whiteColor];
// 文字颜色
[self setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
// 添加指示图片
[self setImage:[UIImage imageNamed:@"arrow"] forState:UIControlStateNormal];
// 图片模式
self.imageView.contentMode = UIViewContentModeScaleAspectFit;
// 添加下分割线
UIImageView *underLine = [[UIImageView alloc] initWithFrame:CGRectMake(0, self.frame.size.height-1, self.frame.size.width, 1)];
// 贴图
[underLine setImage:[UIImage imageNamed:@"line"]];
//改变线的透明度
[underLine setAlpha:0.3];
[self addSubview:underLine];
// 添加点击事件
[self addTarget:self action:@selector(clicked:) forControlEvents:UIControlEventTouchUpInside];
}
/**
* 点击
*/
- (void)clicked:(SectionHeaderView *)sender {
// 如果没展开,顺时针旋转90度
if (!_isOpen) {
[sender.imageView setTransform:CGAffineTransformMakeRotation(M_PI_2)];
}
// 如果展开了,逆时针旋转90度
else {
[sender.imageView setTransform:CGAffineTransformMakeRotation(-M_PI_2)];
}
// 通知代理
[_delegate sectionDidClicked:sender];
// 状态取反
_isOpen = !_isOpen;
}
/**
* 返回代理需要的标题尺寸和图片尺寸
*/
- (CGRect)titleRectForContentRect:(CGRect)contentRect {
return CGRectMake(55, 0, contentRect.size.width, contentRect.size.height);
}
- (CGRect)imageRectForContentRect:(CGRect)contentRect {
return CGRectMake(20, 5, contentRect.size.height-10, contentRect.size.height-10);
}
@end
自定义cell
cell的样式根据需要可以随便设计,这里设置一个最简单的:一个头像,一个名字。
//
// AccountCell.h
// JXHDemo
//
// Created by Xinhou Jiang on 3/11/16.
// Copyright © 2016年 Jiangxh. All rights reserved.
//
#import <UIKit/UIKit.h>
@interface AccountCell : UITableViewCell
// 头像
@property (nonatomic, strong) UIImageView *avatar;
// 昵称
@property (nonatomic, strong) UILabel *name;
@end
//
// AccountCell.m
// JXHDemo
//
// Created by Xinhou Jiang on 3/11/16.
// Copyright © 2016年 Jiangxh. All rights reserved.
//
#define cellH 40 // cell高度
#define ApplicationW [UIScreen mainScreen].bounds.size.width // 屏幕宽度
#import "AccountCell.h"
@implementation AccountCell
-(instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier {
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self) {
// 头像
_avatar = [[UIImageView alloc]initWithFrame:CGRectMake(5, 5, cellH-10, cellH-10)];
_avatar.layer.cornerRadius = (cellH-10)/2;
[self.contentView addSubview:_avatar];
// 账号
_name = [[UILabel alloc]initWithFrame:CGRectMake(cellH, 0, ApplicationW - cellH, cellH)];
_name.font = [UIFont systemFontOfSize:12.0];
[self.contentView addSubview:_name];
}
return self;
}
@end
数据模型
数据主要是section分组的一个数据数组和每个分组的cell数据数组,这里为了简单固定返回了一组死数据,具体有了数据源在request的函数内将数据对应接入即可。
/**
* 记录section的展开状态
*/
@property (nonatomic, strong)NSMutableArray *isOpen;
/**
* 记录section的标题数组
*/
@property (nonatomic, strong)NSArray *titles;
/**
* 请求数据
*/
- (void)initValue {
// 标题数组假数据
_titles = @[@"朋友", @"同学", @"家人", @"同事"];
// 初始化所有section都是折叠状态
_isOpen = [[NSMutableArray alloc] initWithCapacity:_titles.count];
for (int i = 0; i<_titles.count; i++) {
[_isOpen addObject:@NO];
}
}
/**
* cell
*/
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *identifier = @"identifier";
// 具体可以自制cell组件
AccountCell *cell = [[AccountCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:identifier];
// 头像,具体应该从好友数据中取
[cell.avatar setImage:[UIImage imageNamed:@"male"]];
// 昵称,具体应该从好友数据中取
cell.name.text = @"夏明";
//cell颜色
//cell.backgroundColor = RGBColor(200, 200, 200);
return cell;
}
UITableView分组下拉列表
主体就是TableView的一个完整应用,包括section和cell的管理。
//
// FolderTableViewController.h
// JXHDemo
//
// Created by 919575700@qq.com on 10/23/15.
// Copyright (c) 2015 Jiangxh. All rights reserved.
// 可折叠section的表格
#import <UIKit/UIKit.h>
@interface FolderTableViewController : UITableViewController
@end
//
// FolderTableViewController.m
// JXHDemo
//
// Created by 919575700@qq.com on 10/23/15.
// Copyright (c) 2015 Jiangxh. All rights reserved.
//
#define sectionHeaderH 30 //组头部的高度
#define ApplicationW [UIScreen mainScreen].bounds.size.width // 屏幕宽度
#define RGBColor(r, g, b) [UIColor colorWithRed:(r)/255.0 green:(g)/255.0 blue:(b)/255.0 alpha:1.0] // 通过RGB创建颜色
#import "FolderTableViewController.h"
#import "SectionHeaderView.h"
#import "AccountCell.h"
@interface FolderTableViewController ()<SectionHeaderDelegate>
/**
* 记录section的展开状态
*/
@property (nonatomic, strong)NSMutableArray *isOpen;
/**
* 记录section的标题数组
*/
@property (nonatomic, strong)NSArray *titles;
@end
@implementation FolderTableViewController
- (void)viewDidLoad {
[super viewDidLoad];
// 表格基本设置
self.title = @"可展开的TableView";
self.view.backgroundColor = RGBColor(240, 240, 240);
// 清除底部多余cell
[self.tableView setTableFooterView:[[UIView alloc] initWithFrame:CGRectZero]];
// 清除分割线
self.tableView.separatorStyle = UITableViewCellSeparatorStyleNone;
// 请求数据
[self initValue];
}
/**
* 请求数据
*/
- (void)initValue {
// 标题数组假数据
_titles = @[@"朋友", @"同学", @"家人", @"同事"];
// 初始化所有section都是折叠状态
_isOpen = [[NSMutableArray alloc] initWithCapacity:_titles.count];
for (int i = 0; i<_titles.count; i++) {
[_isOpen addObject:@NO];
}
}
#pragma mark - 组设置
/**
* 多少组
*/
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return _titles.count;
}
/**
* section header的高度
*/
- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section {
return sectionHeaderH;
}
/**
* section header的视图
*/
- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section {
// section头部视图
SectionHeaderView *sectionHeader = [[SectionHeaderView alloc] initWithFrame:CGRectMake(0, 0, ApplicationW, sectionHeaderH)];
// 标题
[sectionHeader setTitle:[_titles objectAtIndex:section] forState:UIControlStateNormal];
// 为headerview打上tag
[sectionHeader setTag:section];
// 代理
sectionHeader.delegate = self;
return sectionHeader;
}
#pragma mark SectionHeader实现代理
/**
* 实现代理,sectionheader 点击
*/
- (void)sectionDidClicked:(SectionHeaderView *)sender {
// 取反状态
BOOL reverse = ![_isOpen[sender.tag] boolValue];
_isOpen[sender.tag] = [NSNumber numberWithBool:reverse];
/*** 这里刷新后section的header也会被刷新,导致指示箭头又恢复到旋转之前的状态,待解决 ***/
// 刷新点击的分区(展开或折叠)
[self.tableView reloadSections:[[NSIndexSet alloc] initWithIndex:sender.tag] withRowAnimation:UITableViewRowAnimationNone];
}
#pragma mark - 组内行设置
/**
* 每组多少行
*/
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
if ([_isOpen[section] boolValue]) {
return 5; // 具体应该返回该分组好友的个数
}else {
return 0;
}
}
/**
* cell
*/
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *identifier = @"identifier";
// 具体可以自制cell组件
AccountCell *cell = [[AccountCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:identifier];
// 头像,具体应该从好友数据中取
[cell.avatar setImage:[UIImage imageNamed:@"male"]];
// 昵称,具体应该从好友数据中取
cell.name.text = @"夏明";
//cell颜色
//cell.backgroundColor = RGBColor(200, 200, 200);
return cell;
}
@end
组件的调用
组件的使用很简单,一句话加入,引入TavleViewController类,实例化为一个子viewcontroller即可。
- (void)viewDidLoad {
[super viewDidLoad];
// 显示折叠视图
FolderTableViewController *foldableVC = [[FolderTableViewController alloc]init];
[self addChildViewController:foldableVC];
[self.view addSubview:foldableVC.view];
}
存在的问题(待解决)
存在的一个问题是,点击分组按钮时,刷新的是整个section,包括头部的header按钮,因此虽然头部按钮点击后箭头旋转了,但由于马上被更新了换了新的头部按钮,导致箭头看上去没有反应,而TableView并没有只更新某个section的所有cell而不更新section的header的方法。暂时没有想到比较优雅的解决办法,探索中…orz
Demo下载(不想花积分的请回复邮箱)
http://download.csdn.net/detail/cordova/9673964