iOS 开发技巧大全

iOS全面支持webp格式图片 Custom URLProto

2016-06-23  本文已影响1792人  ShawnDu

iOS 全面支持 webp格式图片 Custom URLProtocol

本文博客地址:http://devthinking.com/ios-%E5%85%A8%E9%9D%A2%E6%94%AF%E6%8C%81-webp%E6%A0%BC%E5%BC%8F%E5%9B%BE%E7%89%87-custom-urlprotocol/

webp格式图片

webp格式图片是google推出的,相比jpg png有着巨大的优势,同样质量的图片webp格式的图片占用空间更小,在像电商这样图片比较多的App中,使用webp格式图片会很有优势。

webp在iOS设备上

当前的iOS不支持webp,不知道以后会不会支持,所以从网络上拿到一个webp格式的图片后,并不能直接显示出来,需要把data数据转化为jpg或者png来显示。

支持webp的方案

使用SDWebImage中带的WebP

在podfile中加入

pod 'SDWebImage/WebP'

可以在SDWebImage中加入UIImage的WebP类别,同时会引入libwebp。在使用SD加载图片时,会判定图片的格式,如果为webp,调用Google的libwebp库进行解析。
调用过程如下:

sd_setImageWithURL -> 
URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error ->
sd_imageWithData->
sd_imageWithWebPData
处理成功后会返回一个UIImage。

在WebView中使用Webp格式图片

如果有一些web页中使用了webp格式的图片,仅用上述方法是不行的,我推荐的做法是写一个自定义的URLSession protocol, 继承自NSURLProtocol, 然后注册。自定义的协议文件为DMCustomURLSessionProtocol这个类,整个工程的地址如下:Demo地址.
关键代码如下:

- (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完成的时候,根据task中的url是否含有webp后缀,做处理,如果有webp后缀,用SD转化图片,转化完成后, 需要把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上,止到这一个请求完成, 在完成的里面 转化webp后 load data。(请求一个图片后,可能在did receive data中收到多次才能收全。)

处理重定向

自定义URL Protocol时,一定要实现重定向的代理方法,不然,在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加载系统注册子类

最后在appdelegate didFinishLaunchingWithOptions中注册上

 [NSURLProtocol registerClass:[DMCustomURLSessionProtocol class]];

当请求被加载时,系统会向每一个注册过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"]))
    {
        //看看是否已经处理过了,防止无限循环
        if ([NSURLProtocol propertyForKey:URLProtocolHandledKey inRequest:request]) {
            return NO;
        }
        return YES;
    }
    return NO;
}

动态webp

SDWebImage 默认是不支持动态webp 格式图片的,4.0以后会支持,目前还是 beta 版,可以 pod install 我的这个https://github.com/dulingkang/sd_webp,

pod 'sd_webp'

是用最新的3.8.2加入动态 webp 支持的。

再发一下地址

微信公众号

开发者思维 devthinking

QQ交流群:295976280

iOS交流群(一)群二维码.png
上一篇下一篇

猜你喜欢

热点阅读