swiftui-webkit
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseSwiftUI WebKit Integration
SwiftUI WebKit 集成
Native struct for SwiftUI. Replaces the old + Representable bridge pattern.
WebViewWKWebViewSwiftUI 专属原生结构体。替代旧有的 + Representable桥接模式。
WebViewWKWebViewCritical Constraints
关键约束
- ❌ DO NOT use +
WKWebVieworUIViewRepresentable→ ✅ UseNSViewRepresentablestruct directlyWebView - ❌ DO NOT use → ✅ Use
WKWebViewConfigurationWebPage.Configuration - ❌ DO NOT use → ✅ Use
WKNavigationDelegateprotocolWebPage.NavigationDeciding - ❌ DO NOT use → ✅ Use
evaluateJavaScript(_:)(async/await)page.callJavaScript(_:) - ❌ DO NOT use for message passing → ✅ Use
WKUserContentControllerwith argumentscallJavaScript
- ❌ 请勿使用+
WKWebView或UIViewRepresentable→ ✅ 直接使用NSViewRepresentable结构体WebView - ❌ 请勿使用→ ✅ 使用
WKWebViewConfigurationWebPage.Configuration - ❌ 请勿使用→ ✅ 使用
WKNavigationDelegate协议WebPage.NavigationDeciding - ❌ 请勿使用→ ✅ 使用
evaluateJavaScript(_:)(async/await)page.callJavaScript(_:) - ❌ 请勿使用进行消息传递 → ✅ 使用带参数的
WKUserContentControllercallJavaScript
Basic WebView
基础WebView
swift
import SwiftUI
import WebKit
struct ContentView: View {
var body: some View {
WebView(url: URL(string: "https://www.apple.com"))
.frame(height: 400)
}
}swift
import SwiftUI
import WebKit
struct ContentView: View {
var body: some View {
WebView(url: URL(string: "https://www.apple.com"))
.frame(height: 400)
}
}WebView with WebPage (Full Control)
搭配WebPage的WebView(完全控制)
swift
struct BrowserView: View {
@State private var page = WebPage()
var body: some View {
NavigationStack {
WebView(page)
.navigationTitle(page.title)
}
.onAppear {
if let url = URL(string: "https://www.apple.com") {
let _ = page.load(URLRequest(url: url))
}
}
}
}swift
struct BrowserView: View {
@State private var page = WebPage()
var body: some View {
NavigationStack {
WebView(page)
.navigationTitle(page.title)
}
.onAppear {
if let url = URL(string: "https://www.apple.com") {
let _ = page.load(URLRequest(url: url))
}
}
}
}Text Search
文本搜索
swift
struct SearchableWebView: View {
@State private var searchVisible = true
var body: some View {
WebView(url: URL(string: "https://www.apple.com"))
.findNavigator(isPresented: $searchVisible)
}
}swift
struct SearchableWebView: View {
@State private var searchVisible = true
var body: some View {
WebView(url: URL(string: "https://www.apple.com"))
.findNavigator(isPresented: $searchVisible)
}
}WebPage Configuration
WebPage配置
swift
var config = WebPage.Configuration()
config.loadsSubresources = true
config.defaultNavigationPreferences.allowsContentJavaScript = true
config.websiteDataStore = .default() // Persistent
// config.websiteDataStore = .nonPersistent() // Ephemeral
let page = WebPage(configuration: config)
page.customUserAgent = "MyApp/1.0"swift
var config = WebPage.Configuration()
config.loadsSubresources = true
config.defaultNavigationPreferences.allowsContentJavaScript = true
config.websiteDataStore = .default() // 持久化
// config.websiteDataStore = .nonPersistent() // 临时
let page = WebPage(configuration: config)
page.customUserAgent = "MyApp/1.0"Loading Content
内容加载
swift
// URL
page.load(URLRequest(url: url))
// HTML string
page.load(html: "<h1>Hello</h1>", baseURL: URL(string: "https://example.com")!)
// Data
page.load(data, mimeType: "text/html", characterEncoding: .utf8, baseURL: baseURL)
// Navigation
page.reload(fromOrigin: false)
page.stopLoading()
// Back/Forward
if let backItem = page.backForwardList.backItem {
page.load(backItem)
}swift
// URL
page.load(URLRequest(url: url))
// HTML字符串
page.load(html: "<h1>Hello</h1>", baseURL: URL(string: "https://example.com")!)
// 数据
page.load(data, mimeType: "text/html", characterEncoding: .utf8, baseURL: baseURL)
// 导航
page.reload(fromOrigin: false)
page.stopLoading()
// 前进/后退
if let backItem = page.backForwardList.backItem {
page.load(backItem)
}JavaScript Execution
JavaScript执行
swift
// Basic
let title = try await page.callJavaScript("document.title")
// With arguments
let script = """
function findElement(selector) {
return document.querySelector(selector)?.textContent;
}
return findElement(selector);
"""
let result = try await page.callJavaScript(script, arguments: ["selector": ".main-content h1"])
// In specific content world
import WebKit
let result = try await page.callJavaScript("document.title", contentWorld: .page)swift
// 基础用法
let title = try await page.callJavaScript("document.title")
// 带参数
let script = """
function findElement(selector) {
return document.querySelector(selector)?.textContent;
}
return findElement(selector);
"""
let result = try await page.callJavaScript(script, arguments: ["selector": ".main-content h1"])
// 在指定内容环境中执行
import WebKit
let result = try await page.callJavaScript("document.title", contentWorld: .page)Navigation Events
导航事件
swift
.onChange(of: page.currentNavigationEvent) { _, newEvent in
if let event = newEvent {
switch event.state {
case .started: isLoading = true
case .finished, .failed: isLoading = false
default: break
}
}
}swift
.onChange(of: page.currentNavigationEvent) { _, newEvent in
if let event = newEvent {
switch event.state {
case .started: isLoading = true
case .finished, .failed: isLoading = false
default: break
}
}
}Custom Navigation Decisions
自定义导航决策
swift
struct MyNavigationDecider: WebPage.NavigationDeciding {
func decidePolicyFor(navigationAction: WebPage.NavigationAction) async -> WebPage.NavigationPreferences? {
if let url = navigationAction.request.url, url.host == "blocked.com" {
return nil // Block navigation
}
var prefs = WebPage.NavigationPreferences()
prefs.allowsContentJavaScript = true
return prefs
}
func decidePolicyFor(navigationResponse: WebPage.NavigationResponse) async -> Bool {
if let http = navigationResponse.response as? HTTPURLResponse {
return http.statusCode == 200
}
return true
}
}
let page = WebPage(configuration: config, navigationDecider: MyNavigationDecider())swift
struct MyNavigationDecider: WebPage.NavigationDeciding {
func decidePolicyFor(navigationAction: WebPage.NavigationAction) async -> WebPage.NavigationPreferences? {
if let url = navigationAction.request.url, url.host == "blocked.com" {
return nil // 阻止导航
}
var prefs = WebPage.NavigationPreferences()
prefs.allowsContentJavaScript = true
return prefs
}
func decidePolicyFor(navigationResponse: WebPage.NavigationResponse) async -> Bool {
if let http = navigationResponse.response as? HTTPURLResponse {
return http.statusCode == 200
}
return true
}
}
let page = WebPage(configuration: config, navigationDecider: MyNavigationDecider())Custom URL Scheme Handler
自定义URL协议处理器
swift
struct MySchemeHandler: URLSchemeHandler {
func start(task: URLSchemeTask) {
guard let url = task.request.url, url.scheme == "myapp" else {
task.didFailWithError(URLError(.badURL)); return
}
let html = "<html><body><h1>Custom Content</h1></body></html>"
let response = URLResponse(url: url, mimeType: "text/html",
expectedContentLength: -1, textEncodingName: "utf-8")
task.didReceive(response)
task.didReceive(Data(html.utf8))
task.didFinish()
}
func stop(task: URLSchemeTask) { }
}
var config = WebPage.Configuration()
config.setURLSchemeHandler(MySchemeHandler(), forURLScheme: "myapp")swift
struct MySchemeHandler: URLSchemeHandler {
func start(task: URLSchemeTask) {
guard let url = task.request.url, url.scheme == "myapp" else {
task.didFailWithError(URLError(.badURL)); return
}
let html = "<html><body><h1>自定义内容</h1></body></html>"
let response = URLResponse(url: url, mimeType: "text/html",
expectedContentLength: -1, textEncodingName: "utf-8")
task.didReceive(response)
task.didReceive(Data(html.utf8))
task.didFinish()
}
func stop(task: URLSchemeTask) { }
}
var config = WebPage.Configuration()
config.setURLSchemeHandler(MySchemeHandler(), forURLScheme: "myapp")Content Capture
内容捕获
swift
// Snapshot
let image = try await page.snapshot(WKSnapshotConfiguration())
// PDF
let pdfData = try await page.pdf(configuration: WKPDFConfiguration())
// Web Archive
let archiveData = try await page.webArchiveData()swift
// 快照
let image = try await page.snapshot(WKSnapshotConfiguration())
// PDF
let pdfData = try await page.pdf(configuration: WKPDFConfiguration())
// Web归档
let archiveData = try await page.webArchiveData()View Modifiers
视图修饰器
swift
WebView(url: url)
.webViewBackForwardNavigationGestures(.disabled)
.webViewMagnificationGestures(.enabled)
.webViewLinkPreviews(.disabled)
.webViewTextSelection(.enabled)
.webViewContentBackground(.color(.systemBackground))
.webViewElementFullscreenBehavior(.enabled)swift
WebView(url: url)
.webViewBackForwardNavigationGestures(.disabled)
.webViewMagnificationGestures(.enabled)
.webViewLinkPreviews(.disabled)
.webViewTextSelection(.enabled)
.webViewContentBackground(.color(.systemBackground))
.webViewElementFullscreenBehavior(.enabled)