iOS 全面支持 webp格式圖片 Custom URLProtocol
webp格式圖片
webp格式圖片是google推出的许昨,相比jpg png有著巨大的優(yōu)勢,同樣質(zhì)量的圖片webp格式的圖片占用空間更小赘来,在像電商這樣圖片比較多的App中,使用webp格式圖片會很有優(yōu)勢。
webp在iOS設(shè)備上
當前的iOS不支持webp棋电,不知道以后會不會支持振劳,所以從網(wǎng)絡(luò)上拿到一個webp格式的圖片后椎组,并不能直接顯示出來,需要把data數(shù)據(jù)轉(zhuǎn)化為jpg或者png來顯示历恐。
支持webp的方案
使用SDWebImage中帶的WebP
在podfile中加入
pod 'SDWebImage/WebP'
可以在SDWebImage中加入UIImage的WebP類別寸癌,同時會引入libwebp。在使用SD加載圖片時弱贼,會判定圖片的格式蒸苇,如果為webp,調(diào)用Google的libwebp庫進行解析吮旅。
調(diào)用過程如下:
sd_setImageWithURL ->
URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error ->
sd_imageWithData->
sd_imageWithWebPData
處理成功后會返回一個UIImage溪烤。
在WebView中使用Webp格式圖片
如果有一些web頁中使用了webp格式的圖片,僅用上述方法是不行的,我推薦的做法是寫一個自定義的URLSession protocol, 繼承自NSURLProtocol, 然后注冊檬嘀。自定義的協(xié)議文件為DMCustomURLSessionProtocol這個類槽驶,整個工程的地址如下:Demo地址.
關(guān)鍵代碼如下:
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(nullable NSError *)error{
if (error == nil) {
if ([task.currentRequest.URL.absoluteString hasSuffix:@"webp"]) {
NSLog(@"webp will changed:%@",task.currentRequest.URL);
UIImage *imgData = [UIImage sd_imageWithData:self.imageData];
NSData *transData = UIImageJPEGRepresentation(imgData, 0.8f);
self.beginAppendData = NO;
self.imageData = nil;
[self.client URLProtocol:self didLoadData:transData];
}
[self.client URLProtocolDidFinishLoading:self];
} else if ( [[error domain] isEqual:NSURLErrorDomain] && ([error code] == NSURLErrorCancelled) ) {
[self.client URLProtocol:self didFailWithError:error];
}else{
[[self client] URLProtocol:self didFailWithError:error];
}
}
在一個task完成的時候,根據(jù)task中的url是否含有webp后綴鸳兽,做處理掂铐,如果有webp后綴,用SD轉(zhuǎn)化圖片贸铜,轉(zhuǎn)化完成后堡纬, 需要把beginAppendData置為NO,imageData置為nil蒿秦,self.client再load一次data烤镐。
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data{
if ([dataTask.currentRequest.URL.absoluteString hasSuffix:@"webp"]) {
self.beginAppendData = YES;
[self.imageData appendData:data];
}
if (!_beginAppendData) {
[self.client URLProtocol:self didLoadData:data];
}
}
在didReceiveData里面要做一些處理,這里面是每次receive一塊新的data棍鳖,當一個請求的地址里面含有webp時炮叶,把beginAppendData置為yes,同時imgeData開始append data渡处,而這時self.client不load data镜悉,每次有新data來,imageData就append上医瘫,止到這一個請求完成侣肄, 在完成的里面 轉(zhuǎn)化webp后 load data。(請求一個圖片后醇份,可能在did receive data中收到多次才能收全稼锅。)
處理重定向
自定義URL Protocol時,一定要實現(xiàn)重定向的代理方法僚纷,不然矩距,在webview里面會出問題。
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task willPerformHTTPRedirection:(NSHTTPURLResponse *)response newRequest:(NSURLRequest *)newRequest completionHandler:(void (^)(NSURLRequest *))completionHandler
{
NSMutableURLRequest * redirectRequest;
redirectRequest = [newRequest mutableCopy];
[[self class] removePropertyForKey:URLProtocolHandledKey inRequest:redirectRequest];
[[self client] URLProtocol:self wasRedirectedToRequest:redirectRequest redirectResponse:response];
[self.session invalidateAndCancel];
[[self client] URLProtocol:self didFailWithError:[NSError errorWithDomain:NSCocoaErrorDomain code:NSUserCancelledError userInfo:nil]];
}
向URL加載系統(tǒng)注冊子類
最后在appdelegate didFinishLaunchingWithOptions中注冊上
[NSURLProtocol registerClass:[DMCustomURLSessionProtocol class]];
當請求被加載時怖竭,系統(tǒng)會向每一個注冊過protocol詢問能否控制這個請求锥债,第一個通過+ canInitWithRequest:返回為YES的protocol會控制這個請求。URLprotocol會被以注冊順序的反序訪問.所以如果只處理webp的圖片痊臭,在canInitWithRequest需要限定好哮肚,可以這樣寫:
+ (BOOL)canInitWithRequest:(NSURLRequest *)request
{
//只處理http和https請求
NSString *scheme = [[request URL] scheme];
if ( (([scheme caseInsensitiveCompare:@"http"] == NSOrderedSame ||
[scheme caseInsensitiveCompare:@"https"] == NSOrderedSame)) && ([scheme hasSuffix:@"webp"]))
{
//看看是否已經(jīng)處理過了,防止無限循環(huán)
if ([NSURLProtocol propertyForKey:URLProtocolHandledKey inRequest:request]) {
return NO;
}
return YES;
}
return NO;
}
動態(tài)webp
SDWebImage 默認是不支持動態(tài)webp 格式圖片的广匙,4.0以后會支持允趟,目前還是 beta 版,可以 pod install 我的這個https://github.com/dulingkang/sd_webp,
pod 'sd_webp'
是用最新的3.8.2加入動態(tài) webp 支持的艇潭。
再發(fā)一下地址
- 工程地址, DMCustomURLSessionProtocol這個類拼窥;