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

简介: iOS 替换WebView网页图片为本地图片

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

UIWebViewWKWebView 都可以适用,但是它会先去加载图片,有的图加载的快再被替换的时候出现闪动,所以如果需求严格,可以选择跟方式二进行配合使用。

// 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


相关文章
|
1月前
|
JSON JavaScript 安全
iOS应用程序数据保护:如何保护iOS应用程序中的图片、资源和敏感数据
iOS应用程序数据保护:如何保护iOS应用程序中的图片、资源和敏感数据
24 1
|
7月前
|
iOS开发
iOS TextView插入表情或者图片后字体变大或变小
iOS TextView插入表情或者图片后字体变大或变小
67 1
|
6天前
|
存储 缓存 安全
基于iOS平台的高效图片缓存策略实现
【4月更文挑战第22天】 在移动应用开发中,图片资源的加载与缓存是影响用户体验的重要因素之一。尤其对于iOS平台,由于设备存储空间的限制以及用户对流畅性的高要求,设计一种合理的图片缓存策略显得尤为关键。本文将探讨在iOS环境下,如何通过使用先进的图片缓存技术,包括内存缓存、磁盘缓存以及网络请求的优化,来提高应用的性能和响应速度。我们将重点分析多级缓存机制的设计与实现,并对可能出现的问题及其解决方案进行讨论。
|
6天前
|
存储 缓存 算法
实现iOS平台的高效图片缓存策略
【4月更文挑战第22天】在移动应用开发中,图片资源的处理是影响用户体验的重要因素之一。特别是对于图像资源密集型的iOS应用,如何有效地缓存图片以减少内存占用和提升加载速度,是开发者们面临的关键挑战。本文将探讨一种针对iOS平台的图片缓存策略,该策略通过结合内存缓存与磁盘缓存的机制,并采用先进的图片解码和异步加载技术,旨在实现快速加载的同时,保持应用的内存效率。
|
19天前
|
存储 缓存 iOS开发
基于iOS的高效图片缓存策略实现
【4月更文挑战第9天】在移动应用开发中,图片资源的加载与缓存是影响用户体验的重要因素之一。特别是对于iOS平台,合理设计图片缓存策略不仅能够提升用户浏览图片时的流畅度,还能有效降低应用程序的内存压力。本文将介绍一种针对iOS环境优化的图片缓存技术,该技术通过多级缓存机制和内存管理策略,实现了图片快速加载与低内存消耗的目标。我们将从系统架构、关键技术细节以及性能评估等方面展开讨论,为开发者提供一套实用的图片缓存解决方案。
18 0
|
24天前
|
存储 缓存 iOS开发
实现iOS平台的高效图片缓存策略
【4月更文挑战第4天】在移动应用开发中,图片资源的加载与缓存是影响用户体验的关键因素之一。尤其对于iOS平台,由于设备存储和内存资源的限制,设计一个高效的图片缓存机制尤为重要。本文将深入探讨在iOS环境下,如何通过技术手段实现图片的高效加载与缓存,包括内存缓存、磁盘缓存以及网络层面的优化,旨在为用户提供流畅且稳定的图片浏览体验。
|
3月前
|
JSON JavaScript 安全
iOS 应用程序数据保护:如何保护 iOS 应用程序中的图片、资源和敏感数据
iOS 应用程序数据保护:如何保护 iOS 应用程序中的图片、资源和敏感数据
|
5月前
|
移动开发 小程序 Android开发
uniapp使用webview将页面转换成图片支持h5、app、小程序
uniapp使用webview将页面转换成图片支持h5、app、小程序
|
7月前
|
缓存 iOS开发
iOS LaunchScreen.storyboard 启动页设置图片不显示
iOS LaunchScreen.storyboard 启动页设置图片不显示
139 0
|
7月前
|
小程序 开发工具 Android开发
微信小程序 - webview 动态加载(网页) 以及加载域名的限制
微信小程序 - webview 动态加载(网页) 以及加载域名的限制
433 0