「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;@endNS_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;@endNS_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.rhkb.cn/news/412641.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

chapter08-面向对象编程——(Object类详解)——day09

目录 319-运算符 320-查看Jdk源码 321-子类重写equals 322-equals课堂练习1 323-equals重写练习2 324-equals重写练习3 325-hashCode 326-toString 327-finalize 319-运算符 引用的都是同一个地址&#xff0c;所以返回true 320-查看Jdk源码 equals只能判断引用类型是…

艾体宝干货丨Redis与MongoDB的区别

Redis&#xff08;Remote Dictionary Server&#xff0c;远程字典服务器&#xff09;和 MongoDB 是两类知名的 NoSQL数据库&#xff0c;其以非结构化的方式存储数据。与传统关系数据库使用表格、行和列来组织数据不同&#xff0c;NoSQL数据库采用了不同的数据存储模型。Redis是…

探索极速Python:Sanic框架的魔力

文章目录 探索极速Python&#xff1a;Sanic框架的魔力背景&#xff1a;为什么选择Sanic&#xff1f;Sanic是什么&#xff1f;如何安装Sanic&#xff1f;简单的库函数使用方法场景应用示例常见Bug及解决方案总结 探索极速Python&#xff1a;Sanic框架的魔力 背景&#xff1a;为什…

【位置编码】【Positional Encoding】直观理解位置编码!把位置编码想象成秒针!

【位置编码】【Positional Encoding】直观理解位置编码&#xff01;把位置编码想象成秒针&#xff01; 你们有没有好奇过为啥位置编码非得长成这样&#xff1a; P E ( p o s , 2 i ) s i n ( p o s 1000 0 2 i / d m o d e l ) P E ( p o s , 2 i 1 ) c o s ( p o s 1000 …

AcWing895. 最长上升子序列

这个代码不知道怎么说&#xff0c;反正就是对着代码手算一次就懂了&#xff0c;无需多言&#xff0c;就是俩for循环里面的第二层for的循环条件是j<i,j是从下标1往下标i-1遍历的&#xff0c;每次a【j】<a【i】就在答案数组f【i】上面做出更新。基本的输入样例已经可以覆盖…

红黑树刨析(删除部分)

文章目录 红黑树删除节点情景分析情景1&#xff1a;删除节点左右子树都为空情景1.1&#xff1a;删除节点为红色情景1.2&#xff1a;删除节点为黑色情况1.2.1&#xff1a;删除节点的兄弟节点是红色情景1.2.2&#xff1a;删除节点的兄弟节点是黑色情景1.2.2.1&#xff1a;删除节点…

Cpp学习手册-基础学习

首先你要去网上下载对应的运行软件&#xff0c;先把对应的 C 环境配置好&#xff0c;配置好了我们就可以开始我们的C 学习之旅了。希望通过学习我们能够成为一个比较不错的 C 开发工程师。我也会持续更新 C 知识。 1. C语法基础 当我通过 CLion 工具创建了一个新的 Project 。…

Redis中的 大/热 key问题 ,如何解决(面试版)

big key 什么是 big key? big key&#xff1a;就是指一个内存空间占用比较大的键(Key) 造成的问题&#xff1a; 内存分布不均。在集群模式下&#xff0c;不同 slot分配到不同实例中&#xff0c;如果大 key 都映射到一个实例&#xff0c;则分布不均&#xff0c;查询效率也…

自建电商网站整合Refersion教程

前言&#xff1a;   先介绍一下Refersion有啥用&#xff0c;如果你有一个自己的跨境电商独立站点&#xff0c;想找一些网红帮忙推广销售自己的商品&#xff0c;然后按照转化订单比例给网红支付佣金&#xff0c;这件事情对双方来说透明性和实时性很重要&#xff0c;Refersion就…

C++ | Leetcode C++题解之第382题链表随机节点

题目&#xff1a; 题解&#xff1a; class Solution {ListNode *head;public:Solution(ListNode *head) {this->head head;}int getRandom() {int i 1, ans 0;for (auto node head; node; node node->next) {if (rand() % i 0) { // 1/i 的概率选中&#xff08;替…

Unity URPShader支持多光源处理

//声明变体并且引用文件 #pragma shader_feature _ _ADDITIONAL_LIGHTS_VERTEX _ADDITIONAL_LIGHTS #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Lighting.hlsl" //在数据结构体中声明需要使用的数据 struct Attributes {float4 posit…

五种多目标优化算法(NSGA3、MOPSO、MOGWO、NGSA2、SPEA2)性能对比,包含47个多目标测试函数,6种评价指标,MATLAB代码

一、五种多目标算法及六种评价指标简介 多目标灰狼优化算法&#xff08;MOGWO&#xff09;&#xff1a; MOGWO是由Mirjalili等人在2016年提出的&#xff0c;基于灰狼优化算法&#xff08;GWO&#xff09;的多目标版本。它引入了存档机制和改进的头狼选择方式&#xff0c;以处理…

2024-08-30作业

作业2 代码 #include <iostream>using namespace std;class Per { private: string name; int age; double* height; double* weight; public: //有参构造函数 Per(string name,int age,int height,int weight):name(name),age(age),height(new double(height)),weigh…

基于STM32开发的智能家居温度控制系统

目录 引言环境准备工作 硬件准备软件安装与配置系统设计 系统架构硬件连接代码实现 系统初始化温度监测与显示风扇/加热器控制Wi-Fi通信与远程监控应用场景 家庭环境的智能温度管理办公楼的节能温控系统常见问题及解决方案 常见问题解决方案结论 1. 引言 随着人们对生活质量…

Flask+LayUI开发手记(六):树型表格的增删改查

树型表格的增删改查功能与数据表格的是完全一致&#xff0c;就是调用layui-form表单组件实现数据输入再提交&#xff0c;比较大的区别是树型节点的编辑&#xff0c;都需要有上级节点的输入&#xff0c;而这个上级节点的展示&#xff0c;必须是以树型方式展示出来。当然&#xf…

使用facebook开源prophet模型预测上证指数etf股价

可以图个乐&#xff0c;没有那么准确&#xff0c;可能还需要更深入的研究分析 蓝线是预测的2024年的走势&#xff0c;绿线是实际走势&#xff0c;红线是历史和未来的分界线。结果上有蛮多差异的。 # 测试预测2024年 coded by luke 伊玛目的门徒 import akshare as ak impor…

信息学奥赛一本通/openjudge Crossing River

题目 一本通题目入口 openjudge题目入口 &#xff08;注&#xff1a;由于一本通题面描述的可能有些欠缺&#xff0c;所以这里的题面采用openjudge英文翻译后的题面&#xff09; 题目分析 首先我们来看样例&#xff0c;为什么样例的结果是17呢?首先观察&#xff0c;“5”和“…

GUI编程04:课堂练习及总结

本节内容视频链接&#xff1a;6、课堂练习讲解及总结_哔哩哔哩_bilibilihttps://www.bilibili.com/video/BV1DJ411B75F?p6&vd_sourceb5775c3a4ea16a5306db9c7c1c1486b5 根据前三节学习到的Frame、Panel、Button知识&#xff0c;画出一下窗口界面&#xff1a; 实现代码如下…

Spring Security基于token的极简示例

1 引言 Spring Security是一个能够为基于Spring的企业应用系统提供声明式的安全访问控制解决方案的安全框架&#xff0c;但是用起来有点复杂&#xff0c;为了便于学习理解&#xff0c;下面将用最简洁的配置和示例&#xff0c;展示整个流程。 2 代码 创建一个spring-security…

单图生成 2D 和 3D 人物,高质量图像处理模型 CharacterGen来啦!

CharacterGen引入了一个简化的生成流程和一个图像条件的多视图扩散模型。该模型有效地将输入姿态校准到规范形式&#xff0c;同时保留输入图像的关键属性&#xff0c;从而解决了多样化姿态带来的挑战。 CharacterGen的另一个核心组成部分是基于Transformer的、可泛化的稀疏视图…