如何清除 WKWebView 的 WKBackForwardList?

How to clear the WKBackForwardList of a WKWebView?

WKWebView 的 backForwardList 似乎是只读的,但我见过人们用一些非常神奇的方法来解决这个问题。我需要想办法清除 WKWebView 的历史记录。任何想法我怎么会这样?到目前为止,我尝试了一些失败的技巧:

我看到有人谈论合成 属性 和扩展 class,但我真的不知道它是如何工作的,也想不通。还有其他想法吗?

这段代码可以编译,但我还没有测试过...

首先,我将 WKWebView 子类化,用我自己的 WKBackForwardList 子类覆盖 backForwardList

然后,在我的 WKBackForwardList 子类中,我可以覆盖 backItemforwardItem 使它们 return 为零,而不是让它们查看各自的列表(这很可能是默认实现)。

或者我可以像在 WKWebViewbackForwardList 中那样覆盖 backListforwardList。我这样做是为了添加 setter,这将允许我从列表中删除项目。

import Foundation
import WebKit

class WebViewHistory: WKBackForwardList {

    /* Solution 1: return nil, discarding what is in backList & forwardList */

    override var backItem: WKBackForwardListItem? {
        return nil
    }

    override var forwardItem: WKBackForwardListItem? {
        return nil
    }

    /* Solution 2: override backList and forwardList to add a setter */

    var myBackList = [WKBackForwardListItem]()

    override var backList: [WKBackForwardListItem] {
        get {
            return myBackList
        }
        set(list) {
            myBackList = list
        }
    }

    func clearBackList() {
        backList.removeAll()
    }
}

class WebView: WKWebView {

    var history: WebViewHistory

    override var backForwardList: WebViewHistory {
        return history
    }

    init(frame: CGRect, configuration: WKWebViewConfiguration, history: WebViewHistory) {
        self.history = history
        super.init(frame: frame, configuration: configuration)
    }

    /* Not sure about the best way to handle this part, it was just required for the code to compile... */

    required init?(coder: NSCoder) {

        if let history = coder.decodeObject(forKey: "history") as? WebViewHistory {
            self.history = history
        }
        else {
            history = WebViewHistory()
        }

        super.init(coder: coder)
    }

    override func encode(with aCoder: NSCoder) {
        super.encode(with: aCoder)
        aCoder.encode(history, forKey: "history")
    }
}

措词于 iOS8 ~ iOS11。

Objective-C

#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
[wek.backForwardList performSelector:NSSelectorFromString(@"_removeAllItems")];
#pragma clang diagnostic pop

Swift 4

webView.backForwardList.perform(Selector(("_removeAllItems")))

!!!!注意!!!!此方法在 WebKit Open Resource 中声明,而不是 public 方法。

阅读文档您可以获得 WKWebView 的 WKBackForwardList。 之后,您可以在 WKBackForwardList 的 backList 中找到第一项,然后使用: func go(to: WKBackForwardListItem) -> WKNavigation? 有了这个,你可以清理历史到第一个 url.

因为 Apple 将 WKBackForwardList 的接口严格锁定,并且对 published public interfaces 以外的开发人员非常严格,您有两个选择:

  1. 最简单的方法(当然也是 Apple 的意图)是创建一个新的 WKWebView 并用它替换当前的。这并不是一个很难实施的解决方案,因为您可能需要从旧实例复制到新实例的配置数量非常有限。您甚至可以创建一个包装器视图来为您完成此操作,这样您只需调用 clearHistory(),包装器视图就会为您进行基础交换。

  2. 如果由于某种原因,你无法替换WKWebView的实例,你可以利用历史的一维特性来清除历史down到两项。恐怕你不能再清除它了。要将 URL 清除为两个 URL,假设它们存储在值 urlA 和 urlB 中,您可以这样做:

// At initialization, ensure that urlA is the first item in the history
let webview = WKWebView()
// ... whatever other init you need
webview.load(URLRequest(url: urlA))

然后“清除”历史记录:

func clearHistory() {
    // First we make sure the webview is on the earliest item in the history
    if webview.canGoBack {
        webview.go(to: webview.backForwardList.backList.first)
    }
    // Then we navigate to our urlB so that we destroy the old "forward" stack
    webview.load(URLRequest(url: urlB))
}