iOS 替换WebView网页图片为本地图片

方式一:图片直接替换,这种是网页加载完成,再去替换成本地图片

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
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

卡尔特斯

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值