0、QQ好友列表實現
-
0、首先說說實現思路
- 自定義
UITableView
,每一個分組都是一個UITableViewHeaderFooterView
,然后自定義cell,這里分組的實現主要是自定義UITableViewHeaderFooterView
,這個折疊效果主要靠這個header的響應
- 自定義
1、實現數據源方法
#pragma mark - UITableViewDataSource
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
// 返回分組個數
return 10;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
// 取出具體的組數,然后再取出每組的內容
SLQFriendGroup *group = self.friendList[section];
// 根據分組是否打開情況確定如何顯示分組,默認關閉
return group.friends.count;
}
/**
* 返回自定義cell
*/
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
// 1、生成cell
SLQFriendCell *cell = [SLQFriendCell cellWithTableView:tableView];
// 3、返回cell
return cell;
}
2、模型定義
- 2.0、在控制器定義個數組,存放解析的plist數據
/**朋友數組*/ @property (nonatomic, copy) NSArray *friendList;
- 2.1、自定義分組模型
@class SLQFriend;
@interface SLQFriendGroup : NSObject
/**分組名稱*/
@property (nonatomic, copy) NSString *name;
/**在線人數*/
@property (nonatomic, assign) int online;
/**分組中用戶數量*/
@property (nonatomic, copy) NSArray *friends;
/**分組是隱藏還是展開*/
@property (nonatomic, assign,getter=isOpened) BOOL opened;
- (instancetype)initWithDictionary:(NSDictionary *)dict;
+ (instancetype)FriendGroupWithDictionary:(NSDictionary *)dict;
@end
// 實現文件
#import "SLQFriendGroup.h"
#import "SLQFriend.h"
@implementation SLQFriendGroup
- (instancetype)initWithDictionary:(NSDictionary *)dict
{
if (self = [super init]) {
// 1、KVC字典轉模型
[self setValuesForKeysWithDictionary:dict];
// 2、然后再轉換數組中信息
NSMutableArray *groupFriends = [NSMutableArray array];
for (NSDictionary *dict in self.friends) {
// 3、字典轉模型
SLQFriend *friend = [SLQFriend friendWithDict:dict];
[groupFriends addObject:friend];
}
self.friends = groupFriends;
}
return self;
}
+ (instancetype)FriendGroupWithDictionary:(NSDictionary *)dict
{
return [[self alloc] initWithDictionary:dict];
}
@end
- 2.2、朋友模型
#import <Foundation/Foundation.h>
@interface SLQFriend : NSObject
/**頭像*/
@property (nonatomic, copy) NSString *icon;
/**昵稱*/
@property (nonatomic, copy) NSString *name;
/**好友簽名*/
@property (nonatomic, copy) NSString *intro;
/**是否是vip*/
@property (nonatomic, assign, getter=isVip) BOOL vip;
- (instancetype)initFriendWithDict:(NSDictionary *)dict;
+ (instancetype)friendWithDict:(NSDictionary *)dict;
@end
// 實現文件
@implementation SLQFriend
- (instancetype)initFriendWithDict:(NSDictionary *)dict
{
if (self = [super init]) {
// kvc字典轉模型
[self setValuesForKeysWithDictionary:dict];
}
return self;
}
+ (instancetype)friendWithDict:(NSDictionary *)dict
{
return [[self alloc] initFriendWithDict:dict];
}
@end
3、字典轉模型
- 在控制器里加載字典數據並轉換成模型
#pragma mark - 懶加載
- (NSArray *)friendList
{
if (!_friendList) {
_friendList = [NSArray array];
// 從plist讀取用戶信息
NSString *path = [[NSBundle mainBundle] pathForResource:@"friends.plist" ofType:nil];
NSArray *friends = [NSArray arrayWithContentsOfFile:path];
/**
* 字典轉模型
*/
NSMutableArray *mutableFriends = [NSMutableArray array];
for (NSDictionary *dict in friends) {
SLQFriendGroup *group = [SLQFriendGroup FriendGroupWithDictionary:dict];
[mutableFriends addObject:group];
}
_friendList = mutableFriends;
}
return _friendList;
}
- 3.1、修改數據源方法
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
// 返回分組個數
return self.friendList.count;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
// 取出具體的組數,然后再取出每組的內容
SLQFriendGroup *group = self.friendList[section];
// 根據分組是否打開情況確定如何顯示分組,默認關閉
return group.isOpened ? group.friends.count : 0;
}
/**
* 返回自定義cell
*/
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
// 1、生成cell
SLQFriendCell *cell = [SLQFriendCell cellWithTableView:tableView];
// 2、傳遞SLQFriend模型
SLQFriendGroup *group = self.friendList[indexPath.section];
cell.Friend = group.friends[indexPath.row];
// 3、返回cell
return cell;
}
4、自定義cell
- 這個自定義cell就不多說了
#import <UIKit/UIKit.h>
@class SLQFriend;
@interface SLQFriendCell : UITableViewCell
/**模型數組*/
@property (nonatomic, strong) SLQFriend *Friend;
+ (SLQFriendCell *)cellWithTableView:(UITableView *)tableView;
@end
#import "SLQFriendCell.H"
#import "SLQFriendGroup.h"
#import "SLQFriend.h"
@implementation SLQFriendCell
/**
* setter方法
*/
- (void)setFriend:(SLQFriend *)Friend
{
_Friend = Friend;
// 更新數據到控件上
self.textLabel.text = Friend.name;
self.detailTextLabel.text = Friend.intro;
self.imageView.image = [UIImage imageNamed:Friend.icon];
self.textLabel.textColor = Friend.isVip ? [UIColor redColor] : [UIColor blackColor];
}
/**
* 返回cell
*/
+ (SLQFriendCell *)cellWithTableView:(UITableView *)tableView
{
static NSString *ID = @"Cell";
SLQFriendCell *cell = [tableView dequeueReusableCellWithIdentifier:ID];
if (cell == nil) {
cell = [[SLQFriendCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:ID];
}
return cell;
}
5、自定義HeaderFooterView
- 這個才是彈出列表的關鍵所在
#import <UIKit/UIKit.h>
@class SLQFriendGroup;
@interface SLQHeader : UITableViewHeaderFooterView
/**用戶分組*/
@property (nonatomic, strong) SLQFriendGroup *friendGroup;
/** 按鈕*/
@property (nonatomic, weak) UIButton *contentButton;
/** 標簽*/
@property (nonatomic, weak) UILabel *onlineLabel;
+ (instancetype)headerWithTableView:(UITableView *)tableView;
@end
- 5.0、重寫setter方法,設置數據
- (void)setFriendGroup:(SLQFriendGroup *)friendGroup
{
_friendGroup = friendGroup;
// 加空格,讓圖片和文字產生一個間隔
NSString *name = [NSString stringWithFormat:@" %@",friendGroup.name];
[self.contentButton setTitle:name forState:UIControlStateNormal];
[self.contentButton setTitleColor:[UIColor darkGrayColor] forState:UIControlStateNormal];
self.onlineLabel.text = [NSString stringWithFormat:@"%zd/%zd",friendGroup.online,friendGroup.friends.count];
}
- 5.1、封裝header生成操作,返回header
// 返回生成好的header
+ (instancetype)headerWithTableView:(UITableView *)tableView
{
static NSString *ID = @"header";
SLQHeader *header = [tableView dequeueReusableHeaderFooterViewWithIdentifier:ID];
if (header == nil) {
header = [[SLQHeader alloc] initWithReuseIdentifier:ID];
}
return header;
}
- 5.2、布局子控件
- (void)layoutSubviews
{
[super layoutSubviews];
// 布局添加的子控件
// 1、背景
self.contentButton.frame = self.bounds;
// 稍微向右偏移一點
self.contentButton.contentEdgeInsets = UIEdgeInsetsMake(0, 10, 0, 0);
// 2、標簽:在線人數/組內總人數
NSInteger countWidth = 150;
NSInteger countHeight = self.bounds.size.height;
NSInteger countX = self.bounds.size.width - 10 - countWidth;
NSInteger countY = 0;
self.onlineLabel.frame = CGRectMake(countX, countY, countWidth, countHeight);
// 3、改變小箭頭的方向
// 由於tableView刷新數據后,所有header會被重新創建,所以要在這里對箭頭朝向做出修改
// 改變箭頭朝向,順時針旋轉90度
CGFloat rotation = self.friendGroup.isOpened? M_PI_2 : 0;
self.contentButton.imageView.transform = CGAffineTransformMakeRotation(rotation);
}
- 5.3、自定義
initWithReuseIdentifier:ID
方法,添加自定義控件
/** 重寫初始化方法, 給header加上圖標、組名、在線人數等子控件 */
- (instancetype)initWithReuseIdentifier:(NSString *)reuseIdentifier
{
if (self = [super initWithReuseIdentifier:reuseIdentifier])
{
// 1、添加按鈕
UIButton *contentButton = [[UIButton alloc] init];
[self.contentView addSubview:contentButton];
// 1.0 添加背景圖片
[contentButton setImage:[UIImage imageNamed:@"buddy_header_arrow"] forState:UIControlStateNormal];
[contentButton setBackgroundImage:[UIImage imageNamed:@"buddy_header_bg"] forState:UIControlStateNormal];
[contentButton setBackgroundImage:[UIImage imageNamed:@"buddy_header_bg_highlighted"] forState:UIControlStateHighlighted];
// 設置對齊方式
contentButton.contentHorizontalAlignment = UIControlContentHorizontalAlignmentLeft;
// 1.1 添加點擊事件
[contentButton addTarget:self action:@selector(contentButtonClick:) forControlEvents:UIControlEventTouchUpInside];
_contentButton = contentButton;
// 2、最右邊標簽
UILabel *onlineLabel = [[UILabel alloc] init];
[self.contentView addSubview:onlineLabel];
[onlineLabel setTextAlignment:NSTextAlignmentRight];
_onlineLabel = onlineLabel;
}
return self;
}
- 5.4、響應header點擊事件,點擊過后展開或者收縮內部cell,這個牽涉到tableView的數據更新,那么可以將這個事件傳遞出去(使用代理)
- (void)contentButtonClick:(UIButton *)btn
{
// 隱藏\顯示 好友
self.friendGroup.opened = !self.friendGroup.isOpened;
// 調用代理方法傳遞按鈕點擊消息
if ([self.delegate respondsToSelector:@selector(headerDidClicked:)]) {
[self.delegate headerDidClicked:self];
}
}
- 5.5、代理實現,只需實現一個方法就行,而且是可選的
@class SLQHeader;
@protocol SLQHeaderDelegate <NSObject>
@optional
- (void)headerDidClicked:(SLQHeader *)header;
@end
@interface SLQHeader : UITableViewHeaderFooterView
/**用戶分組*/
@property (nonatomic, strong) SLQFriendGroup *friendGroup;
/** 按鈕*/
@property (nonatomic, weak) UIButton *contentButton;
/** 標簽*/
@property (nonatomic, weak) UILabel *onlineLabel;
/**delegate*/
@property (nonatomic, weak) id<SLQHeaderDelegate> delegate;
+ (instancetype)headerWithTableView:(UITableView *)tableView;
@end
- 5.6、在控制器里設置代理監聽按鈕點擊即可
#pragma mark - UITableViewDelegate
- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section
{
// 1、自定義header
SLQHeader *header = [SLQHeader headerWithTableView:tableView];
// 2、傳遞模型
SLQFriendGroup *group = self.friendList[section];
header.friendGroup = group;
header.delegate = self;
// 3、返回頭部
return header;
}
- (void)headerDidClicked:(SLQHeader *)header
{
// 刷新表格
[self.tableView reloadData];
}
- 這是俺的github
https://github.com/slq0378/05-QQ- - 還有一點就是原作者的那個工程有點問題,就是那個按鈕圖片的旋轉不起作用,我這里直接把旋轉代碼放到
layoutSubviews
,這樣只要布局改變就會旋轉箭頭