标签:
此文,将尝试动态从某个不确定的文件夹中加载资源文件.文章,会继续完善自定义的 imageNamed 函数,并为下一篇文章铺垫.
正如我们经常所说的那样,大多数情景知道做事的意义往往比做事的方法本身更有意义.意义本身,往往蕴含着目的,最终的需求一类的东西;而方法,只是我们暂时寻找的用来达到最终的目的采取的一种可行的手段.知晓意义本身的意义在于,在以后的以后,我们有可能找到更合适的方法来实现目的;也就是我们所说的,到知识的丰富性得到一定程度之后,许多人在自己的个人技能提升过程中,多少总会有那种融会贯通,一通百通的情况出现.可以肯定的是,那种醍醐灌顶的感觉,肯定不是单纯的编码行数的变化的引起的;更多的,是由于你在有意无意中关于某个编码需求本身的意义的探寻所促成的.
具体到这里,我们为什么需要动态的资源文件夹呢?就目前的探讨本身所透露出来的信息而言,主要是因为我们的main.bundle放在了app里,而iOS App本身的打包进去的文件,在用户手机上是只读的.这样表述,有三层含义:
从特定的缓存目录读取加载资源文件,可以看做动态资源文件夹的一种特殊形式,所以我们先试着处理这种单一的情况.
在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:0] 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/
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); } }
代码,基本就像上面那样,有几个点我想着重说一下:
因为目录是特定的,我们只要每次App启动后,根据相对路径动态获取绝对路径,进而拿到 缓存目录中 main.bundle 资源包路径,然后就可以使用已有的方法,从 bundle 里取图片即可:
NSArray * LibraryPaths = NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES); NSString * cacheBundleDir = [[LibraryPaths objectAtIndex:0] 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:0] 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:0] stringByAppendingFormat:@"/Caches/Patch/default/"]; NSLog(@"缓存资源目录: %@", cacheBundleDir);
标签:
原文地址:http://www.cnblogs.com/ios122/p/5930305.html