WKWebView消息处理器首次无法获取文本框值,二次点击正常
问题分析与解决方案:WKWebView首次获取HTML表单值为空,二次点击正常
问题核心原因
你遇到的问题本质是异步执行顺序错误:
webkitview.evaluateJavaScript("submitForm()")是异步操作,调用后会立即返回,不会等待JavaScript代码执行完成并通过postMessage把数据传回Swift。- 首次点击按钮时,
showUser(email: lsusername, name: lspassword)会先执行,此时lsusername和lspassword还是初始的空字符串;当submitForm()执行完成、userContentController回调更新变量后,二次点击按钮时,变量已经被上次的回调更新,因此能显示正确值。 - 另外代码里存在一个笔误:
baseview.addSubview(self.appvalleyRegistration)应该改为baseview.addSubview(self.webkitview),否则WebView根本不会被添加到视图中(你能二次点击生效可能是测试时的临时修正)。
解决方案一:直接通过evaluateJavaScript的回调获取值
这种方式不需要依赖WKScriptMessageHandler,直接通过JS执行结果拿到表单值,彻底避免异步消息的顺序问题:
@IBAction func registerUser(_ sender: Any) { webkitview.evaluateJavaScript("JSON.stringify({email: document.getElementById('email').value, passw: document.getElementById('passw').value})") { [weak self] result, error in guard let self = self, error == nil, let jsonStr = result as? String, let data = jsonStr.data(using: .utf8), let formData = try? JSONSerialization.jsonObject(with: data, options: []) as? [String: String], let email = formData["email"], let passw = formData["passw"] else { print("获取表单值失败") return } self.lsusername = email self.lspassword = passw self.showUser(email: email, name: passw) } }
解决方案二:在ScriptMessage回调中触发弹窗
如果坚持使用postMessage的方式,将showUser的调用移到消息回调中,确保只有当数据从JS传回Swift后才显示弹窗:
- 修改
registerUser方法,仅触发JS函数:
@IBAction func registerUser(_ sender: Any) { webkitview.evaluateJavaScript("submitForm()") }
- 修改
userContentController回调,数据更新后再显示弹窗:
func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) { if let data = message.body as? [String : String], let passw = data["passw"], let email = data["email"] { lsusername = email; lspassword = passw; showUser(email: email, name: passw) } else { print("No data."); } }
- 修正WebView添加的代码(并添加自适应约束):
override func viewDidLoad() { super.viewDidLoad() self.webkitview = WKWebView(frame: .zero, configuration: self.getWKWebViewConfiguration()) baseview.addSubview(self.webkitview) webkitview.translatesAutoresizingMaskIntoConstraints = false NSLayoutConstraint.activate([ webkitview.topAnchor.constraint(equalTo: baseview.topAnchor), webkitview.leadingAnchor.constraint(equalTo: baseview.leadingAnchor), webkitview.trailingAnchor.constraint(equalTo: baseview.trailingAnchor), webkitview.bottomAnchor.constraint(equalTo: baseview.bottomAnchor) ]) let url = URL(string: "https://redacted.com/registrationform.php")! webkitview.load(URLRequest(url: url)) }
补充:为什么sleep()无效
sleep()是阻塞主线程的操作,而WKWebView的JS执行、消息传递都是在主线程异步处理的。阻塞主线程反而会延缓JS执行流程,根本解决不了异步顺序问题,必须通过回调或异步等待的方式处理。
内容的提问来源于stack exchange,提问作者Jonathan
相关产品推荐
相关产品推荐

