简介

此文,将尝试动态从某个不确定的文件夹中加载资源文件.文章,会继续完善自定义的 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. myeclipse中导入js报如下错误Syntax error on token "Invalid Regular Expression Options", no accurate correc

    今天在使用bootstrap的时候引入的js文件出现错误Syntax error on token "Invalid Regular Expression Options", no ...

  2. LogStash-2.4.0自定义区域信息插件-ISP

    由于直接复制粘贴有问题,所以给出链接: http://note.youdao.com/share/?id=6dfb1f03240e156c1db4a56c85e3b6db&type=note# ...

  3. Logging with Debug And Trace (一)

    对于一个应用程序而言,Log 必不可少. 在.net 里面,最简单的方式就是用Console 来输出 信息了,例如下面的例子: public class Program { public static ...

  4. Castle中AdditionalInterfaces用法介绍

    首先见下图(图一),其中FooController是一个没有实现任何Interface的空类.需要实现的效果是:通过FooController对象调用FooService的Do方法.设置这一不常见的场 ...

  5. 修改注册表 去除Windows快捷方式图标小箭头

    一些朋友不喜欢Windows系统中快捷方式图标上面的小箭头,下面介绍如何修改注册表去除快捷方式图标上的小箭头. 1.开始->运行->输入regedit,启动注册表编辑器,然后; 2.依次展 ...

  6. 【原】Telerik radwindow 在IE10下偶尔报 unable to get value of the property 'toLowerCase' 的解决办法

    笔者正在开发的新项目在升级了最新版本的Telerik Control和决定支持IE10后,遇到了一个很棘手的问题,偶尔会在打开Radwindow的时候报JS error :unable to get ...

  7. java即时通信小例子

    学习java一段时间了,今天写来一个即时通信的小例子练手在其过程中也学到了一些知识拿出来和大家分享,请路过的各位大神多多赐教... 好了下面讲一下基本的思路: 首先,编写服务器端的程序,简单点说吧就是 ...

  8. Knockout中文开发指南(完整版API中文文档) 目录索引

    a, .tree li > span { padding: 4pt; border-radius: 4px; } .tree li a { color:#46cfb0; text-decorat ...

  9. .Net 转战 Android 4.4 日常笔记(7)--apk的打包与反编译

    apk(android package)就是我们安卓系统的安装文件,可以在模拟器和手机中直接打开安装,从项目中打包apk有几种方式可取 一.最简单的方法(类似我们的winfrom) 只要我们调试或者运 ...

  10. 如何在Windows Server 2008 上添加RD (远程桌面)会话主机配置的远程桌面授权服务器

    在Windows Server系列的现存活跃产品中都默认的会开放两个随机附送的远程控制的授权,而一些特殊条件下我们需要启用多个远程终端连接,在购买了相应的授权之后,我们如何将配置好的服务器添加到远程桌 ...