標籤:ios nsbundle pathforresource
本文只針對通過NSBundle對象的方法 pathForResource 擷取本地圖片資源遇到的圖片名無法自動識別@2x與@3x名稱的問題進行測試、總結與分享。
載入本地圖片資源的方式一般通過以下兩種方法:
第1種:
UIImage *img = [UIImage imageNamed:@"imageName"];
第2種:
UIImage *img = [UIImage imageWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"imageName" ofType:@"imageType"]];
註:其他方法如NSData等本文不涉及,如需瞭解請找某哥或某娘,謝謝合用。
假定我們都知道第1種方法適合讀取重複使用且佔用記憶體小的圖片資源,且能根據當前手機硬體能th自動識別“@2x”圖或“@3x”圖。但如果需要載入不常使用且佔用記憶體很大如上百kb甚至上M的圖片資源的時候還使用這方式,記憶體佔用勢必會很嚴重。解決這種載入圖片資源佔用記憶體問題首選方案是換到第2種,但傳入的資源名必須與“.尾碼名”前的名稱一致,如果資源名添加了“@2x”或“@3x”,而傳入的resource名稱帶或不帶“@2x”或“@3x”標識,結果分別會是怎麼樣的呢?下面我們來測試一下。
> 不帶“@2x”或“@3x”標識:
650) this.width=650;" src="https://s1.51cto.com/oss/201711/16/8b57e24dc94791653df6e98dc81544e4.png" title="img1.png" alt="8b57e24dc94791653df6e98dc81544e4.png" />
> 帶“@2x”或“@3x”標識
650) this.width=650;" src="https://s1.51cto.com/oss/201711/16/d542ba5a5a99c1993c61a676ee9f4640.png" title="img2.png" alt="d542ba5a5a99c1993c61a676ee9f4640.png" />
顯然傳入的名稱帶標識後能正常擷取到圖片資源。
但現在我就是想能過第2種方法載入本地圖片資源能像第1種方法一樣,不需要傳入帶“@2x”和“@3x”的標識就能正常讀取到圖片資源,我們要怎麼處理呢?
方法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圖){ 讀取不帶@2x和@3x資源圖片; } } } else if (是@2x圖裝置) { 讀取@2x圖路徑; if(不存在@2x圖){ 讀取@3x資源圖片; if(不存在@3x圖){ 讀取不帶@2x和@3x資源圖片; } } } else { 讀取不帶@2x和@3x資源圖片; 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類型的值:
650) this.width=650;" src="https://s3.51cto.com/oss/201711/16/50e7f8c3ae909cb8ca819e828f3c41ea.png" title="img3.png" alt="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;}
通過上述處理後,測試結果如下:
650) this.width=650;" src="https://s3.51cto.com/oss/201711/16/08fe1f3e78e550907b380e7c857620e5.png" title="img4.png" alt="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
本文出自 “江山風雨” 部落格,請務必保留此出處http://965678322.blog.51cto.com/4935622/1982197
iOS開發之0行代碼載入NSBundle中的@2x與@3x圖片