盒子
盒子
Posts List
  1. 前言
  2. 正文
  3. WebP是什么?
  4. iOS中如何使用WebP格式图片?
  5. 实现方式一 NSURLProtocol
  6. 实现方式二 通过JavaScript与OC共同完成

如何使UIWebview/WKWebView支持WebP格式图片

作者Talent•C
转载请注明出处

前言

在一般的app中占用流量最大的内容一般都是图片,以苹果公司 Retina 产品为代表的高 PPI 屏对图片的质量提出了更高的要求,如何保证在图片的精细度不降低的前提下缩小图片体积,成为了一个有价值且值得探索的事情。
但如今对于 JPEG、PNG 和 GIF 这些图片格式的优化几乎已经达到了极致, 若想改变现状开辟新局面,便要有釜底抽薪的胆量和气魄,而 Google 给了我们一个新选择:WebP。

正文

WebP是什么?

WebP(发音 weppy,来自:Google WebP),是一种支持有损压缩和无损压缩的图片文件格式,派生自图像编码格式 VP8。根据 Google 的测试,无损压缩后的 WebP 比 PNG 文件少了 45% 的文件大小,即使这些 PNG 文件经过其他压缩工具压缩之后,WebP 还是可以减少 28% 的文件大小。
在 Google 的明星产品如 Youtube、Gmail、Google Play 中都可以看到 WebP 的身影,而 Chrome 网上商店甚至已完全使用了 WebP。国外公司如 Facebook、ebay 和国内公司如腾讯、淘宝、美团等也早已尝鲜。
下面是QQ图片格式对比图

iOS中如何使用WebP格式图片?

很幸运,SDWebImage支持WebP格式图片,可以讲WebP数据–>NSData–>UIImage

There are 3 subspecs available now: Core, MapKit and WebP (this means you can install only some of the SDWebImage modules. By default, you get just Core, so if you need WebP, you need to specify it).
Podfile example:
$pod ‘SDWebImage/WebP’
摘自SDWebImage

我们需要手动下载WebP这个库,由于是从Google下载的,如果下载失败,请翻墙重试!!!
下载后的文件路径

Xcode 需要如下配置 targets->build settings ->preprocessor Macros 填写 SD_WEBP=1 如图

到此app 中支持 WebP图片基本完成,但是重点来了,由于部分app使用到了UIWebview/WKWebview 这两个控件是不支持WebP图片的,目前有两种方式可以让其支持WebP格式图片。

实现方式一 NSURLProtocol

对于NSURLProtocol的作用及使用以后找个时间再讲了~~~~可以参考Apple 开发者文档
NSURLProtocol, UIWebView 直接就可以支持,但是WKWebView是不支持的,如何让WKWebView也支持NSURLProtocol可以参考这篇文章,不过WKWebView自定义NSURLProtocol会丢失boay数据。文章结尾会附上Demo下载地址。

WKWebView 拓展支持NSURLProtocol 具体代码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
FOUNDATION_STATIC_INLINE Class ContextControllerClass() {
static Class cls;
if (!cls) {
cls = [[[WKWebView new] valueForKey:@"browsingContextController"] class];
}
return cls;
}
FOUNDATION_STATIC_INLINE SEL RegisterSchemeSelector() {
return NSSelectorFromString(@"registerSchemeForCustomProtocol:");
}
FOUNDATION_STATIC_INLINE SEL UnregisterSchemeSelector() {
return NSSelectorFromString(@"unregisterSchemeForCustomProtocol:");
}
@implementation NSURLProtocol (WebKitExt)
+ (void)wk_registerScheme:(NSString *)scheme {
Class cls = ContextControllerClass();
SEL sel = RegisterSchemeSelector();
if ([(id)cls respondsToSelector:sel]) {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
[(id)cls performSelector:sel withObject:scheme];
#pragma clang diagnostic pop
}
}
+ (void)wk_unregisterScheme:(NSString *)scheme {
Class cls = ContextControllerClass();
SEL sel = UnregisterSchemeSelector();
if ([(id)cls respondsToSelector:sel]) {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
[(id)cls performSelector:sel withObject:scheme];
#pragma clang diagnostic pop
}
}

好了,现在UIWebView与WKWebView 都已经支持自定义NSURLProtocol了;
我们创建一个类CLURLProtocol 继承自NSURLProtocol
下面这几个方法必须实现

+(BOOL)canInitWithRequest:(NSURLRequest )request;
+(NSURLRequest
)canonicalRequestForRequest:(NSURLRequest *)request;
-(void)stopLoading;
-(void)startLoading;

这里不过多废话了 直接上代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
+ (BOOL)canInitWithRequest:(NSURLRequest *)request {
/**
判断是否启用SD_WEBP 并且图片格式为webp 如果为YES 则标记请求需要自行处理并且防止无限循环 为NO则不处理
*/
BOOL useCustomUrlProtocol = NO;
NSString *urlString = request.URL.absoluteString;
if (!SD_WEBP || ([urlString.pathExtension compare:@"webp"] != NSOrderedSame)) {
useCustomUrlProtocol = NO;
}else {
//防止无限循环
if ([NSURLProtocol propertyForKey:CLProtocolKey inRequest:request] == nil) {
useCustomUrlProtocol = YES;
}else {
useCustomUrlProtocol = NO;
}
}
return useCustomUrlProtocol;
}
+ (NSURLRequest *)canonicalRequestForRequest:(NSURLRequest *)request {
return request;
}
- (void)stopLoading{
//将截获的请求使用NSURLConnection | NSURLSession 获取数据 这里使用的是NSURLConnection
}

我们在创建一个WebV继承自UIViewController 用来展示webView

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
- (void)viewDidLoad {
[super viewDidLoad];
self.automaticallyAdjustsScrollViewInsets = NO;
if ([self.webView isKindOfClass:[WKWebView class]]) {
//WKWebView 注册自定义 NSURLProtocol
[NSURLProtocol registerClass:NSClassFromString(@"CLURLProtocol")];
[NSURLProtocol wk_registerScheme:@"http"];
[NSURLProtocol wk_registerScheme:@"https"];
//发起请求
WKWebView *web = (WKWebView *)self.webView;
[web loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"http://onzbjws3p.bkt.clouddn.com/testForwebpSrc/testWebpForHtml.html"]]];
}else if ([self.webView isKindOfClass:[UIWebView class]])
{
//UIWebView 注册自定义 NSURLProtocol
[NSURLProtocol registerClass:NSClassFromString(@"CLURLProtocol")];
//发起请求
UIWebView *web = (UIWebView *)self.webView;
[web loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"http://onzbjws3p.bkt.clouddn.com/testForwebpSrc/testWebpForHtml.html"]]];
}
}
//因为NSURLProtocol 一旦被注册将会使整个app的request请求都会被拦截 我们这里只在进入WebVC时向系统注册,退出WebVC时取消注册 具体时机请自行决定
- (void)dealloc
{
NSLog(@"WebVC -- dealloc");
if ([self.webView isKindOfClass:[WKWebView class]]) {
[NSURLProtocol unregisterClass:NSClassFromString(@"CLURLProtocol")];
[NSURLProtocol wk_unregisterScheme:@"http"];
[NSURLProtocol wk_unregisterScheme:@"https"];
}else if ([self.webView isKindOfClass:[UIWebView class]])
{
[NSURLProtocol unregisterClass:NSClassFromString(@"CLURLProtocol")];
}
}

到此为止UIWebView/WKWebView 均已支持加载webp格式图片
效果如图
WKWebView 展示效果

UIWebView 展示效果

UIImageView 展示效果

优点:
适合所有网页,可以不用修改网页内部html内容。

缺点:
NSURLProtocol 拦截App 的所有请求, 使用时需要根据个人项目情况而定, WKWebView 在post请求时会丢失boay, 目前解决方式为在WKWebView的 开始加载的 代理方法判断是否为post,为post解除注册自定义的NSURLProtocol,为GET请求时注册自定义NSURLProtocol。

实现方式二 通过JavaScriptOC共同完成

实现思路:
1、向网页内注入JS
2、在App 本地开启线程下载图片,下载完成后,将图片转码由 webP—> png—>Base64。
3、将 Base64及原图片下载地址一一对应调用JS准备好的方法进行替换。
4、将下载后的图片进行缓存,并进行管理。

注意注意:
A、图片未真正加载完毕时,网页中图片为了体验好可以添加默认占位图片。
B、图片显示成功前应该保持网页布局不调整,需要由JS预先设置好布局。
C、图片在本地的缓存需要管理。

获取网页img标签的js代码

1
2
3
4
5
6
7
8
9
10
11
12
function talentcGetAllImageSrc ()
{
var imagesList = document.images;
var srcList = [];
var patt1 = new RegExp("\.webp$");
for(var i = 0; i < imagesList.length; i++) {
if(patt1.test(imagesList[i].src)) {
srcList.push(imagesList[i].src);
}
}
return JSON.stringify(srcList);
};

替换网页img标签的js代码

1
2
3
4
5
6
7
function talentcReplaceWebPImg (src, localPath)
{
var elementList = document.querySelectorAll('img[src="'+src+'"]');
for(var element in elementList) {
elementList[element].src = localPath;
}
}

优点:
对于UIWebviewWKWebView是通用不需要特殊处理,也不会拦截App中的请求。

缺点:
对于展示的第三方网页需要根据网页做一些适配, 例如部分网页展示图片使用的divbackground-image

UIWebView 展示效果

WKWebView 展示效果

全文终 本文demo 下载

支持一下
扫一扫,支持Talent•C