iOS處理圖片的一些小Tip

\color{red}{如何把GIF動圖保存到相冊中?}

iOS相冊是支持保存GIF和APNG動圖的嗤栓,只是不能直接播放爬橡。

在iOS9之前治唤,保存圖片使用:

[ALAssetsLibrary writeImageDataToSavedPhotosAlbum: metadata: completionBlock:];

但是在iOS9之后,AssetsLibrary廢棄了糙申,被Photos庫取代了宾添,所以在iOS9之后保存圖片使用:

[[PHPhotoLibrary sharedPhotoLibrary] performChanges:^{
    [PHAssetChangeRequest creationRequestForAssetFromImage:self.imageView.image];
} completionHandler:^(BOOL success, NSError * _Nullable error) {
}];

Tip:
如果直接使用[PHAssetChangeRequest creationRequestForAssetFromImage:self.imageView.image];則會出現(xiàn)如下崩潰信息:

reason: 'This method can only be called from inside of -[PHPhotoLibrary performChanges:completionHandler:] 
or -[PHPhotoLibrary performChangesAndWait:error:]'

結(jié)論:凡是圖片的增刪改查操作,都放在 performChanges中進行操作 柜裸,如下:

[[PHPhotoLibrary sharedPhotoLibrary] performChanges:^{
   //TODO: 做相應(yīng)的增刪改查操作
} completionHandler:^(BOOL success, NSError * _Nullable error) {
}];

如果使用UIImageWriteToSavedPhotosAlbum()方法直接寫入相冊的話缕陕,那么圖像會被強制轉(zhuǎn)換為png格式。

\color{red}{將UIImage 保存到磁盤疙挺,用什么方式最好扛邑?}

目前來說,保存UIImage有三種方式:
1铐然、直接使用NSKeyedArchiver 把UIImage序列化保存蔬崩。
2恶座、用UIImagePNGRepresentation()先把圖片轉(zhuǎn)為png保存。
3沥阳、用UIImageJPEGRepresentation()把圖片壓縮成JPEG保存跨琳。

實際上,NSKeyedArchiver 是調(diào)用了 UIImagePNGRepresentation 進行序列化的桐罕,用它來保存圖片是消耗最大的脉让。蘋果對 JPEG 有硬編碼和硬解碼,保存成 JPEG 會大大縮減編碼解碼時間功炮,也能減小文件體積溅潜。所以如果圖片不包含透明像素時,UIImageJPEGRepresentation(0.9) 是最佳的圖片保存方式死宣,其次是 UIImagePNGRepresentation()伟恶。

\color{red}{UIImage緩存是怎么回事?}

通過imageNamed 創(chuàng)建 UIImage時毅该,系統(tǒng)實際上只是在Bundle內(nèi)查找到文件名,然后把這個文件名放到UIImage里面返回潦牛,并沒有進行實際的文件的讀取和解碼眶掌。當UIImage第一次顯示到屏幕上的時候,其內(nèi)部解碼方法才會被調(diào)用巴碗,同時解碼結(jié)果會保存到一個全局的緩存中朴爬,在圖片解碼后,App第一次退到后臺和收到內(nèi)存警告時橡淆,該圖片的緩存才會被清空召噩,其他情況下緩存會一直存在。

\color{red}{要是使用imageWithData 能不能避免緩存逸爵?}

不能具滴,通過數(shù)據(jù)創(chuàng)建UIImage時,UIImage底層是調(diào)用ImageIO的CGImageSourceCreateWithData()方法师倔,該方法有個參數(shù)叫ShouldCache构韵,在64位的設(shè)備上,這個參數(shù)是默認開啟的趋艘。這個圖片也是同樣在第一次顯示到屏幕時才會被解碼疲恢,隨后解碼數(shù)據(jù)被緩存到 CGImage 內(nèi)部。與 imageNamed 創(chuàng)建的圖片不同瓷胧,如果這個圖片被釋放掉显拳,其內(nèi)部的解碼數(shù)據(jù)也會被立刻釋放。

\color{red}{怎樣能避免緩存搓萧?}

  1. 手動調(diào)用 CGImageSourceCreateWithData() 來創(chuàng)建圖片杂数,并把 ShouldCache 和 ShouldCacheImmediately 關(guān)掉遇八。這么做會導致每次圖片顯示到屏幕時,解碼方法都會被調(diào)用耍休,造成很大的 CPU 占用刃永。
  2. 把圖片用 CGContextDrawImage() 繪制到畫布上,然后把畫布的數(shù)據(jù)取出來當作圖片羊精。這也是常見的網(wǎng)絡(luò)圖片庫的做法斯够。

\color{red}{我能直接取到圖片解碼后的數(shù)據(jù),而不是通過畫布取到嗎喧锦?}

  1. CGImageSourceCreateWithData(data) 創(chuàng)建 ImageSource读规。
  2. CGImageSourceCreateImageAtIndex(source) 創(chuàng)建一個未解碼的 CGImage。
  3. CGImageGetDataProvider(image) 獲取這個圖片的數(shù)據(jù)源燃少。
  4. CGDataProviderCopyData(provider) 從數(shù)據(jù)源獲取直接解碼的數(shù)據(jù)束亏。
    ImageIO 解碼發(fā)生在最后一步,這樣獲得的數(shù)據(jù)是沒有經(jīng)過顏色類型轉(zhuǎn)換的原生數(shù)據(jù)(比如灰度圖像)阵具。

\color{red}{如何判斷一個文件的圖片類型碍遍?}
通過讀取文件或數(shù)據(jù)的頭幾個字節(jié)然后和對應(yīng)圖片格式標準進行比對。具體可以參考YYImageCoder類里面的方法:

YYImageType YYImageDetectType(CFDataRef data) {
    if (!data) return YYImageTypeUnknown;
    uint64_t length = CFDataGetLength(data);
    if (length < 16) return YYImageTypeUnknown;
    
    const char *bytes = (char *)CFDataGetBytePtr(data);
    
    uint32_t magic4 = *((uint32_t *)bytes);
    switch (magic4) {
        case YY_FOUR_CC(0x4D, 0x4D, 0x00, 0x2A): { // big endian TIFF
            return YYImageTypeTIFF;
        } break;
            
        case YY_FOUR_CC(0x49, 0x49, 0x2A, 0x00): { // little endian TIFF
            return YYImageTypeTIFF;
        } break;
            
        case YY_FOUR_CC(0x00, 0x00, 0x01, 0x00): { // ICO
            return YYImageTypeICO;
        } break;
            
        case YY_FOUR_CC(0x00, 0x00, 0x02, 0x00): { // CUR
            return YYImageTypeICO;
        } break;
            
        case YY_FOUR_CC('i', 'c', 'n', 's'): { // ICNS
            return YYImageTypeICNS;
        } break;
            
        case YY_FOUR_CC('G', 'I', 'F', '8'): { // GIF
            return YYImageTypeGIF;
        } break;
            
        case YY_FOUR_CC(0x89, 'P', 'N', 'G'): {  // PNG
            uint32_t tmp = *((uint32_t *)(bytes + 4));
            if (tmp == YY_FOUR_CC('\r', '\n', 0x1A, '\n')) {
                return YYImageTypePNG;
            }
        } break;
            
        case YY_FOUR_CC('R', 'I', 'F', 'F'): { // WebP
            uint32_t tmp = *((uint32_t *)(bytes + 8));
            if (tmp == YY_FOUR_CC('W', 'E', 'B', 'P')) {
                return YYImageTypeWebP;
            }
        } break;
        /*
        case YY_FOUR_CC('B', 'P', 'G', 0xFB): { // BPG
            return YYImageTypeBPG;
        } break;
        */
    }
    
    uint16_t magic2 = *((uint16_t *)bytes);
    switch (magic2) {
        case YY_TWO_CC('B', 'A'):
        case YY_TWO_CC('B', 'M'):
        case YY_TWO_CC('I', 'C'):
        case YY_TWO_CC('P', 'I'):
        case YY_TWO_CC('C', 'I'):
        case YY_TWO_CC('C', 'P'): { // BMP
            return YYImageTypeBMP;
        }
        case YY_TWO_CC(0xFF, 0x4F): { // JPEG2000
            return YYImageTypeJPEG2000;
        }
    }
    
    // JPG             FF D8 FF
    if (memcmp(bytes,"\377\330\377",3) == 0) return YYImageTypeJPEG;
    
    // JP2
    if (memcmp(bytes + 4, "\152\120\040\040\015", 5) == 0) return YYImageTypeJPEG2000;
    
    return YYImageTypeUnknown;
}

\color{red}{怎樣像瀏覽器那樣邊下載邊顯示圖片阳液?}

第一種是 baseline怕敬,即逐行掃描。默認情況下帘皿,JPEG东跪、PNG、GIF 都是這種保存方式鹰溜。
第二種是 interlaced虽填,即隔行掃描。PNG 和 GIF 在保存時可以選擇這種格式曹动。
第三種是 progressive斋日,即漸進式。JPEG 在保存時可以選擇這種方式仁期。
在下載圖片時桑驱,首先用 CGImageSourceCreateIncremental(NULL) 創(chuàng)建一個空的圖片源,隨后在獲得新數(shù)據(jù)時調(diào)用
CGImageSourceUpdateData(data, false) 來更新圖片源跛蛋,最后在用 CGImageSourceCreateImageAtIndex() 創(chuàng)建圖片來顯示熬的。

可以用 PINRemoteImage 或者 YYWebImage 來實現(xiàn)這個效果。SDWebImage 并沒有用 Incremental 方式解碼赊级,所以顯示效果很差押框。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市理逊,隨后出現(xiàn)的幾起案子橡伞,更是在濱河造成了極大的恐慌盒揉,老刑警劉巖,帶你破解...
    沈念sama閱讀 212,383評論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件兑徘,死亡現(xiàn)場離奇詭異刚盈,居然都是意外死亡,警方通過查閱死者的電腦和手機挂脑,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,522評論 3 385
  • 文/潘曉璐 我一進店門藕漱,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人崭闲,你說我怎么就攤上這事肋联。” “怎么了刁俭?”我有些...
    開封第一講書人閱讀 157,852評論 0 348
  • 文/不壞的土叔 我叫張陵橄仍,是天一觀的道長。 經(jīng)常有香客問我牍戚,道長侮繁,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,621評論 1 284
  • 正文 為了忘掉前任翘魄,我火速辦了婚禮鼎天,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘暑竟。我一直安慰自己,他們只是感情好育勺,可當我...
    茶點故事閱讀 65,741評論 6 386
  • 文/花漫 我一把揭開白布但荤。 她就那樣靜靜地躺著,像睡著了一般涧至。 火紅的嫁衣襯著肌膚如雪腹躁。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,929評論 1 290
  • 那天南蓬,我揣著相機與錄音纺非,去河邊找鬼。 笑死赘方,一個胖子當著我的面吹牛烧颖,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播窄陡,決...
    沈念sama閱讀 39,076評論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼炕淮,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了跳夭?” 一聲冷哼從身側(cè)響起涂圆,我...
    開封第一講書人閱讀 37,803評論 0 268
  • 序言:老撾萬榮一對情侶失蹤们镜,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后润歉,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體模狭,經(jīng)...
    沈念sama閱讀 44,265評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,582評論 2 327
  • 正文 我和宋清朗相戀三年踩衩,在試婚紗的時候發(fā)現(xiàn)自己被綠了嚼鹉。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,716評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡九妈,死狀恐怖反砌,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情萌朱,我是刑警寧澤宴树,帶...
    沈念sama閱讀 34,395評論 4 333
  • 正文 年R本政府宣布,位于F島的核電站晶疼,受9級特大地震影響酒贬,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜翠霍,卻給世界環(huán)境...
    茶點故事閱讀 40,039評論 3 316
  • 文/蒙蒙 一锭吨、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧寒匙,春花似錦零如、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,798評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至会宪,卻和暖如春肖卧,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背掸鹅。 一陣腳步聲響...
    開封第一講書人閱讀 32,027評論 1 266
  • 我被黑心中介騙來泰國打工塞帐, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人巍沙。 一個月前我還...
    沈念sama閱讀 46,488評論 2 361
  • 正文 我出身青樓葵姥,卻偏偏與公主長得像,于是被迫代替她去往敵國和親赎瞎。 傳聞我的和親對象是個殘疾皇子牌里,可洞房花燭夜當晚...
    茶點故事閱讀 43,612評論 2 350

推薦閱讀更多精彩內(nèi)容