热门标签 | 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,最终,它变得更加容易理解,也更利于团队协作。


推荐阅读
  • IhaveconfiguredanactionforaremotenotificationwhenitarrivestomyiOsapp.Iwanttwodiff ... [详细]
  • REVERT权限切换的操作步骤和注意事项
    本文介绍了在SQL Server中进行REVERT权限切换的操作步骤和注意事项。首先登录到SQL Server,其中包括一个具有很小权限的普通用户和一个系统管理员角色中的成员。然后通过添加Windows登录到SQL Server,并将其添加到AdventureWorks数据库中的用户列表中。最后通过REVERT命令切换权限。在操作过程中需要注意的是,确保登录名和数据库名的正确性,并遵循安全措施,以防止权限泄露和数据损坏。 ... [详细]
  • 本文介绍了Hive常用命令及其用途,包括列出数据表、显示表字段信息、进入数据库、执行select操作、导出数据到csv文件等。同时还涉及了在AndroidManifest.xml中获取meta-data的value值的方法。 ... [详细]
  • 本文介绍了数据库的存储结构及其重要性,强调了关系数据库范例中将逻辑存储与物理存储分开的必要性。通过逻辑结构和物理结构的分离,可以实现对物理存储的重新组织和数据库的迁移,而应用程序不会察觉到任何更改。文章还展示了Oracle数据库的逻辑结构和物理结构,并介绍了表空间的概念和作用。 ... [详细]
  • android listview OnItemClickListener失效原因
    最近在做listview时发现OnItemClickListener失效的问题,经过查找发现是因为button的原因。不仅listitem中存在button会影响OnItemClickListener事件的失效,还会导致单击后listview每个item的背景改变,使得item中的所有有关焦点的事件都失效。本文给出了一个范例来说明这种情况,并提供了解决方法。 ... [详细]
  • 拥抱Android Design Support Library新变化(导航视图、悬浮ActionBar)
    转载请注明明桑AndroidAndroid5.0Loollipop作为Android最重要的版本之一,为我们带来了全新的界面风格和设计语言。看起来很受欢迎࿰ ... [详细]
  • 本文详细介绍了MySQL表分区的创建、增加和删除方法,包括查看分区数据量和全库数据量的方法。欢迎大家阅读并给予点评。 ... [详细]
  • ubuntu用sqoop将数据从hive导入mysql时,命令: ... [详细]
  • 本文分析了Wince程序内存和存储内存的分布及作用。Wince内存包括系统内存、对象存储和程序内存,其中系统内存占用了一部分SDRAM,而剩下的30M为程序内存和存储内存。对象存储是嵌入式wince操作系统中的一个新概念,常用于消费电子设备中。此外,文章还介绍了主电源和后备电池在操作系统中的作用。 ... [详细]
  • STL迭代器的种类及其功能介绍
    本文介绍了标准模板库(STL)定义的五种迭代器的种类和功能。通过图表展示了这几种迭代器之间的关系,并详细描述了各个迭代器的功能和使用方法。其中,输入迭代器用于从容器中读取元素,输出迭代器用于向容器中写入元素,正向迭代器是输入迭代器和输出迭代器的组合。本文的目的是帮助读者更好地理解STL迭代器的使用方法和特点。 ... [详细]
  • iOS开启Google位置服务器和显示定位权限的方法
    本文介绍了在iOS开发中如何开启Google位置服务器和显示定位权限的方法,包括导入CoreLocation和MapKit库、在界面导入头文件和在info.plist文件中添加授权等步骤。同时还介绍了iOS11中NSLocationAlwaysAndWhenInUseUsageDescription的功能变化。阅读本文可以帮助开发者了解如何在iOS应用中使用Google位置服务器和处理定位权限相关的问题。 ... [详细]
  • Shodan简单用法Shodan简介Shodan是互联网上最可怕的搜索引擎,与谷歌不同的是,Shodan不是在网上搜索网址,而是直接进入互联网的背后通道。Shodan可以说是一款“ ... [详细]
  • 校园表白墙微信小程序,校园小情书、告白墙、论坛,大学表白墙搭建教程
    小程序的名字必须和你微信注册的名称一模一样在后台注册好小程序。mp.wx-union.cn后台域名https。mp.wx-union.cn ... [详细]
  • 最近学习了关于使用最为流行的jquery发送请求,在实践中以最为简单的聊天室作为测验的辅助工具,对相关网页开发有一个初步的认识,希望大家能够一起学习进步。首先介绍一下 ... [详细]
  • 题目描述http:acm.nyist.netJudgeOnlineproblem.php?pid306Dr.Kong设计的机器人卡多非常爱玩,它常常偷偷跑出实验室,在某个游乐场 ... [详细]
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社区 版权所有