GithubHelp home page GithubHelp logo

Comments (1)

ShannonChenCHN avatar ShannonChenCHN commented on June 27, 2024

『如何写好一个UITableView』

一、痛点

  • UITableView 的代理方法和数据源方法
  • 网络请求、解析数据
  • 下拉、上拉

二、MVC

  • 控制器 controller 负责 model 和 view 的交互
  • Model 不仅仅是一个 model 类,而是一个 model 层
  • Controller 只做不能复用的事情?
  • 解耦不仅仅是把代码拆开,更应该是关注代码结构的可重用性、可维护性、可扩展性。

三、传统的做法

现状

Controller 中实现 delegate 和 dataSource 方法,管理 UI 和数据、交互。

  • 违背 MVC 模式,现在是 V 持有 C 和 M(?)
  • C 管理了全部逻辑,耦合太严重
  • 其实绝大多数 UI 相关都是由 Cell 而不是 UITableView 自身完成的(?)

数据源

总共有以下几类方法:

  • Row count
  • Row display
  • Editing
  • Moving/reordering
  • Index
  • Data manipulation

其中两个最常用的方法:

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView;
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section;
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath;

代理

总共有以下几类方法:

  • Display customization
  • Variable height support
  • Section header & footer information
  • Accessories (disclosures)
  • Selection
  • Editing
  • Moving/reordering
  • Indentation
  • Copy/Paste
  • Focus

其中以下几个方法最常用:

- (nullable UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section;   
- (nullable UIView *)tableView:(UITableView *)tableView viewForFooterInSection:(NSInteger)section;

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath;
- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section;
- (CGFloat)tableView:(UITableView *)tableView heightForFooterInSection:(NSInteger)section;

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath;

四、优化

1. 数据源

  • 基本**:将数据源方法抽取出来到一个单独的类中

  • 包含哪些内容:

    • 每个 DataSource 都应该有一个 SectionModels 数组,一个 Section 对应一个 SectionModel
    • 每个 SectionModel 中又包含 Header、Footer、Cell 相关的数据,其中这个 section 中所有的 cell 数据都放到 CellModels 数组中
    • CellModel 中可以保存 cell reuse identifier 和 cell height 缓存、以及 cell 展示信息等数据
  • 实现哪些方法:下面三个方法都要在这个数据源类中实现

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView;
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section;
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath;
  • 自定义一个继承于 UITableViewDataSource 的 dataSource 协议,包含两个方法
// 每个 indexPath 对应的 cell 的 class,主要是用来给 `cellForRow` 方法创建 cell 用的,子类需要重写
- (Class)tableView:(UITableView*)tableView cellClassForModel:(SCTableViewCellModel *)model;

// 提供一个接口用于获取指定 indexPath 对应的 model
- (SCTableViewCellModel *)tableView:(UITableView *)tableView modelForRowAtIndexPath:(NSIndexPath *)indexPath;
  • 自定义 Cell 要做的事情:实现 -setModel 方法,或者提供 model 属性,用来接收 model 数据

2. 代理

  • 两个问题
    • cell 高度:跟 UI 和数据相关,应该交给 cell 自己去计算,提供 model 就行了----> cell 中提供一个计算高度的静态方法
    • 点击事件:主要是根据 model 来处理,位置并不太需要关心---->定义一个继承于 UITableViewDelegate 的协议,提供一个用于处理点击事件的方法,作为系统 cell 点击方法的中转,顺便将 model 传递出来

3.如何将上面针对数据源和代理的优化串联起来

  • 定义一个继承于 UITableView 的自定义 table view
  • 定义两个属性 customDelegatecustomDataSource,分别用来接收自定义数据源和自定义代理,如果数据源中有数据,自定义 table view 可以直接在内部处理一些系统的代理方法,如果没有,也可以交给外面的 controller 自己去实现
  • 设置 tableView 的 delegate 为自定义 table view 自身
  • 重写 customDataSource 的 setter 方法,将 dataSource 中转给 UITableViewdataSource 属性
  • 在自定义 table view 中实现 UITableViewDelegate 的方法,用来计算 cell 高度,和分发点击事件等

4. 成果

  • 此时的 MVC
    • M 就是数据源及相关的 Model 类
    • V 就是自定义的 table view
    • C 就是一个更清爽的 controller,只负责创建数据源、table view,以及处理一些不能复用的逻辑
  • 解决了什么痛点
    • 不再需要每次写一大堆重复的 UITableView 代理方法和数据源方法
    • 实现简单的数据绑定,不再需要关注代理方法和数据源方法,只需要关注数据源(Model)和 cell 自身(View)
    • 更清晰的 MVC
  • 如何使用
    • 创建你的 View Controller,创建数据源对象,实现“数据绑定”
    • 创建一个数据源子类,在数据源中重写一些基类的方法,比如 cell 的 class
    • 创建自定义 cell 的子类,实现 setModel 方法、cell 高度计算的方法

5. 下一步

  • 通信:cell 如何和 controller 通信 ——> 代理回调或者弱持有 controller(是不是可以搞个类似于 router 那种中间层呢)
  • 下拉刷新和上拉加载的处理
  • 网络请求、数据解析的处理
  • (网络错误或者数据为空时的占位图)

五、网络层

1. 为什么需要网络层

  • 考虑到后期更新——第三方网络库的迁移,比如由 ASI 迁移到 AFNetworking
  • 需要第三方网络库基础上添加一些扩展功能,比如请求的取消,翻页,计算请求耗时,对 header 的处理等等
  • 其他跟网络请求有关的自定义扩展,请求失败的 toast,空态占位图,log 等等

2. 三个环节

  • 发起请求
    • 集约式(命令式):只定义一个类,提供接口接受参数来发请求
    • 分布式(声明式):定义一个基类,然后针对各个 API 再分别创建对应的 API Manager 子类
  • 回调处理
    • block
    • delegate
  • 数据解析

3. 实现

  • 定义一个 API Manager 类
    • 封装网络请求的具体实现,让调用更简单
    • 扩展一些自定义功能:网络请求的状态、返回时的数据处理
    • 处理一些公共逻辑:耗时统计等
  • 定义 BaseItem:主要用来 JSON 解析,保存解析后的数据
  • 定义 BaseModel
    • 管理对应 API 的网络请求
    • 管理数据模型 BaseItem

4. 实践

  • 定义 BaseItem 的子类
  • 定义 BaseModel 的子类,并持有 Item 和 API Manager
  • 在 controller 中创建所需的 Model 对象,发起网络请求,并添加处理回调的逻辑,回调时可以从 Model 中获取到解析后 Item 数据

六、下拉刷新和上拉加载更多

1. 两个问题

  • 封装 UI 细节
  • 翻页

2.实现

  • 继承 baseTableController,提供设置上拉下拉控件的接口和处理一些基础逻辑(比如 endRefresh 操作), 并在自定义 tableView 中处理上下拉控件的添加细节
  • 继承 BaseModel,实现翻页的逻辑

七、总结

  • 没有最通用的架构,只有最合适的架构
  • 实际情况下,架构的设计应该是自顶向下的,先有了问题和需求,再一层一层抽象

一些思考:
核心**:复用+解耦+易于理解、逻辑清晰

  1. 值得借鉴之处:
  2. 不同看法:
  3. 补充:
  • 空态页
  • 网络错误提示 toast
  • 页面缓存

from ios-app-architecture.

Related Issues (13)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.