iOS之仿QQ好友列表展开收缩效果的实现
程序员文章站
2022-03-11 23:23:26
使用UICollectionView实现思路很明显整体它是一个列表,它的分组是一个列表,它里面的好友列表也是一个列表,所以就可以使用组头来设置分组列表,使用cell设置好友列表;当点击组头的时候会展开好友列表,其实原理上就是单独刷新某一组的数据。流程控制器的代码(HomeViewController):本类主要配置UICollectionView显示,以及处理组展开/关闭状态。#import "HomeViewController.h"#import "YDWFriendListSho...
使用UICollectionView实现
思路
- 很明显整体它是一个列表,它的分组是一个列表,它里面的好友列表也是一个列表,所以就可以使用组头来设置分组列表,使用cell设置好友列表;
- 当点击组头的时候会展开好友列表,其实原理上就是单独刷新某一组的数据。
流程
- 控制器的代码(HomeViewController):本类主要配置UICollectionView显示,以及处理组展开/关闭状态。
#import "HomeViewController.h"
#import "YDWFriendListShowCollectionViewCell.h"
#import "YDWFriendHeaderReusableView.h"
static NSString * const kYDWFriendListShowCollectionViewCellIndentifier = @"YDWFriendListShowCollectionViewCell";
static NSString * const kYDWFriendHeaderReusableViewIndentifier = @"YDWFriendHeaderReusableView";
@interface HomeViewController ()<UICollectionViewDelegate,UICollectionViewDataSource,FriendHeaderReusableViewDelegate>
/**
添加collectionView
*/
@property(nonatomic,strong) UICollectionView *collectionView;
/**
好友组头列表数组
*/
@property(nonatomic,strong) NSArray *headerArray;
/**
好友列表数据
*/
@property (nonatomic, strong) NSMutableArray * dataArray;
/**
存储是否展开的BOOL值
*/
@property (nonatomic, strong) NSMutableArray * boolArray;
@end
@implementation HomeViewController
- (void)viewDidLoad {
[super viewDidLoad];
[self setupNavigation];
[self addSubviews];
[self getListData];
}
#pragma mark - UICollectionViewDelegate,UICollectionViewDataSource
- (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView {
return self.headerArray.count;
}
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section {
// 判断是否展开,如果未展开则返回0
if ([self.boolArray[section] boolValue] == NO) {
return 0;
} else {
return [self.dataArray[section] count];
}
}
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
YDWFriendListShowCollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:kYDWFriendListShowCollectionViewCellIndentifier forIndexPath:indexPath];
cell.contentView.backgroundColor = [UIColor whiteColor];
cell.nameLabel.text = [NSString stringWithFormat:@"好友%ld", (long)indexPath.item];
cell.detailsLabel.text = [NSString stringWithFormat:@"签名%ld", (long)indexPath.item];
return cell;
}
- (UICollectionReusableView *)collectionView:(UICollectionView *)collectionView viewForSupplementaryElementOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath {
YDWFriendHeaderReusableView *reusView = [collectionView dequeueReusableSupplementaryViewOfKind:UICollectionElementKindSectionHeader withReuseIdentifier:kYDWFriendHeaderReusableViewIndentifier forIndexPath:indexPath];
reusView.delegte = self;
reusView.backgroundColor = [UIColor whiteColor];
reusView.tag = indexPath.section;
// 三目运算选择展开或者闭合时候的图标
reusView.imageView.image = [self.boolArray[indexPath.section] boolValue] ? [UIImage imageNamed:[NSString stringWithFormat:@"zhankai"]] : [UIImage imageNamed:[NSString stringWithFormat:@"shouqi"]];
reusView.titleLabel.text = self.headerArray[indexPath.section];
reusView.numLabel.text = [NSString stringWithFormat:@"%ld/%lu",(long)indexPath.section, (unsigned long)[self.dataArray[indexPath.section] count]];
return reusView;
}
#pragma mark - FriendHeaderReusableViewDelegate
- (void)friendHeaderReusableView:(YDWFriendHeaderReusableView *)friendHeaderReusableView didSelectItemAtSection:(NSInteger)section {
if ([self.boolArray[section] boolValue] == YES) {
[self.boolArray replaceObjectAtIndex:section withObject:@NO];
} else {
[self.boolArray replaceObjectAtIndex:section withObject:@YES];
}
[self.collectionView reloadSections:[NSIndexSet indexSetWithIndex:section]];
}
#pragma mark - Private Methods
- (void)setupNavigation {
self.navigationItem.title = @"联系人";
}
- (void)addSubviews {
self.collectionView.frame = CGRectMake(0, 100, SCREEN_WIDTH, SCREEN_HEIGHT - 15);
[self.view addSubview:self.collectionView];
}
- (void)getListData {
// 准备组头的数据
NSArray *headerArr = [NSArray arrayWithObjects:@"特别关心", @"我的好友", @"朋友", @"家人",nil];
self.headerArray = headerArr;
// 准备单元格的数据
NSArray *itemArr = [NSArray arrayWithObjects:@5, @10, @7,@3, nil];
for (int i = 0; i < self.headerArray.count; i++) {
// 所有的分组默认关闭
[self.boolArray addObject:@NO];
// 给每个分组添加数据
NSMutableArray * friendArr = [[NSMutableArray alloc] init];
for (int j = 0; j < [itemArr[i] intValue]; j++) {
[friendArr addObject:@(j)];
}
[self.dataArray addObject:friendArr];
}
[self.collectionView reloadData];
}
#pragma mark - Lazy Loading
- (NSMutableArray *)dataArray {
if (!_dataArray) {
_dataArray = [[NSMutableArray alloc] init];
}
return _dataArray;
}
- (NSMutableArray *)boolArray {
if (!_boolArray) {
_boolArray = [[NSMutableArray alloc] init];
}
return _boolArray;
}
- (UICollectionView *)collectionView {
if (!_collectionView) {
UICollectionViewFlowLayout *layout = [[UICollectionViewFlowLayout alloc] init];
layout.itemSize = CGSizeMake(SCREEN_WIDTH, 70);
layout.minimumLineSpacing = 0;
layout.minimumInteritemSpacing = 0;
layout.scrollDirection = UICollectionViewScrollDirectionVertical;
layout.headerReferenceSize = CGSizeMake(SCREEN_WIDTH, 40);
_collectionView = [[UICollectionView alloc] initWithFrame:CGRectZero collectionViewLayout:layout];
_collectionView.backgroundColor = [UIColor whiteColor];
_collectionView.delegate = self;
_collectionView.dataSource = self;
_collectionView.alwaysBounceVertical = YES;
_collectionView.showsVerticalScrollIndicator = YES;
[_collectionView registerClass:[YDWFriendHeaderReusableView class] forSupplementaryViewOfKind:UICollectionElementKindSectionHeader withReuseIdentifier:kYDWFriendHeaderReusableViewIndentifier];
[_collectionView registerClass:[YDWFriendListShowCollectionViewCell class] forCellWithReuseIdentifier:kYDWFriendListShowCollectionViewCellIndentifier];
}
return _collectionView;
}
@end
- YDWFriendHeaderReusableView:本类主要布局组标题以及指示(展开/关闭)控件,其次,通过添加点击手势结合委托模式,传递tag值。
YDWFriendHeaderReusableView .h和YDWFriendHeaderReusableView.m:
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
@class YDWFriendHeaderReusableView;
@protocol FriendHeaderReusableViewDelegate <NSObject>
- (void)friendHeaderReusableView:(YDWFriendHeaderReusableView *)friendHeaderReusableView didSelectItemAtSection:(NSInteger)section;
@end
@interface YDWFriendHeaderReusableView : UICollectionReusableView
@property(nonatomic,weak) id <FriendHeaderReusableViewDelegate>delegte;
/**
图标
*/
@property(nonatomic,weak) UIImageView *imageView;
/**
标题
*/
@property(nonatomic,weak) UILabel *titleLabel;
/**
人数
*/
@property(nonatomic,weak) UILabel *numLabel;
@end
NS_ASSUME_NONNULL_END
#import "YDWFriendHeaderReusableView.h"
@interface YDWFriendHeaderReusableView ()
@end
@implementation YDWFriendHeaderReusableView
#pragma mark - 初始化
- (instancetype)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
self.userInteractionEnabled = YES;
UITapGestureRecognizer *tapGes = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(clickView:)];
[self addGestureRecognizer:tapGes];
[self addSubviews];
}
return self;
}
#pragma mark - 设置UI界面
- (void)addSubviews {
// 添加图标
UIImageView *imageView = [[UIImageView alloc] init];
[self addSubview:imageView];
self.imageView = imageView;
// 添加标题
UILabel *titleLabel = [[UILabel alloc] init];
titleLabel.font = [UIFont systemFontOfSize:18];
titleLabel.textColor = [UIColor darkGrayColor];
[self addSubview:titleLabel];
self.titleLabel = titleLabel;
// 添加人数
UILabel *numLabel = [[UILabel alloc] init];
numLabel.font = [UIFont systemFontOfSize:14];
numLabel.textAlignment = NSTextAlignmentRight;
numLabel.textColor = [UIColor lightGrayColor];
[self addSubview:numLabel];
self.numLabel = numLabel;
}
/**
调用父类布局子视图
*/
- (void)layoutSubviews {
[super layoutSubviews];
// 图标
self.imageView.frame = CGRectMake(10, 10, 20, 20);
// 标题
CGFloat numW = 60;
CGFloat titleX = CGRectGetMaxX(self.imageView.frame) + 10;
CGFloat titleW = SCREEN_WIDTH - titleX - numW -15;
self.titleLabel.frame = CGRectMake(titleX, 10, titleW, 20);
// 人数
CGFloat numX = SCREEN_WIDTH - numW - 10;
self.numLabel.frame = CGRectMake(numX, 10, numW, 20);
}
#pragma mark - 监听事件
- (void)clickView:(UITapGestureRecognizer *)tapGes {
if ([self.delegte respondsToSelector:@selector(friendHeaderReusableView:didSelectItemAtSection:)]) {
[self.delegte friendHeaderReusableView:self didSelectItemAtSection:self.tag];
}
}
@end
- YDWFriendListShowCollectionViewCell:主要布局好友信息包括头像、昵称以及签名,具体实现如下:
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
@interface YDWFriendListShowCollectionViewCell : UICollectionViewCell
@property(nonatomic,weak) UIImageView *imageView;
@property(nonatomic,weak) UILabel *nameLabel;
@property(nonatomic,weak) UILabel *detailsLabel;
@end
NS_ASSUME_NONNULL_END
#import "YDWFriendListShowCollectionViewCell.h"
@interface YDWFriendListShowCollectionViewCell ()
@end
@implementation YDWFriendListShowCollectionViewCell
#pragma mark - 初始化
- (instancetype)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
[self addSubviews];
}
return self;
}
#pragma mark - 设置UI界面
- (void)addSubviews {
// 添加图标
UIImageView *imageView = [[UIImageView alloc] init];
imageView.layer.cornerRadius = 50/2;
imageView.layer.masksToBounds = YES;
imageView.backgroundColor = [UIColor colorWithRed:arc4random_uniform(256)/255.0 green:arc4random_uniform(256)/255.0 blue:arc4random_uniform(256)/255.0 alpha:1.0];
[self addSubview:imageView];
self.imageView = imageView;
// 添加标题
UILabel *nameLabel = [[UILabel alloc] init];
nameLabel.font = [UIFont systemFontOfSize:18];
nameLabel.textColor = [UIColor darkGrayColor];
[self addSubview:nameLabel];
self.nameLabel = nameLabel;
// 添加签名
UILabel *detailsLabel = [[UILabel alloc] init];
detailsLabel.font = [UIFont systemFontOfSize:14];
detailsLabel.textAlignment = NSTextAlignmentLeft;
detailsLabel.textColor = [UIColor lightGrayColor];
[self addSubview:detailsLabel];
self.detailsLabel = detailsLabel;
}
/**
调用父类布局子视图
*/
- (void)layoutSubviews {
[super layoutSubviews];
//1、图标
self.imageView.frame = CGRectMake(10, 10, 50, 50);
//2、标题
CGFloat nameX = CGRectGetMaxX(self.imageView.frame) + 10;
CGFloat nameW = SCREEN_WIDTH - nameX -10;
self.nameLabel.frame = CGRectMake(nameX, 10, nameW, 20);
//3、人数
CGFloat detailsX = CGRectGetMaxX(self.imageView.frame) + 10;
CGFloat detailsY = CGRectGetMaxY(self.nameLabel.frame) + 5;
CGFloat detailsW = SCREEN_WIDTH - detailsX - 10;
self.detailsLabel.frame = CGRectMake(detailsX, detailsY, detailsW, 20);
}
@end
效果展示
使用UITableView实现
思路
- 这个可以展开收缩的UITableView,点击的是TableView的section,每个section下面都对应一个数组,点击section,就展开sction然后展示数组数据。
- 每次点击section都要刷新当前点击的这个section,不用reloadData,提高效率。
- 那么点击的这个sction怎么知道自己是展开呢还是折叠起来呢?那么关键就是对这里的处理,需要加一个条件判断是展开还是折叠。
实现方法
- 用一个boolArray去记录每个section的状态,当然光记录还是不行的,还是不断的改变这个boolArray对应section的值,展开了就把值替换为1,闭合了替换了0.那么这个0和1就是我们的依据,依据这个就可以返回这个scetion对应的row了。
- 用一个类去记录和不断的替换状态,该model类增加一个状态判断的字段;
流程
- 控制器的代码:本类主要配置UITableView显示,以及处理组展开/关闭状态。
#import "YDWSecondViewController.h"
#import "YDWFriendListShowTableViewCell.h"
#import "YDWFriendHeadView.h"
static NSString * const kYDWFriendListShowTableViewCellIndentifier = @"YDWFriendListShowTableViewCell";
static NSString * const kYDWFriendHeadViewIndentifer = @"YDWFriendHeadView";
@interface YDWSecondViewController ()<UITableViewDelegate,UITableViewDataSource,FriendHeadViewDelegate>
@property (nonatomic, strong) UITableView *tableView;
@property(nonatomic,strong) NSArray *headerArray;
@property (nonatomic, strong) NSMutableArray * dataArray;
@property (nonatomic, strong) NSMutableArray * boolArray;
@end
@implementation YDWSecondViewController
- (void)viewDidLoad {
[super viewDidLoad];
[self setupNavigation];
[self setupAddTableView];
[self getListData];
}
#pragma mark - UITableViewDelegate,UITableViewDataSource
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return self.dataArray.count;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
if ([self.boolArray[section] boolValue] == NO) {
return 0;
} else {
return [self.dataArray[section] count];
}
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
YDWFriendListShowTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:kYDWFriendListShowTableViewCellIndentifier];
if (!cell) {
cell = [[NSBundle mainBundle] loadNibNamed:@"YDWFriendListShowTableViewCell" owner:self options:nil].firstObject;
}
cell.selectionStyle = UITableViewCellSelectionStyleNone;
return cell;
}
- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section {
return 40;
}
- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section {
YDWFriendHeadView *headeView = [tableView dequeueReusableHeaderFooterViewWithIdentifier:kYDWFriendHeadViewIndentifer];
if (headeView == nil) {
headeView = [[YDWFriendHeadView alloc] initWithReuseIdentifier:kYDWFriendHeadViewIndentifer];
}
headeView.delegte = self;
headeView.tag = section;
headeView.imageView.image = [self.boolArray[section] boolValue] ? [UIImage imageNamed:[NSString stringWithFormat:@"zhankai"]] : [UIImage imageNamed:[NSString stringWithFormat:@"shouqi"]];
headeView.titleLabel.text = self.headerArray[section];
headeView.numLabel.text = [NSString stringWithFormat:@"%ld/%lu",(long)section, (unsigned long)[self.dataArray[section] count]];
return headeView;
}
#pragma mark - FriendHeadViewDelegate
- (void)friendHeaderReusableView:(YDWFriendHeadView *)friendHeaderReusableView didSelectItemAtSection:(NSInteger)section {
if ([self.boolArray[section] boolValue] == YES) {
[self.boolArray replaceObjectAtIndex:section withObject:@NO];
} else {
[self.boolArray replaceObjectAtIndex:section withObject:@YES];
}
[self.tableView reloadSections:[NSIndexSet indexSetWithIndex:section] withRowAnimation:UITableViewRowAnimationFade];
}
#pragma mark - Private Methods
- (void)setupNavigation {
self.navigationItem.title = @"联系人";
}
- (void)setupAddTableView {
self.tableView.frame = self.view.bounds;
[self.view addSubview:self.tableView];
}
- (void)getListData {
// 准备组头的数据
NSArray *headerArr = [NSArray arrayWithObjects:@"特别关心", @"我的好友", @"朋友", @"家人",nil];
self.headerArray = headerArr;
// 准备单元格的数据
NSArray *itemArr = [NSArray arrayWithObjects:@5, @10, @7,@3, nil];
for (int i = 0; i < self.headerArray.count; i++) {
// 所有的分组默认关闭
[self.boolArray addObject:@NO];
// 给每个分组添加数据
NSMutableArray * friendArr = [[NSMutableArray alloc] init];
for (int j = 0; j < [itemArr[i] intValue]; j++) {
[friendArr addObject:@(j)];
}
[self.dataArray addObject:friendArr];
}
[self.tableView reloadData];
}
#pragma mark - Lazying Load
- (NSMutableArray *)boolArray {
if (!_boolArray) {
_boolArray = [[NSMutableArray alloc] init];
}
return _boolArray;
}
- (NSMutableArray *)dataArray {
if (!_dataArray) {
_dataArray = [[NSMutableArray alloc] init];
}
return _dataArray;
}
- (UITableView *)tableView {
if (!_tableView) {
_tableView = [[UITableView alloc] init];
_tableView.backgroundColor = [UIColor whiteColor];
_tableView.showsHorizontalScrollIndicator = NO;
_tableView.delegate = self;
_tableView.dataSource = self;
_tableView.separatorStyle = UITableViewCellSeparatorStyleNone;
_tableView.tableFooterView = [UIView new];
[_tableView registerClass:[YDWFriendHeadView class] forHeaderFooterViewReuseIdentifier:kYDWFriendHeadViewIndentifer];
}
return _tableView;
}
@end
- YDWFriendHeadView:本类主要布局组标题以及指示(展开/关闭)控件,其次,通过添加点击手势结合委托模式,传递tag值。
NS_ASSUME_NONNULL_BEGIN
@class YDWFriendHeadView;
@protocol FriendHeadViewDelegate <NSObject>
- (void)friendHeaderReusableView:(YDWFriendHeadView *)friendHeaderReusableView didSelectItemAtSection:(NSInteger)section;
@end
@interface YDWFriendHeadView : UITableViewHeaderFooterView
@property(nonatomic,weak) id <FriendHeadViewDelegate>delegte;
/**
图标
*/
@property(nonatomic,weak) UIImageView *imageView;
/**
标题
*/
@property(nonatomic,weak) UILabel *titleLabel;
/**
人数
*/
@property(nonatomic,weak) UILabel *numLabel;
@end
NS_ASSUME_NONNULL_END
#import "YDWFriendHeadView.h"
@implementation YDWFriendHeadView
#pragma mark - 初始化
- (instancetype)initWithReuseIdentifier:(NSString *)reuseIdentifier {
if (self = [super initWithReuseIdentifier:reuseIdentifier]) {
self.userInteractionEnabled = YES;
self.contentView.backgroundColor = [UIColor whiteColor];
UITapGestureRecognizer *tapGes = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(clickView:)];
[self addGestureRecognizer:tapGes];
[self addSubviews];
}
return self;
}
- (void)awakeFromNib {
[super awakeFromNib];
[self addSubviews];
}
#pragma mark - 设置UI界面
- (void)addSubviews {
// 添加图标
UIImageView *imageView = [[UIImageView alloc] init];
[self addSubview:imageView];
self.imageView = imageView;
// 添加标题
UILabel *titleLabel = [[UILabel alloc] init];
titleLabel.font = [UIFont systemFontOfSize:18];
titleLabel.textColor = [UIColor darkGrayColor];
[self addSubview:titleLabel];
self.titleLabel = titleLabel;
// 添加人数
UILabel *numLabel = [[UILabel alloc] init];
numLabel.font = [UIFont systemFontOfSize:14];
numLabel.textAlignment = NSTextAlignmentRight;
numLabel.textColor = [UIColor lightGrayColor];
[self addSubview:numLabel];
self.numLabel = numLabel;
}
/**
调用父类布局子视图
*/
- (void)layoutSubviews {
[super layoutSubviews];
// 图标
self.imageView.frame = CGRectMake(10, 10, 20, 20);
// 标题
CGFloat numW = 60;
CGFloat titleX = CGRectGetMaxX(self.imageView.frame) + 10;
CGFloat titleW = SCREEN_WIDTH - titleX - numW -15;
self.titleLabel.frame = CGRectMake(titleX, 10, titleW, 20);
// 人数
CGFloat numX = SCREEN_WIDTH - numW - 10;
self.numLabel.frame = CGRectMake(numX, 10, numW, 20);
}
#pragma mark - 监听事件
- (void)clickView:(UITapGestureRecognizer *)tapGes {
if ([self.delegte respondsToSelector:@selector(friendHeaderReusableView:didSelectItemAtSection:)]) {
[self.delegte friendHeaderReusableView:self didSelectItemAtSection:self.tag];
}
}
@end
- YDWFriendListShowTableViewCell:主要布局好友信息包括头像、昵称以及签名,具体实现如下:
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
@interface YDWFriendListShowTableViewCell : UITableViewCell
@property (weak, nonatomic) IBOutlet UIImageView *headImageView;
@property (weak, nonatomic) IBOutlet UILabel *nameLabel;
@property (weak, nonatomic) IBOutlet UILabel *signLabel;
@end
NS_ASSUME_NONNULL_END
#import "YDWFriendListShowTableViewCell.h"
@implementation YDWFriendListShowTableViewCell
- (void)awakeFromNib {
[super awakeFromNib];
self.headImageView.backgroundColor = [UIColor colorWithRed:arc4random_uniform(256)/255.0 green:arc4random_uniform(256)/255.0 blue:arc4random_uniform(256)/255.0 alpha:1.0];
[self.headImageView.layer setMasksToBounds:YES];
[self.headImageView.layer setCornerRadius:25];
}
- (void)setSelected:(BOOL)selected animated:(BOOL)animated {
[super setSelected:selected animated:animated];
}
@end
效果展示:与UICollectionView实现得的效果一致。
- 详细代码与具体逻辑见demo:iOS之仿QQ好友列表展开收缩效果的实现。
本文地址:https://blog.csdn.net/Forever_wj/article/details/107407277
上一篇: 将从LeanCloud下载的图片文件用ImageView显示
下一篇: 他们用深度学习「追星」