当前位置: 首页 > 编程日记 > 正文

优雅地分离tableview回调

你是否遇到过这样的需求,在tableview中显示一列数据,点击某一个cell时,在此cell下显示相应的附加信息。如下图:


1.gif

你是不是觉得需求很容易实现,只要使用tableview的insertRowsAtIndexPaths:withRowAnimation:插入一个附加cell就可以了(为表述清楚,我们姑且称被点击的cell为原始cell,展示的附加信息为附加cell)。如果你是这么做的,那么后遗症来了:展开的附加cell影响了后面cell的indexPath,当我们对后面的cell配置、点击、增加、删除、系统回调等等操作时,都需要考虑附加cell对indexPath的影响。例如我们已经展开了row == 0的原始cell的附加cell,配置row == 0的附加cell实际是对tableview的row == 1的配置;配置原始数据的row == 1时,实际是配置tableview的row == 2的cell。每次indexPath相关操作都要转换一遍(特别是当展开的附件cell很多时),非常麻烦,且数据源杂乱、代码可读性差。

本文介绍了一种方法消除附加cell对后面的indexPath产生的影响;并且使delegate和dataSourse回调只配置原始cell,将附加cell的配置分离出来。

1.首先定义两种数据类型:

typedef NSIndexPath ActualIndexPath;

实际indexPath:考虑附加cell影响的indexPath,暨系统调用传入的indexPath。

typedef NSIndexPath LogicIndexPath;

逻辑indexPath:不考虑附加cell影响的indexPath,暨我们希望在代码中使用的indexPath。

参照上面的例子解释:展开row == 0的原始cell后,row == 0的原始cell的附加cell的实际indexPath.row为 1,逻辑indexPath.row为0;row == 1的原始cell实际indexPath.row为2,逻辑indexPath.row 为1。

2.为tableview增加了一个assistantDelegate,负责附加cell的配置。

@interfaceYFAssistantTableView :UITableView

@property(nonatomic,weak)idassistantDelegate;

YFAssistantDelegate中声明了配置附加cell的方法(为了减少过多的protocol声明,在不影响代码可读性前提下,将附加cell的delegate和dataSource统一放到assistantDelegate里)。理论上YFAssistantDelegate里面的方法应该是UITableViewDelegate和UITableViewDataSource的并集,但很多方法并不会使用所以不再声明。

@protocol YFAssistantDelegate

//截取了部分YFAssistantDelegate的方法

@required

- (UITableViewCell*)YFAssistantTableView:(YFAssistantTableView*)tableView cellForRowAtIndexPath:(NSIndexPath*)indexPath;

- (BOOL)YFAssistantTableView:(YFAssistantTableView*)tableView shouldSpreadAssistantAtIndexPath:(NSIndexPath*)indexPath;- (CGFloat)YFAssistantTableView:(YFAssistantTableView*)tableView heightForRowAtIndexPath:(NSIndexPath*)indexPath; @end

3.将ActualIndexPath转化为LogicIndexPath

- (LogicIndexPath*)actualIndexPath2Logic:(ActualIndexPath*)indexPath{

NSIntegersection = indexPath.section;

__block NSInteger row = indexPath.row;

//self.assistantsIndexPaths存储了所有已展开的附加cell的indexPath

[self.assistantsIndexPaths enumerateObjectsUsingBlock:^(ActualIndexPath *obj,NSUInteger idx,BOOL *stop) {

if(obj.section== section && obj.row<=indexPath.row) {

row--;

}

}];

return[NSIndex PathindexPathForRow:row inSection:section];

}

3.将对附加cell的方法调用从delegate(dataSource)转到assistantDelegate。

这里用Method Swizzling钩住delegate(dataSource)的每一个方法,在hook方法里首先将ActualIndexPath转为LogicIndexPath,然后判断传进来的indexPath指向的cell的类型,如果指向原始cell那么调用delegate(dataSource)方法并传入LogicIndexPath;如果指向附加cell那么调用assistantDelegate方法并传入LogicIndexPath。(以下以tableView:cellForRowAtIndexPath:为例,其他方法一样)

Method method1 = class_getInstanceMethod([delegate class], @selector(tableView:cellForRowAtIndexPath:));

const char *type = method_getTypeEncoding(method1);

IMP imp = class_getMethodImplementation([self class], @selector(hookTableView:cellForRowAtIndexPath:);

class_addMethod([delegate class], @selector(hookTableView:cellForRowAtIndexPath:), imp, type);

Method method2 = class_getInstanceMethod([delegate class], @selector(hookTableView:cellForRowAtIndexPath:));

method_exchangeImplementations(method1, method2);

- (UITableViewCell*)hookTableView:(YFAssistantTableView*)tableView cellForRowAtIndexPath:(ActualIndexPath*)indexPath{

LogicIndexPath *logicIndexPath = 实际indexPath转为逻辑indexPath(indexPath);

if(indexPath指向附加cell) {

return [tableView.assistantDelegate YFAssistantTableView:tableView cellForRowAtIndexPath:logicIndexPath];

}

//indexPath指向原始cell

return [self hookTableView:tableView cellForRowAtIndexPath:logicIndexPath];

}

至此,YFAssistantTableView的基本思路和主要方法已经介绍完毕。在实际使用中,我们只需要关心哪个indexPath的附加cell需要展开、收回,而不用关心indexPath的变化;并将原始cell和附加cell的分离解耦,调用配置数据清晰顺畅、代码可读性强。附上demo


1.png

2.png

转自 :http://www.cocoachina.com/ios/20161008/17687.html

源码:https://github.com/haltNoShut/YFAssistantTableView


相关文章:

next.js_Next.js手册

next.jsI wrote this tutorial to help you quickly learn Next.js and get familiar with how it works.我编写本教程是为了帮助您快速学习Next.js并熟悉其工作方式。 Its ideal for you if you have zero to little knowledge of Next.js, you have used React in the past,…

Redux 入门教程(一):基本用法

一年半前&#xff0c;我写了《React 入门实例教程》&#xff0c;介绍了 React 的基本用法。 React 只是 DOM 的一个抽象层&#xff0c;并不是 Web 应用的完整解决方案。有两个方面&#xff0c;它没涉及。代码结构组件之间的通信对于大型的复杂应用来说&#xff0c;这两方面恰恰…

Elasticsearch——Rest API中的常用用法

本篇翻译的是Elasticsearch官方文档中的一些技巧&#xff0c;是使用Elasticsearch必不可少的必备知识&#xff0c;并且适用于所有的Rest Api。 返回数据格式化 当在Rest请求后面添加?pretty时&#xff0c;结果会以Json格式化的方式显示。另外&#xff0c;如果添加?formatyaml…

Python几种主流框架

从GitHub中整理出的15个最受欢迎的Python开源框架。这些框架包括事件I/O&#xff0c;OLAP&#xff0c;Web开发&#xff0c;高性能网络通信&#xff0c;测试&#xff0c;爬虫等。 Django: Python Web应用开发框架Django 应该是最出名的Python框架&#xff0c;GAE甚至Erlang都有框…

Git Fetch vs Pull:Git Fetch和Git Pull命令之间有什么区别?

Git pull and fetch are two commands that are regularly used by Git users. Let’s see the difference between both commands.Git pull和fetch是Git用户经常使用的两个命令。 让我们看看两个命令之间的区别。 For the sake of context, it’s worth remembering that we’…

Redux 入门教程(二):中间件与异步操作

上一篇文章&#xff0c;我介绍了 Redux 的基本做法&#xff1a;用户发出 Action&#xff0c;Reducer 函数算出新的 State&#xff0c;View 重新渲染。但是&#xff0c;一个关键问题没有解决&#xff1a;异步操作怎么办&#xff1f;Action 发出以后&#xff0c;Reducer 立即算出…

day09_读写分离_组件介绍

mysql中间件研究&#xff08;Mysql-prxoy&#xff0c;Atlas&#xff0c;阿米巴&#xff0c;cobar&#xff0c;TDDL&#xff09;mysql-proxyMySQL Proxy就是这么一个中间层代理&#xff0c;简单的说&#xff0c;MySQL Proxy就是一个连接池&#xff0c;负责将前台应用的连接请求转…

Idea其他设置

一、生成javadoc Tools->Gerenate JavaDoc 1. 选择是整个项目还是模块还是单个文件 2. 文档输出路径 3. Locale 选择地区&#xff0c;这个决定了文档的语言&#xff0c;中文就是zh_CN 4. 传入JavaDoc的参数&#xff0c;一般这样写 -encoding UTF-8 -charset UTF-8 -windowti…

freecodecamp_常见技术支持问题– freeCodeCamp常见问题解答

freecodecamp问题&#xff1a;我刚刚登录我的帐户&#xff0c;但看不到过去的任何进展。 (Question: I just signed into my account and I dont see any of my past progress.) Answer: You have created a duplicate account. Sign out of your account and try signing in u…

Redux 入门教程(三):React-Redux 的用法

前两篇教程介绍了 Redux 的基本用法和异步操作&#xff0c;今天是最后一部分&#xff0c;介绍如何在 React 项目中使用 Redux。 为了方便使用&#xff0c;Redux 的作者封装了一个 React 专用的库 React-Redux&#xff0c;本文主要介绍它。 这个库是可以选用的。实际项目中&…

SDN第二次上机作业

1、安装floodlight 2、生成拓扑并连接控制器floodlight&#xff0c;利用控制器floodlight查看图形拓扑 3、利用字符界面下发流表&#xff0c;使得‘h1’和‘h2’ ping 不通 4、利用字符界面下发流表&#xff0c;通过测试‘h1’和‘h3’的联通性&#xff0c;来验证openflow的har…

[KIWI syslog]Install document

安全日志的标准是rfc5425 它介绍SSL-tunnel日志标准和相关要求的标准定义。此外&#xff0c;IANA分配TCP端口6514作为一个标准的端口安全日志。 安装说明&#xff1a; 1.安装日志服务器 官方下载地址&#xff1a;http://www.kiwisyslog.com/products/. 安装步骤&#xff1a; 转…

机器学习数据拆分_解释了关键的机器学习概念-数据集拆分和随机森林

机器学习数据拆分数据集分割 (Dataset Splitting) Splitting up into Training, Cross Validation, and Test sets are common best practices. This allows you to tune various parameters of the algorithm without making judgements that specifically conform to trainin…

Oracle 导表异常处理方案 (解决空表导出出错问题)

Select alter table ||table_name|| allocate extent; from user_tables where num_rows0 or num_rows is null 然后执行查询语句 再导出数据 一个语句搞定&#xff1a; declare stmt varchar2(200); begin for tb in (select table_name from user_tables where seg…

10个你必须知道的ios框架

你好&#xff0c;iOS 开发者们&#xff01;我的名字叫 Pawe?&#xff0c;我是一个独立 iOS 开发者&#xff0c;并且是 Enter Universe 的作者。 接近两年前我发布了iOS开源库&#xff0c;让你的开发坐上火箭吧。这是我在这里最棒的文章了&#xff08;根据 Medium 用户的反馈来…

生成N个不相等的随机数

近期项目中须要生成N个不相等的随机数。实现的时候。赶工期&#xff0c;又有项目中N非常小(0-100)直接谢了一个最直观的方法: public static List<Integer> randomSet(int num,int threshold){Random random new Random();if(num > threshold) return null;Set<In…

kafka streams_如何使用Kafka Streams实施更改数据捕获

kafka streamsChange Data Capture (CDC) involves observing the changes happening in a database and making them available in a form that can be exploited by other systems. 更改数据捕获 (CDC)涉及观察数据库中发生的更改&#xff0c;并将其以可被其他系统利用的形式…

iOS超全开源框架、项目和学习资料汇总(1)UI篇

上下拉刷新控件**1. ** MJRefresh --仅需一行代码就可以为UITableView或者CollectionView加上下拉刷新或者上拉刷新功能。可以自定义上下拉刷新的文字说明。&#xff08;推荐&#xff09;**2. ** SVPullToRefresh --下拉刷新控件4500star&#xff0c;值得信赖**3. ** CBStoreHo…

day16 递归函数

一、递归 函数 为什么要有函数&#xff0c;提高代码的可读性&#xff0c;避免重复的代码&#xff0c;提高代码的复用性 在函数中能用return的不要print 1、递归的最大深度997 def foo(n):print(n)n1foo(n) foo(1) 递归的最大深度2、修改递归的最大深度 由此我们可以看出&#x…

设计模式之笔记--抽象工厂模式(Abstract Factory)

抽象工厂模式&#xff08;Abstract Factory&#xff09; 定义 抽象工厂模式&#xff08;Abstract Factory&#xff09;&#xff0c;提供一个创建一系列相关或相互依赖对象的接口&#xff0c;而无需指定它们具体的类。 类图 描述 多个抽象产品类&#xff0c;每个抽象产品类可以派…

用户体验改善案例_如何检测用户的设备,以便改善他们的用户体验

用户体验改善案例A few months ago I watched a great talk from the Chrome Dev Summit about performance in slow devices.几个月前&#xff0c;我观看了Chrome开发者峰会上有关慢速设备性能的精彩演讲。 It blew my mind all the work done by Facebook in identifying de…

【如何快速的开发一个完整的iOS直播app】(采集篇)

前言在看这篇之前&#xff0c;如果您还不了解直播原理&#xff0c;请查看这篇文章如何快速的开发一个完整的iOS直播app(原理篇)开发一款直播app&#xff0c;首先需要采集主播的视频和音频&#xff0c;然后传入流媒体服务器&#xff0c;本篇主要讲解如何采集主播的视频和音频&am…

easyui 报表合并单元格

前段时间工作中碰到有需求&#xff0c;要求数据按下图所示格式来显示&#xff0c;当时在园子里看到了一篇文章&#xff08;时间久了&#xff0c;想不起是哪一篇&#xff09;&#xff0c;研究了后做出了如下的DEMO&#xff0c;在此当作学习笔记&#xff0c;简单记录一下。 首先是…

HDU2594 KMP next数组的应用

这道题就是给你两个串s1, s2让你求出s1 s2的最长相同前缀和后缀&#xff0c; 我们直接将s1 s2连接到一起然后处理一下next数组即可&#xff0c; 注意答案应该是min(len(s1), len(s2) , next[len]), 代码如下&#xff1a; #include <cstdio> #include <cstring> #in…

c语言中浮点数和整数转换_C中的数据类型-整数,浮点数和空隙说明

c语言中浮点数和整数转换C中的数据类型 (Data Types in C) There are several different ways to store data in C, and they are all unique from each other. The types of data that information can be stored as are called data types. C is much less forgiving about d…

【如何快速的开发一个完整的iOS直播app】(美颜篇)

前言在看这篇之前&#xff0c;如果您还不了解直播原理&#xff0c;请查看这篇文章如何快速的开发一个完整的iOS直播app(原理篇)开发一款直播app&#xff0c;美颜功能是很重要的&#xff0c;如果没有美颜功能&#xff0c;可能分分钟钟掉粉千万&#xff0c;本篇主要讲解直播中美颜…

Linux内核分析——第五章 系统调用

第五章 系统调用 5.1 与内核通信 1、系统调用在用户空间进程和硬件设备之间添加了一个中间层&#xff0c;该层主要作用有三个&#xff1a; &#xff08;1&#xff09;为用户空间提供了一种硬件的抽象接口 &#xff08;2&#xff09;系统调用保证了系统的稳定和安全 &#xff08…

BZOJ 3110

http://www.lydsy.com/JudgeOnline/problem.php?id3110 整体二分区间修改树状数组维护 #include<cstdio> #define FOR(i,s,t) for(register int is;i<t;i) inline int max(int a,int b){return a>b?a:b;} inline int min(int a,int b){return a<b?a:b;} type…

css 选择器 伪元素_CSS伪元素-解释选择器之前和之后

css 选择器 伪元素选择器之前 (Before Selector) The CSS ::before selector can be used to insert content before the content of the selected element or elements. It is used by attaching ::before to the element it is to be used on.CSS ::before选择器可用于在选定…

各种面试题啊1

技术 基础 1.为什么说Objective-C是一门动态的语言&#xff1f; 什么叫动态静态 静态、动态是相对的&#xff0c;这里动态语言指的是不需要在编译时确定所有的东西&#xff0c;在运行时还可以动态的添加变量、方法和类 Objective-C 可以通过Runtime 这个运行时机制&#xff0c…