實現iOS圖片等資源文件的熱更新化(三):動態的資源文件夾

簡介

此文,將嘗試動態從某個不肯定的文件夾中加載資源文件.文章,會繼續完善自定義的 imageNamed 函數,併爲下一篇文章鋪墊.ios

這麼作的意義

正如咱們常常所說的那樣,大多數情景知道作事的意義每每比作事的方法自己更有意義.意義自己,每每蘊含着目的,最終的需求一類的東西;而方法,只是咱們暫時尋找的用來達到最終的目的採起的一種可行的手段.知曉意義自己的意義在於,在之後的之後,咱們有可能找到更合適的方法來實現目的;也就是咱們所說的,到知識的豐富性獲得必定程度以後,許多人在本身的我的技能提高過程當中,多少總會有那種融會貫通,一通百通的狀況出現.能夠確定的是,那種醍醐灌頂的感受,確定不是單純的編碼行數的變化的引發的;更多的,是因爲你在有意無心中關於某個編碼需求自己的意義的探尋所促成的.git

具體到這裏,咱們爲何須要動態的資源文件夾呢?就目前的探討自己所透露出來的信息而言,主要是由於咱們的main.bundle放在了app裏,而iOS App自己的打包進去的文件,在用戶手機上是隻讀的.這樣表述,有三層含義:github

  1. 若是你的資源文件是放置在App ipa包裏的,嘗試直接更新它,是不可能的 -- 至少對於一個native的 iOS App 是這樣;
  2. 若是你的main.bundle是從網上動態下載的,每次下載都放置到用戶文件夾特定位置,那你的確是不須要考慮過多動態資源文件夾的;
  3. 若是某一天iOS機制的發生變化,或者你爲其餘平臺編寫app,可是其自己的App資源文件是可寫的,那你也極可能是能夠不用動態資源文件夾的;

從特定的緩存目錄讀取資源文件

從特定的緩存目錄讀取加載資源文件,能夠看作動態資源文件夾的一種特殊形式,因此咱們先試着處理這種單一的狀況.緩存

1.動態拼接處特定的緩存目錄

在iOS App中, 固定 的緩存目錄和 特定 的緩存目錄,仍是有區別的.主要是由於真機上iOS App每次啓動時,其對應的文件目錄是動態變化的.也就是說,咱們之後若是有存儲文件路徑的需求,必定要記住只能存儲文件相對於程序沙盒主目錄 NSHomeDirectory 的相對路徑.順便說一句,主目錄的程序主目錄的可見子目錄有3個,分別是: Documents , Library , tmp ,具體介紹,可參考博文: iOS沙盒文件讀寫app

  • 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/

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);
    }
}

代碼,基本就像上面那樣,有幾個點我想着重說一下:.net

  1. fileExistsAtPath 斷定 緩存目錄的有無來斷定是不是第一次啓動.這個邏輯,在真實的補丁邏輯中,極可能是不嚴密的,後續會使用其餘方式,此處夠用便可;
  2. createDirectoryAtPath 用於目錄不存在時,先構建目錄的層級結構;不然若是直接複製,頗有可能會報錯的 -- 這取決於你的複製的目標目錄與已有目錄的層級差是否爲1;
  3. copyItemAtPath:toPath:toPath 是一個完整的且不存在的目標路徑,不必定非得與 copyItemAtPath 參數的最後一級路徑同名,此處僅爲簡化處理;之後若是有須要,此函數是能夠經過同時執行復制和重命名兩個操做的,如將 main.bundle 重名爲 default.bundle ;
  4. 代碼最好放在 AppDelegate.m 中;
  5. 在模擬器上,你能夠很容易地看到函數執行後的效果:右擊finder --> 前往文件夾 --> 輸入Xcode輸出的 緩存資源目錄.

前往文件夾 輸入Xcode輸出的 緩存資源目錄 模擬器結果

3.從特定緩存目錄加載文件

由於目錄是特定的,咱們只要每次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);

參考資源:

本節內容完整可執行Xcode工程代碼,只有100k

相關文章
相關標籤/搜索