简介

此文,将尝试动态从某个不确定的文件夹中加载资源文件.文章,会继续完善自定义的 imageNamed 函数,并为下一篇文章铺垫.

这么做的意义

正如我们经常所说的那样,大多数情景知道做事的意义往往比做事的方法本身更有意义.意义本身,往往蕴含着目的,最终的需求一类的东西;而方法,只是我们暂时寻找的用来达到最终的目的采取的一种可行的手段.知晓意义本身的意义在于,在以后的以后,我们有可能找到更合适的方法来实现目的;也就是我们所说的,到知识的丰富性得到一定程度之后,许多人在自己的个人技能提升过程中,多少总会有那种融会贯通,一通百通的情况出现.可以肯定的是,那种醍醐灌顶的感觉,肯定不是单纯的编码行数的变化的引起的;更多的,是由于你在有意无意中关于某个编码需求本身的意义的探寻所促成的.

具体到这里,我们为什么需要动态的资源文件夹呢?就目前的探讨本身所透露出来的信息而言,主要是因为我们的main.bundle放在了app里,而iOS App本身的打包进去的文件,在用户手机上是只读的.这样表述,有三层含义:

  1. 如果你的资源文件是放置在App ipa包里的,尝试直接更新它,是不可能的 -- 至少对于一个native的 iOS App 是这样;
  2. 如果你的main.bundle是从网上动态下载的,每次下载都放置到用户文件夹特定位置,那你的确是不需要考虑过多动态资源文件夹的;
  3. 如果某一天iOS机制的发生变化,或者你为其他平台编写app,但是其本身的App资源文件是可写的,那你也很可能是可以不用动态资源文件夹的;

从特定的缓存目录读取资源文件

从特定的缓存目录读取加载资源文件,可以看做动态资源文件夹的一种特殊形式,所以我们先试着处理这种单一的情况.

1.动态拼接处特定的缓存目录

在iOS App中, 固定 的缓存目录和 特定 的缓存目录,还是有区别的.主要是因为真机上iOS App每次启动时,其对应的文件目录是动态变化的.也就是说,我们以后如果有存储文件路径的需求,一定要记住只能存储文件相对于程序沙盒主目录 NSHomeDirectory 的相对路径.顺便说一句,主目录的程序主目录的可见子目录有3个,分别是: Documents , Library , tmp ,具体介绍,可参考博文: iOS沙盒文件读写

  • Documents:苹果建议将程序创建产生的文件以及应用浏览产生的文件数据保存在该目录下,iTunes备份和恢复的时候会包括此目录
  • Library:存储程序的默认设置或其它状态信息;
  • Library/Caches:存放缓存文件,保存应用的持久化数据,用于应用升级或者应用关闭后的数据保存,不会被itunes同步,所以为了减少同步的时间,可以考虑将一些比较大的文件而又不需要备份的文件放到这个目录下。
  • tmp:提供一个即时创建临时文件的地方,但不需要持久化,在应用关闭后,该目录下的数据将删除,也可能系统在程序不运行的时候清除。

现在我们的资源目录,将假定固定放在相对目录 Library/Caches/patch 中,其名为 main.bundle

那么在需要时,我们就可以这样访问到我们的资源文件夹:

NSArray * LibraryPaths = NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES);
NSString * cacheBundleDir = [[LibraryPaths objectAtIndex:] stringByAppendingFormat:@"/Caches/Patch/"];
NSLog(@"缓存资源目录: %@", cacheBundleDir); // 模拟器示例输出: 缓存资源目录: /Users/yanfeng/Library/Developer/CoreSimulator/Devices/930159D0-850F-43CE-88D2-08BE9D4A7E7F/data/Containers/Data/Application/EE3A92AB-2DBE-44C5-9103-11BAC7AECE15/Library/Caches/Patch/

2.App第一次初始化时,将资源文件复制到特定缓存目录

NSString * bundleName = @"main.bundle";
NSError * err = nil;
NSFileManager * defaultManager = [NSFileManager defaultManager];
if ( ! [defaultManager fileExistsAtPath:cacheBundleDir]) {
[defaultManager createDirectoryAtPath:cacheBundleDir withIntermediateDirectories:YES attributes:nil error: &err]; if(err){
NSLog(@"初始化目录出错:%@", err);
} NSString * defaultBundlePath = [[NSBundle mainBundle].resourcePath stringByAppendingPathComponent: bundleName]; NSString * cacheBundlePath = [cacheBundleDir stringByAppendingPathComponent:bundleName];
[defaultManager copyItemAtPath: defaultBundlePath toPath:cacheBundlePath error: &err]; if(err){
NSLog(@"复制初始资源文件出错:%@", err);
}
}

代码,基本就像上面那样,有几个点我想着重说一下:

  1. fileExistsAtPath 判定 缓存目录的有无来判定是否是第一次启动.这个逻辑,在真实的补丁逻辑中,很可能是不严密的,后续会使用其他方式,此处够用即可;
  2. createDirectoryAtPath 用于目录不存在时,先构建目录的层级结构;否则如果直接复制,很有可能会报错的 -- 这取决于你的复制的目标目录与已有目录的层级差是否为1;
  3. copyItemAtPath:toPath: 的 toPath 是一个完整的且不存在的目标路径,不一定非得与 copyItemAtPath 参数的最后一级路径同名,此处仅为简化处理;以后如果有需要,此函数是可以通过同时执行复制和重命名两个操作的,如将 main.bundle 重名为 default.bundle ;
  4. 代码最好放在 AppDelegate.m 中;
  5. 在模拟器上,你可以很容易地看到函数执行后的效果:右击finder --> 前往文件夹 --> 输入Xcode输出的 缓存资源目录.

 

3.从特定缓存目录加载文件

因为目录是特定的,我们只要每次App启动后,根据相对路径动态获取绝对路径,进而拿到 缓存目录中 main.bundle 资源包路径,然后就可以使用已有的方法,从 bundle 里取图片即可:

NSArray * LibraryPaths = NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES);
NSString * cacheBundleDir = [[LibraryPaths objectAtIndex:] stringByAppendingFormat:@"/Caches/Patch/"]; NSString * bundleName = @"main.bundle";
NSString * imgName = @"sample@3x"; NSString * bundlePath = [cacheBundleDir stringByAppendingPathComponent: bundleName];
NSBundle * cacheMainBundle = [NSBundle bundleWithPath:bundlePath];
NSString * imgPath = [cacheMainBundle pathForResource:imgName ofType:@"png"];
UIImage * image = [UIImage imageWithContentsOfFile: imgPath];
self.sampleImageView.image = image;

从动态的缓存目录读取资源文件

这里,主要是和实现iOS图片等资源文件的热更新化(二):自定义的动态 imageNamed的类目方法结合扩展下,使原来的类目扩展支持从动态的缓存目录读取bundle,思路本身也很简单,只要更改下用于确定bundle位置处的代码即可:

+ (UIImage *)imageNamed:(NSString *)imgName bundle:(NSString *)bundleName cacheDir:(NSString *)cacheDir
{
NSArray * LibraryPaths = NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES); NSString * cacheBundleDir = [NSBundle mainBundle].resourcePath; if (cacheDir) {
cacheBundleDir = [[[LibraryPaths objectAtIndex:] stringByAppendingFormat:@"/Caches"] stringByAppendingPathComponent:cacheDir];
} bundleName = [NSString stringWithFormat:@"%@.bundle",bundleName];
imgName = [NSString stringWithFormat:@"%@@3x",imgName]; NSString * bundlePath = [cacheBundleDir stringByAppendingPathComponent: bundleName];
NSBundle * mainBundle = [NSBundle bundleWithPath:bundlePath];
NSString * imgPath = [mainBundle pathForResource:imgName ofType:@"png"]; UIImage * image;
static NSString * model; if (!model) {
model = [[UIDevice currentDevice]model];
} if ([model isEqualToString:@"iPad"]) {
NSData * imageData = [NSData dataWithContentsOfFile: imgPath];
image = [UIImage imageWithData:imageData scale:2.0];
}else{
image = [UIImage imageWithContentsOfFile: imgPath];
}
return image;
}

原来的从 ipa 包中加载 资源文件的逻辑可以基于此方法重写:

+ (UIImage *)imageNamed:(NSString *)imgName bundle:(NSString *)bundleName
{
return [self imageNamed:imgName bundle:bundleName cacheDir:nil];
}

+ (UIImage *)imageNamed:(NSString *)imgName bundle:(NSString *)bundleName cacheDir:(NSString *)cacheDir 方法中的 cacheDir 也是支持多级目录的,如:

UIImage * image = [UIImage imageNamed:@"sub/sample" bundle:@"main" cacheDir:@"patch/default"];
self.sampleImageView.image = image;

注意,此时初始复制到缓存目录的逻辑,也是适当对应子目录变更下:

NSArray * LibraryPaths = NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES);
NSString * cacheBundleDir = [[LibraryPaths objectAtIndex:] stringByAppendingFormat:@"/Caches/Patch/default/"];
NSLog(@"缓存资源目录: %@", cacheBundleDir);

参考资源:

实现iOS图片等资源文件的热更新化(三):动态的资源文件夹的更多相关文章

  1. 实现iOS图片等资源文件的热更新化(零): 序

    必要的序 以后在写系列文章,准备把基本的规划和动机等,单独作为一个小的序言部分给独立出来.序言部分,可以较为完整地交待系列文章的写作动机,所展示的编码技术可能的应用场景等.个人,我还是比较看重文章或者 ...

  2. 实现iOS图片等资源文件的热更新化(五): 一个简单完整的资源热更新页面

    简介 一个简单的关于页面,有一个图片,版本号,App名称等,着重演示各个系列的文章完整集成示例. 动机与意义 这是系列文章的最后一篇.今天抽空写下,收下尾.文章本身会在第四篇的基础上,简单扩充下代码, ...

  3. 实现iOS图片等资源文件的热更新化(二):自定义的动态 imageNamed

    这篇文章,要解决的是,使用一个自定义的 imageNamed 函数来替代系统的 imageNamed 函数.内部逻辑,将贯穿对比论证 关于"合适"的图片的定义.对iOS加载图片的规 ...

  4. 实现iOS图片等资源文件的热更新化(一): 从Images.xcassets导出合适的图片

    本文会基于一个已有的脚本工具自动导出所有的图片;最终给出的是一个从 Images.xcassets 到基于文件夹的精简 合适 的图片资源集的完整过程.难点在于从完整图片集到精简图片集,肯定是基于一个定 ...

  5. 实现iOS图片等资源文件的热更新化(四): 一个最小化的补丁更新逻辑

    简介 以前写过一个补丁更新的文章,此处会做一个更精简的最小化实现,以便于集成.为了使逻辑具有通用性,将剥离对AFNetworking和ReativeCocoa的依赖.原来的文章,可以先看这里: htt ...

  6. IntelliJ IDEA - 热部署插件JRebel ,对静态资源文件进行热部署?javascript、css、vm文件

    IntelliJ IDEA - 热部署插件JRebel ,对静态资源文件进行热部署?javascript.css.vm文件https://blog.csdn.net/feng_pump/article ...

  7. 大屏iPhone的适配 +iOS 图片尺寸要求

    摘自:http://blog.ibireme.com/2014/09/16/adapted_to_iphone6/ 苹果公司官网设计介绍到:Retina显示屏的超高像素密度已超过人眼能分辨的范围.Re ...

  8. Webpack多入口文件、热更新等体验

    Webpack现今流行的前端打包工具,今儿本人也来分享下自己学习体验. 一.html-webpack-plugin 实现html模板文件的解析与生成 在plugins加入HtmlWebpackPlug ...

  9. jquery mobile上传图片完整例子(包含ios图片横向问题处理和C#后台图片压缩)

    上传图片本身是个基本的小功能,但是到了移动端就不那么简单了,相信找到这篇文章的你一定有深深的同感. 本文实例是:在(移动端)页面中点击图片,然后选择文件,然后保存.使用Asp.net 难点一:后台获取 ...

随机推荐

  1. xamarin 学习

    http://www.cnblogs.com/lonelyxmas/p/5174934.html http://www.cnblogs.com/phytan/p/xamarincrack2.html ...

  2. cron表达式

    Cron表达式是一个字符串,字符串以5或6个空格隔开,分为6或7个域,每一个域代表一个含义,Cron有如下两种语法格式: Seconds Minutes Hours DayofMonth Month ...

  3. androidannotations 简单复制与点击事件(1)

    现在最火的android开发框架 简单描述一下 这一篇简单描述寻找控件以及事件的使用 1.该方法可以不用写setconteview @EActivity(R.layout.activity_main) ...

  4. [Window Title] (没有登录) [Content] ORA-12514: TNS: 监听程序当前无法识别连接描述符中请求的服务 [OK]

    重启Oracle服务

  5. PHP Strict Standards:问题解决

    异常信息: ( ! ) Strict standards: Declaration of SugarEmailAddress::save() should be compatible with tha ...

  6. Python黑帽编程2.3 字符串、列表、元组、字典和集合

    Python黑帽编程2.3  字符串.列表.元组.字典和集合 本节要介绍的是Python里面常用的几种数据结构.通常情况下,声明一个变量只保存一个值是远远不够的,我们需要将一组或多组数据进行存储.查询 ...

  7. Python黑帽编程 3.3 MAC洪水攻击

    Python灰帽编程 3.3 MAC洪水 传统的交换机(我只对我目前使用的交互机做过测试,按照常识只能这样表述)在数据转发过程中依靠对CAM表的查询来确定正确的转发接口,一旦在查询过程中无法找到相关目 ...

  8. 进一步封装highchart,打造自己的图表插件:jHighChart.js

    Highcharts 是一个用纯JavaScript编写的一个图表库, 能够很简单便捷的在web网站或是web应用程序添加有交互性的图表.支持的图表类型有曲线图.区域图.柱状图.饼状图.散状点图和综合 ...

  9. 实战JS正则表达式

    -正则表达式是一种文本模式的匹配工具. -文章导读: --1.正则对象的属性和方法 --2.字符串对象的方法 --3.使用正则表达式: ---3.1 给字符串加上千分符 ---3.2 字符串中出现次数 ...

  10. MySQL 分区介绍总结

    200 ? "200px" : this.width)!important;} --> 介绍 分区是指根据一定的规则将一个大表分解成多个更小的部分,这里的规则一般就是利用分区 ...