iOS開發之0行代碼加載NSBundle中的@2x與@3x圖片

    本文只針對經過NSBundle對象的方法 pathForResource 獲取本地圖片資源遇到的圖片名沒法自動識別@2x與@3x名稱的問題進行測試、總結與分享。git


    加載本地圖片資源的方式通常經過如下兩種方法:
github

第1種:ide

    UIImage *img = [UIImage imageNamed:@"imageName"];

第2種:測試

    UIImage *img = [UIImage imageWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"imageName" ofType:@"imageType"]];


注:其餘方法如NSData等本文不涉及,如需瞭解請找某哥或某娘,謝謝合做。spa

    

    假定咱們都知道第1種方法適合讀取重複使用且佔用內存小的圖片資源,且能根據當前手機硬件能自動識別「@2x」圖或「@3x」圖。但若是須要加載不常使用且佔用內存很大如上百kb甚至上M的圖片資源的時候還使用這方式,內存佔用勢必會很嚴重。解決這種加載圖片資源佔用內存問題首選方案是換到第2種,但傳入的資源名必須與「.後綴名」前的名稱一致,若是資源名添加了「@2x」或「@3x」,而傳入的resource名稱帶或不帶「@2x」或「@3x」標識,結果分別會是怎麼樣的呢?下面咱們來測試一下。.net

> 不帶「@2x」或「@3x」標識:3d

8b57e24dc94791653df6e98dc81544e4.png


> 帶「@2x」或「@3x」標識orm


d542ba5a5a99c1993c61a676ee9f4640.png


    顯然傳入的名稱帶標識後能正常獲取到圖片資源。對象


    但如今我就是想能過第2種方法加載本地圖片資源能像第1種方法同樣,不須要傳入帶「@2x」和「@3x」的標識就能正常讀取到圖片資源,咱們要怎麼處理呢?
blog

    方法1:在每處都對當前設備進行判斷,並保證輸入的文件名正確,即Bundle裏存在帶或不帶標識的資源圖片文件。

    if(是@3x圖設備) {
        讀取@3x資源圖片路徑;
    }
    else if (是@2x圖設備) {
        讀取@2x資源圖片路徑;
    }
    else {
        讀取不帶@2x和@3x資源圖片路徑;
    }

可是請問有誰會願意如上述方法在每一個地方做這個判斷呢?


方法2:給NSBundle添加Category,輸入帶或不帶標識,自動識別對應資源圖片文件。

這種方法實際上是對方法1的封裝,思路同方法1,但略有完善。

邏輯以下:

    if(是@3x圖設備) {
        讀取@3x圖路徑;
        if(不存在@3x圖){
           讀取@2x資源圖片;
           if(不存在@2x圖){
               讀取@1x資源圖片;
           }
        }
    }
    else if (是@2x圖設備) {
        讀取@2x圖路徑;
        if(不存在@2x圖){
           讀取@3x資源圖片;
           if(不存在@3x圖){
               讀取@1x資源圖片;
           }
        }
    }
    else {
        讀取@1x資源圖片;
        if(不存在@1x圖){
           讀取@2x資源圖片;
           if(不存在@2x圖){
               讀取@3x資源圖片;
           }
        }
    }


代碼實現以下:

    運用Runtime知識,在類方法 load 裏做方法替換:

+ (void)load {
    Method originMethod = class_getInstanceMethod(self, @selector(pathForResource:ofType:));
    Method newMethod = class_getInstanceMethod(self, @selector(tempPathForResource:ofType:));
    method_exchangeImplementations(originMethod, newMethod);
}


替換的方法爲:

- (NSString *)tempPathForResource:(NSString *)name ofType:(NSString *)ext {
    NSString *path = [self tempPathForResource:name ofType:ext];
    if (path) {
        return path;
    }
    CGFloat scale = [UIScreen mainScreen].scale;
    if (ABS(scale-3) <= 0.001) {
        path = [self tempPathForResource_3x:name ofType:ext];
        if (!path) {
            path = [self tempPathForResource_2x:name ofType:ext];
            if (!path) {
                path = [self tempPathForResource_x:name ofType:ext];
            }
        }
        
    }
    else if (ABS(scale-2) <= 0.001){
        path = [self tempPathForResource_2x:name ofType:ext];
        if (!path) {
            path = [self tempPathForResource_3x:name ofType:ext];
            if (!path) {
                path = [self tempPathForResource_x:name ofType:ext];
            }
        }
    }
    else {
        path = [self tempPathForResource_x:name ofType:ext];
        if (!path) {
            path = [self tempPathForResource_2x:name ofType:ext];
            if (!path) {
                path = [self tempPathForResource_3x:name ofType:ext];
            }
        }
    }
    
    return path;
}


    在這個方法裏,優先使用原生系統的方法,若是資源能找到即返回了資源圖片的path,則直接返回;不然進入下面的查找流程。在每一次查找結束後均進行判斷,若是查找成功跳出if判斷並返回查找到的path,不然進入下一種設備的查找。其中,查找@2x圖仍是@3x圖,經過下面這個值判斷的:

    CGFloat scale = [UIScreen mainScreen].scale;

    在使用變量 scale 進行判斷的時候,使用的是「ABS(差) <= 0.001」方式,由於UIScreen對象的屬性「scale」是一個CGFloat類型的值:

50e7f8c3ae909cb8ca819e828f3c41ea.png


    在查找資源圖片的時候有這麼一個問題,若是當前設備是@3x的設備,如iPhone6 Plus 或 iPhone7 Plus 或其它須要@3x圖資源的設備,但咱們添加進來的是@2x圖資源或@1x圖資源,即這正是本文要解決的問題。

    針對倍率不一樣的設備,處理的邏輯也是不同的。

>對@1x圖的設備:

if(輸入的資源圖片名爲@3x的){
    把"@3x"去掉;
}
else if (輸入的資源圖片名爲@2x的) {
    把"@2x"去掉;
}
else {
    不做處理;
}
調用原生系統方法讀取path;


>對@2x圖的設備:

if(輸入的資源圖片名爲@3x的){
    把"@3x"替換爲"@2x";
}
else if (輸入的資源圖片名爲@2x的) {
    不做處理;
}
else {
    給資源圖片名加"@2x"後綴;
}
調用原生系統方法讀取path;


>對@3x圖的設備:

if(輸入的資源圖片名爲@3x的){
    不做處理;
}
else if (輸入的資源圖片名爲@2x的) {
    把"@2x"替換爲"@3x";
}
else {
    給資源圖片名加"@3x"後綴;
}
調用原生系統方法讀取path;


以上三種邏輯的代碼分別以下:

>對@1x圖的設備:

- (NSString *)tempPathForResource_x:(NSString *)name ofType:(NSString *)ext {
    NSString *path = nil;
    NSString *teampName = nil;
    
    if ([name hasSuffix:@"@3x"]) {
        teampName = [name stringByReplacingOccurrencesOfString:@"@3x" withString:@""];
    }
    else if ([name hasSuffix:@"@2x"]) {
        teampName = [name stringByReplacingOccurrencesOfString:@"@2x" withString:@""];
    }
    else {
        teampName = name;
    }
    path = [self tempPathForResource:teampName ofType:ext];
    
    return path;
}


>對@2x圖的設備:

- (NSString *)tempPathForResource_2x:(NSString *)name ofType:(NSString *)ext {
    NSString *path = nil;
    NSString *teampName = nil;
    
    if ([name hasSuffix:@"@3x"]) {
        teampName = [name stringByReplacingOccurrencesOfString:@"@3x" withString:@"@2x"];
    }
    else if ([name hasSuffix:@"@2x"]) {
        teampName = name;
    }
    else {
        teampName = [NSString stringWithFormat:@"%@@2x", name];
    }
    path = [self tempPathForResource:teampName ofType:ext];
    
    return path;
}


>對@3x圖的設備:

- (NSString *)tempPathForResource_3x:(NSString *)name ofType:(NSString *)ext {
    NSString *path = nil;
    NSString *teampName = nil;
    
    if ([name hasSuffix:@"@3x"]) {
        teampName = name;
    }
    else if ([name hasSuffix:@"@2x"]) {
        teampName = [name stringByReplacingOccurrencesOfString:@"@2x" withString:@"@3x"];
    }
    else {
        teampName = [NSString stringWithFormat:@"%@@3x", name];
    }
    path = [self tempPathForResource:teampName ofType:ext];
    
    return path;
}


經過上述處理後,測試結果以下:

08fe1f3e78e550907b380e7c857620e5.png



本文源代碼見:

https://github.com/zhoushejun/iOSNotes/blob/master/SJNotes/Classes/UI/Utilities/Categories/NSBundle%2BResource



參考資料:

http://blog.csdn.net/null29/article/details/53640179

http://www.jianshu.com/p/f40313d37049

相關文章
相關標籤/搜索