table中的展示图片_在iOS中使用WKWebView如何支持展示webp格式图片(包括本地html)?...

3e3bbea0351107ef9dedd77c7721494e.png

频繁有客人反馈我们图片在某些地区如:意大利 反馈我们APP图片展示太慢,印象客人下单体验,于是我们开始着手分析。

  1. 因为我们是混合开发项目,首选确定了iOS不支持,安卓系统4.x以上天然支持不需要处理。
  2. 目前,后端给前段传递了图片的原始链接,前段根据不同页面展示需要,动态调整参数。 例如:
  • 后端提供的参数: http://imgs.intramirror.com/upload/1f53aa5f-c9e0-4c2f-81be-4fe13ec5e3f
  • 前段在商详页展示的参数: http://imgs.intramirror.com/upload/1f53aa5f-c9e0-4c2f-81be-4fe13ec5e3f?x-oss-process=image/resize,m_pad,w_750,h_750/quality,Q_100/auto-orient,0/format,jpg

其中粗体部分的两个参数,可以极大的影响图片数据包大小:

  • 在quality不变的情况下,只调整format,从jpg改为webp大概能达到数据包减1/3~1/2的效果,视觉质量没有明显变化
  • 如果在format不变的情况下,调整quality从100降低到90,大概能达到数据包减1/3~1/2的效果,视觉质量没有明显变化
  • 两者同时调整,大概能达到数据包减2/3左右的效果,视觉质量没有明显变化

一番查阅资料

参考链接:参考1 参考2

参考了一些deme发现只能拦截http式的webView,对应加载本地html的方式就无法加载处理,笔者在两者基础上修改实现完成对本地html的支持

以下代码开源在github了 https://github.com/niunaruto/iOSWKWebViewWebp

其实现展示webp格式的原理是:

[NSURLProtocol registerClass:[HybridNSURLProtocol class]];

对APP发起的所有请求进行拦截,并进行重定向

+ (BOOL)canInitWithRequest:(NSURLRequest *)request
{
    NSLog(@"request.URL.absoluteString = %@",request.URL.absoluteString);
    NSString *scheme = [[request URL] scheme];
    if ( ([scheme caseInsensitiveCompare:@"http"]  == NSOrderedSame ||
          [scheme caseInsensitiveCompare:@"https"] == NSOrderedSame ))
    {
        //看看是否已经处理过了,防止无限循环
        if ([NSURLProtocol propertyForKey:KHybridNSURLProtocolHKey inRequest:request])
            return NO;
        return YES;
    }
    return NO;
}

加载完成后

/**
 * 加载完毕
 */
- (void)connectionDidFinishLoading:(NSURLConnection *)connection{
    if ([connection.currentRequest.URL.absoluteString hasSuffix:@"webp"]) {
        NSData *imageData = self.recData;
        UIImage *image = [UIImage sd_imageWithWebPData:self.recData];
        imageData = UIImagePNGRepresentation(image);
        if (!imageData) {
            imageData = UIImageJPEGRepresentation(image, 1);
        }
        [self.client URLProtocol:self didLoadData:imageData];
        [self.client URLProtocolDidFinishLoading:self];
    }
}

以下是具体代码:

NSURLProtocol+WKWebVIew.h 文件

#import <Foundation/Foundation.h>

@interface NSURLProtocol (WKWebVIew)

+ (void)wk_registerScheme:(NSString*)scheme;

+ (void)wk_unregisterScheme:(NSString*)scheme;


@end

NSURLProtocol+WKWebVIew.m 文件

#import "NSURLProtocol+WKWebVIew.h"
#import <WebKit/WebKit.h>
//FOUNDATION_STATIC_INLINE 属于属于runtime范畴,你的.m文件需要频繁调用一个函数,可以用static inline来声明。从SDWebImage从get到的。
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 (WebKitSupport)

+ (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
    }
}

@end

HybridNSURLProtocol.h

#import <Foundation/Foundation.h>

@interface HybridNSURLProtocol : NSURLProtocol

@end

HybridNSURLProtocol.m

import "HybridNSURLProtocol.h"
#import <UIKit/UIKit.h>
#import <UIImage+WebP.h>
static NSString*const sourUrl  = @"https://m.baidu.com/static/index/plus/plus_logo.png";
static NSString*const sourIconUrl  = @"http://m.baidu.com/static/search/baiduapp_icon.png";
static NSString*const localUrl = @"http://mecrm.qa.medlinker.net/public/image?id=57026794&certType=workCertPicUrl&time=1484625241";

static NSString* const KHybridNSURLProtocolHKey = @"KHybridNSURLProtocol";
@interface HybridNSURLProtocol ()<NSURLSessionDelegate>
@property (nonnull,strong) NSURLSessionDataTask *task;

@property (strong, nonatomic) NSURLConnection *connection;
@property (strong, nonatomic) NSMutableData *recData;
@end


@implementation HybridNSURLProtocol
- (void)dealloc{
    self.recData = nil;
}

+ (BOOL)canInitWithRequest:(NSURLRequest *)request
{
    NSLog(@"request.URL.absoluteString = %@",request.URL.absoluteString);
    NSString *scheme = [[request URL] scheme];
    if ( ([scheme caseInsensitiveCompare:@"http"]  == NSOrderedSame ||
          [scheme caseInsensitiveCompare:@"https"] == NSOrderedSame ))
    {
        //看看是否已经处理过了,防止无限循环
        if ([NSURLProtocol propertyForKey:KHybridNSURLProtocolHKey inRequest:request])
            return NO;
        return YES;
    }
    return NO;
}

+ (NSURLRequest *)canonicalRequestForRequest:(NSURLRequest *)request
{
    NSMutableURLRequest *mutableReqeust = [request mutableCopy];
    
    //request截取重定向
    if ([request.URL.absoluteString isEqualToString:sourUrl])
    {
        NSURL* url1 = [NSURL URLWithString:localUrl];
        mutableReqeust = [NSMutableURLRequest requestWithURL:url1];
    }
    
    return mutableReqeust;
}

+ (BOOL)requestIsCacheEquivalent:(NSURLRequest *)a toRequest:(NSURLRequest *)b
{
    return [super requestIsCacheEquivalent:a toRequest:b];
}

- (void)startLoading
{
    NSMutableURLRequest *mutableReqeust = [[self request] mutableCopy];
    //给我们处理过的请求设置一个标识符, 防止无限循环,
    [NSURLProtocol setProperty:@YES forKey:KHybridNSURLProtocolHKey inRequest:mutableReqeust];
    //这里最好加上缓存判断,加载本地离线文件, 这个直接简单的例子。
    if ([mutableReqeust.URL.absoluteString hasSuffix:@"webp"])
    {
        
        NSMutableURLRequest *newRequest = [self cloneRequest:self.request];
        NSString *urlString = newRequest.URL.absoluteString;
        NSLog(@"######截获WebP url:%@",urlString);
        [NSURLProtocol setProperty:@YES forKey:KHybridNSURLProtocolHKey inRequest:newRequest];
        [self sendRequest:newRequest];
    }
    else
    {
        NSURLSession *session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration] delegate:self delegateQueue:nil];
        self.task = [session dataTaskWithRequest:self.request];
        [self.task resume];
    }
}
- (void)stopLoading
{
    if (self.task != nil)
    {
        [self.task  cancel];
    }
    if (self.connection) {
           [self.connection cancel];
    }
   self.connection = nil;
}


- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveResponse:(NSURLResponse *)response completionHandler:(void (^)(NSURLSessionResponseDisposition))completionHandler {
    [[self client] URLProtocol:self didReceiveResponse:response cacheStoragePolicy:NSURLCacheStorageAllowed];
    
    completionHandler(NSURLSessionResponseAllow);
}

- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data {
    [[self client] URLProtocol:self didLoadData:data];
}

- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(nullable NSError *)error {
    [self.client URLProtocolDidFinishLoading:self];
}


#pragma mark - NSURLConnectionDataDelegate
/**
 * 收到服务器响应
 */
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response{
    NSURLResponse *returnResponse = response;
    [self.client URLProtocol:self didReceiveResponse:returnResponse cacheStoragePolicy:NSURLCacheStorageAllowed];
}
/**
 * 接收数据
 */
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data{
    if (!self.recData) {
        self.recData = [NSMutableData new];
    }
    if (data) {
        [self.recData appendData:data];
    }
}
/**
 * 重定向
 */
- (nullable NSURLRequest *)connection:(NSURLConnection *)connection willSendRequest:(NSURLRequest *)request redirectResponse:(nullable NSURLResponse *)response{
    if (response) {
        [self.client URLProtocol:self wasRedirectedToRequest:request redirectResponse:response];
    }
    return request;
}
/**
 * 加载完毕
 */
- (void)connectionDidFinishLoading:(NSURLConnection *)connection{
    if ([connection.currentRequest.URL.absoluteString hasSuffix:@"webp"]) {
        NSData *imageData = self.recData;
        UIImage *image = [UIImage sd_imageWithWebPData:self.recData];
        imageData = UIImagePNGRepresentation(image);
        if (!imageData) {
            imageData = UIImageJPEGRepresentation(image, 1);
        }
        [self.client URLProtocol:self didLoadData:imageData];
        [self.client URLProtocolDidFinishLoading:self];
    }
}
/**
 * 加载失败
 */
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error{
    [self.client URLProtocol:self didFailWithError:error];
}




#pragma mark - 网络请求
- (void)sendRequest:(NSURLRequest *)request{
    self.connection = [[NSURLConnection alloc] initWithRequest:request delegate:self];
}

//复制Request对象
- (NSMutableURLRequest *)cloneRequest:(NSURLRequest *)request{
    NSMutableURLRequest *newRequest = [NSMutableURLRequest requestWithURL:request.URL cachePolicy:request.cachePolicy timeoutInterval:request.timeoutInterval];
    
    newRequest.allHTTPHeaderFields = request.allHTTPHeaderFields;
    [newRequest setValue:@"image/webp,image/*;q=0.8" forHTTPHeaderField:@"Accept"];
    if (request.HTTPMethod) {
        newRequest.HTTPMethod = request.HTTPMethod;
    }
    if (request.HTTPBodyStream) {
        newRequest.HTTPBodyStream = request.HTTPBodyStream;
    }
    if (request.HTTPBody) {
        newRequest.HTTPBody = request.HTTPBody;
    }
    newRequest.HTTPShouldUsePipelining = request.HTTPShouldUsePipelining;
    newRequest.mainDocumentURL = request.mainDocumentURL;
    newRequest.networkServiceType = request.networkServiceType;
    return newRequest;
    
}

@end

以上

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值