在講解如何選擇圖片格式以前,我感受有必要先了解下,圖片是如何展現的。若是咱們要展現一張圖片,通常步驟是這樣的:html
/// Assets.xcassets中的圖片,不須要後綴
let image = UIImage(named: "icon")
let imageView = UIImageView(frame: rect)
imageView.image = image
view.addSubview(imageView)
複製代碼
運行程序,咱們就能夠在指定位置看到這個icon。看似簡單的代碼背後隱藏了不少細節工做。一張圖片的展現,從代碼執行到展現出來大體經歷了這些步驟:ios
1. 加載圖片git
從磁盤中加載一張圖片;github
而後將生成的 UIImage
賦值給 UIImageView
;web
接着一個隱式的 CATransaction
捕獲到了 UIImageView
圖層樹的變化;算法
分配內存緩衝區用於管理文件 IO 和解壓縮操做,將文件數據從磁盤讀到內存中;swift
2. 圖片解碼(解壓)xcode
3. 圖片渲染緩存
Core Animation
中CALayer
使用解壓(解碼)的位圖數據渲染 UIImageView
的圖層;bash
CPU計算好圖片的Frame,對圖片解壓以後,就會交給GPU來作圖片渲染渲染流程;
GPU獲取獲取圖片的座標,將座標交給頂點着色器(頂點計算),將圖片光柵化(獲取圖片對應屏幕上的像素點),片元着色器計算(計算每一個像素點的最終顯示的顏色值);
從幀緩存區中渲染到屏幕上;
這其中有個關鍵步驟是圖片解碼。那爲何要解碼呢,這是由於咱們日常使用的圖片通常爲了節約空間都會通過一些壓縮算法進行封裝,而使用時屏幕要精確的渲染到每一個像素點,這就須要把壓縮的圖片解碼展開,便於系統處理。
有損壓縮:指在壓縮文件大小的過程當中,損失了一部分圖片的信息,也即下降了圖片的質量,而且這種損失是不可逆的,咱們不可能從有一個有損壓縮過的圖片中恢復出全來的圖片。常見的有損壓縮手段,是按照必定的算法將臨近的像素點進行合併。
無損壓縮:只在壓縮文件大小的過程當中,圖片的質量沒有任何損耗。咱們任什麼時候候均可以從無損壓縮過的圖片中恢復出原來的信息。
索引色:用一個數字來表明(索引)一種顏色,在存儲圖片的時候,存儲一個數字的組合,同時存儲數字到圖片顏色的映射。這種方式只能存儲有限種顏色,一般是256種顏色,對應到計算機系統中,使用一個字節的數字來索引一種顏色。
直接色:使用四個數字來表明一種顏色,這四個數字分別表明這個顏色中紅色、綠色、藍色以及透明度。如今流行的顯示設備能夠在這四個維度分別支持256種變化,因此直接色能夠表示2的32次方種顏色。固然並不是全部的直接色都支持這麼多種,爲壓縮空間使用,有可能只有表達紅、綠、藍的三個數字,每一個數字也可能不支持256種變化之多。
點陣圖:也叫作位圖,像素圖。構成點陣圖的最小單位是象素,位圖就是由象素陣列的排列來實現其顯示效果的,每一個象素有本身的顏色信息,在對位圖圖像進行編輯操做的時候,可操做的對象是每一個象素,咱們能夠改變圖像的色相、飽和度、明度,從而改變圖像的顯示效果。點陣圖縮放會失真,用最近很是流行的沙畫來比喻最恰當不過,當你從遠處看的時候,畫面細膩多彩,可是當你靠的很是近的時候,你就能看到組成畫面的每粒沙子以及每一個沙粒的顏色。
矢量圖:也叫作向量圖。矢量圖並不紀錄畫面上每一點的信息,而是紀錄了元素形狀及顏色的算法,當你打開一張矢量圖的時候,軟件對圖形象對應的函數進行運算,將運算結果[圖形的形狀和顏色]顯示給你看。不管顯示畫面是大仍是小,畫面上的對象對應的算法是不變的,因此,即便對畫面進行倍數至關大的縮放,其顯示效果仍然相同(不失真)。
一張圖片,若是咱們將它的每個像素及其對應的顏色都存儲起來(BMP格式),是會很大的。爲了減少圖片佔用的存儲空間,派生出了各類不一樣壓縮算法所表明的圖片格式。常見的圖片格式有png、jpeg、heic、gif、webp,svg等。
PNG有兩種類型:PNG-8和PNG-24。
PNG-8是無損的、索引色、點陣圖。它支持透明度的調節。
PNG-24是無損的、直接色、點陣圖。由於使用直接色,顏色範圍更大,也佔用更大的空間。他的目標是替代JPEG,但通常而言,PNG-24的文件大小是JPEG的五倍之多,而顯示效果則一般只能得到一點點提高。因此若是不是對圖片質量要求特別高,不建議使用PNG-24
PNG是蘋果推薦的圖片格式,並且這些圖片會被一個叫pngcrush的開源工具優化,這樣iOS設備就能在顯示時更快的解壓和渲染圖片。該工具位於目錄:
/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/usr/bin
複製代碼
JPEG是有損的、採用直接色的、點陣圖壓縮方式。 JEPG目標是在不影響人類可分辨的圖片質量的前提下,儘量的壓縮文件大小。通常都是用於相機圖片的格式。但由於有損,會致使圖片失真。iOS能夠經過如下方式壓縮圖片:
// 壓縮比範圍從0到1
func jpegData(compressionQuality: CGFloat) -> Data?
複製代碼
題外話 通常來講,相同的圖片採用JPEG的壓縮方式會比png獲得更小的尺寸,但也有例外。
在網上查了資料說是JEPG更適合處理帶有不少雜色的風景圖,而對於使用數位板等電子繪製的純色卡通系風格圖片,JEPG的壓縮方式會拔苗助長,致使體積更大。
HEIC是HEIF(High Efficiency Image Format 高效率圖像文件格式)的一種。它並不是蘋果開發,而是由運動圖像專家組(MPEG)開發。它同時支持有損壓縮、無損壓縮、透明度等特性。HEIF規範的完成是在2015年,是這幾種圖片格式中最新的一種了,目前除了蘋果,尚未哪家大廠去擁抱這種格式。
在iOS 11更新後,iPhone 7及其後硬件,在拍攝照片時的默認圖像存儲格式。與JPG相比,它佔用的空間更小,畫質更加無損。HEIC的目的就是做爲JPEG的繼任者,之後或許會成爲一種趨勢。目前能夠想到的在開發中的應用是,對於一些須要下載的大圖能夠轉成HEIC格式,供客戶端使用。可是當前卻不多應用,大機率是考慮到圖片兼容問題吧。
題外話
一個有趣的現象,我用相機(iPhoneXR)拍攝一張照片,經過AirDrop傳到電腦,顯示爲HEIC格式。當我在拍照時選擇系統自帶的任意一種濾鏡,圖片格式就變成了JPEG。這是爲何?
有小夥伴解答:
iOS拍照選擇濾鏡會「轉」爲JPEG,是由於拍照的格式仍是HEIF,加濾鏡和編輯圖片都是至關於複製了一份再作操做的,點擊復原又會「轉」爲HEIF。
通過測試確實是這樣的,並且既然蘋果提供復原的操做,說明原圖(HEIF)並無被覆蓋。那爲何濾鏡不能直接在HEIF格式下操做,猜想多是跟濾鏡的算法相關,該算法只能對JEPG格式編碼的圖片進行渲染,因此須要中間轉成JEPG。
Live Photo
Live圖片的實質是:一張heic格式封面圖 + mov格式視頻。
對於Live Photo的展現,在原生應用中可使用PHLivePhotoView,在Web應用中可使用LivePhotosKit JS。
WebP最初由Google發佈於2010年,圖片格式派生自VP8視頻編碼,也同時支持有損壓縮、無損壓縮、透明度等特性。2013年低,推出了Animated WebP,還能夠支持動圖。
WebP 集合了多種圖片文件格式的特色。它像 JPEG 同樣適合壓縮照片和其餘細節豐富的圖片,像 GIF 同樣能夠顯示動態圖片,像 PNG 同樣支持透明圖像。根據 Google 的測試,WebP 無損壓縮圖片比 PNG 圖片少了 45% 的文件體積,即便這些 PNG 圖片在使用 pngcrush
和 PNGOUT
處理後,WebP 依舊能夠減小 28% 的文件體積。能夠在點擊這裏查看WebP對其它格式轉換的效果。
小是WebP的最大優勢,小意味着更少的流量,這也是各大流量入口在乎的地方。目前Google、Facebook、阿里等大廠已經在普遍使用WebP格式,國內的一些圖牀服務(七牛、又拍雲)也支持將圖片自動轉成WebP格式。
誠然WebP很是優秀,獨自完成了圖片格式"大一統"的任務。但蘋果對WebP的支持卻不多,只Safari目前還不支持WebP顯示就阻斷了不少人應用WebP的決心。
若是咱們須要在項目中顯示WebP格式圖片就不得不導入Google的libwebp
解碼庫。固然WebP的解碼任務在iOS端有些庫已經封裝好了,OC端能夠用SDWebImageWebPCoder,Swift端能夠用KingfisherWebP。如下是使用Kingfisher展現WebP圖像的事例:
// 全局配置對WebP圖片的解碼(僅針對WebP格式)
KingfisherManager.shared.defaultOptions += [
.processor(WebPProcessor.default),
.cacheSerializer(WebPSerializer.default)
]
// 本地webp圖片解碼
let localUrl = Bundle.main.url(forResource: "sunset", withExtension: "webp")
let dataProvider = LocalFileImageDataProvider.init(fileURL: localUrl!)
imageView.kf.setImage(with: dataProvider)
// 遠程webp圖片解碼。一些圖像服務器可能指望「Accept」標頭包含「image/webp」,咱們還須要加上
let modifier = AnyModifier { request in
var req = request
req.addValue("image/webp */*", forHTTPHeaderField: "Accept")
return req
}
KingfisherManager.shared.defaultOptions += [
.requestModifier(modifier),
// ... other options
]
複製代碼
pdf圖片一般是矢量的,它的導入方式有些特殊。咱們須要在Assets.xcassets
文件,建立一個New Image Set
,而後將該文件的Scales
設置爲Single Scale
,拖入1x尺寸的pdf文件便可:
使用時咱們能夠把它當作普通圖片對待:
let image = UIImage(named: "sunset")
複製代碼
在運行期間Xcode會根據屏幕的比例因子生成對應尺寸的png圖像。好比導入一張100x100的pdf圖片,在2x和3x的機型裏面會生成對應的200x200,300x300的png(能夠在Assets.car中找到)。因此pdf只不過是Xcode處理圖片的中間狀態,下載到手機的應用包裏面是沒有這張pdf的。
這種處理方式有一個好處就是,當蘋果之後發佈一款4x屏幕的手機時,使用pdf處理的圖片會自適應生成對應的4x資源,不須要再手動導入。但相比優勢,pdf做爲圖片資源的缺點更多。
首先是尺寸上,由於是自動生成對應的png,並無任何優化和壓縮,並且咱們也並不能在這中間作什麼。對比相同尺寸通過ImageOptim壓縮過的png,在大小上後者會是前者的1/2,甚至1/4。
另外pdf對陰影和漸變的處理會存在失真的狀況:
左邊是png,右邊是pdf。在一些漸變和光影的圖像部分能夠看出明顯的失真。
更多關於pdf和png的差異,能夠看這篇:Why I don't use PDFs for iOS assets: bjango.com/articles/id…
SVG是一種無損的矢量圖,是衆多矢量圖中的一種,它的特色是使用XML來描述圖片。使用XML的優勢是,任什麼時候候你均可以把它當作一個文本文件來對待,也就是說,你能夠很是方便的修改SVG圖片,你所須要的只須要一個文本編輯器。
在iOS13以前應用中直接使用SVG的場景很是少,但從iOS13開始,蘋果推出了SF Symbol,一種svg格式的矢量符號集。並且蘋果還提供了多於1500多種icon模板,咱們能夠在這裏下載查看。
咱們能夠從中選擇適合本身的icon,選中以後,從File > Export Custom Symbol Templete中導出svg格式圖片集,而後拖到Xcode的Assets.xcassets
。
SF Symbol有9種粗細的調節——從ultralight到black——每一種都至關於San Francisco
系統字體的重量(weight)。(SF Symbol中的SF是San Francisco
(舊金山)的縮寫)。這種對應使您可以在符號和相鄰文本之間實現精確的權重匹配,同時支持不一樣大小和上下文的靈活性。
固然若是這些圖標都不能知足需求,咱們還能夠自定義SF圖標,而後經過SF Symbol App進行驗證和導出。操做細節能夠看這裏:Creating Custom Symbol Images for Your App。
SF Symbol使用起來也很簡單:
let configuration = UIImage.SymbolConfiguration.init(scale: .small)
imageView.image = UIImage(systemName: "alarm", withConfiguration: configuration)
複製代碼
SF Symbol能夠一次性解決相同icon,不一樣尺寸,不一樣粗細的問題,它讓咱們處理圖片像處理字體同樣方便。能夠想象這就是應用圖標的將來。
當看到SF Symbol僅支持iOS13+,watchOS6+,我又不得不退回到現實,png也挺好的。
題外話
我在測試SF Symbol圖標時,從生成的應用包中查看圖片,會獲得這樣的結果:
代碼中的我將圖片設置爲100x100,僅有這一處地方使用。跟pdf相似咱們找不到svg源文件,這好理解,svg只是中間狀態,咱們最終使用的仍是png,但爲何會有多個小尺寸的png圖像呢?
咱們日常開發時,使用最多的就是png了,甚至多是不加考慮的所有使用png。其實這樣是很差的,咱們應該充分發揮不一樣格式圖片的優勢,從兼容性、空間佔用、展現效果三方面考量選取最佳格式。
關於圖片格式的選擇,蘋果的Human Interface Guidelines有如下說法:
通常狀況下,使用PNG圖片,由於PNG支持透明性,並且是無損的,壓縮工件不會模糊重要的細節或改變顏色。對於須要陰影、紋理和高光效果的複雜藝術品來講,這是一個不錯的選擇。使用8位的PNG圖形,不須要徹底24位的顏色。8位PNG能夠在不下降圖像質量的狀況下減少文件大小。精緻的應用圖標最好使用png。
對於照片應該使用JPEG格式,它的壓縮算法一般比無損格式產生更小的尺寸,並且很難在照片中辨別出來。應該嘗試優化JPEG文件,在大小和質量之間找到平衡。大多數JPEG文件能夠被壓縮而不會致使圖像明顯的退化。即便是少許的壓縮也能夠節省大量的磁盤空間。
使用PDF處理字形和其餘須要高分辨率縮放的平面矢量圖形。
最終能夠作如下總結。
圖片格式 | 適用範圍 | 注意事項 |
---|---|---|
png | 應用icon,界面icon,卡通風格的背景圖 | 導入項目前可使用ImageOptim進行壓縮 |
jpeg | 尺寸較大的風景圖,照片 | 不支持透明度;由於能夠調節壓縮比,能夠在大小和質量之間尋找最佳平衡。 |
webp | 支持有損、無損壓縮、透明度、動圖等特性,由於蘋果自己不支持通常只應用於服務端返回來的圖片 | 沒法在xcode預覽,不建議內置該類型圖片 |
字形,高分辨率的矢量圖 | 存在展開尺寸較大,光效失真的狀況 | |
svg(sf symbol) | 指示性icon | 僅支持iOS13及以上,系統sf符號是有版權的,使用時要注意應用範圍和蘋果要求 |
Why I don't use PDFs for iOS assets