接下来我们来看看在SwiftUI 怎麽使用WebView 网页的元件,SwiftUI 框架 有一个缺点,就是它未能提供所有 UIKit 有的 UI 控件,例如说由於 SwiftUI 还没有定义 WebView,所以Apple 有一个 UIViewRepresentable 协定,让你可以轻松打包 (wrap) 一个 UIView,并让 SwiftUI 专案使用,以下我们就将传统的 UI 元件包装成 SwiftUI view,并使用 WKWebView 来加载 Web 应用程序,让我们照着以下步骤来完成
在 SwiftUI 中使用 UIKit 中的任何组件都需要用 UIViewRepresentable 包装
首先我们先新增一个 Swift File:并命名为 WebView.swift
,里面内容为struct WebView,并实作UIViewRepresentable
协定
import SwiftUI
import WebKit
struct SwiftUIWebView: UIViewRepresentable {
}
实作 UIViewRepresentable 表示此 struct 定义的WebView,是有着 UIView 元件特性的 SwiftUI view,所以它可以包装 UICollectionView,WKWebView,MKMapView 等 UI 元件,待会我们将用它包装 WKWebView
要符合 UIViewRepresentable
协定,需实作两个方法,makeUIView(context:)
以及updateUIView(_:context:)
在程序区块上建立makeUIView(context:)
方法,可经由Xcode 能自动完成
再把makeUIView
方法里回传的型别改成我们要用的WKWebView
func makeUIView(context: Context) -> WKWebView {
}
同样的方式,建立updateUIView(_:context:)
方法,记得把参数uiView
改成我们要用的WKWebView
func updateUIView(_ uiView: WKWebView, context: Context) {
}
现在我们来完成以上这两个方法的实作,首先makeUIView
里我们要回传 WKWebView 的物件,这边可以透过WKWebpagePreferences
以及WKWebViewConfiguration
来对我们的WebView 设置一些偏好设定
范例:
func makeUIView(context: Context) -> WKWebView {
let prefs = WKWebpagePreferences()
prefs.allowsContentJavaScript = false
let config = WKWebViewConfiguration()
config.defaultWebpagePreferences = prefs
return WKWebView(
frame: .zero,
configuration: config
)
}
接下来是updateUIView
,它将在 SwiftUI view 画面更新时被呼叫,我们可以在 WebView 里宣告一个属性来由外部传入网址,然後在生成 WebView 时传入内容,在 updateUIView 才载入网页
如下:
struct WebView: UIViewRepresentable {
let url: URL?
func makeUIView(context: Context) -> WKWebView {
let prefs = WKWebpagePreferences()
prefs.allowsContentJavaScript = false
let config = WKWebViewConfiguration()
config.defaultWebpagePreferences = prefs
return WKWebView(
frame: .zero,
configuration: config
)
}
func updateUIView(_ uiView: WKWebView, context: Context) {
guard let myURL = url else {
return
}
let request = URLRequest(url: myURL)
uiView.load(request)
}
}
接下来我们就可以使用我们的WebView 来载入我们要的网页了
struct ContentView: View {
var body: some View {
WebView(url: URL(string: "https://www.youtube.com/"))
}
}
目前我们只讨论了 UIViewRepresentable
协定中的几种方法。如果你需要在 UIKit 中使用委托 (delegate) 并与 SwiftUI 沟通,就必须实现 makeCoordinator
方法,并提供一个 Coordinator
实例。Coordinator
是 UIView 的委托 和 SwiftUI 之间的桥梁
我们在WebView struct 中,创建一个 Coordinator
类别并采用WKNavigationDelegate
协定来处理webView 的导航事件,并在 makeCoordinator
方法回传其实例,可搭配数据流等来做资料的传递
范例:
struct WebView: UIViewRepresentable {
@Binding var title: String
var url: URL
var loadStatusChanged: ((Bool, Error?) -> Void)? = nil
func makeCoordinator() -> WebView.Coordinator {
Coordinator(self)
}
func makeUIView(context: Context) -> WKWebView {
let view = WKWebView()
view.navigationDelegate = context.coordinator
view.load(URLRequest(url: url))
return view
}
func updateUIView(_ uiView: WKWebView, context: Context) {
// you can access environment via context.environment here
// Note that this method will be called A LOT
}
func onLoadStatusChanged(perform: ((Bool, Error?) -> Void)?) -> some View {
var copy = self
copy.loadStatusChanged = perform
return copy
}
class Coordinator: NSObject, WKNavigationDelegate {
let parent: WebView
init(_ parent: WebView) {
self.parent = parent
}
func webView(_ webView: WKWebView, didCommit navigation: WKNavigation!) {
parent.loadStatusChanged?(true, nil)
}
func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
parent.title = webView.title ?? ""
parent.loadStatusChanged?(false, nil)
}
func webView(_ webView: WKWebView, didFail navigation: WKNavigation!, withError error: Error) {
parent.loadStatusChanged?(false, error)
}
}
}
struct ContentView: View {
@State var title: String = ""
@State var error: Error? = nil
var body: some View {
WebView(title: $title, url: URL(string: "https://www.apple.com/")!)
.onLoadStatusChanged { loading, error in
if loading {
print("Loading started")
self.title = "Loading…"
}
else {
print("Done loading.")
if let error = error {
self.error = error
if self.title.isEmpty {
self.title = "Error"
}
}
else if self.title.isEmpty {
self.title = "Some Place"
}
}
}
}
}
<<: Day 23 资料宝石:【Lab】RDS架构 建立自己的第一台云端资料库 (下)
今天我们谈到 declarative 声明式和 imperative 命令式, 他的概念比较像是以下...
大家好,我是 Ian,因为一些原因,我在年初的时候接触到嵌入式系统开发与系统软件实作。在这一年来也...
if else 与 else if 参考 Day06 - 变数(03) - BMI、Day11 - ...
花一份单点披萨的钱,就可以吃到12种口味的披萨,是很划算的。 这一天在爱买水湳店附近处理一些事情,忙...
未经实锤的资讯 中国电子科大表示:学校学工部从未发布过相关的简讯内容,表示「已经提醒各院系辅导员通知...