如何清除WKWebView的WKBackForwardList?

9
看起来 WKWebView 的 backForwardList 是只读的,但我看到有人用了一些神奇的方法绕过了这个限制。我需要找出一种清除 WKWebView 历史记录的方法。你有什么想法吗?到目前为止,我尝试过以下几个方法都失败了:
  • 使用 keyValue:forKey 没有起作用。
  • 使用 C 指针 -> 也没有起作用。
我看到有人谈论合成属性并扩展类,但我不知道它是如何工作的,也无法弄清楚。还有其他的想法吗?

你可以尝试实现自己的存储... WKBackForwardList 是不可变的。 - Papershine
使用 allowsBackForwardNavigationGesturescanGoBackcanGoForward 怎么样?或者您需要特别清除 backForwardList 吗? - nyg
@nyg 我需要清除历史记录,而不仅仅是防止滑动。 - Chet
4个回答

5

适用于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 中声明,而不是公共方法。


“Declear” 是什么意思?其次,你是从哪里找到如何做到这一点的? - Chet
@Chet 它是“declared”,拼写错误。在WebKit Open Resource中查找。 - xu tong
徐彤的解决方案会被苹果接受吗? 因此会有任何拒绝吗? - JDroid
2
绝对会因为使用这种方法而被拒绝。它非常简单,不在WKBackForwardList API文档中列出,因此属于私有API。这是一种非常糟糕的解决问题的方式,并且在苹果决定在未来版本的iOS中更改该签名时可能会导致崩溃。 - Axy
这意味着它现在可以工作,但它不是一种记录在案的方法,因此您不应该使用它,因为它可能会在没有警告的情况下消失。 - drewster

3

由于苹果对 WKBackForwardList 接口的控制非常严格,对那些超出公开接口范围的开发人员很严格,因此您有两个选择:

  1. 最简单的方法(也是苹果预期的)是创建一个新的WKWebView并用其替换当前的WebView。这不是一个很难实现的解决方案,因为从旧实例到新实例之间可以复制的配置数量相当有限。甚至可以创建一个包装视图来代替您执行这项任务,这样您只需要调用clearHistory()即可,包装视图将为您执行底层交换。

  2. 如果由于某种原因,您无法替换WKWebView实例,则可以利用历史记录的一维性质将历史记录清除至最后两项。很抱歉,您不能清除更多了。要将 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))
} 

2

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

首先,我创建了一个WKWebView的子类,重写了其中的backForwardList方法,并使用自己的WKBackForwardList子类。

然后,在我的WKBackForwardList子类中,我可以重写backItemforwardItem方法,使它们返回nil,而不是查看它们各自的列表(这很可能是默认实现)。

或者我可以像在WKWebView中使用backForwardList时那样,重写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")
    }
}

@Chet,我看到了你最新的提交,修正了方法名称中的一个拼写错误。这是否解决了错误? - nyg
@Chet,请检查CDVWKWebViewEngine.m文件,第138行,您仍在使用[WKWebView alloc]。我认为应该是[CVDWKWebView alloc],对吗? - nyg
3
在导航过程中,backForwardList 不会改变。在我的应用程序中它始终为空。有什么问题吗? - Oscar Yuandinata
3
不幸的是,这个绝妙的想法行不通。似乎backList只是一个派生属性,因此WebKit内部并没有使用它来存储实际访问过的项。 - Ely
2
确实,不起作用。一种解决方法是创建一个新的WKWebView并传递旧配置。 - Joel
显示剩余5条评论

0
阅读文档可以得知,可以使用WKWebView获取WKBackForwardList。然后,可以找到WKBackForwardList的backList中的第一个项目,并使用以下方法进行操作:func go(to: WKBackForwardListItem) -> WKNavigation?。通过这种方式,您可以清除历史记录直到第一个URL。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接