更轻量的 View Controllers

NYTPhotoViewer:适用于iOS的现代照片查看体验 纽约时报浏览器 NYTPhotoViewer是一种幻灯片显示和图像查看器,其中包括双击以缩放,字幕,支持多张图像,交互式轻击以关闭,动画缩放演示等等。 用法 用法很简单,可以选择通过委托关系在需要时进行更复杂的自定义。 在最基本的实现中,只需使用一组照片对象初始化视图控制器,然后按正常方式显示它: NYTPhotosViewController *photosViewController = [[NYTPhotosViewController alloc ] initWithPhotos: photos]; [ self presentViewController: photosViewController animated: YES completion: nil ]; 运行示例 在本地克隆此文件,然后在NYTPhotoViewer库的本地工作区中,运行./scripts/boots 立即下载

View controllers 通常是 iOS 项目中最大的文件,并且它们包含了许多不必要的代码。所以 View controllers 中的代码几乎总是复用率最低的。我们将会看到给 view controllers 瘦身的技术,让代码变得可以复用,以及把代码移动到更合适的地方。

你可以在 Github 上获取关于这个问题的示例项目。

把 Data Source 和其他 Protocols 分离出来

把 UITableViewDataSource 的代码提取出来放到一个单独的类中,是为 view controller 瘦身的强大技术之一。当你多做几次,你就能总结出一些模式,并且创建出可复用的类。

举个例,在示例项目中,有个 PhotosViewController 类,它有以下几个方法:

# pragma mark Pragma

- (Photo*)photoAtIndexPath:(NSIndexPath*)indexPath {
    return photos[(NSUInteger)indexPath.row];
}

- (NSInteger)tableView:(UITableView*)tableView
 numberOfRowsInSection:(NSInteger)section {
    return photos.count;
}

- (UITableViewCell*)tableView:(UITableView*)tableView
        cellForRowAtIndexPath:(NSIndexPath*)indexPath {
    PhotoCell* cell = [tableView dequeueReusableCellWithIdentifier:PhotoCellIdentifier
                                                      forIndexPath:indexPath];
    Photo* photo = [self photoAtIndexPath:indexPath];
    cell.label.text = photo.name;
    return cell;
}

这些代码基本都是围绕数组做一些事情,更针对地说,是围绕 view controller 所管理的 photos 数组做一些事情。我们可以尝试把数组相关的代码移到单独的类中。我们使用一个 block 来设置 cell,也可以用 delegate 来做这件事,这取决于你的习惯。

@implementation ArrayDataSource

- (id)itemAtIndexPath:(NSIndexPath*)indexPath {
    return items[(NSUInteger)indexPath.row];
}

- (NSInteger)tableView:(UITableView*)tableView
 numberOfRowsInSection:(NSInteger)section {
    return items.count;
}

- (UITableViewCell*)tableView:(UITableView*)tableView
        cellForRowAtIndexPath:(NSIndexPath*)indexPath {
    id cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier
                                              forIndexPath:indexPath];
    id item = [self itemAtIndexPath:indexPath];
    configureCellBlock(cell,item);
    return cell;
}

@end

现在,你可以把 view controller 中的这 3 个方法去掉了,取而代之,你可以创建一个 ArrayDataSource 类的实例作为 table view 的 data source。

void (^configureCell)(PhotoCell*, Photo*) = ^(PhotoCell* cell, Photo* photo) {
   cell.label.text = photo.name;
};
photosArrayDataSource = [[ArrayDataSource alloc] initWithItems:photos
                                                cellIdentifier:PhotoCellIdentifier
                                            configureCellBlock:configureCell];
self.tableView.dataSource = photosArrayDataSource;

现在你不用担心把一个 index path 映射到数组中的位置了,每次你想把这个数组显示到一个 table view 中时,你都可以复用这些代码。你也可以实现一些额外的方法,比如 tableView:commitEditingStyle:forRowAtIndexPath:,在 table view controllers 之间共享。

这样的好处在于,你可以单独测试这个类,再也不用写第二遍。该原则同样适用于数组之外的其他对象。

在今年我们做的一个应用里面,我们大量使用了 Core Data。我们创建了相似的类,但和之前使用的数组不一样,它用一个 fetched results controller 来获取数据。它实现了所有动画更新、处理 section headers、删除操作等逻辑。你可以创建这个类的实例,然后赋予一个 fetch request 和用来设置 cell 的 block,剩下的它都会处理,不用你操心了。

此外,这种方法也可以扩展到其他 protocols 上面。最明显的一个就是 UICollectionViewDataSource。这给了你极大的灵活性;如果,在开发的某个时候,你想用 UICollectionView 代替 UITableView,你几乎不需要对 view controller 作任何修改。你甚至可以让你的 data source 同时支持这两个协议。

将业务逻辑移到 Model 中

下面是 view controller(来自其他项目)中的示例代码,用来查找一个用户的目前的优先事项的列表:

- (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 中会变得更加清晰,处理之后,在 View Controller.m 中看起来就是这样:

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

在 User+Extensions.m 中:

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

有些代码不能被轻松地移动到 model 对象中,但明显和 model 代码紧密联系,对于这种情况,我们可以使用一个 Store:

创建 Store 类

在我们第一版的示例程序的中,有些代码去加载文件并解析它。下面就是 view controller 中的代码:

- (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 对象来做这些事。通过分离,我们就可以复用这些代码,单独测试他们,并且让 view controller 保持小巧。Store 对象会关心数据加载、缓存和设置数据栈。它也经常被称为服务层或者仓库。

把网络请求逻辑移到 Model 层

和上面的主题相似:不要在 view controller 中做网络请求的逻辑。取而代之,你应该将它们封装到另一个类中。这样,你的 view controller 就可以在之后通过使用回调(比如一个 completion 的 block)来请求网络了。这样的好处是,缓存和错误控制也可以在这个类里面完成。

把 View 代码移到 View 层

不应该在 view controller 中构建复杂的 view 层次结构。你可以使用 Interface Builder 或者把 views 封装到一个 UIView 子类当中。例如,如果你要创建一个选择日期的控件,把它放到一个名为 DatePickerView 的类中会比把所有的事情都在 view controller 中做好好得多。再一次,这样增加了可复用性并保持了简单。

如果你喜欢 Interface Builder,你也可以在 Interface Builder 中做。有些人认为 IB 只能和 view controllers 一起使用,但事实上你也可以加载单独的 nib 文件到自定义的 view 中。在示例程序中,我们创建了一个 PhotoCell.xib,包含了 photo cell 的布局:

PhotoCell.xib screenshot

就像你看到的那样,我们在 view(我们没有在这个 nib 上使用 File's Owner 对象)上面创建了 properties,然后连接到指定的 subviews。这种技术同样适用于其他自定义的 views。

通讯

其他在 view controllers 中经常发生的事是与其他 view controllers,model,和 views 之间进行通讯。这当然是 controller 应该做的,但我们还是希望以尽可能少的代码来完成它。

关于 view controllers 和 model 对象之间的消息传递,已经有很多阐述得很好的技术(比如 KVO 和 fetched results controllers)。但是 view controllers 之间的消息传递稍微就不是那么清晰了。

当一个 view controller 想把某个状态传递给多个其他 view controllers 时,就会出现这样的问题。较好的做法是把状态放到一个单独的对象里,然后把这个对象传递给其它 view controllers,它们观察和修改这个状态。这样的好处是消息传递都在一个地方(被观察的对象)进行,而且我们也不用纠结嵌套的 delegate 回调。这其实是一个复杂的主题,我们可能在未来用一个完整的话题来讨论这个主题。

总结

我们已经看到一些用来创建更小巧的 view controllers 的技术。我们并不是想把这些技术应用到每一个可能的角落,只是我们有一个目标:写可维护的代码。知道这些模式后,我们就更有可能把那些笨重的 view controllers 变得更整洁。

药物对接仿真软件:HADDOCK_(9).结果分析方法 HADDOCK是一种强大的药物对接仿真工具,通过对生成的输出文件进行详细的分析,我们可以验证仿真结果的可靠性,并为进一步优化药物设计提供依据。常见的分析方法包括解读输出文件、使用可视化工具进行结构分析、评估对接模型的稳定性和准确性等。通过这些步骤,我们可以更全面地了解受体和配体的结合模式,从而提高药物设计的成功率。 阅读详情

相关推荐

ROS2_IGH方案配置(二)——Ubuntu22.04 实时内核性能验证与调优

本文详细介绍了在Ubuntu22.04系统上配置ROS2_IGH方案时,如何验证和调优PREEMPT_RT实时内核性能。通过使用cyclictest工具进行基准测试和压力测试,分析中断与硬件延迟,并优化内核参数,确保系统满足工业控制领域对硬实时性的苛刻要求。文章还提供了针对ROS2的特殊优化建议和性能监控方案,帮助开发者实现毫秒级延迟的稳定控制。

weixin_30791095的博客 224

对抗 | 利用de4dot解密被混淆的.NET代码

0x01 背景由dotNet安全矩阵星球圈友们组成的微信群里大家伙常常聊着.NET话题,这不今天有个群友下午1:06分抛出反编译后还是混淆的代码,那么肯定需要加密后获取正常的.NET代码,笔者1:35看到后快速响应私聊了这位师傅,拿到需要解密的DLL,大约45分钟后2:20左右解密成功,此文主要把中间的过程踩的坑和解密方法记录一下。这位师傅用反编译工具打开后部分方法名显示...

zls365365的博客 1044

查看电脑mac地址+主机名+用户名并导出

张三:李四,应公司要求,今天下班前必须将这张表格里面用户电脑的mac地址统计出来!李四:我该怎么办呢?一个一个查看,太没有效率了吧!对了,我可以做一个bat脚本啊!

拥抱着青春 1974

ViewController详解

在我之前的学习笔记中讨论过ViewController,过了这么久,对它也有了新的认识和体会,ViewController是我们在开发过程中碰到最多的朋友,今天就来好好认识一下它。ViewController是IOS开发中MVC模式中的C,ViewController是view的controller,ViewController的职责主要包括管理内部各个view的加载显示和卸载,同时负责与其他ViewController的通信和协调。在IOS中,有两类ViewController,一类是显示内容的,比如UI

feelinghappy的专栏 824

VMware Ubuntu虚拟机突然断网?我用5步彻底解决了(附避坑指南)

Ubuntu虚拟机断网问题通常由网络配置冲突和VMware服务未正确绑定导致。本文提供5步解决方案:1)清理Ubuntu重复有线连接;2)重启Windows主机的VMware服务;3)检查VMware虚拟网络配置;4)重新配置Ubuntu网络参数;5)测试网络连通性。关键避坑点包括:避免多连接共存、修改配置后必重启服务、确保虚拟网卡绑定主机。通过"网卡状态→IP/路由→网关→服务"的排查思路,可快速解决虚拟机网络故障。文末附有详细的故障症状对照表,便于快速定位问题。

m0_58530495的博客 627

Kimi Code国产AI编程助手安装使用详细教程

Kimi Code 开发者智能编程服务简介:Kimi Code 是专为开发者提供的 AI 编程辅助工具,基于旗舰模型 Kimi K3(2.8万亿参数,支持100万token上下文)。提供 CLI、VS Code 插件等形式,支持代码阅读、编辑等功能。用户可通过API Key接入第三方工具。安装简单(Windows/Mac/Linux一键脚本),需在官网生成API密钥并登录使用。注意账户需充值余额方可正常调用服务。

猿小白的博客 2067

推荐开源项目:NYTPhotoViewer —— 强大的图片查看与滑动展示库

推荐开源项目:NYTPhotoViewer —— 强大的图片查看与滑动展示库 去发现同类优质开源项目:https://gitcode.com/ 项目介绍 NYTPhotoViewer 是一款由纽约时报开发的iOS应用程序组件,它提供了一个优雅的图片查看器和幻灯片展示功能。这个库不仅支持双击缩放、标题显示、多图切换,还内置了交互式轻拂退出、动画化缩放呈现等丰富特性。通过简单的API接口,你可以轻松地...

gitblog_00071的博客 611

使用R语言绘制Logistic回归模型的亚组分析森林图

亚组分析是一种常用的统计方法,用于探索不同亚组之间的差异,并确定是否存在某些变量对结果的调节作用。在本文中,我们将使用R语言来绘制Logistic回归模型的亚组分析森林图。在该图中,每个亚组都用一个点表示,点的位置表示亚组的均值,而线段表示亚组的置信区间。垂直参考线用于表示效应值为0的点,帮助我们判断亚组之间的差异。希望本文对你有所帮助,让你能够使用R语言绘制Logistic回归模型的亚组分析森林图。是一个二分类的变量,我们希望在亚组分析中探索它与响应变量之间的关系。函数来计算每个亚组的均值,并使用。

PixelNinja的博客 2003

iOS开发进阶(十一):ViewController 控制器详解

iOS 界面开发最重要的首属和View是View的控制器,也就是一般的页面,用来管理页面的生命周期(它相当于安卓里的Activity,两者很像,但又有一些差异)。的特点是它有好几种。一种最基本的。所谓容器,就是它们本身不能单独用来显示,必须在里面放一个或几个。用于导航栏管理页面;用于底部tab管理页面;用于切换器管理页面;容器还可以嵌套,比如把放进里面,这样在tab页面里,可以用启动导航栏样式的二级子页面。

IT全栈 华强工作室 3062

大模型在电力交易功率预测中的应用:短期,超短期,中长期的完整解决方案代码详解

本文介绍了基于大模型的电力交易功率预测系统,该系统覆盖超短期(15分钟-1小时)、短期(1-6小时)和中长期(1天-1月)三个时间尺度。文章详细阐述了数据准备与特征工程,包括多源数据集成和特征重要性分析。在模型构建方面,采用了Temporal Fusion Transformer (TFT)模型进行超短期预测,Informer模型进行短期预测,以及N-HiTS模型进行中长期预测。此外,文章还探讨了概率预测与风险分析,并介绍了模型部署与交易策略集成的实现。最后,文章总结了系统的贡献,并展望了未来发展方向,如市

专注AI大模型,软件混淆,授权 1264

十五、SAP-物料需求计划(MRP)

业务逻辑、功能讲解、图文解析、章节总结

m0_72403327的博客 1094
上一篇: UITableViewCell刚出现时,出现的动画
下一篇: 底层并发 API
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值