IOS开发入门iOS控制器-面向超类编程
白羽 2019-05-21 来源 :网络 阅读 426 评论 0

摘要:本文将带你了解IOS开发入门iOS控制器-面向超类编程,希望本文对大家学IOS有所帮助。

    本文将带你了解IOS开发入门iOS控制器-面向超类编程,希望本文对大家学IOS有所帮助。


IOS开发入门iOS控制器-面向超类编程


今天写这篇文章的目的,是提供一种思路,来帮助大家解决控制器非常臃肿的问题,对控制器瘦身。

   如果手边有项目,不妨打开工程看一下你的控制器代码有多少行,是不是非常多?再看一下tableView的代理方法cellForRow和heightForRow的代码是不是也是非常多?里面夹杂着switch和大量if  esle的判断逻辑的代码。后期维护看着这些if else是不是特别烦躁?特别是自己在维护前人写的代码,并且还没有注释  一团糟,是不是有更想骂人的冲动?别怕,这里给您提供一种解决思路,让你的tableView代理方法再也没有这种让人头疼的if  else判断逻辑,让你的控制器代码量大大减少,并且后期维护成本也大大的减少。
   在说具体解决思路前,先给大家简单复习一下MVC和MVVM,因为今天的主题也是和MVVM有关系。MVC模式大家都很熟悉了,就是Model,View,Controller三层,Model负责数据层,Controller负责业务逻辑层,View负责界面显示层,Model和View通过Controller来实现桥接交互,程序的扩展性很好,好处多多。但是呢,MVC也有它自身的缺陷,那就是控制器太臃肿,如果你想在控制器中定位某一个点是比较麻烦的事。
   那么为什么控制器如此庞大,就是因为tableView的代理方法里的cell  判断逻辑全在控制器,以及网络请求也在控制器发起,另外还有一些其他的业务逻辑。有没有什么更高的模式呢?MVVM模式就是MVC模式的升级版。
   MVVM中Model依然负责数据层,Controller单单负责View的展示和更新,其他业务逻辑不管。View依然负责界面显示。那么ViewController之前负责的业务逻辑现在谁来负责呢?我们再新建一个ViewModel层,处在ViewController层和Model层之间,专门负责业务逻辑,以及网络请求等任务。ViewController从ViewModel中获取数据然后显示在View上,它并不和Model层直接打交道,和Model层直接打交道的是ViewModel层  。
   下面附上一张经典gif图片,帮助大家理解两者之间的关系。
   
   
   好了,前面的都是回顾一些相关知识,为理解接下来的内容做基础,如果想要深入了解MVC,MVVM可以网上找下,这类文章很多。
   大家可能疑惑到底什么是面向超类编程,其实就是围绕继承这个特性,子类cell继承父类cell,面向父类这个对象来编程,最终对控制器的tableView进行瘦身,也不止是对tableView优化,配合MVVM新建ViewModel可以抽离很大一部分控制器的代码。现在还不清楚没关系,下面会有很详细的描述让你明白。^_^  我下面先把大家常用的控制器tableView代理方法的写法,给黏贴出来,然后再用新的面向超类的写法给黏贴出来,大家就可以明显体会到使用面向超类写法的好处了。
   
   老方式大众写法
   
   面向超类编程写法
   看到这里,可能会有人吐槽了,新写法就比老式写法少了十几行嘛?其实不是这样的,首先这个demo我只写了三个cell作为例子,真实的项目极少一个控制器只有三个cell吧?控制器的cell越多,好处越明显,  因为在后期不管添加多少cell,控制器tableView的代理方法中的代码几乎都不会增加,相当于构建了一个模版,只需要在新添加cell的内部配置即可。其次,真实的项目也不可能业务逻辑这么简单吧?肯定在if  else中嵌套了很多其他的逻辑代码,致使tableView看起来很臃肿。
   面向超类编程的好处:
   1.控制器瘦身。[控制器内部代码量大幅度减少,逻辑更加清晰]
   2.后期维护成本大大降低。[后期如果想添加或者删除cell,只需要新建或者删除一个子类cell,在viewModel中添加或删除一个identifier即可,控制器几乎不用加任何代码]
   面向超类的坏处:
   1.新建更多的cell文件和一个viewModel文件,包大小会响应增加。
   下面就具体讲解面向超类编程瘦身大概要做什么:
   一,新建一个继承自UITableViewCell的父类cell
  #import#import  "ResponseNewProgrammeData.h"#import  "NewProgrammeCellHeightProtocol.h" //子类需要有回调事件的代理@protocol  NewProgrammeTableViewCellProtocol-  (void)cell1DidSelectedRightButton;- (void)cell2DidSelectedRightButton;-  (void)cell3DidSelectedRightButton;@end @interface NewProgrammeBaseCell :  UITableViewCell@property  (nonatomic,   weak) id delegate;@property (nonatomic, strong) ResponseNewProgrammeData *  responseNewProgrammeData;@end
   首先,要包含控制器的数据源,因为子类cell的UI等操作全靠这个父类的数据源。
   其次,要实现NewProgrammeCellHeightProtocol协议,作为计算高度用,具体用法在第二点讲解。
   最后,如果子类cell有点击事件需要回调操作的,可再写一个协议NewProgrammeTableViewCellProtocol作为属性持有,在控制器中将delegate指向控制器作为回调使用。
   二,新建一个NewProgrammeCellHeightProtocol
   #import //针对cell的高度写的协议@protocol  NewProgrammeCellHeightProtocol@optional+ (BOOL)isStaticCell;+  (float)cellHeight;@end
   (BOOL)isStaticCell方法是在子类中使用的,如果当前cell是高度固定的静态cell,就在返回YES,并且在cellHeight方法中返回固定高度。否则返回NO即可,也不需要写+  (float)cellHeight方法。这两个方法会在控制器的heightForRow方法中使用,计算当前cell高度。
   三,新建控制器所需要的所有cell,且继承自刚才的父类cell
   #import  "NewProgrammeCell1.h" @interface NewProgrammeCell1 ()@property  (nonatomic, weak) IBOutlet UILabel *lblName;@end @implementation  NewProgrammeCell1@synthesize responseNewProgrammeData =  _responseNewProgrammeData; -  (void)setResponseNewProgrammeData:(ResponseNewProgrammeData  *)responseNewProgrammeData{    _responseNewProgrammeData  = responseNewProgrammeData;    self.lblName.text =  _responseNewProgrammeData.string1;}+  (BOOL)isStaticCell{    return YES;}+  (float)cellHeight{    return 44;}-  (IBAction)didPressedPush:(id)sender {    if  (self.delegate && [self.delegate  respondsToSelector:@selector(cell1DidSelectedRightButton)])  {         [self.delegate  cell1DidSelectedRightButton];    }}@end
   这个cell的高度是固定静态的,所以isStaticCell方法返回YES,cellHeight返回高度。
   这个cell中可以拿到控制器数据源,根据自己的需要去获取数据。
   这个cell的didPressedPush方法是模拟需要点击事件的,回调给控制器。
   其他cell的配置都大致是这样的。
   四,新建viewModel文件
   #import  "NewProgrammeViewModel.h" static NSString * const  NewProgrammeCell1Identifier = @"NewProgrammeCell1";static NSString  * const NewProgrammeCell2Identifier = @"NewProgrammeCell2";static  NSString * const NewProgrammeCell3Identifier =  @"NewProgrammeCell3"; @implementation  NewProgrammeViewModel - (NSArray  *)getIdentifierList{    return   @[NewProgrammeCell1Identifier,              NewProgrammeCell2Identifier,              NewProgrammeCell3Identifier];}-  (void)requestData{    self.responseNewProgrammeData =  [[ResponseNewProgrammeData alloc] init];} @end
   viewModel负责配置控制器所需要注册的cell以及真正要显示的cell,getIdentifierList返回需要注册的所有cell。因为某些页面的cell不是固定显示的,可能根据数据源动态的来配置。同时,viewModel也负责网络请求数据解析等其他业务逻辑代码。这里的viewModel相当于MVVM模式中的胖Model,不仅处理网络请求,还处理页面UI的配置等其他业务逻辑,这样就不会使控制器那么臃肿。
   五,在控制器中做相应代码配置
   -  (void)configTableViewCell{    for (NSString * identifer  in [self.viewModel getIdentifierList])  {        [self.tableView  registerNib:[UINib nibWithNibName:identifer bundle:nil]   forCellReuseIdentifier:identifer];    }} #pragma  mark - UITableViewDelegate - (NSInteger)tableView:(UITableView  *)tableView  numberOfRowsInSection:(NSInteger)section{    return  self.viewModel.getIdentifierList.count;} - (UITableViewCell  *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath  *)indexPath{    NSString * cellIdentifier =  [self.viewModel.getIdentifierList objectAtIndex:indexPath.row];    NewProgrammeBaseCell  * cell = [tableView  dequeueReusableCellWithIdentifier:cellIdentifier];    cell.delegate  = self;    cell.responseNewProgrammeData =  self.viewModel.responseNewProgrammeData;    return  cell;} - (CGFloat)tableView:(UITableView *)tableView  heightForRowAtIndexPath:(NSIndexPath  *)indexPath{    NSString * cellIdentifier  =  [self.viewModel.getIdentifierList objectAtIndex:indexPath.row];    Class cellClass = NSClassFromString(cellIdentifier);    CGFloat  height = 0;    if ([cellClass isStaticCell])  {        height  = [cellClass  cellHeight];        return  height;    } else  {        NewProgrammeBaseCell * cell  = (NewProgrammeBaseCell*)[self tableView:tableView  cellForRowAtIndexPath:indexPath];        height  = [cell.contentView  systemLayoutSizeFittingSize:UILayoutFittingExpandedSize].height;        return  height;    }}
   首先,要实例化viewModel对象,在configTableViewCell方法中获取需要注册的所有cell,遍历注册。
   其次,在cellForRow代理方法中,从viewModel对象中获取所有注册的cell的identifier,然后从tableView中获取赋值给父类cell。再针对父类cell做一些赋值操作,也就是分别调用了子类cell,充分利用多态的特性。
   最后,在heightForRow代理方法中,仍然是根据viewModel对象获取所有注册的cell的identifier,再根据identifier反射成对象子类cell的类对象,接着调用cell中的协议方法,来计算高度。


本文由职坐标整理并发布,希望对同学们有所帮助。了解更多详情请关注职坐标移动开发之IOS频道!

本文由 @白羽 发布于职坐标。未经许可,禁止转载。
喜欢 | 0 不喜欢 | 0
看完这篇文章有何感觉?已经有0人表态,0%的人喜欢 快给朋友分享吧~
评论(0)
后参与评论

您输入的评论内容中包含违禁敏感词

我知道了

助您圆梦职场 匹配合适岗位
验证码手机号,获得海同独家IT培训资料
选择就业方向:
人工智能物联网
大数据开发/分析
人工智能Python
Java全栈开发
WEB前端+H5

请输入正确的手机号码

请输入正确的验证码

获取验证码

您今天的短信下发次数太多了,明天再试试吧!

提交

我们会在第一时间安排职业规划师联系您!

您也可以联系我们的职业规划师咨询:

小职老师的微信号:z_zhizuobiao
小职老师的微信号:z_zhizuobiao

版权所有 职坐标-一站式IT培训就业服务领导者 沪ICP备13042190号-4
上海海同信息科技有限公司 Copyright ©2015 www.zhizuobiao.com,All Rights Reserved.
 沪公网安备 31011502005948号    

©2015 www.zhizuobiao.com All Rights Reserved

208小时内训课程