Controller不是tableView的垃圾桶

简介: <p style="margin-top:0px; margin-bottom:20px; padding-top:0px; padding-bottom:0px; color:rgb(37,37,37); font-family:'Helvetica Neue',Helvetica,STheiti,微软雅黑,黑体,Arial,Tahoma,sans-serif,serif; font-s

说在前面:

最近有个MVVM模式非常火热,相信它的出现是为了模块化iOS开发,其实在我看来,它始终还是MVC模式,只是一个变种罢了。(当然有人用到了响应式编程的思路颠覆了常规, 但我们今天把讨论点集中于代码的设计模式)。

与其专注于说明 MVVM 的来历,不如让我们看一个典型的 iOS 是如何构建的,并从那里了解MVVM:

687474703a2f2f7777772e6f626a632e696f2f696d616765732f69737375652d31332f6d76766d312e706e67.png

Typical Model-View-Controller setup

我们看到的是一个典型的 MVC设置。Model呈现数据,View呈现用户界面,而 View Controller调节它两者之间的交互。

稍微考虑一下,虽然View 和 View Controller是技术上不同的组件,但它们几乎总是手牵手在一起,成对的。你什么时候看到一个 View能够与不同 View Controller配对?或者反过来?所以,为什么不正规化它们的连接呢?

687474703a2f2f7777772e6f626a632e696f2f696d616765732f69737375652d31332f696e7465726d6564696174652e706e67.png

Intermediate

这更准确地描述了你可能已经编写的 MVC代码。但它并没有做太多事情来解决iOS应用中日益增长的重量级视图控制器。在典型的 MVC 应用里,许多逻辑被放在View Controller里。它们中的一些确实属于View Controller,但更多的是所谓的“用于显示的逻辑”,以 MVVM 属术语来说——就是那些从Model转换数据为 View可以呈现的东西的事情,例如将一个NSDate 转换为一个格式化过的 NSString。

我们的图解里缺少某些东西。某些使我们可以放置所有表示逻辑的东西。我们打算将其称为“View Model”——它位于 View/Controller与 Model之间:

22.png

Model-View-ViewModel

看起好多了!这个图解准确地描述了什么是 MVVM:一个 MVC 的增强版,我们正式连接了视图和控制器,并将表示逻辑从 Controller 移出放到一个新的对象里,即 View Model。MVVM 听起来很复杂,但它本质上就是一个精心优化的 MVC架构,而 MVC你早已熟悉。

好了, 引言说完了, 这是一个铺垫 .

如果你认为下图右边的方法全部放在ViewController里便于日后维护和扩展的话 . 你大可固执己见然后点击浏览器右上角的"×" ...

838591-72c4b2291cce3c5b.png

屏幕快照 2015-12-14 下午3.58.00.png

当然, 关于瘦身ViewController有很多方面 . 然而今天我们讲讲从Controller中分离TableView的表示逻辑。为什么引言MVVM设计模式,也是阐述这个主要思想是相通的。 就是把"逻辑部分"尽量移到Model层, 你可以认为它是一个中间层,所谓"逻辑部分"可以是各种delegate,网络请求,缓存,数据库,coredata等等等等,而controller正是用来组织串联他们,使得整个程序走通。

正文

我们很容易想到把 UITableViewDataSource和UITableViewDelegate 的代码提取出来放到一个单独的类。

但我发现还是有东西可以抽象出来。

例如cell的生成、cell行高以及点击等等。这里我还用了block的形式使得函数能够回调。如果你对block还不太了解先看这里

此外,如果你也重度使用.xib生成Cell,那和我封装的类会非常契合。

记住我默认习惯用.xib前的文件名来定义cell的Identifier。如果你想把它用于实战,记得在xib设置cell的Identifier不要设错。

处理类XTTableDataDelegate的.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#import #import @class XTRootCustomCell ;
 
typedef void    (^TableViewCellConfigureBlock)(NSIndexPath *indexPath, id item, XTRootCustomCell *cell) ;
typedef CGFloat (^CellHeightBlock)(NSIndexPath *indexPath, id item) ;
typedef void    (^DidSelectCellBlock)(NSIndexPath *indexPath, id item) ;
 
@interface XTTableDataDelegate : NSObject  //1
- (id)initWithItems:(NSArray *)anItems
      cellIdentifier:(NSString *)aCellIdentifier
  configureCellBlock:(TableViewCellConfigureBlock)aConfigureCellBlock
     cellHeightBlock:(CellHeightBlock)aHeightBlock
      didSelectBlock:(DidSelectCellBlock)didselectBlock ;
//2
- (void)handleTableViewDatasourceAndDelegate:(UITableView *)table ;
//3
- (id)itemAtIndexPath:(NSIndexPath *)indexPath ;
@end

注释: 
//1. 初始化方法: 传数据源,cellIdentifier, 三个block分别对应配置、行高和点击.

//2. 将UITableViewDataSource和UITableViewDelegate设于XTTableDataDelegate

//3. 默认indexPath.row对应每个dataSource .相应返回item

此外, 为了更彻底, 有必要抽象出"根Cell"。

1
2
3
4
5
6
7
8
9
10
11
12
#import @interface XTRootCustomCell : UITableViewCell
 
+ (void)registerTable:(UITableView *)table
         nibIdentifier:(NSString *)identifier ;  // 1
         
- (void)configure:(UITableViewCell *)cell
         customObj:(id)obj
         indexPath:(NSIndexPath *)indexPath ;  // 2
         
+ (CGFloat)getCellHeightWithCustomObj:(id)obj
                             indexPath:(NSIndexPath *)indexPath ;  // 3
@end

故所有cell必须是XTRootCustomCell的子类, 通过子类重写父类方法来实现新Cell .

注释: 
//1 .不解释.

//2. 根据数据源配置并绘制cell 子类务必重写该方法

//3. 根据数据源计算cell的高度 子类可重写该方法, 若不写为默认值44.0

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#pragma mark - Public
+ (void)registerTable:(UITableView *)table
         nibIdentifier:(NSString *)identifier
{
     [table registerNib:[self nibWithIdentifier:identifier] forCellReuseIdentifier:identifier] ;
}
 
#pragma mark --
#pragma mark - Rewrite these func in SubClass !
- (void)configure:(UITableViewCell *)cell
         customObj:(id)obj
         indexPath:(NSIndexPath *)indexPath
{
     // Rewrite this func in SubClass !
     
}
 
+ (CGFloat)getCellHeightWithCustomObj:(id)obj
                             indexPath:(NSIndexPath *)indexPath
{
     // Rewrite this func in SubClass if necessary
     if  (!obj) {
         return  0.0f ;  // if obj is null .
     }
     return  44.0f ;  // default cell height
}

那么新cell类的实现如下: 重写父类两个方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
- (void)configure:(UITableViewCell *)cell
         customObj:(id)obj
         indexPath:(NSIndexPath *)indexPath
{
     MyObj *myObj = (MyObj *)obj ;
     MyCell *mycell = (MyCell *)cell ;
     mycell.lbTitle.text = myObj.name ;
     mycell.lbHeight.text = [NSString stringWithFormat:@ "my Height is : %@" , @(myObj.height)] ;
     cell.backgroundColor = indexPath.row % 2 ? [UIColor greenColor] : [UIColor brownColor] ;
}
 
+ (CGFloat)getCellHeightWithCustomObj:(id)obj
 
                             indexPath:(NSIndexPath *)indexPath
{
     return  ((MyObj *)obj).height ;
}

看下结果, 瘦身后的controller干净的不像实力派, 只剩下了这一个方法 .呵呵呵呵 .

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
- (void)setupTableView
{
     self.table.separatorStyle = 0 ;
     
     TableViewCellConfigureBlock configureCell = ^(NSIndexPath *indexPath, MyObj *obj, XTRootCustomCell *cell) {
         [cell configure:cell customObj:obj indexPath:indexPath] ;
     } ;
     
     CellHeightBlock heightBlock = ^CGFloat(NSIndexPath *indexPath, id item) {
         return  [MyCell getCellHeightWithCustomObj:item indexPath:indexPath] ;
     } ;
     
     DidSelectCellBlock selectedBlock = ^(NSIndexPath *indexPath, id item) {
         NSLog(@ "click row : %@" ,@(indexPath.row)) ;
     } ;
     
     self.tableHander = [[XTTableDataDelegate alloc] initWithItems:self.list
                                                    cellIdentifier:MyCellIdentifier
                                                configureCellBlock:configureCell
                                                   cellHeightBlock:heightBlock
                                                    didSelectBlock:selectedBlock] ;
                                                    
     [self.tableHander handleTableViewDatasourceAndDelegate:self.table] ;
}

诸多.m文件太过于冗长,我就不贴到博客了,博客主要是讲思路,思路是王道。

目录
相关文章
|
6月前
|
JSON 监控 API
1688 商品列表 API 深度拆解:从参数配置到数据获取
1688 是重要的批发采购平台,其商品列表 API 接口为开发者、商家和数据分析人员提供批量获取商品基础信息(如名称、价格、销量等)的能力。该接口支持市场调研、竞品分析等场景,助力商业决策与效率提升。接口基于 HTTPS 协议,采用 GET 或 POST 请求方式,需提供通用参数(如 app_key、timestamp 等)和业务参数(如 category_id、page_no 等)。响应数据以 JSON 格式返回,包含商品详情及分页信息。
190 13
|
7月前
|
机器学习/深度学习 存储 人工智能
AI职场突围战:夸克应用+生成式人工智能认证,驱动“打工人”核心竞争力!
在AI浪潮推动下,生成式人工智能(GAI)成为职场必备工具。文中对比了夸克、豆包、DeepSeek和元宝四大AI应用,夸克以“超级入口”定位脱颖而出。同时,GAI认证为职场人士提供系统学习平台,与夸克结合助力职业发展。文章还探讨了职场人士如何通过加强学习、关注技术趋势及培养合规意识,在AI时代把握机遇。
|
6月前
|
人工智能 算法 Java
后端程序员逆袭之路:巧用 AI 工具,拿下高薪 offer
在技术职场中,后端程序员面临诸多挑战,如复杂业务逻辑、繁琐代码编写与调试及持续学习压力。然而,AI 工具的兴起为后端开发带来了全新机遇。智能代码生成工具如飞算 JavaAI 可高效完成需求分析、设计与代码生成;智能调试工具如 CodeGuru 能快速定位问题;知识学习工具如 ChatGPT 助力技术提升。借助这些工具,后端程序员不仅能显著提高项目质量和效率,还能展示技术前瞻性与学习能力,拓展技能边界,从而在求职市场中脱颖而出,顺利拿下高薪 offer。
|
6月前
|
SQL 数据可视化 BI
Quick BI产品测评:从数据连接到智能分析的全流程体验
瓴羊智能商业分析-Quick BI是阿里云旗下的云端智能BI平台,连续五年入选Gartner ABI魔力象限。它提供从数据接入到决策的全链路服务,支持零代码操作、40+可视化组件与OLAP分析,实现跨终端呈现。其创新点包括云原生架构、企业级安全体系及智能决策引擎,适用于零售、金融等行业。评测中,通过免费试用与官方文档,体验了数据准备、仪表板搭建及智能小Q功能,发现智能化能力强大但部分文档需更新优化。
612 67
|
9月前
|
存储 监控 对象存储
ACK 容器监控存储全面更新:让您的应用运行更稳定、更透明
针对本地存储和 PVC 这两种容器存储使用方式,我们对 ACK 的容器存储监控功能进行了全新升级。此次更新完善了对集群中不同存储类型的监控能力,不仅对之前已有的监控大盘进行了优化,还针对不同的云存储类型,上线了全新的监控大盘,确保用户能够更好地理解和管理容器业务应用的存储资源。
568 274
|
7月前
|
机器学习/深度学习 人工智能 自然语言处理
QwQ-32B为襄阳职业技术学院拥抱强化学习的AI力量
信息技术学院大数据专业学生团队与UNHub平台合作,利用QwQ-32B模型开启AI教育新范式。通过强化学习驱动,构建职业教育智能化实践平台,支持从算法开发到应用的全链路教学。QwQ-32B具备320亿参数,优化数学、编程及复杂逻辑任务处理能力,提供智能教学助手、科研加速器和产教融合桥梁等应用场景,推动职业教育模式创新。项目已进入关键训练阶段,计划于2025年夏季上线公测。
228 10
QwQ-32B为襄阳职业技术学院拥抱强化学习的AI力量
|
8月前
|
存储 人工智能 算法
《探秘AI绿色计算:降低人工智能硬件能耗的热点技术》
在人工智能快速发展的背景下,硬件能耗问题日益突出。为实现绿色计算,降低能耗成为关键课题。新型硬件架构如CRAM、自旋电子器件和量子计算硬件,以及优化的低功耗芯片设计、3D集成技术和液冷散热技术等,正崭露头角。同时,硬件与软件协同优化,通过模型压缩、算法适配等手段,进一步提升能效。这些技术将推动AI向更绿色、高效的方向发展,助力应对全球气候变化。
321 19
|
9月前
|
人工智能 测试技术
陶哲轩联手60多位数学家出题,世界顶尖模型通过率仅2%!专家级数学基准,让AI再苦战数年
著名数学家陶哲轩联合60多位数学家推出FrontierMath基准测试,评估AI在高级数学推理方面的能力。该测试涵盖数论、实分析等多领域,采用新问题与自动化验证,结果显示最先进AI通过率仅2%。尽管存在争议,这一基准为AI数学能力发展提供了明确目标和评估工具,推动AI逐步接近人类数学家水平。
314 37
|
9月前
|
存储 人工智能 安全
《探索鸿蒙Next上开发人工智能游戏应用的技术难点》
在科技飞速发展的今天,鸿蒙Next系统为人工智能游戏应用开发带来新机遇与挑战。开发者需解决多设备协同、自适应布局、AI模型训练、实时反应、游戏引擎选择、图形渲染、数据管理和安全保护等技术难点。同时,还需进行兼容性测试和性能优化,确保游戏流畅运行并满足用户需求。通过不断学习创新,开发者有望打造出高质量的鸿蒙Next游戏应用。
333 23
|
编解码 开发工具 C#
Windows电脑如何启动RTSP服务实现本地摄像头数据共享
本文介绍如何利用大牛直播SDK中的轻量级RTSP服务,在Windows平台上轻松采集摄像头数据并生成本地RTSP流。通过SDK提供的SmartPublisherDemo工具,用户能简便地选择摄像头、配置分辨率与帧率,并启动RTSP服务。此外,还支持音频采集、多端口服务以及动态水印等功能。生成的RTSP URL可用于其他终端拉流播放,无需额外部署服务器。该服务适配多种应用场景,如安防监控、电子教室等,并兼容Windows 7及以上版本。对于希望集成此功能的开发者,SDK提供了C++及C#接口,并支持多种编译模式。
704 0