当前位置: 首页 > article >正文

「OC」CAlayer——巧用动画实现一个丝滑的折叠cell

「OC」CAlayer——巧用动画实现一个丝滑的折叠cell

前言

在这个暑假集训后的时间,都在家里做着学习笔记的整理,深入学习了CALayer的相关知识,掌握了第三方库Masonry自动布局的用法,以及学习了MVC的相关内容,正好组内新学期的第一个任务就是写一个折叠cell的小demo,所以就打算将暑假学习过的内容,尽量整合在一块,进行巩固复习。

分装Model

由于我们只是简单的写一个折叠cell,所以单个的cell并不需要太过复杂的内容,所以我们将section进行分装即可,一个section就有着一个布尔值,判断是否展开,一个存储section数据的数组,还有对应section的headerView的标题

@interface Model : NSObject

@property (nonatomic, strong) NSString *name;
@property (nonatomic, strong) NSArray<NSString *> *items;
@property (nonatomic, assign) BOOL collapsed;

- (instancetype)initWithName:(NSString *)name items:(NSArray<NSString *> *)items collapsed:(BOOL)collapsed;

@end

NS_ASSUME_NONNULL_END
#import "Model.h"

@implementation Model

- (instancetype)initWithName:(NSString *)name items:(NSArray<NSString *> *)items collapsed:(BOOL)collapsed {
    self = [super init];
    if (self) {
        _name = name;
        _items = items;
        _collapsed = collapsed;
    }
    return self;
}

@end

重写headerView

为了更好的分装,提现MVC的实现,我写了一个headerView的子类,使用Masonry进行布局

//Header.h
#import <UIKit/UIKit.h>

NS_ASSUME_NONNULL_BEGIN

@interface Header : UITableViewHeaderFooterView

@property (nonatomic, strong) UILabel *titleLabel;
@property (nonatomic, strong) UIButton *rowButton;


@end

NS_ASSUME_NONNULL_END


//Header.m
#import "Header.h"
#import "Masonry.h"
@implementation Header

- (instancetype)initWithReuseIdentifier:(NSString *)reuseIdentifier {
    self = [super initWithReuseIdentifier:reuseIdentifier];
    if (self) {
        [self setupViews];
    }
    return self;
}

- (void)setupViews {
    self.contentView.backgroundColor = [UIColor purpleColor];
    
    self.titleLabel = [[UILabel alloc] init];
    self.titleLabel.font = [UIFont boldSystemFontOfSize:16];
    self.titleLabel.textColor = [UIColor whiteColor];
    [self.contentView addSubview:self.titleLabel];
    
    self.rowButton = [UIButton buttonWithType:UIButtonTypeSystem];
    [self.rowButton setImage:[UIImage systemImageNamed:@"chevron.right"] forState:UIControlStateNormal];
    self.rowButton.tintColor = [UIColor darkTextColor];
    [self.contentView addSubview:self.rowButton];
    
    [self.titleLabel mas_makeConstraints:^(MASConstraintMaker *make) {
        make.left.equalTo(self.contentView).offset(15);
        make.centerY.equalTo(self.contentView);
        make.right.equalTo(self.contentView);
    }];
    
    [self.rowButton mas_makeConstraints:^(MASConstraintMaker *make) {
        make.width.equalTo(@30);
        make.height.equalTo(@30);
        make.right.equalTo(self.contentView).offset(-15);
        make.centerY.equalTo(self.contentView);
    }];

}

@end

我们还需要在控制器之中对headerView进行注册

- (void)viewDidLoad {
    [super viewDidLoad];
    
    [self setupSections];
    self.tableView = [[UITableView alloc] initWithFrame:self.view.bounds style:UITableViewStyleGrouped];
    self.tableView.delegate = self;
    self.tableView.dataSource = self;
    [self.view addSubview:self.tableView];
    [self.tableView registerClass:[Header class] forHeaderFooterViewReuseIdentifier:@"header"];
    [self.tableView registerClass:[UITableViewCell class] forCellReuseIdentifier:@"cell"];
}

- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section {
    Header *header = [tableView dequeueReusableHeaderFooterViewWithIdentifier:@"header"];
    
    if (!header) {
        header = [[Header alloc] initWithReuseIdentifier:@"header"];
    }
    
    Model *sectionData = self.models[section];
    header.titleLabel.text = sectionData.name;
    header.rowButton.tag = section;
    [header.rowButton addTarget:self action:@selector(toggleCollapse:) forControlEvents:UIControlEventTouchUpInside];
    
    [self rotateButton:header.rowButton collapsed:sectionData.collapsed];
    
    return header;
}

折叠的相关逻辑

其实早在暑假的3Gshare之中就已经学习折叠cell,只不过那时候写的比较大粗糙,还是为了应付是直接设置了一个button,点击就将tableView展开,当button不再选中状态的时候tableView就收回,这次编写的是使用tableView的headerView来进行操作,不过大致展开的逻辑还是相同的

我们先前在Model之中定义了一个判定是否展开的布尔值

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    Model *sectionData = self.models[section];
    return sectionData.collapsed ? 0 : sectionData.items.count;
}

- (void)toggleCollapse:(UIButton *)sender {//设置相关的方法使得headerView的按钮能触发该事件
    NSInteger section = sender.tag;
    Model *sectionData = self.models[section];
    sectionData.collapsed = !sectionData.collapsed;
    
    [self.tableView reloadSections:[NSIndexSet indexSetWithIndex:section] withRowAnimation:UITableViewRowAnimationNone];//使用动画进行自动变化
  
}	

设置动画

由于按钮图片之中的箭头,需要在点击的时候顺带进行,90度的翻转,所以,我们我用上了CALayer之中的旋转动画,内容如下

- (void)rotateButton:(UIButton *)button collapsed:(BOOL)collapsed {
    CGFloat angle = collapsed ? 0.0 : M_PI_2;
    button.transform = CGAffineTransformMakeRotation(angle);
}

完整代码

控制器的完整代码如下

#import "ViewController.h"
#import "Model.h"
#import "Header.h"

@interface ViewController ()<UITableViewDelegate,UITableViewDataSource>

@property (nonatomic, strong) NSMutableArray<Model *> * models;
@property (nonatomic, strong) UITableView *tableView;
@end

@implementation ViewController



- (void)viewDidLoad {
    [super viewDidLoad];
    
    [self setupSections];
    self.tableView = [[UITableView alloc] initWithFrame:self.view.bounds style:UITableViewStyleGrouped];
    self.tableView.delegate = self;
    self.tableView.dataSource = self;
    [self.view addSubview:self.tableView];
    [self.tableView registerClass:[Header class] forHeaderFooterViewReuseIdentifier:@"header"];
    [self.tableView registerClass:[UITableViewCell class] forCellReuseIdentifier:@"cell"];
}

- (void)setupSections {
    self.models = [NSMutableArray array];
    [self.models addObject:[[Model alloc] initWithName:@"Mac" items:@[@"MacBook", @"MacBook Air", @"MacBook Pro", @"iMac", @"Mac Pro", @"Mac mini", @"Accessories", @"OS X El Capitan"] collapsed:YES]];
    [self.models addObject:[[Model alloc] initWithName:@"iPad" items:@[@"iPad Pro", @"iPad Air 2", @"iPad mini 4", @"Accessories"] collapsed:NO]];
    [self.models addObject:[[Model alloc] initWithName:@"iPhone" items:@[@"iPhone 6s", @"iPhone 6", @"iPhone SE", @"Accessories"] collapsed:NO]];
}



- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
    return self.models.count;
}

-(CGFloat)tableView:(UITableView *)tableView heightForFooterInSection:(NSInteger)section {
    return 0;
}

- (UIView *)tableView:(UITableView *)tableView viewForFooterInSection:(NSInteger)section {
    UIView *view = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 0, 0)];
    return view;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    Model *sectionData = self.models[section];
    return sectionData.collapsed ? 0 : sectionData.items.count;
}

- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section {
    Header *header = [tableView dequeueReusableHeaderFooterViewWithIdentifier:@"header"];
    
    if (!header) {
        header = [[Header alloc] initWithReuseIdentifier:@"header"];
    }
    
    Model *sectionData = self.models[section];
    header.titleLabel.text = sectionData.name;
    header.rowButton.tag = section;
    [header.rowButton addTarget:self action:@selector(toggleCollapse:) forControlEvents:UIControlEventTouchUpInside];
    
    [self rotateButton:header.rowButton collapsed:sectionData.collapsed];
    
    return header;
}

- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section {
    return 50;
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"cell" forIndexPath:indexPath];
    
    Model *sectionData = self.models[indexPath.section];
    cell.textLabel.text = sectionData.items[indexPath.row];
    
    return cell;
}



- (void)toggleCollapse:(UIButton *)sender {
    NSInteger section = sender.tag;
    Model *sectionData = self.models[section];
    sectionData.collapsed = !sectionData.collapsed;
    
    [self.tableView reloadSections:[NSIndexSet indexSetWithIndex:section] withRowAnimation:UITableViewRowAnimationNone];
}

- (void)rotateButton:(UIButton *)button collapsed:(BOOL)collapsed {
    CGFloat angle = collapsed ? 0.0 : M_PI_2;
    button.transform = CGAffineTransformMakeRotation(angle);
}
@end

展示

实现的完整内容如下

Aug-30-2024 10-58-33

参考资料

如何在 IOS 中实现可折叠 / 展开的 Table Section?!

ios-swift-collapsible-table-section


http://www.kler.cn/news/283572.html

相关文章:

  • 云端集中管控边缘服务:利用 EMQX ECP 在 K8s 上快速部署 NeuronEX
  • 删除微信重复文件的简易工具
  • unreal engine5中character角色和怪物交互时发生穿模
  • Kubernetes存储Volume
  • 借题《黑神话悟空》,聊聊UE5 游戏开发中基本的 C++ 概念
  • 日常刷题(24)
  • 【为项目做准备】Linux操作系统day1
  • STM32G474采用“多个单通道ADC转换”读取3个ADC引脚的电压
  • 报错:E: 无法修正错误,因为您要求某些软件包保持现状,就是它们破坏了软件包间的依赖关系。
  • [java][基础]ArrayList基本使用
  • NLP(三):词向量
  • 如何利用ChatGPT高效学习英语:从零基础到流利表达
  • 优化 spring boot 的启动速度
  • 打工人 Excel 插件 - 电子表格智能辅助插件正版购买
  • okhttp异步请求连接阻塞问题排查
  • 安卓13 背光反向 亮度反向 android13 backlight reverse
  • 家里两个路由器IP地址一样吗?‌IP地址冲突怎么办?‌
  • 数据结构代码分享
  • 机器视觉--光源打光技巧
  • C++中的异常处理与资源管理
  • 79、ansible-----playbook2
  • 以太坊 MEV 提案续篇:一文了解 Execution Tickets 和 Execution Auction
  • 金融涉案账户压降行动的实施成效与挑战
  • <WPF> xaml代码如何使用c#编写
  • 深入理解 Java 中 Map 和 Set 接口的高级用法
  • 【Rust光年纪】Rust多媒体处理库全面比较:探索安全高效的多媒体处理利器
  • Docker私有镜像仓库Harbor安装并推拉镜像
  • uniapp APP版本更新
  • easyExcel 填充写时,动态合并单元格
  • SQL视图:简化复杂查询的利器