方式一:图片直接替换,这种是网页加载完成,再去替换成本地图片
UIWebView
WKWebView
都可以适用,但是它会先去加载图片,有的图加载的快再被替换的时候出现闪动,所以如果需求严格,可以选择跟方式二进行配合使用。
// MARK: UIWebViewDelegate
func webViewDidFinishLoad(_ webView: UIWebView) {
// 本地图片二进制
let imegData:Data = UIImagePNGRepresentation(CW_PI_KTT_420_220_BG!)!
// 转换
let imageSource:String = String.init(format: "data:image/jpg;base64,%@", imegData.base64EncodedString(options: .endLineWithLineFeed))
// 获取所有的IMG标签或者IMAGE标签 进行替换
webView.stringByEvaluatingJavaScript(from: "var imgs = document.getElementsByTagName('img'); for (var i = 0; i < imgs.length; i++) { imgs[i].src = '\(imageSource)'; }")
}
如果需要拦截网页里面所有图片信息,需要重写 URLProtocol
,在 NSURLSessionDataDelegate
中拦截文件Head判断 Content-Type
是否为图片格式 "image/jpeg","image/gif","image/png"...
方式二:重写 URLProtocol
拦截所有图片,这种方式可以在网页加载之前就拦截下来并替换掉图片
-
这种方式也是需要用到
方式一
中的图片替换的,方式二
只是单纯的禁用了手机APP浏览器的禁止加载指定格式的图片,图片不加载了,那还得替换成我们的占位图片,要不然就会出现一个图片加载失败的img
标签在浏览器中,所以我们还是用方式一
中的图片替换配合。 -
在允许加载图片开关处使用即可
// 图片加载开关
func CW_LOADING_PICTURE_SWITCH(_ isOn:Bool) {
if isOn {
URLProtocol.unregisterClass(CWURLProtocol.classForCoder())
CWURLProtocol.wk_unregister()
}else{
URLProtocol.registerClass(CWURLProtocol.classForCoder())
CWURLProtocol.wk_register()
}
}
CWURLProtocol.swift
扩展文件,可以直接使用,也可以根据自己情况转成 OC 使用
//
// CWURLProtocol.swift
// XFKD
//
// Created by 邓泽淼 on 2018/7/11.
// Copyright © 2018年 邓泽淼. All rights reserved.
//
import UIKit
extension CWURLProtocol {
/// 注册
class func wk_register() {
CWURLProtocol.wk_registerScheme("http")
CWURLProtocol.wk_registerScheme("https")
}
/// 注销
class func wk_unregister() {
CWURLProtocol.wk_unregisterScheme("http")
CWURLProtocol.wk_unregisterScheme("https")
}
}
class CWURLProtocol: URLProtocol, URLSessionDataDelegate {
private var session:URLSession!
/// 是否拦截处理指定的请求,返回YES表示要拦截处理,返回NO表示不拦截处理
override class func canInit(with request: URLRequest) -> Bool {
// 防止无限循环,因为一个请求在被拦截处理过程中,也会发起一个请求,这样又会走到这里,如果不进行处理,就会造成无限循环
if URLProtocol.property(forKey: "CWURLProtocol", in: request) != nil { return false }
let url = request.url?.absoluteString ?? ""
// 如果为 http || https 则拦截处理
if url.hasPrefix("http") { return true }
return false
}
/// 如果需要对请求进行重定向,添加指定头部等操作,可以在该方法中进行
override class func canonicalRequest(for request: URLRequest) -> URLRequest {
return request
}
/// 开始加载
override func startLoading() {
// 表示该请求已经被处理,防止无限循环
URLProtocol.setProperty(true, forKey: "CWURLProtocol", in: request as! NSMutableURLRequest)
session = URLSession(configuration: URLSessionConfiguration.default, delegate: self, delegateQueue: OperationQueue.main)
session.dataTask(with: request).resume()
}
/// 停止并取消请求
override func stopLoading() {
session.invalidateAndCancel()
session = nil
}
// MARK: NSURLSessionDataDelegate
/// 请求结束或者是失败的时候调用
func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) {
if error != nil { client?.urlProtocol(self, didFailWithError: error!)
}else{ client?.urlProtocolDidFinishLoading(self) }
}
/// 接收到服务器的响应 它默认会取消该请求
func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive response: URLResponse, completionHandler: @escaping (URLSession.ResponseDisposition) -> Void) {
/*
NSURLSessionResponseCancel = 0, 取消 默认
NSURLSessionResponseAllow = 1, 接收
NSURLSessionResponseBecomeDownload = 2,变成下载任务
NSURLSessionResponseBecomeStream 变成流
*/
let pathExtension:String = (response.url?.absoluteString as? NSString)?.pathExtension ?? ""
if ["jpeg","gif","png"].contains(pathExtension) {
completionHandler(.cancel)
}else{
client?.urlProtocol(self, didReceive: response, cacheStoragePolicy: URLCache.StoragePolicy.notAllowed)
completionHandler(.allow)
}
}
func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, willCacheResponse proposedResponse: CachedURLResponse, completionHandler: @escaping (CachedURLResponse?) -> Void) {
completionHandler(proposedResponse)
}
/// 接收到服务器返回的数据 调用多次
func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive data: Data) {
client?.urlProtocol(self, didLoad: data)
}
}
-
CWURLProtocol
注册 跟 注销 都需要使用到另外一个扩展文件NSURLProtocol+WKWebView
-
NSURLProtocol+WKWebView.h
//
// NSURLProtocol+WKWebView.h
// XFKD
//
// Created by dengzemiao on 2019/1/18.
// Copyright © 2019年 邓泽淼. All rights reserved.
//
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface NSURLProtocol (WKWebView)
+ (void)wk_registerScheme:(NSString *)scheme;
+ (void)wk_unregisterScheme:(NSString *)scheme;
@end
NS_ASSUME_NONNULL_END
NSURLProtocol+WKWebView.m
//
// NSURLProtocol+WKWebView.m
// XFKD
//
// Created by dengzemiao on 2019/1/18.
// Copyright © 2019年 邓泽淼. All rights reserved.
//
#import <Foundation/Foundation.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 (WKWebView)
+ (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