使用WebKit从Swift调用WKWebView JavaScript和从JavaScript调用Swift

许多移动应用程序都将远程网页合并为被动(静态)内容,或者在这种情况下作为UI的组成部分。 使用此处介绍的WebKit / WKWebView技术,您的本机应用程序可以更好地与Web内容集成,并为最终用户提供卓越的体验。

Swift和JavaScript之间的双向集成

在本文中,我们将构建一个完整的混合本机/ Web应用程序示例,该应用程序在本机iOS应用程序(用Swift编写)和移动网页(用HTML / JavaScript编写)之间使用双向函数调用。

通过利用这两个功能,我们可以构建一个高度健壮的混合应用程序,其中本机和Web组件作为平等的合作伙伴协作,以提供有价值的客户解决方案。

解决方案概述

最终的解决方案包含两个组件:

  1. 在Swift中开发的本机iOS应用程序
  2. 托管在远程Web服务器上的静态HTML / JavaScript网页(在本例中为Microsoft Azure托管)。

完成学习的应用程序实现了三个主要功能:

#1-从远程加载网页。 如果您使用过WKWebView,那么您将了解所有有关此功能的信息。 加载UIViewController时,会将网页URL设置为WKWebView,该WebKView使用HTTP GET方法获取HTML内容。

#2 —从Swift操纵WebView的外观。 接下来,我们通过向WKWebView内容页面发送命令以根据本机Segment控件中的用户选择来更改页面背景颜色,来轻而易举进入互操作性领域。

#3-从HTML / JavaScript回调到Swift。 最后,通过将本机iOS应用程序中的地理位置功能公开到网页,我们使解决方案更加复杂和有趣。 当用户输入地址时,在Web视图页面上按一个按钮,将完成以下操作:

  1. 网页(使用JavaScript)调用Swift函数,将用户输入的地址作为JSON对象传入。
  2. Swift本机应用程序使用CLLocation异步调用Apple,以确定用户输入地址的纬度和经度。
  3. 从Apple返回纬度/经度时,Swift本机应用程序将调用网页中的JavaScript函数,以使用输入地址的纬度/经度来更新网页。

解决方案演示

在遍历代码之前,让我们演示完整的应用程序的外观(动画GIF)。

UI故事板设计

学习应用程序包含一个名为ViewController的 UIViewController。 ViewController在Storyboard中只有两个UI控件:

  1. 一个UISegmentedControl,允许用户将WebView背景颜色更改为五种颜色之一。
  2. UIView,放置在情节提要中,用作WKWebView控件的容器视图。

更改网页颜色

为了深入研究混合解决方案,让我们实现一个 Swift WKWebView的简单调用。

ViewController具有与本机视图顶部的Segment控件中的颜色选择相对应的颜色成员数组。

 让颜色= [“黑色”,“红色”,“蓝色”,“绿色”,“紫色”] 

当用户在Segment控件中点击新的细分时,事件处理程序将调用JavaScript函数changeBackgroundColor ,并传递与用户选择相对应的字符串:

  @IBAction func colorChoiceChanged(_发送者:UISegmentedControl){ 
webView.evaluateJavaScript
(“ changeBackgroundColor('(colors [sender.selectedSegmentIndex])')'”,
completeHandler:无)
  } 

Swift代码并不真正知道网页上有一个名为changeBackgroundColor的JavaScript例程。 要做的工作是格式化将在WebView中成功运行的JavaScript片段。

WKWebView中的HTML内容具有匹配的JavaScript例程,该例程仅将页面的背景色设置为从Swift传递给它的字符串:

 函数changeBackgroundColor(colorText){ 
document.body.style.background = colorText;
}

设置消息处理程序

下一个功能是将用户输入的地址从HTML页面发送到本地Swift应用程序进行地理编码。 要实现此功能,分三个步骤:

  1. 将消息处理程序添加到WKWebView的WKUserContentController。 这建立了一个约定,该约定保证Swift代码可以通过JavaScript从HTML页面调用时,可以响应命名的消息处理程序。
  2. 实现WKScriptMessageHandler委托方法didReceive消息以接收来自JavaScript的调用。
  3. 从Web内容JavaScript调用消息处理程序。

创建一个消息处理程序(1)

  // 一种 
让contentController = WKUserContentController();
contentController.add(自己,名称:“ geocodeAddress”)
  // B 
让config = WKWebViewConfiguration()
config.userContentController = contentController
  // C 
webView = WKWebView(框架:webViewContainer.bounds,配置:配置)

(A)创建一个WKUserContentController。 contentController保存geocodeAddress消息处理程序的注册。

WKUserContentController已添加到(B)处的新WKWebViewConfiguration中。

最后,在实例化WKWebView的(C)中 ,将在(B)中创建的已配置WKWebViewConfiguration传递给初始化程序。

实现WKScriptMessageHandler委托(2)

现在,将geocodeAddress处理程序定义为WKWebView,我们需要实现一个委托方法,该方法在调用WKWebView事件处理程序时触发。

在此解决方案中,定义了扩展以在ViewController类上实现WKScriptMessageHandler协议。

 扩展ViewController:WKScriptMessageHandler { 
func userContentController(
_ userContentController:
WKUserContentController,
didReceive消息:WKScriptMessage){
如果message.name ==“ geocodeAddress”,
让dict = message.body为? NSDictionary {
geocodeAddress(dict:dict)
}
}
}

didReceive处理程序将检查消息名称是否符合预期( geocodeAddress ),如果是,则从消息正文中提取JSON对象(作为NSDictionary),然后调用ViewController实例方法geocodeAddress

请注意,消息处理程序是字符串类型的 ,因此请注意, didReceive中的字符串比较正确匹配使用WKUserContentController进行的原始消息处理程序注册。

从HTML / JavaScript页面调用geocodeAddress(3)

在HTML中,表单的INPUT按钮调用一个称为geocodeAddress的JavaScript函数:

   

JavaScript geocodeAddress函数的主体通过调用同名的Swift消息处理程序进行响应,并将地址详细信息作为JSON对象传递。

 函数geocodeAddress(){ 
尝试{
webkit.messageHandlers.geocodeAddress.postMessage(
{
街道:document.getElementById(“街道”).value,
城市:document.getElementById(“ city”)。value,
状态:document.getElementById(“状态”).value,
国家/地区:document.getElementById(“ country”)。value
});
  document.querySelector('h1')。style.color =“绿色”; 
} catch(err){
document.querySelector('h1')。style.color =“红色”;
}
}

注意:在JavaScript geocodeAddress()函数中,此处的H1样式更改仅出于测试目的,而不是实际解决方案的一部分。

将纬度/经度传递回HTML页面

到目前为止,HTML页面已经在一系列INPUT字段中接受了来自用户的地址输入,并将其发送到本机Swift应用程序。 现在,让我们完成最后的要求-对地址进行地理编码并将其返回到网页UI。

回想一下,Swift消息处理程序调用了一个名为geocodeAddress(dict 🙂的Swift函数来对地址进行地理编码。

  func geocodeAddress(dict:NSDictionary){ 
让geocoder = CLGeocoder()

让street = dict [“ street”]如? 字符串?? “”
让city = dict [“ city”]为? 字符串?? “”
让状态= dict [“状态”]为? 字符串?? “”
让国家= dict [“国家”]为? 字符串?? “”

让addressString =“ \(街道),\(城市),\(州),\(国家)”
  geocoder.geocodeAddressString( 
addressString,
completeHandler:geocodeComplete)
}

解决方案的这一部分是简单的CoreLocation。 在geocodeAddressString异步函数将地址发送给Apple之后,响应将提供给Swift方法geocodeComplete

  func geocodeComplete(placemarks:[CLPlacemark] ?,错误:Error?){ 
如果让地标=地标,则placemarks.count> 0 {
let lat = placemarks [0] .location?.coordinate.latitude ?? 0.0
let lon =地标[0] .location?.coordinate.longitude? 0.0
  webView.evaluateJavaScript( 
“ setLatLon('\(lat)','\(lon)')”,complementHandler:无)
}
}

此方法检查以确保为提供的地址找到至少一个地标,从第一个地标中提取纬度和经度,然后通过调用setLatLon JavaScript函数将其发送回HTML页面。

更新HTML页面

将纬度/经度发送回网页的过程与设置背景色的先前功能在功能上相同。

setLatLon JavaScript函数的实现如下:

 函数setLatLon(lat,lon){ 
document.getElementById(“ latitude”)。value = lat;
document.getElementById(“ longitude”)。value = lon;
}

与背景颜色功能一样, setLatLon只需将HTML表单的INPUT字段值设置为传递的参数值。

摘要

WKWebView的最常见用法是在本机iOS应用程序的上下文中提供Web内容的简单显示-但是它可以做的更多,并且在本文中,我们已经看到了如何结合使用Web和本机组件来构建增强的本机应用程序,甚至混合本机/网络应用程序。

下载代码

以上片段提供了学习解决方案的核心功能。 完整的Xcode项目可在此处从Github下载。