轻量化ViewController的几个小技巧

轻量化ViewController

MVC最令人头疼的问题可能就是随着项目愈发复杂,ViewController的代码也会变得越来越冗长。阅读了objc的《Lighter View Controllers》《Clean Table 》这两篇文章之后,总结了一些常用的轻量化ViewController的小技巧。

分离业务重点

既然要简化ViewController中的代码,那么在不改变原来实现方式的前提下,唯一的方法就是把一些可以不用放在ViewController中的代码转移出去。同时,也应该理解,MVC只是一种设计典范,并没有说一定只是由Model-View-Controller这三个文件组成,而所谓的Controller也可以是一组文件。

一些业务逻辑的转移

假设原来的ViewController中有一个方法叫做“loadPriorities”,用来给自己这个类的priorities赋值,

- (void)loadPriorities {
    NSDate* now = [NSDate date];
    NSString* formatString = @"startDate = %@";
    NSPredicate* predicate = [NSPredicate predicateWithFormat:formatString, now, now];
    NSSet* priorities = [self.user.priorities filteredSetUsingPredicate:predicate];
    self.priorities = [priorities allObjects];
}

我们完全可以把这段代码移动到User类的Category中,从而把loadPriorities方法简化为:

- (void)loadPriorities {
    self.priorities = [user currentPriorities];
}

文件读写的转移

我们都有过读写plist或者其他文件的经历,一旦读写文件或者读取到的数据处理起来较为复杂,就可以专门建立一个Store类处理这些任务。比如原来在ViewController中的这段代码就可以被分离出去:

- (void)readArchive {
    NSBundle* bundle = [NSBundle bundleForClass:[self class]];
    NSURL *archiveURL = [bundle URLForResource:@"photodata"
                                 withExtension:@"bin"];
    NSAssert(archiveURL != nil, @"Unable to find archive in bundle.");
    NSData *data = [NSData dataWithContentsOfURL:archiveURL
                                         options:0
                                           error:NULL];
    NSKeyedUnarchiver *unarchiver = [[NSKeyedUnarchiver alloc] initForReadingWithData:data];
    _users = [unarchiver decodeObjectOfClass:[NSArray class] forKey:@"users"];
    _photos = [unarchiver decodeObjectOfClass:[NSArray class] forKey:@"photos"];
    [unarchiver finishDecoding];
}

通过分离,我们就可以复用这些代码,单独测试他们,并且让 view controller 保持小巧。

Store 对象会关心数据加载、缓存和设置数据栈。它也经常被称为服务层或者仓库。

转移网络请求逻辑

和文件读写类似,网络请求的逻辑可以转移到一个单独的Manager类或者Model的Category中去。这样做,不仅可以简化ViewController并在ViewController中利用回调block来请求网络,也可以在单独的类中实现错误处理和缓存控制等相关问题。

ViewController之间的消息传递

ViewController与Model和View之间的信息交互已经在MVC设计模式中阐述得非常清楚了,但是ViewController和其他多个ViewController之间的信息传递却是一个无法回避也并不容易的事。

一个比较好的方案是把信息放到一个单独的对象里,然后把这个对象传递给其它 view controllers,它们观察和修改这个信息。这样的好处是消息传递都在一个地方(被观察的对象)进行,而且我们也不用纠结嵌套的 delegate 回调。

优化UITableViewController

UITableView在各个app中被大量的使用,同时也是ViewController简洁性的杀手。对于它的优化自然也是重中之重。

UITableViewController的优缺点

TableViewControllers 相对于标准ViewControllers 的一个特别的好处是它支持 Apple 实现的“下拉刷新”。目前,文档中唯一的使用 UIRefreshControl 的方式就是通过 table view controller 。虽然有诸多框架也实现了这一功能,但毕竟不是官方文档提供的框架,下一个版本的系统是否还支持并不好说。

TableViewControllers也有自己最大的缺点。它的 view 属性永远都是一个 table view。如果我们稍后决定在 table view 旁边显示一些东西(比如一个地图),如果不依赖于那些奇怪的技巧,估计就没什么办法了。

了解了TableViewControllers的优缺点后就应该根据实际需求选择比较合适的实现方法了。接下来就是几个优化table view的小技巧。

使用Child View Controller

一旦决定把UITableViewController作为Child View Controller,最重要的一点是在UITableViewController和Parent View Controller之间建立消息传递的渠道。比如用户选择了一个 table view 中的 cell,parent view controller 需要知道这个事件来推入其他 view controller。根据使用习惯,通常最清晰的方式是为这个 table view controller 定义一个 delegate protocol,然后到 parent view controller 中去实现。parent view controller中的代码如下:

@protocol DetailsViewControllerDelegate
- (void)didSelectPhotoAttributeWithKey:(NSString *)key;
@end

- (void)didSelectPhotoAttributeWithKey:(NSString *)key
{
    DetailViewController *controller = [[DetailViewController alloc] init];
    controller.key = key;
    [self.navigationController pushViewController:controller animated:YES];
}

分离DataSource

DateSource中的代上基本都是围绕数组做一些事情,更针对地说,是围绕 view controller(此时它自身作为table view的DataSource) 所管理的数组做一些事情。我们可以尝试把数组相关的代码移到单独的类中,与此同时,可以使用一个 block 来设置 cell,也可以用 delegate 来做这件事。

经过简化,ViewController中的代码现在看上去是这个样子的:

void (^configureCell)(Cell*, Model*) = ^(Cell* cell, Model* model) {
   cell.label.text = model.name;
};

modelsArrayDataSource = [[ArrayDataSource alloc] initWithItems:models
cellIdentifier:PhotoCellIdentifier                                           configureCellBlock:configureCell];

self.tableView.dataSource = modelsArrayDataSource;

传入modelsArrayDataSource的block主要是在cellForRowAtIndexPath方法中用来配置cell。

这样的好处在于,我们可以单独测试这个类,再也不用写第二遍。该原则同样适用于数组之外的其他对象。除此以外,这种方法还可以扩展到其他 protocols 上面。最明显的一个就是UICollectionViewDataSource。这给我们带来了极大的灵活性;如果,在开发的某个时候想用 UICollectionView 代替 UITableView,几乎不需要对 view controller 作任何修改。甚至可以让你的 data source 同时支持这两个协议。

在 Cell 内部控制 Cell 的状态

如果要修改Cell默认的选中或者高亮状态下的表现,可以通过代理去设置。

- (void)tableView:(UITableView *)tableView
        didHighlightRowAtIndexPath:(NSIndexPath *)indexPath
{
    PhotoCell *cell = [tableView cellForRowAtIndexPath:indexPath];
    cell.photoTitleLabel.shadowColor = [UIColor darkGrayColor];
    cell.photoTitleLabel.shadowOffset = CGSizeMake(3, 3);
}

但是更好的解决方案是对Controller隐藏Cell的实现细节,把这些实现细节放到Cell内部实现,而cell仅仅对外暴露一个接口。

@implementation Cell
// ...
- (void)setHighlighted:(BOOL)highlighted animated:(BOOL)animated
{
    [super setHighlighted:highlighted animated:animated];
    if (highlighted) {
        self.photoTitleLabel.shadowColor = [UIColor darkGrayColor];
        self.photoTitleLabel.shadowOffset = CGSizeMake(3, 3);
    } else {
        self.photoTitleLabel.shadowColor = nil;
    }
}
@end

总结

View controller 应该在 model 和 view 对象之间扮演协调者和调解者的角色。它不应该关心明显属于 view 层或 model 层的任务。我们应该始终记住这点,这样 delegate 和 data source 方法会变得更小巧,最多包含一些简单地样板代码。

这不仅减少了 table view controllers 那样的大小和复杂性,而且还把业务逻辑和 view 的逻辑放到了更合适的地方。Controller 层的里里外外的实现细节都被封装成了简单地 API,最终,它变得更加容易理解,也更利于团队协作。

时间: 2024-10-14 00:28:59

轻量化ViewController的几个小技巧的相关文章

iOS Method Swizzling和分类的妙用AppDelegate轻量化处理

http://www.cocoachina.com/ios/20151117/14167.html 简介 在iOS工程中,AppDelegate往往会有上千行,甚至几千行,这样就会给维护AppDelegate带来诸多麻烦.比方说,老板想在出现HomeViewController之前弹出广告并停顿几秒,这样你就要加入插入广告的逻辑:又比方说,老板想在开始做个请求,判断某个开关是否打开.这样就会在AppDelegate中插入很多相关的不相关的代码. 在AppDelegate中,- (BOOL)app

XIB、Storyboard操作小技巧

本文收集了其他优秀博主,论坛上的相关技巧,及自己总结出的一些tips,小白级tip,大神们请轻喷,或给出建议分享,谢谢! SB push Tab时隐藏Tab 相信有很多人和我一样,开始使用 tabBar 时苦恼过在 tabBar 内置的 viewControllers 里添加navigationController的问题 苹果是希望我们使用tabBar时是作为根视图的,当然如果你固执地将其作为 navigationController 的 rootViewController 也是可以运行成功并

iOS 小技巧总结,绝对有你想要的

iOS 小技巧总结,绝对有你想要的 原文链接:http://www.jianshu.com/p/4523eafb4cd4 在这里总结一些 iOS 开发中的小技巧,能大大方便我们的开发,持续更新. —— 由 xcvxvxc分享 在这里总结一些iOS开发中的小技巧,能大大方便我们的开发,持续更新. UITableView的Group样式下顶部空白处理 在viewWillAppear里面添加如下代码: //分组列表头部空白处理 CGRect frame = myTableView.tableHeade

轻量化ViewControllers,读文章做的总结

推荐一个网站 http://objccn.io/ 我这两天才开始看 获益匪浅 看了第一篇文章 <更轻量的View Controllers>感觉写的不错 感觉作者 原文地址 http://objccn.io/issue-1-1/ 示例项目的代码有点旧 Xcode6运行出错 懒的理了 所以我大概模仿他写了一点测试代码 运行环境Xcode7/iOS9 轻量化ViewControllers 顾名思义 就是把ViewController的代码进行简化 让控制器更简单 更清晰 一.把DataSource和

代码手写UI,xib和StoryBoard间的博弈,以及Interface Builder的一些小技巧

代码手写UI,xib和StoryBoard间的博弈,以及Interface Builder的一些小技巧 最近接触了几个刚入门的iOS学习者,他们之中存在一个普遍和困惑和疑问,就是应该如何制作UI界面.iOS应用是非常重视用户体验的,可以说绝大多数的应用成功与否与交互设计以及UI是否漂亮易用有着非常大的关系.而随着iOS开发发展至今,可以说在UI制作上大家逐渐分化为了三种主要流派:使用代码手写UI及布局:使用单个xib文件组织viewController或者view:使用StoryBoard来通过

关于Eclipse主题设置小技巧

之前写过一篇文章是介绍windows下搭建Selenium+Eclipse+Python环境,但看着Eclipse总觉得很丑,而且又笨重,不像UliPad那么轻,也不像sublime_text2那么漂亮,其实很多人都是看sublime_text2的主题漂亮才投怀送抱的,我就是其中之一,但一直没解决GUI的调试问题,又跑回来了Eclipse;但实在不能忍受Eclipse默认的主题,看久了,真心觉得丑啊; 心想,Eclipse那么强大的东西,各种插件都有,支持各种语言,不可能没人做一个主题来美化它老

10个提升MySQL百家乐性能baijiale的小技巧

从工作量分析到索引的三条规则,这些专家见解肯定会让您的MySQL服务器尖叫. 在所有的关系数据库中,MySQL已经被证明了完全是一头野兽,只要通知停止运行就绝对不会让你多等一秒钟,使你的应用置于困境之中,你的工作也承受极大的风险. 不过事实是,普通的错误都在MySQL性能错误的射程之内.所以为了使你的MySQL服务器能够高速运转,提供稳定且持续的服务,消除这些错误是非常有必要的,但是这可能常常会被你的繁忙工作或配置陷阱微妙地遮蔽了. 幸运的是,许多MySQL性能问题其实都有相似的解决办法,发现并

iOS中TableView小技巧

摘要: TableView是ios开发中经经常使用到的控件,这里统一记录一下开发中遇到的经常使用小技巧,不断探索更新.也希望大家能够告诉我很多其它经常使用的小技巧啦~一起进步 1.去除多余的列表线条 原始的TableView在没有数据的行也会显示一条条的线条,不太美观,用一行代码能够解决,一般放在ViewDidLoad中 self.tableView.tableFooterView = [[UIView alloc] init]; 详细原理还没弄懂.知道的麻烦不吝赐教一下~ 2.选中列表条目后取

js便签笔记(14)——用nodejs搭建最简单、轻量化的http server

1. 引言 前端程序猿主要关注的是页面,你可能根本就用不到.net,java,php等后台语言. 但是你制作出来的网页总要运行.总要测试吧?——那就免不了用到http server.我先前都是用visual studio的,虽然很好用,功能很强大,但是我就开发一个html.javascript.css,干嘛用这种傻大本粗的东西.打开一次特别慢,占内存特别厉害,安装时C盘占去好几个G的空间. 后来闲来无事就换成了nodejs.不用安装任何插件,只需要手动创建三个小文件(总共才2KB),运行即可,速