源码下载地址:http://download.youkuaiyun.com/detail/liu537192/8478745
效果图:
核心代码:
//
// JLViewController.m
// 02-QQ好友列表
//
// Created by XinYou on 15-3-6.
// Copyright (c) 2015年 vxinyou. All rights reserved.
//
#import "JLViewController.h"
#import "JLFriend.h"
#import "JLFriendGroup.h"
#import "JLHeaderView.h"
#import "JLFriendCell.h"
@interface JLViewController ()
@property (nonatomic,strong)NSArray *groups;
@end
@implementation JLViewController
- (void)viewDidLoad
{
[super viewDidLoad];
// 设置tableView的行高
self.tableView.rowHeight = 50;
// 设置tableView每一组headerView的高度
self.tableView.sectionHeaderHeight = 44;
}
/**
* 隐藏状态栏
*/
- (BOOL)prefersStatusBarHidden{
return YES;
}
- (NSArray *)groups{
if (_groups == nil) {
// 获取plist文件的路径
NSString *path = [[NSBundle mainBundle] pathForResource:@"friends.plist" ofType:nil];
// 加载plist文件中的内容到数组
NSArray *dictArray = [NSArray arrayWithContentsOfFile:path];
NSMutableArray *tempArray = [NSMutableArray array];
for (NSDictionary *dict in dictArray) {
JLFriendGroup *group = [JLFriendGroup groupWithDict:dict];
[tempArray addObject:group];
}
_groups = tempArray;
}
return _groups;
}
#pragma mark -JLHeaderViewDelegate的方法
- (void)headerViewDidClickNameView:(JLHeaderView *)headerView{
// 重新加载数据,刷新表格
[self.tableView reloadData];
}
#pragma mark -数据源方法
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView{
return self.groups.count;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
JLFriendGroup *group = self.groups[section];
// return group.friends.count;
// 控制每组的展开和闭合
return (group.isOpened == NO ? 0 :group.friends.count);
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
// static NSString *ID = @"qq";
//
// UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:ID];
//
// if (cell == nil) {
//
// cell = [[UITableViewCell alloc]initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:ID];
//
// }
//
// JLFriendGroup *group = self.groups[indexPath.section];
// JLFriend *friend = group.friends[indexPath.row];
//
// cell.imageView.image = [UIImage imageNamed:friend.icon];
// cell.textLabel.text = friend.name;
// // vip红色昵称
// cell.textLabel.textColor = (friend.isVip ? [UIColor redColor] : [UIColor blackColor]);
// cell.detailTextLabel.text = friend.intro;
// 将以上创建cell的代码封装起来,变成如下代码:
JLFriendGroup *group = self.groups[indexPath.section];
JLFriend *friend = group.friends[indexPath.row];
// 创建cell
JLFriendCell *cell = [JLFriendCell cellWithTableView:tableView];
// 给cell设置数据
cell.friendData = friend;
return cell;
}
// 使用系统自带的headerView,不能满足条件
//- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section{
//
// JLFriendGroup *group = self.groups[section];
// return group.name;
//}
#pragma mark -代理方法
/**
* 使用自定义的headerView
*/
- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section{
// 创建headerView
JLHeaderView *headerView= [JLHeaderView headViewWithTableView:tableView];
// 给headerView设置数据
headerView.group = self.groups[section];
headerView.delegate = self;
return headerView;
}
@end
//
// JLFriend.h
// 02-QQ好友列表
//
// Created by XinYou on 15-3-6.
// Copyright (c) 2015年 vxinyou. All rights reserved.
//
#import
@interface JLFriend : NSObject
/**
* 好友的名称
*/
@property (nonatomic,copy) NSString *name;
/**
* 好友的头像
*/
@property (nonatomic,copy) NSString *icon;
/**
* 好友的详细信息
*/
@property (nonatomic,copy) NSString *intro;
/**
* 是否是vip
*/
@property (nonatomic,assign,getter = isVip) BOOL vip;
- (instancetype)initWithDict:(NSDictionary *)dict;
+ (instancetype)friendWithDict:(NSDictionary *)dict;
@end
//
// JLFriend.m
// 02-QQ好友列表
//
// Created by XinYou on 15-3-6.
// Copyright (c) 2015年 vxinyou. All rights reserved.
//
#import "JLFriend.h"
@implementation JLFriend
- (instancetype)initWithDict:(NSDictionary *)dict{
if (self = [super init]) {
[self setValuesForKeysWithDictionary:dict];
}
return self;
}
+ (instancetype)friendWithDict:(NSDictionary *)dict{
return [[self alloc] initWithDict:dict];
}
@end
//
// JLFriendGroup.h
// 02-QQ好友列表
//
// Created by XinYou on 15-3-6.
// Copyright (c) 2015年 vxinyou. All rights reserved.
//
#import
@interface JLFriendGroup : NSObject
/**
* 组名
*/
@property (nonatomic,copy) NSString *name;
/**
* 数组中的元素都是JLFriend模型
*/
@property (nonatomic,strong)NSArray *friends;
/**
* 某一组的当前在线人数
*/
@property (nonatomic,assign) int online;
/**
* 标记这组是否需要打开。YES表示打开,NO表示不打开
*/
@property (nonatomic,assign,getter = isOpened) BOOL opened;
- (instancetype)initWithDict:(NSDictionary *)dict;
+ (instancetype)groupWithDict:(NSDictionary *)dict;
@end
//
// JLFriendGroup.m
// 02-QQ好友列表
//
// Created by XinYou on 15-3-6.
// Copyright (c) 2015年 vxinyou. All rights reserved.
//
#import "JLFriendGroup.h"
#import "JLFriend.h"
@implementation JLFriendGroup
- (instancetype)initWithDict:(NSDictionary *)dict{
if (self = [super init]) {
// KVC
[self setValuesForKeysWithDictionary:dict];
// 经过KVC处理后,friends数组中装得都是NSDictionary对象
// 而我们需要friends中装的都是JLFriend对象
NSArray *dictArray = self.friends;
NSMutableArray *tempArray = [NSMutableArray array];
// 1,取出NSDictionary对象
for (NSDictionary *dict in dictArray) {
// 2,NSDictionary转模型对象
JLFriend *friend = [JLFriend friendWithDict:dict];
[tempArray addObject:friend];
}
// 3,对friends重新赋值,这样friends里面装的都是JLFriend对象
self.friends = tempArray;
}
return self;
}
+ (instancetype)groupWithDict:(NSDictionary *)dict{
return [[self alloc] initWithDict:dict];
}
@end
//
// JLHeaderView.h
// 02-QQ好友列表
//
// Created by XinYou on 15-3-6.
// Copyright (c) 2015年 vxinyou. All rights reserved.
//
#import
@class JLFriendGroup,JLHeaderView;
@protocol JLHeaderViewDelegate
@optional
- (void)headerViewDidClickNameView:(JLHeaderView *)headerView;
@end
// 既然是自定义headerView,为什么还需要继承UItableViewHeaderFooterView,而不是继承UIView呢?
@interface JLHeaderView : UITableViewHeaderFooterView
@property (nonatomic,strong)JLFriendGroup *group;
@property (nonatomic,strong)id
delegate;
+ (instancetype)headViewWithTableView:(UITableView *)tableView;
@end
//
// JLHeaderView.m
// 02-QQ好友列表
//
// Created by XinYou on 15-3-6.
// Copyright (c) 2015年 vxinyou. All rights reserved.
//
#import "JLHeaderView.h"
#import "JLFriendGroup.h"
@interface JLHeaderView()
/**
* 组名
*/
@property (nonatomic,weak)UIButton *nameView;
/**
* 该组在线人数
*/
@property (nonatomic,weak)UILabel *countView;
@end
@implementation JLHeaderView
+ (instancetype)headViewWithTableView:(UITableView *)tableView{
static NSString *ID = @"header";
// 在JLHeaderView.h中我们留下了一个疑问——“既然是自定义headerView,为什么还需要继承UItableViewHeaderFooterView,而不是继承UIView呢?”
// 因为继承UItableViewHeaderFooterView可以像UITableViewCell一样对headerView进行复用,提升性能
JLHeaderView *headerView = [tableView dequeueReusableHeaderFooterViewWithIdentifier:ID];
if (headerView == nil) {
headerView = [[self alloc] initWithReuseIdentifier:ID];
}
return headerView;
}
- (id)initWithReuseIdentifier:(NSString *)reuseIdentifier{
// 在初始化方法中添加headerView的子控件
if (self = [super initWithReuseIdentifier:reuseIdentifier]) {
// 1,添加组名控件
UIButton *nameView = [[UIButton alloc] init];
// UIButton *nameView = [UIButton buttonWithType:UIButtonTypeCustom];//和上面一句代码的作用一样
// nameView.backgroundColor = [UIColor redColor];
[self.contentView addSubview:nameView];
self.nameView = nameView;
// 设置按钮的背景图片
[self.nameView setBackgroundImage:[UIImage imageNamed:@"buddy_header_bg"] forState:UIControlStateNormal];
[self.nameView setBackgroundImage:[UIImage imageNamed:@"buddy_header_bg_highlighted"] forState:UIControlStateHighlighted];
// 设置按钮的文本内容与文本内容所在区域的边距
self.nameView.titleEdgeInsets = UIEdgeInsetsMake(0, 10, 0, 0);
// 设置按钮的内容与按钮四条边的边距
self.nameView.contentEdgeInsets = UIEdgeInsetsMake(0, 10, 0, 0);
// 设置按钮的内容水平方向左对齐
self.nameView.contentHorizontalAlignment = UIControlContentHorizontalAlignmentLeft;
// 设置按钮的字体颜色
[self.nameView setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
// 设置按钮内部文字左边的图片
[self.nameView setImage:[UIImage imageNamed:@"buddy_header_arrow"] forState:UIControlStateNormal];
// UIViewContentModeCenter : contents remain same size. positioned adjusted.
// 意思是大小不变,位置居中。
// 假设imageView的高>宽,当imageView旋转90度后,就会变成宽>高,
// 虽然居中显示,但是imageView的左边和右边还是会超出边框,所以还需要设置“超出边框的内容不需要裁剪”
self.nameView.imageView.contentMode = UIViewContentModeCenter;
// 超出边框的内容不需要裁剪
self.nameView.imageView.clipsToBounds = NO;
// 给nameView设置监听
[self.nameView addTarget:self action:@selector(nameViewClick) forControlEvents:UIControlEventTouchUpInside];
// 此时self.bounds还没有值,这是为什么呢?因为这是UITableViewHeaderFooterView的初始化方法
// 假设我们初始化一个UILabel:UILabel *label = [[UILable alloc] init],
// 经过这句代码后label的frame或者bounds属性都还是没有值的。
// self.nameView.frame = self.bounds;
// 2,添加在线人数控件
UILabel *countView = [[UILabel alloc] init];
// countView.backgroundColor = [UIColor greenColor];
[self.contentView addSubview:countView];
self.countView = countView;
// 设置UILabel中的文本右对齐
self.countView.textAlignment = NSTextAlignmentRight;
// 设置UILabel的文本颜色
self.countView.textColor = [UIColor grayColor];
// 不能在这里设置frame
// self.countView.frame = CGRectMake(CGFloat x, CGFloat y, CGFloat width, CGFloat height)
}
return self;
}
/**
* 当一个控件的frame发生改变的时候就会调用
* 一般在这个方法中设置子控件的frame
*/
- (void)layoutSubviews{
// 一定要调用父类的方法
[super layoutSubviews];
// 设置nameView这个Button填充父控件
self.nameView.frame = self.bounds;
// 设置countView与父控件左边距为10,宽度150,高度=父控件高度
CGFloat padding = 10;
CGFloat countW = 150;
CGFloat countX = self.frame.size.width - padding - countW;
CGFloat countY = 0;
CGFloat countH = self.frame.size.height;
self.countView.frame = CGRectMake(countX, countY, countW, countH);
}
- (void)setGroup:(JLFriendGroup *)group{
_group = group;
// 设置组名
[self.nameView setTitle:group.name forState:UIControlStateNormal];
// 设置在线人数
self.countView.text = [NSString stringWithFormat:@"%d/%d", group.online, group.friends.count];
}
- (void)nameViewClick{
// 设置该组的打开状态,对上一次状态取反
self.group.opened = !self.group.isOpened;
// 通知代理(控制器),刷新数据
if ([self.delegate respondsToSelector:@selector(headerViewDidClickNameView:)]) {
[self.delegate headerViewDidClickNameView:self];
}
}
/**
* 当一个控件被添加到父控件中就会调用
* 在这里控制控制组名左边图片的旋转
为什么在这里控制图片的旋转呢?
当某组的headerView中的nameView被点击,触发了nameViewClick方法,该组的opened状态改变,然后控制器刷新数据
注意这里的刷新数据,会把之前UITableView中的数据全部销毁,然后全部重新创建,重新创建就需要把headerView添加到UITableView中。
所以didMoveToSuperView在每次刷新数据后都会调用,也就是每次点击nameView后都会调用
*/
- (void)didMoveToSuperview{
if (self.group.opened) {
// M_PI_2表示π/2 , M_2_PI表示2π
self.nameView.imageView.transform = CGAffineTransformMakeRotation(M_PI_2);
} else {
self.nameView.imageView.transform = CGAffineTransformMakeRotation(0);
}
}
@end
//
// JLFriendCell.h
// 02-QQ好友列表
//
// Created by XinYou on 15-3-6.
// Copyright (c) 2015年 vxinyou. All rights reserved.
//
#import
@class JLFriend; @interface JLFriendCell : UITableViewCell + (instancetype)cellWithTableView:(UITableView *)tableView; /** * friend是C++的关键字,不能作为属性名存在 */ @property (nonatomic,strong)JLFriend *friendData; @end // // JLFriendCell.m // 02-QQ好友列表 // // Created by XinYou on 15-3-6. // Copyright (c) 2015年 vxinyou. All rights reserved. // #import "JLFriendCell.h" #import "JLFriend.h" @implementation JLFriendCell + (instancetype)cellWithTableView:(UITableView *)tableView{ static NSString *ID = @"qq"; JLFriendCell *cell = [tableView dequeueReusableCellWithIdentifier:ID]; if (cell == nil) { cell = [[JLFriendCell alloc]initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:ID]; } return cell; } - (void)setFriendData:(JLFriend *)friendData{ _friendData = friendData; self.imageView.image = [UIImage imageNamed:_friendData.icon]; self.textLabel.text = _friendData.name; // vip红色昵称 self.textLabel.textColor = (_friendData.isVip ? [UIColor redColor] : [UIColor blackColor]); self.detailTextLabel.text = _friendData.intro; } @end