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


相关文章
|
6月前
|
JSON JavaScript 安全
iOS应用程序数据保护:如何保护iOS应用程序中的图片、资源和敏感数据
iOS应用程序数据保护:如何保护iOS应用程序中的图片、资源和敏感数据
59 1
|
iOS开发
iOS TextView插入表情或者图片后字体变大或变小
iOS TextView插入表情或者图片后字体变大或变小
113 1
|
6月前
|
存储 缓存 安全
基于iOS平台的高效图片缓存策略实现
【4月更文挑战第22天】 在移动应用开发中,图片资源的加载与缓存是影响用户体验的重要因素之一。尤其对于iOS平台,由于设备存储空间的限制以及用户对流畅性的高要求,设计一种合理的图片缓存策略显得尤为关键。本文将探讨在iOS环境下,如何通过使用先进的图片缓存技术,包括内存缓存、磁盘缓存以及网络请求的优化,来提高应用的性能和响应速度。我们将重点分析多级缓存机制的设计与实现,并对可能出现的问题及其解决方案进行讨论。
|
6月前
|
存储 缓存 算法
实现iOS平台的高效图片缓存策略
【4月更文挑战第22天】在移动应用开发中,图片资源的处理是影响用户体验的重要因素之一。特别是对于图像资源密集型的iOS应用,如何有效地缓存图片以减少内存占用和提升加载速度,是开发者们面临的关键挑战。本文将探讨一种针对iOS平台的图片缓存策略,该策略通过结合内存缓存与磁盘缓存的机制,并采用先进的图片解码和异步加载技术,旨在实现快速加载的同时,保持应用的内存效率。
|
6月前
|
存储 缓存 编解码
实现iOS平台的高效图片缓存策略
【4月更文挑战第23天】在移动应用开发领域,尤其是图像处理密集型的iOS应用中,高效的图片缓存策略对于提升用户体验和节省系统资源至关重要。本文将探讨一种针对iOS平台设计的图片缓存方案,该方案通过结合内存缓存与磁盘缓存的多层次结构,旨在优化图片加载性能并降低内存占用。我们将深入分析其设计理念、核心组件以及在实际场景中的应用效果,同时对比其他常见缓存技术的优势与局限。
|
6月前
|
存储 Web App开发 Android开发
iOS不支持WebP格式图片解决方案和iPhone 7及其后硬件拍照的HEIC格式图片
iOS不支持WebP格式图片解决方案和iPhone 7及其后硬件拍照的HEIC格式图片
642 1
iOS不支持WebP格式图片解决方案和iPhone 7及其后硬件拍照的HEIC格式图片
|
5月前
|
Android开发 UED Kotlin
kotlin webview 加载网页失败后如何再次重试
在Kotlin中,当使用WebView加载网页失败时,可通过设置WebViewClient并覆盖`onReceivedError`方法来捕获失败事件。在该回调中,可以显示错误信息或尝试使用`reload()`重试加载。以下是一个简要示例展示如何处理加载失败
|
5月前
|
安全 JavaScript 前端开发
kotlin开发安卓app,JetPack Compose框架,给webview新增一个按钮,点击刷新网页
在Kotlin中开发Android应用,使用Jetpack Compose框架时,可以通过添加一个按钮到TopAppBar来实现WebView页面的刷新功能。按钮位于右上角,点击后调用`webViewState?.reload()`来刷新网页内容。以下是代码摘要:
|
5月前
|
JavaScript 前端开发 Android开发
kotlin安卓在Jetpack Compose 框架下使用webview , 网页中的JavaScript代码如何与native交互
在Jetpack Compose中使用Kotlin创建Webview组件,设置JavaScript交互:`@Composable`函数`ComposableWebView`加载网页并启用JavaScript。通过`addJavascriptInterface`添加`WebAppInterface`类,允许JavaScript调用Android方法如播放音频。当页面加载完成时,执行`onWebViewReady`回调。
|
5月前
|
Web App开发 移动开发 前端开发
52. 【Android教程】网页视图:WebView
52. 【Android教程】网页视图:WebView
90 1