OCiOS开发:表格视图实现腾讯好友列表展开收缩效果

来源:互联网 发布:知乎阿里云免费开通码 编辑:程序博客网 时间:2024/06/06 03:38

前言

这几天刚刚给学生讲到了表格视图的使用,有学生问我,如果要实现类似于腾讯好友列表展开收缩的效果该怎么实现呢?以前我写过有关这样效果的demo,但是一直没有发表成文,今天刚好有时间,于是写出来,分享给各位,当然要实现这种效果方法还是有很多,这里就讲解下我的实现方式,不到之处,还往各位提点建议,我会及时改进。

效果

这里写图片描述

为了简化数据,所以每个分组里面的好友数据都是一样的。

实现思路

1、用一个字典来判断,将section作为key,如果字典对应key有值,则展开组,反之则关闭组。

2、自定义头部视图,并且给头部视图添加点击事件(UITapGestureRecognizer)监测用户点击分组。

3、在主页为表视图创建头部视图的时候,为其设置tag值,其值为当前所在的组(section),并且在自定义头部视图类中,通过委托模式,将其tag值传递到主页,为其获取到对应头部视图的section,设为字典key。

4、涉及技术:委托模式、自定义单元格、自定义头部视图、plist文件读取、点击手势以及协议传值。

数据处理

好友列表展示的数据,我已预先存到plist文件中,包括图片素材,也一并导入了工程,如下所示:

这里写图片描述

素材下载地址:http://download.csdn.net/detail/hierarch_lee/9412991

代码实现

由于代码相对较简单,并且关键部分我也详细注明了代码解释,因此,就不做具体的讲解。
涉及文件

1、LHY_HomeViewController:主页,继承于UIViewController,用于展示好友列表。

2、LHY_FriendsListCell:自定义单元格,继承于UITableViewCell,用于布局好友列表信息;

3、LHY_HeaderView:自定义头部视图,继承于UITableViewHeaderFooterView,用于布局分组显示数据;

代码块1:为主页添导航栏,这里我是手动创建UIWindow,首先在Info.plist将Main字段删除,然后在AppDelegate.m文件中,导入LHY_HomeViewController,添加如下代码:

  • AppDelegate.m
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {    // Override point for customization after application launch.    LHY_HomeViewController *lhyVc = [[LHY_HomeViewController alloc] init];    UINavigationController *nav = [[UINavigationController alloc] initWithRootViewController:lhyVc];    _window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];    _window.backgroundColor = [UIColor blackColor];    _window.rootViewController = nav;    [_window makeKeyAndVisible];    return YES;}

代码块2:自定义单元格,LHY_FriendsListCell,主要布局好友信息包括头像、昵称以及签名,具体实现如下:

  • LHY_FriendsListCell.h
#import <UIKit/UIKit.h>@interface LHY_FriendsListCell : UITableViewCell@property (strong, nonatomic) UIImageView *headPortraitImageView; /**< 头像 */@property (strong, nonatomic) UILabel     *nicknameLabel;         /**< 昵称 */@property (strong, nonatomic) UILabel     *signatureLabel;        /**< 签名 */@end
  • LHY_FriendsListCell.m
#import "LHY_FriendsListCell.h"@implementation LHY_FriendsListCell- (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier {    if (self = [super initWithStyle:style reuseIdentifier:reuseIdentifier]) {        // 加载视图        [self.contentView addSubview:self.headPortraitImageView];        [self.contentView addSubview:self.nicknameLabel];        [self.contentView addSubview:self.signatureLabel];    }    return self;}#pragma mark *** Getters ***- (UIImageView *)headPortraitImageView {    if (!_headPortraitImageView) {        _headPortraitImageView = [[UIImageView alloc] init];        _headPortraitImageView.bounds = CGRectMake(0, 0, 60, 60);        _headPortraitImageView.center = CGPointMake(40, 40);        // 设置文本现实模式        _headPortraitImageView.contentMode = UIViewContentModeScaleAspectFit;        // 设置圆角        _headPortraitImageView.layer.cornerRadius = 30;        // 设置图层超出父视图部分不显示        _headPortraitImageView.layer.masksToBounds = YES;    }    return _headPortraitImageView;}- (UILabel *)nicknameLabel {    if (!_nicknameLabel) {        _nicknameLabel = [[UILabel alloc] initWithFrame:CGRectMake(80, 5, 310, 30)];        _nicknameLabel.font = [UIFont boldSystemFontOfSize:18];        _nicknameLabel.textColor = [UIColor redColor];    }    return _nicknameLabel;}- (UILabel *)signatureLabel {    if (!_signatureLabel) {        _signatureLabel = [[UILabel alloc] initWithFrame:CGRectMake(80, 50, 310, 20)];        _signatureLabel.font = [UIFont systemFontOfSize:15];    }    return _signatureLabel;}@end

代码块3:自定义头部视图,LHY_HeaderView,本类主要布局组标题以及指示(展开/关闭)控件,其次,通过添加点击手势结合委托模式,传递tag值。

  • LHY_HeaderView.h
#import <UIKit/UIKit.h>@class LHY_HeaderView;// 协议声明@protocol LHY_HeaderViewDelegate <NSObject>- (void)lhyHeaderView:(LHY_HeaderView *)lhyHeaderView didSelectedWithTag:(NSInteger)tag;@end@interface LHY_HeaderView : UITableViewHeaderFooterView@property (nonatomic, strong) UILabel                *sectionTitleLabel; /**< 组标题 */@property (nonatomic, strong) UIImageView            *indictorImageView; /**< 指示图片 */@property (nonatomic, weak) id <LHY_HeaderViewDelegate> delegate;        /**< 代理 */@end
  • LHY_HeaderView.m
#import "LHY_HeaderView.h"@implementation LHY_HeaderView- (instancetype)initWithReuseIdentifier:(nullable NSString *)reuseIdentifier {    if (self = [super initWithReuseIdentifier:reuseIdentifier]) {        self.contentView.backgroundColor = [UIColor clearColor];        self.layer.borderWidth = 0.34;        self.layer.borderColor = [UIColor lightGrayColor].CGColor;        // 加载控件        [self.contentView addSubview:self.indictorImageView];        [self.contentView addSubview:self.sectionTitleLabel];        // 添加点击手势        UITapGestureRecognizer *tapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(respondsToTapGesture:)];        [self addGestureRecognizer:tapGesture];    }    return self;}#pragma mark *** Gestures ***- (void)respondsToTapGesture:(UITapGestureRecognizer *)gesture {    // 判断代理是否存并且遵守协议,如果满足条件,则让代理执行协议方法,并且将对应组以及tag值传递给代理;    if (_delegate && [_delegate conformsToProtocol:@protocol(LHY_HeaderViewDelegate)]) {        [_delegate lhyHeaderView:self didSelectedWithTag:self.tag];    }}#pragma mark *** Getters ***- (UIImageView *)indictorImageView {    if (!_indictorImageView) {        _indictorImageView = [[UIImageView alloc] init];        _indictorImageView.bounds = CGRectMake(0, 0, 20, 20);        _indictorImageView.center = CGPointMake(15, 22);    }    return _indictorImageView;}- (UILabel *)sectionTitleLabel {    if (!_sectionTitleLabel) {        _sectionTitleLabel = [[UILabel alloc] init];        _sectionTitleLabel.bounds = CGRectMake(0, 0, 200, 30);        _sectionTitleLabel.center = CGPointMake(CGRectGetMaxX(self.indictorImageView.frame) + CGRectGetMidX(_sectionTitleLabel.bounds) + 10, 22);        _sectionTitleLabel.font = [UIFont boldSystemFontOfSize:18];    }    return _sectionTitleLabel;}@end

代码块4:加载主页,LHY_HomeViewController,本类主要配置表格视图显示,以及处理组展开/关闭状态。

  • LHY_HomeViewController.h
#import <UIKit/UIKit.h>@interface LHY_HomeViewController : UIViewController@end
  • LHY_HomeViewController.m
#import "LHY_HomeViewController.h"#import "LHY_HeaderView.h"#import "LHY_FriendsListCell.h"static NSString *const kLHY_ViewControllerTitle         = @"好友列表";static NSString *const kReusableCellIdentifier          = @"a1b1c1";static NSString *const kHeaderFooterViewReuseIdentifier = @"a2b2c2";@interface LHY_HomeViewController () <UITableViewDataSource, UITableViewDelegate, LHY_HeaderViewDelegate>{    NSArray *_sectionTitles;   /**< 分组标题集合 */    NSArray *_friendsListInfo; /**< 好友信息集合 */    NSMutableDictionary *_openDict; /**< 用于设置当前分组展开或关闭 */}@property (nonatomic, strong) UITableView *tableView; /**< 表格视图 */- (void)initializeDataSource;    /**< 初始化数据源 */- (void)initializeUserInterface; /**< 初始化用户界面 */@end@implementation LHY_HomeViewController- (void)viewDidLoad {    [super viewDidLoad];    [self initializeDataSource];    [self initializeUserInterface];}#pragma mark *** Initialize methods ***- (void)initializeDataSource {    // 初始化分组数据    _sectionTitles = @[@"同事", @"家人", @"朋友", @"同学", @"陌生人", @"黑名单"];    // 根据plist文件获取好友列表数据    NSString *plistPath = [[NSBundle mainBundle] pathForResource:@"FriendsList" ofType:@"plist"];    _friendsListInfo = [NSArray arrayWithContentsOfFile:plistPath];}- (void)initializeUserInterface {    self.title = kLHY_ViewControllerTitle;    // 关闭系统自动偏移    self.automaticallyAdjustsScrollViewInsets = NO;    self.view.backgroundColor = [UIColor whiteColor];    // 加载表格视图    [self.view addSubview:self.tableView];}#pragma mark *** <UITableViewDataSource, UITableViewDelegate> ***// 设置组数- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {    // 返回分组数据集合的个数    return _sectionTitles.count;}// 设置行数- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {    // 返回好友列表信息集合的个数    return _friendsListInfo.count;}// 设置单元格- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {    LHY_FriendsListCell *cell = [tableView dequeueReusableCellWithIdentifier:kReusableCellIdentifier forIndexPath:indexPath];    // 超出父视图不显示,如果不将其值设为YES(默认为NO),那么现实在表格视图上的数据会出现重叠的bug。    cell.layer.masksToBounds = YES;    // 获取值    NSString *nickName = _friendsListInfo[indexPath.row][@"nickName"];    NSString *headPortrait = _friendsListInfo[indexPath.row][@"headPortrait"];    NSString *signature = _friendsListInfo[indexPath.row][@"context"];    // 赋值控件    cell.nicknameLabel.text = nickName;    cell.signatureLabel.text = signature;    cell.headPortraitImageView.image = [UIImage imageNamed:headPortrait];    return cell;}// 设置行高- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {    // 根据当前组获取字典key    NSString *key = [NSString stringWithFormat:@"%ld", indexPath.section];    // 判断如果字典对应key是否有值,如果有值,说明当前组展开,返回 80 高度;否则为关闭,返回 0 高度;    if ([_openDict objectForKey:key]) {        return 80;    }else {        return 0;    }}// 设置组高- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section {    return 44;}// 自定义头部视图- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section {    // 创建头部视图    // 头部视图重用    LHY_HeaderView *lhyHeaderView = [tableView dequeueReusableHeaderFooterViewWithIdentifier:kHeaderFooterViewReuseIdentifier];    // 设置代理    lhyHeaderView.delegate = self;    // 配置视图显示    lhyHeaderView.sectionTitleLabel.text = _sectionTitles[section];    lhyHeaderView.indictorImageView.image = [UIImage imageNamed:@"flag.png"];    // 设置tag值    lhyHeaderView.tag = section;    // 根据字典对应key(section)是否有值来判断当前组应该展开还是收缩    // 如果有值,则说明应该展开组,无值则应该关闭组    NSString *key = [NSString stringWithFormat:@"%ld", section];    // 通过动画效果实现展开与关闭指示状态的显示    [UIView animateWithDuration:0.4 animations:^{        // 如果展开,则将指示图片旋转45°,箭头向下,意为展开;        // 如果关闭,则移除transform属性,回复原样,箭头向右,意为关闭;        lhyHeaderView.indictorImageView.transform = [_openDict objectForKey:key] ? CGAffineTransformMakeRotation(M_PI_2) : CGAffineTransformIdentity;    }];    return lhyHeaderView;}#pragma mark *** LHY_HeaderViewDelegate ***- (void)lhyHeaderView:(LHY_HeaderView *)lhyHeaderView didSelectedWithTag:(NSInteger)tag {    // 判断字典是否存在,如果不存在,则新建;    // 如果对一个未初始化的可变字典作数据操作,将会导致奔溃;    if (!_openDict) {        _openDict = [NSMutableDictionary dictionary];    }    // 获取tag值,并将其作为字典key    NSString *key = [NSString stringWithFormat:@"%ld", tag];    // 模拟展开与关闭    // 判断当前组是否有值,有值则移除对应key-value对,无值则设置key-value对。    if (![_openDict objectForKey:key]) {        [_openDict setObject:key forKey:key];    }else{        [_openDict removeObjectForKey:key];    }    // 刷新表格视图指定组    [self.tableView reloadSections:[NSIndexSet indexSetWithIndex:tag] withRowAnimation:UITableViewRowAnimationFade];}#pragma mark *** Getters ***- (UITableView *)tableView {    if (!_tableView) {        _tableView = [[UITableView alloc] initWithFrame:CGRectMake(0, 64, CGRectGetWidth(self.view.bounds), CGRectGetHeight(self.view.bounds) - 64) style:UITableViewStylePlain];        // 设置代理        _tableView.delegate = self;        // 设置数据源        _tableView.dataSource = self;        // 设置尾部视图(此设置可解决当表格视图在屏幕能完全展示数据,并且有多余空间情况下出现分割线的问题)        _tableView.tableFooterView = [[UIView alloc] init];        // 注册单元格        [_tableView registerClass:[LHY_FriendsListCell class] forCellReuseIdentifier:kReusableCellIdentifier];        // 注册头部视图        [_tableView registerClass:[LHY_HeaderView class] forHeaderFooterViewReuseIdentifier:kHeaderFooterViewReuseIdentifier];    }    return _tableView;}@end

到了这一步,运行工程,你将会看到上述展示的效果。如果还有朋友不太熟悉思路,欢迎加Q:894416347,填好备注信息,或留言,一起探讨。

4 0
原创粉丝点击