当前位置: 首页 > 编程日记 > 正文

WKWebView Safari调试、JS互调、加载进度条、JS中alert、confirm、prompt

主要内容

  • Safari调试
  • swift/OC与JS互调
  • 增加加载进度条
  • 支持JS中alert、confirm、prompt

Safari调试

设置 —> safari --> 高级,开启JavaScript、网页检查器
image.png
打开Safari浏览器,选择调试的网页,同样在js里面可以断点调试:
image.png

image.png

swift/OC与JS互调

这里只介绍Swift,OC可以照着swift翻译即可

  • swift调用JS,并传参数
    先定义一个JS函数,如:
    function add(params) {let msg  = JSON.stringify(params);showMsg("调用了add函数")}

swift调用JS实际上就是调用WKWebView的evaluateJavaScript方法

    /* @abstract Evaluates the given JavaScript string.@param javaScriptString The JavaScript string to evaluate.@param completionHandler A block to invoke when script evaluation completes or fails.@discussion The completionHandler is passed the result of the script evaluation or an error.Calling this method is equivalent to calling `evaluateJavaScript:inFrame:inContentWorld:completionHandler:` with:- A `frame` value of `nil` to represent the main frame- A `contentWorld` value of `WKContentWorld.pageWorld`*/open func evaluateJavaScript(_ javaScriptString: String, completionHandler: ((Any?, Error?) -> Void)? = nil)

参数javaScriptString就是JS的函数名+参数,如:
调用add的JS函数,javaScriptString为"add({\n “name” : “jack”\n})"
可以简单封装下调用方法:

    /// 调用JS函数/// - Parameters:///   - jsFunName: JS函数名///   - params: 传给JS函数的参数/// - Returns:func callJSFunction(jsFunName: String, params: Any?) -> Void {var js = String(format: "%@()", jsFunName)if let valueStr = params as? String {js = String(format: "%@('%@')", jsFunName, valueStr)} else if let valueNum = params as? NSNumber {js = String(format: "%@(%@)", jsFunName, valueNum)} else if let valueBool = params as? Bool {if valueBool {js = jsFunName + "(true)"} else {js = jsFunName + "(false)"}} else if let valueDiction = params {if let temValue =  converObjToJson(obj: valueDiction) {js = String(format: "%@(%@)", jsFunName, temValue)} else {js = String(format: "%@()", jsFunName)}} else {js = String(format: "%@()", jsFunName)}self.webView?.evaluateJavaScript(js) { (item, error) in}}
  • JS调用swift,并传参数
    在JS端调用window.webkit.messageHandlers.自定义一个名称.postMessage(params),如:
    var params = {“name”: “JSName”};
    window.webkit.messageHandlers.minus.postMessage(params);
    这个自定义的名称需要提前设置到WKWebView上,设置方法:
    /** @abstract Adds a script message handler to the main world used by page content itself.@param scriptMessageHandler The script message handler to add.@param name The name of the message handler.@discussion Calling this method is equivalent to calling addScriptMessageHandler:contentWorld:name:with [WKContentWorld pageWorld] as the contentWorld argument.*/open func add(_ scriptMessageHandler: WKScriptMessageHandler, name: String)

如:

        let userContent = WKUserContentController()// 配置JS可以调用的名称let jsCallFun: [String] = ["minus"]for value in jsCallFun {userContent.add(self, name:  value)}let config = WKWebViewConfiguration()config.preferences = preferencesconfig.userContentController = userContentconfig.suppressesIncrementalRendering = true

配置完后,当JS调用window.webkit.messageHandlers.minus.postMessage(params);时会进入到WSWebView的WKScriptMessageHandler代理方法userContentController
如:

    // MARK: - WKScriptMessageHandlerfunc userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {let name = message.name // 注册过的JS可调用的名称if name.elementsEqual("minus") {if let params = message.body as? NSDictionary { // JS传递的参数if let name = params.value(forKey: "name") as? String {minus(name: name)}}}}func minus(name: String) -> Void {showAlert(title: "Swift minus方法", message: "name:\(name)", modelArray: nil, cancelTitle: "确定", cancelHandle: {}, vc: self, sourceView: webView)}

总结:
我们常用的需求更多的场景应该是JS调用swift的某个方法后,再回调JS的某个方法,达到异步回调的效果,这时我们可以这样设计:
把JS的回调函数名当作参数传给swift的方法,swift处理完逻辑后直接调用这个参数:

 var params = {"name": "JSName","callBackFun": "minusCallback"};
window.webkit.messageHandlers.minus.postMessage(params);

而swift端:

    // MARK: - WKScriptMessageHandlerfunc userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {let name = message.name // 注册过的JS可调用的名称if name.elementsEqual("minus") {if let params = message.body as? NSDictionary { // JS传递的参数if let name = params.value(forKey: "name") as? String {let callback = params.value(forKey: "callBackFun") as? Stringminus(name: name, callBack: callback)}}}}func minus(name: String, callBack: String?) -> Void {showAlert(title: "Swift minus方法", message: "name:\(name)", modelArray: nil, cancelTitle: "确定", cancelHandle: { [weak self] inif let callBackFun = callBack {guard let self = self else { return }self.callJSFunction(jsFunName: callBackFun, params: nil)}}, vc: self, sourceView: webView)}

swift调用JS同理!

增加加载进度条

进度条使用第三方库NJKWebViewProgress

pod 'NJKWebViewProgress'

在WKWebView上面预留2个高度显示进度条,使用ReactiveCocoa监听WKWebView的进度:

        self.webView?.reactive.signal(forKeyPath: "estimatedProgress").observeValues({ [weak self](result) inguard let self = self else {return}if let progress = result as? NSNumber {self.progressView?.setProgress(progress.floatValue, animated: true)if progress.floatValue >= 1.0 {self.progressWrapViewHeightConst.constant = 0}}})

其他地方涉及到进度的地方再优化下:

     // MARK: - WKNavigationDelegate// MARK: 页面开始加载时调用func webView(_ webView: WKWebView, didStartProvisionalNavigation navigation: WKNavigation!) {self.progressWrapViewHeightConst.constant = self.progressHeight}// MARK: 内容开始返回时调用func webView(_ webView: WKWebView, didCommit navigation: WKNavigation!) {}func webView(_ webView: WKWebView, didFail navigation: WKNavigation!, withError error: Error) {self.progressWrapViewHeightConst.constant = 0}func webView(_ webView: WKWebView, didFailProvisionalNavigation navigation: WKNavigation!, withError error: Error) {self.progressWrapViewHeightConst.constant = 0}func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {self.progressWrapViewHeightConst.constant = 0}

支持JS中alert、confirm、prompt

在JS端调用alert、confirm、prompt这几个方法,实际上会回调到WKWebView的WKUIDelegate代理中,对应的方法为:

    @available(iOS 8.0, *)optional func webView(_ webView: WKWebView, runJavaScriptAlertPanelWithMessage message: String, initiatedByFrame frame: WKFrameInfo, completionHandler: @escaping () -> Void)@available(iOS 8.0, *)optional func webView(_ webView: WKWebView, runJavaScriptConfirmPanelWithMessage message: String, initiatedByFrame frame: WKFrameInfo, completionHandler: @escaping (Bool) -> Void)@available(iOS 8.0, *)optional func webView(_ webView: WKWebView, runJavaScriptTextInputPanelWithPrompt prompt: String, defaultText: String?, initiatedByFrame frame: WKFrameInfo, completionHandler: @escaping (String?) -> Void)

需要实现具体方法才能有效果:

 // MARK: - WKUIDelegatefunc webView(_ webView: WKWebView, runJavaScriptAlertPanelWithMessage message: String, initiatedByFrame frame: WKFrameInfo, completionHandler: @escaping () -> Void) {showAlert(title: "温馨提示", message: message, modelArray: nil, cancelTitle: "确定", cancelHandle: {completionHandler()}, vc: self, sourceView: webView)}func webView(_ webView: WKWebView, runJavaScriptConfirmPanelWithMessage message: String, initiatedByFrame frame: WKFrameInfo, completionHandler: @escaping (Bool) -> Void) {var modelArray: [AlertModel] = [AlertModel]()let model = AlertModel(title: "确定") {completionHandler(true)}modelArray.append(model)showAlert(title: "温馨提示", message: message, modelArray: modelArray, cancelTitle: "取消", cancelHandle: {completionHandler(false)}, vc: self, sourceView: webView)}func webView(_ webView: WKWebView, runJavaScriptTextInputPanelWithPrompt prompt: String, defaultText: String?, initiatedByFrame frame: WKFrameInfo, completionHandler: @escaping (String?) -> Void) {let alertController = UIAlertController(title: prompt, message: defaultText, preferredStyle: UIAlertController.Style.alert)alertController.addTextField { (txtField) intxtField.text = defaultText}alertController.addAction(UIAlertAction(title: "确定", style: UIAlertAction.Style.default, handler: { (alertAction) inlet txt = alertController.textFields?[0].textcompletionHandler(txt)}))self.present(alertController, animated: true) {}}

项目源码: https://codechina.csdn.net/ios1/projectcommon

如果觉得可以就点个👍吧,欢迎粉丝收藏,土豪打赏,您的关注就是我们创作的动力!

读者有什么想看的相关技术篇章,欢迎评论留言!

QQ交流群:908058499

相关文章:

CentOS7 打包RPM 升级OpenSSH8.3

目录 一、源码包 二、打包RPM 2.1、准备阶段 2.2、打包排错阶段 三、升级 漏扫设备发现OpenSSH有漏洞,需要升级到OpenSSH 8.1及以上版本,那么干脆就直接升级到发文时最新的版本,OpenSSH 8.3。做法是找到OpenSSH 8.3的源码包,…

步步为营-44-窗体之间传值--观察者模式

说明 :观察者模式又叫发布-订阅模式,其中又涉及到中介者模式 1 结构 2 创建Main窗体(中介者),ChildForm1(发布者),ChildForm2(订阅者),ChildForm3(订阅者), 2.1 ChildForm1中添加按钮,当按钮被点击是ChildForm2(订阅者),ChildForm3(订阅者),的文本框汇中获取信息 2.2 定义接口 …

java指令详解

Java是通过java虚拟机来装载和执行编译文件(class文件)的,java虚拟机通过命令java option 来启动,-option为虚拟机参数,通过这些参数可对虚拟机的运行状态进行调整. 一、如何查看参数列表: 虚拟机参数分为基本和扩展两…

wrs-arcface虹软人脸识别

前言 虹软人脸识别组件,支持活体识别、离线识别、图片人脸特征识别、图片是否同一人对比、相机人脸识别或对比,虹软免费版请使https://ext.dcloud.net.cn/plugin?id6084 功能 支持活体识别、离线识别图片人脸特征识别(年龄、性别、3DAngle)两张图片是否是同一人…

C++指针与引用的区别

(1)指针是一个变量,本身占有内存,内存中存储的是所指向对象的地址。引用是内存的别名。 (2)指针可以通过解引用的方式,取出所指向内存中的值。引用没有解引用。 (3)指针可…

Linux08-日志

目录 一、systemd的日志 1.1、sytemd-journald与systemd日志 1.2、systemd日志的持久化 二、系统常规日志 2.1、系统日志概述 2.2、查看系统日志文件 2.3、日志的轮转 2.4、分析系统日志 2.5、使用logger发送消息到日志 RHEL7的日志由2个服务负责记录,分别…

Java的小实验——各种测试以及说明

日期:2018.10.07 星期五 博客期:014 一、Java中的位运算 代码如下: 1 package Morts107;2 3 public class Test107 {4 public static void main(String[] args) {5 int z;6 z 13>>1;//00001101(13)---------------…

C++内存的分区

C内存分为四个区: (1)代码区:存放代码转译成的二进制代码。 (2)全局区:存放全局变量、静态变量(static)、常量(如字符串常量)。 全局区中还包含一…

SpringCloud的服务网关zuul

演示如何使用api网关屏蔽各服务来源 一、概念和定义 1、zuul最终还是使用Ribbon的,顺便测试一下Hystrix断路保护2、zuul也是一个EurekaClient,访问服务注册中心,获取元数据,使用本地的Ribbon负载均衡,Hystrix断路保护&…

wrs-tuya-cloud

前言 wrs-tuya-cloud是涂鸦官网针对云开发的插件,包含垂直品类硬件API(万能红外开放能力、设备连接服务、设备OTA固件升级、实时音视频、睡眠带开放能力、体脂秤开放能力、智能门锁开放能力、视频云存储 、邮件服务 、 语音消息服务、消息推送服务、短信服务 、内测…

Windows Server 2016 笔记

从业界普遍实践结果来看,Windows Server在服务器领域真是不太好用。但是,有些时候由于种种原因不得不用,所以还是有必要了解一下的。今天参加了一个Windows Server的培训,主要面对Windows Server 2016,写下这篇博客备忘…

(办公)网页发送到桌面快捷方式怎么做

转载自百度:https://jingyan.baidu.com/article/f79b7cb303d50a9145023e6e.html 有时候一个网页我们需要经常用到,每次找那个需要的网页很耗时间,那么我们怎么把我们需要的网页发送到桌面快捷方式呢? 这样下次我们直接点击桌面上的快捷方式就…

C++程序编译过程

程序编译的过程,是将源代码转换为计算机可执行的机械语言的过程。分为预处理、编译、汇编、链接四步。 (1)预处理:对程序进行预处理,比如将头文件的代码直接赋值到当前代码中等等. (2)编译&am…

Java的注释(详细版)

注释是对代码进行必要的说明,以便于后期的修改、维护和升级。Java的注释分为三种:第一种是**单行注释**:用双斜杠“//”来进行实例://单行注释第二种是**文档注释**:用斜杠“/”和星号“*”来进行实例:/***…

Hadoop的存储架构介绍

http://lxw1234.com/archives/2016/04/638.htm 该文章介绍了Hadoop的架构原理,简单易懂。 目前公司提供Hadoop的运算集群BMR,可以直接申请集群资源。转载于:https://www.cnblogs.com/blog-of-Fourier/p/6809811.html

编译OpenSSH8.4的RPM包及升级

目录 一、安装相关依赖包 二、创建rpmbuild目录并下载源码 三、打包及排错 四、升级到OpenSSH 8.4p1 以下是打包好的OpenSSH 8.4p1,包括7个rpm包,欢迎下载使用。 OpenSSH-8.4p1-Bundle 一、安装相关依赖包 根据以往经验,需要安装wget、…

centos 系统使用verdaccio搭建npm私库

.安装nodejs yum install -y nodejs 2.安装verdaccio npm install -g verdaccio --unsafe-perm 3.配置 a.修改配置文件 config.yaml,在其最后添加监听端口(使其可在外网访问) listen: 0.0.0.0:4873 b.对外开放4873端口 firewall-cmd --state …

视觉SLAM中PNP求解

PNP(Perspective-n-points)是SLAM中估计位姿的重要方法。已知条件为路标点在相机1中的相机坐标以及投影到相机2中的像素坐标,据此去估计相机1、相机2间的位姿。主要解法包括DLT、P3P、EPNP P3P 已知A、B、C在相机1坐标系下的坐标&#xff0…

Java程序的运行原理 用记事本编写Java代码

首先将Java代码写入源文件(.java)中→ 通过 javac 生成class文件(.class) → 再通过java命令执行程序:◆将class文件加载内存(相当于将东西输入大脑)◆检验class文件(大脑检查是否有语法等错误,若无误)◆将…

Linux下修改mysql的root密码后数据库消失怎么处理

Linux系统下如果没有通过password()函数修改mysql的root密码就会导致mysql数据库消失。有些人可能不知道而直接修改了mysql的root密码,于是产生了mysql数据库消失的问题,这个时候该怎么处理呢? 可以用下面的办法解决&a…

编译httpd-2.4.46的RPM包

目录 一、下载源码 二、编译&排错 2.1、第一次编译,解决依赖包问题。 2.2、第二次编译,解决anaconda导致的环境变量问题 2.3、第三次编译,解决apr版本过低问题 提供 apr-1.7.0、httpd-2.4.46 的RPM包下载。 apr-1.7.0-bundle.zip …

C/s模式B/S模式

C/s模式:是客户端/服务器(Client/Server)模式,主要指的是传统的桌面级的应用程序。比如我们经常用的信息管理系统。 C/S 客户端/服务器 例如QQ,网络游戏,需要下载客户端才能访问服务器的程序 B/S 浏览器/服务器 例如Intel&#xf…

分割catalina.out 每天生成一个文件

1. touch xxx(文件名字).sh 2. vim xxx.sh 写入 ----------------------- #!/bin/shcd dirname $0pwdddate %Y%m%dd7date -d7 day ago %Y%m%dcd ../logs/cp catalina.out catalina.out.${d}cat /dev/null > catalina.outrm -rf catalina.out.${d7} ----------…

、|| 和 、| 的区别(详尽版)

&&和|| 是逻辑运算符(也包括 !) 逻辑运算符含义&&逻辑与(两者为真才为真,一者为假即为假)︱︱逻辑或(两者为假才为假,一者为真即为真)!逻辑非(本来值的…

C++负数、小数如何保存

负整数 正整数直接按照源码存储,负整数按照补码存储。 原码、反码、补码 首先要清楚原码、反码、补码: 计算机中一个字节为8位,在此以一个字节为例: 原码: 十进制1:0000 0001 十进制-1:1000 …

MySQL00-这都不知道还TM学啥MySQL

目录 一、MySQL架构概述 1.1、客户端连接器 1.2、连接层 1.3、可插拔存储引擎 1.4、文件系统与文件 二、配置文件 三、数据文件 四、日志文件(以MySQL5.7.32为例) 4.1、错误日志 Error Log 4.2、一般查询日志 General Query Log 4.3、二进制日…

ORB_SLAM2 PnPSolver

EPNP:已知4组(默认)3D-2D匹配点,构建参考点,通过计算参考点的相机坐标,线性组合成路标点的相机坐标。然后使用ICP估计相机间的位姿变换。   需要注意的事,EPNP可以同时使用N组路标点构建M矩阵…

iOS开发UI篇—多控制器和导航控制器简单介绍

iOS开发UI篇—多控制器和导航控制器简单介绍 一、多控制器 一个iOS的app很少只由一个控制器组成,除非这个app极其简单。当app中有多个控制器的时候,我们就需要对这些控制器进行管理 有多个view时,可以用一个大的view去管理1个或者多个小view,控制器也是如…

值传递 和 地址传递 的区别(好多句话才可以说清)

要知道什么是**值传递**和**地址传递**,首先要知道什么是**基本数据类型**和**引用类型**。 Java的基本数据类型就八种(除了这八种之外都是引用类型):◆整型:byte(8位) short(16位) int(32位) long(64位)&#xff1…

日期类的时间从为什么是从1970年1月1日(格林威治时间)

I suspect that Java was born and raised on a UNIX system.UNIX considers the epoch (when did time begin) to be midnight, January 1, 1970.是说java起源于UNIX系统,而UNIX认为1970年1月1日0点是时间纪元. 但这依然没很好的解释"为什么",出于好奇&a…