热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

轻量化ViewController的几个小技巧

轻量化ViewControllerMVC最令人头疼的问题可能就是随着项目愈发复杂,ViewController的代码也会变得越来越冗长。阅读了objc的《LighterViewCont

轻量化ViewController

MVC最令人头疼的问题可能就是随着项目愈发复杂,ViewController的代码也会变得越来越冗长。阅读了objc的《Lighter View Controllers》和《Clean Table View Code》这两篇文章之后,总结了一些常用的轻量化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,最终,它变得更加容易理解,也更利于团队协作。


推荐阅读
  • ASP.NET MVC中Area机制的实现与优化
    本文探讨了在ASP.NET MVC框架中,如何通过Area机制有效地组织和管理大规模应用程序的不同功能模块。通过合理的文件夹结构和命名规则,开发人员可以更高效地管理和扩展项目。 ... [详细]
  • Java项目分层架构设计与实践
    本文探讨了Java项目中应用分层的最佳实践,不仅介绍了常见的三层架构(Controller、Service、DAO),还深入分析了各层的职责划分及优化建议。通过合理的分层设计,可以提高代码的可维护性、扩展性和团队协作效率。 ... [详细]
  • 深入解析SpringMVC核心组件:DispatcherServlet的工作原理
    本文详细探讨了SpringMVC的核心组件——DispatcherServlet的运作机制,旨在帮助有一定Java和Spring基础的开发人员理解HTTP请求是如何被映射到Controller并执行的。文章将解答以下问题:1. HTTP请求如何映射到Controller;2. Controller是如何被执行的。 ... [详细]
  • dotnet 通过 Elmish.WPF 使用 F# 编写 WPF 应用
    本文来安利大家一个有趣而且强大的库,通过F#和C#混合编程编写WPF应用,可以在WPF中使用到F#强大的数据处理能力在GitHub上完全开源Elmis ... [详细]
  • 深入理解ASP.NET MVC中的_ViewStart.cshtml
    本文介绍了_ViewStart.cshtml文件在ASP.NET MVC 3.0及以上版本中的作用和使用方法。该文件位于Views目录下,主要用于统一配置视图布局和其他全局设置。 ... [详细]
  • Struts与Spring框架的集成指南
    本文详细介绍了如何将Struts和Spring两个流行的Java Web开发框架进行整合,涵盖从环境配置到代码实现的具体步骤。 ... [详细]
  • ssm框架整合及工程分层1.先创建一个新的project1.1配置pom.xml ... [详细]
  • SpringMVC RestTemplate的几种请求调用(转)
    SpringMVCRestTemplate的几种请求调用(转),Go语言社区,Golang程序员人脉社 ... [详细]
  • 本章将深入探讨移动 UI 设计的核心原则,帮助开发者构建简洁、高效且用户友好的界面。通过学习设计规则和用户体验优化技巧,您将能够创建出既美观又实用的移动应用。 ... [详细]
  • 本文介绍了在Windows环境下使用pydoc工具的方法,并详细解释了如何通过命令行和浏览器查看Python内置函数的文档。此外,还提供了关于raw_input和open函数的具体用法和功能说明。 ... [详细]
  • 本文介绍如何使用阿里云的fastjson库解析包含时间戳、IP地址和参数等信息的JSON格式文本,并进行数据处理和保存。 ... [详细]
  • TCP长连接设备管理平台:架构与功能概览
    本文介绍了基于TCP长连接的设备管理平台的设计理念、技术选型及主要功能模块。最初,项目旨在实现简单的协议测试,但随着需求扩展,逐步演变为一个完整的前后端分离系统。 ... [详细]
  • 分享一个简化版的Silverlight链接图项目:Link Map Simplified
    本文介绍了一个使用Silverlight开发的可视化工具,主要用于展示和操作复杂的实体关系图(Graph)。该工具在犯罪调查系统中得到了广泛应用,帮助用户直观地获取和理解相关信息。 ... [详细]
  • 深入解析ESFramework中的AgileTcp组件
    本文详细介绍了ESFramework框架中AgileTcp组件的设计与实现。AgileTcp是ESFramework提供的ITcp接口的高效实现,旨在优化TCP通信的性能和结构清晰度。 ... [详细]
  • ElasticSearch 集群监控与优化
    本文详细介绍了如何有效地监控 ElasticSearch 集群,涵盖了关键性能指标、集群健康状况、统计信息以及内存和垃圾回收的监控方法。 ... [详细]
author-avatar
谢乔云文辰雅婷
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有