此文,将尝试动态从某个不肯定的文件夹中加载资源文件.文章,会继续完善自定义的 imageNamed 函数,并为下一篇文章铺垫.ios
正如咱们常常所说的那样,大多数情景知道作事的意义每每比作事的方法自己更有意义.意义自己,每每蕴含着目的,最终的需求一类的东西;而方法,只是咱们暂时寻找的用来达到最终的目的采起的一种可行的手段.知晓意义自己的意义在于,在之后的之后,咱们有可能找到更合适的方法来实现目的;也就是咱们所说的,到知识的丰富性获得必定程度以后,许多人在本身的我的技能提高过程当中,多少总会有那种融会贯通,一通百通的状况出现.能够确定的是,那种醍醐灌顶的感受,确定不是单纯的编码行数的变化的引发的;更多的,是因为你在有意无心中关于某个编码需求自己的意义的探寻所促成的.git
具体到这里,咱们为何须要动态的资源文件夹呢?就目前的探讨自己所透露出来的信息而言,主要是由于咱们的main.bundle放在了app里,而iOS App自己的打包进去的文件,在用户手机上是只读的.这样表述,有三层含义:github
从特定的缓存目录读取加载资源文件,能够看作动态资源文件夹的一种特殊形式,因此咱们先试着处理这种单一的状况.缓存
在iOS App中, 固定 的缓存目录和 特定 的缓存目录,仍是有区别的.主要是由于真机上iOS App每次启动时,其对应的文件目录是动态变化的.也就是说,咱们之后若是有存储文件路径的需求,必定要记住只能存储文件相对于程序沙盒主目录 NSHomeDirectory 的相对路径.顺便说一句,主目录的程序主目录的可见子目录有3个,分别是: Documents , Library , tmp ,具体介绍,可参考博文: iOS沙盒文件读写app
- Documents:苹果建议将程序建立产生的文件以及应用浏览产生的文件数据保存在该目录下,iTunes备份和恢复的时候会包括此目录
如今咱们的资源目录,将假定固定放在相对目录 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); } }
代码,基本就像上面那样,有几个点我想着重说一下:.net
由于目录是特定的,咱们只要每次App启动后,根据相对路径动态获取绝对路径,进而拿到 缓存目录中 main.bundle 资源包路径,而后就可使用已有的方法,从 bundle 里取图片便可:code
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位置处的代码便可:orm
+ (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);