iOS 使用 WKWebView 显示网页内容

3,539 阅读8分钟

1.什么是 WKWebView?

WKWebViewUIKit框架中的一个视图组件,用于在iOS应用中显示Web内容。相比于旧版的UIWebViewWKWebView具有更好的性能、更低的内存占用以及更多的现代Web功能。它采用了WebKit引擎,能够渲染HTML、CSS和JavaScript,同时还支持与Web内容的交互。

2.使用 WKWebView

以下是使用WKWebView的基本步骤:

  1. 打开Xcode并创建一个新的Single View App项目。
  2. 打开Main.storyboard文件。
  3. 在显示WKWebView的界面上,拖拽一个WKWebView组件用作容器。
  4. 在视图控制器的代码文件中,创建一个IBOutlet来引用您在Storyboard中添加的WKWebView
  5. 在视图控制器代码文件中,导入WebKit框架:
import WebKit

创建一个WKWebView实例,并将其添加到容器视图中:

import UIKit
import WebKit

class ViewController: UIViewController, WKNavigationDelegate {

    @IBOutlet weak var webViewContainer: WKWebView!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        // 设置 WKNavigationDelegate
        webViewContainer.navigationDelegate = self
        
        // 加载网页
        if let url = URL(string: "https://www.example.com") {
            let request = URLRequest(url: url)
            webViewContainer.load(request)
        }
    }

    // ...
}

}

用代码创建WKWebView

创建一个WKWebView实例,并将其添加到容器视图中:

import WebKit

class ViewController: UIViewController, WKNavigationDelegate {

    var webView: WKWebView!

    override func viewDidLoad() {
        super.viewDidLoad()

        // 创建 WKWebView 实例
        webView = WKWebView(frame: view.bounds)
        webView.navigationDelegate = self

        // 将 WKWebView 添加到容器视图中
        view.addSubview(webView)
    }

    // ...
}

使用load()方法加载网页内容:

if let url = URL(string: "https://www.example.com") {
    let request = URLRequest(url: url)
    webView.load(request)
}

如果当前页面就只有一个WKWebView,还可以在Main.storyboard去掉WKWebView然后直接重写视图控制器生命周期loadView函数。

    var webView: WKWebView!

    //自定义根视图--当整个页面是WebView时推荐这么做

    override func loadView() {

        let config = WKWebViewConfiguration()

        config.allowsInlineMediaPlayback = true

        webView = WKWebView(frame: .zero, configuration: config)

        

        //用于控制是否允许使用手势进行前进和后退的导航

        webView.allowsBackForwardNavigationGestures = true

        webView.uiDelegate = self

        webView.navigationDelegate = self

        view = webView

    }

直接替换页面根视图,相对比较节省资源。

3.与 Web 内容交互

WKWebView不仅可以加载网页内容,还可以与Web内容进行交互。您可以通过JavaScript与Web页面通信,从而实现从应用中向Web内容发送数据,或者从Web内容接收数据。以下是一些常见的交互方式:

  • 通过JavaScript注入,向Web页面传递数据:
let script = "myFunction('Hello from iOS!');"
webView.evaluateJavaScript(script, completionHandler: nil)
  • 通过WKScriptMessageHandler协议,从Web页面接收数据:
class ViewController: UIViewController, WKNavigationDelegate, WKScriptMessageHandler {

    override func viewDidLoad() {
        super.viewDidLoad()

        // ...
        
        let userContentController = webView.configuration.userContentController
        userContentController.add(self, name: "messageHandlerName")
    }

    func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
        if message.name == "messageHandlerName", let messageBody = message.body as? String {
            print("Received message from web: \(messageBody)")
        }
    }

    // ...
}

4.实现 WKNavigationDelegate

如果希望在WKWebView的加载过程中监控和处理一些事件,可以实现WKNavigationDelegate协议。例如,可以在页面加载完成后执行某些操作,或者处理加载错误等情况。

class ViewController: UIViewController, WKNavigationDelegate {

    // ...
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        // ...
        
        // 设置 WKNavigationDelegate
        webView.navigationDelegate = self
    }

    // 实现 WKNavigationDelegate 方法
    func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
        print("Web page loaded successfully.")
    }

    func webView(_ webView: WKWebView, didFail navigation: WKNavigation!, withError error: Error) {
        print("Web page loading failed with error: \(error.localizedDescription)")
    }

    // ...
}

WKNavigationDelegate是一个用于WKWebView的协议,它允许监控和控制WKWebView的导航行为,包括网页加载、跳转、错误处理等。通过实现WKNavigationDelegate的方法,可以在不同的导航事件发生时执行特定的操作。

以下是WKNavigationDelegate中常见方法的详细说明:

  1. webView(_:decidePolicyFor:decisionHandler:) : 这是WKNavigationDelegate的主要方法之一。当发起一个导航行为(如链接点击或页面跳转)时,此方法会被调用。可以在这里决定是否允许导航,以及如何处理导航行为。需要调用decisionHandler闭包,并传入.cancel.allow来决定是否允许导航。
  2. webView(_:didStartProvisionalNavigation:) : 在开始加载页面内容时调用。可以在这里执行一些UI操作,例如显示加载指示器。
  3. webView(_:didCommit:) : 在开始接收网页数据并将其渲染时调用。在此阶段,页面的内容已经开始显示,可以继续进行一些UI更新。
  4. webView(_:didFinish:) : 在页面加载完成后调用。可以在此方法中执行一些操作,例如隐藏加载指示器、更新界面元素等。
  5. webView(_:didFail:withError:) : 如果页面加载失败,则会调用此方法。可以在这里处理加载错误,例如显示错误信息或执行其他操作。
  6. webView(_:didReceive:completionHandler:) : 当接收到服务器的身份验证挑战时(例如需要登录时),此方法会被调用。可以在这里提供身份验证凭据。
  7. webView(_:didReceiveServerRedirectForProvisionalNavigation:) : 当网页重定向时,此方法会被调用。可以在此方法中处理重定向的情况。
  8. webViewWebContentProcessDidTerminate(_:) : 当WKWebView的Web内容进程终止时,此方法会被调用。可以在此重新加载页面或执行其他操作。

通过实现这些方法,可以全面控制WKWebView的导航行为和加载过程。例如,可以拦截导航行为以防止某些网页被加载,处理页面加载错误,实现自定义的身份验证等。在使用WKNavigationDelegate时,确保在适当的时机调用decisionHandler以控制导航的继续或取消。

5.通过 WKWebViewConfiguration 设置各种属性和选项

WKWebViewConfiguration是用于配置WKWebView的一个类,它允许您通过设置各种属性和选项,来定制和控制WKWebView的行为和外观。使用WKWebViewConfiguration,您可以在加载和显示Web内容时进行更细粒度的控制,以满足不同的应用需求。

以下是WKWebViewConfiguration中一些常见属性和配置选项的详细讲解:

  1. processPool: WKProcessPool对象,用于共享Web内容处理的进程池。多个WKWebView可以共享同一个进程池,以便实现资源共享,如cookie、缓存等。
  2. preferences: WKPreferences对象,用于设置WKWebView的偏好选项,如是否启用JavaScript、是否允许图片加载等。
  3. userContentController: WKUserContentController对象,用于管理与JavaScript交互相关的内容,如添加自定义的JavaScript脚本、消息处理等。
  4. websiteDataStore: WKWebsiteDataStore对象,用于管理网站数据,如Cookies、缓存等。每个WKWebView可以有自己的数据存储,或者共享同一个数据存储。
  5. applicationNameForUserAgent: 设置应用程序的名称,将出现在User Agent中,用于标识应用程序。
  6. allowsInlineMediaPlayback: 一个布尔值,指定是否允许在WKWebView内直接播放音频或视频。
  7. mediaTypesRequiringUserActionForPlayback: 一个枚举值,用于设置媒体类型是否需要用户交互才能自动播放。
  8. selectionGranularity: 设置文本选取的粒度,可以是字符、词语、句子等。
  9. suppressesIncrementalRendering: 一个布尔值,用于控制是否抑制增量渲染,即在页面加载完成前禁止显示内容。
  10. ignoresViewportScaleLimits: 一个布尔值,指定是否忽略视口缩放限制。
  11. dataDetectorTypes: 设置要检测的数据类型,如电话号码、链接等。
  12. defaultWebpagePreferences: WKWebpagePreferences对象,用于设置默认的网页首选项,如预览类型、格式等。

通过配置WKWebViewConfiguration,可以调整WKWebView的行为和外观。在创建WKWebView实例时,将这些配置应用于配置对象,然后将配置对象传递给WKWebView的初始化方法,从而实现对WKWebView的定制。

let configuration = WKWebViewConfiguration()
configuration.preferences.javaScriptEnabled = true
configuration.userContentController = WKUserContentController()
let webView = WKWebView(frame: CGRect.zero, configuration: configuration)

5.通过 WKUIDelegate 处理与用户界面相关的事件

WKUIDelegate是一个用于WKWebView的协议,用于处理与用户界面相关的事件,如弹出警报框、确认框、输入框等。通过实现WKUIDelegate中的方法,您可以在WKWebView中的Web内容需要显示与用户界面交互的元素时,执行相应的操作。

以下是WKUIDelegate中一些常见方法的详细说明:

  1. webView(_:runJavaScriptAlertPanelWithMessage:initiatedByFrame:completionHandler:) : 当Web页面调用alert函数时,该方法会被调用。您可以在这里显示一个警报框,并根据用户的操作调用completionHandler闭包。
  2. webView(_:runJavaScriptConfirmPanelWithMessage:initiatedByFrame:completionHandler:) : 当Web页面调用confirm函数时,该方法会被调用。您可以在这里显示一个确认框,并根据用户的选择调用completionHandler闭包。
  3. webView(_:runJavaScriptTextInputPanelWithPrompt:defaultText:initiatedByFrame:completionHandler:) : 当Web页面调用prompt函数时,该方法会被调用。您可以在这里显示一个输入框,并根据用户的输入调用completionHandler闭包。
  4. webView(_:createWebViewWith:for:windowFeatures:) : 当Web页面请求创建新的WKWebView时,该方法会被调用。您可以在这里创建一个新的WKWebView实例,并返回它。
  5. webViewDidClose(_:) : 当一个WKWebView被关闭时,该方法会被调用。您可以在这里执行一些清理操作。

当使用WKWebView加载包含JavaScript的alertconfirmprompt函数的Web页面时,可以通过实现WKUIDelegate的相应方法来定制这些弹窗的外观和行为。以下是如何在WKUIDelegate中定制这三种弹窗的示例:

import UIKit
import WebKit

class ViewController: UIViewController, WKUIDelegate {

    var webView: WKWebView!

    override func viewDidLoad() {
        super.viewDidLoad()

        let configuration = WKWebViewConfiguration()
        configuration.preferences.javaScriptEnabled = true
        
        // 创建 WKWebView 实例并设置 WKUIDelegate
        webView = WKWebView(frame: view.bounds, configuration: configuration)
        webView.uiDelegate = self
        view.addSubview(webView)
        
        // 加载包含弹窗调用的 Web 页面
        if let url = URL(string: "https://www.example.com") {
            let request = URLRequest(url: url)
            webView.load(request)
        }
    }
    
    // 实现 WKUIDelegate 方法来定制弹窗
    func webView(_ webView: WKWebView, runJavaScriptAlertPanelWithMessage message: String, initiatedByFrame frame: WKFrameInfo, completionHandler: @escaping () -> Void) {
        // 创建自定义的警报框
        let alertController = UIAlertController(title: "Alert", message: message, preferredStyle: .alert)
        alertController.addAction(UIAlertAction(title: "OK", style: .default, handler: { _ in
            // 用户点击了OK按钮后调用completionHandler
            completionHandler()
        }))
        
        // 显示警报框
        self.present(alertController, animated: true, completion: nil)
    }
    
    func webView(_ webView: WKWebView, runJavaScriptConfirmPanelWithMessage message: String, initiatedByFrame frame: WKFrameInfo, completionHandler: @escaping (Bool) -> Void) {
        // 创建自定义的确认框
        let alertController = UIAlertController(title: "Confirm", message: message, preferredStyle: .alert)
        alertController.addAction(UIAlertAction(title: "Cancel", style: .default, handler: { _ in
            // 用户点击了Cancel按钮后调用completionHandler
            completionHandler(false)
        }))
        alertController.addAction(UIAlertAction(title: "OK", style: .default, handler: { _ in
            // 用户点击了OK按钮后调用completionHandler
            completionHandler(true)
        }))
        
        // 显示确认框
        self.present(alertController, animated: true, completion: nil)
    }
    
    func webView(_ webView: WKWebView, runJavaScriptTextInputPanelWithPrompt prompt: String, defaultText: String?, initiatedByFrame frame: WKFrameInfo, completionHandler: @escaping (String?) -> Void) {
        // 创建自定义的输入框
        let alertController = UIAlertController(title: "Input", message: prompt, preferredStyle: .alert)
        alertController.addTextField(configurationHandler: { textField in
            textField.text = defaultText
        })
        alertController.addAction(UIAlertAction(title: "Cancel", style: .default, handler: { _ in
            // 用户点击了Cancel按钮后调用completionHandler
            completionHandler(nil)
        }))
        alertController.addAction(UIAlertAction(title: "OK", style: .default, handler: { _ in
            // 用户点击了OK按钮后调用completionHandler
            completionHandler(alertController.textFields?.first?.text)
        }))
        
        // 显示输入框
        self.present(alertController, animated: true, completion: nil)
    }
    
    // 实现其他 WKUIDelegate 方法...
}

文章结束。