如何清除 WKWebView 的 WKBackForwardList?
How to clear the WKBackForwardList of a WKWebView?
WKWebView 的 backForwardList
似乎是只读的,但我见过人们用一些非常神奇的方法来解决这个问题。我需要想办法清除 WKWebView 的历史记录。任何想法我怎么会这样?到目前为止,我尝试了一些失败的技巧:
- 使用 keyValue:forKey 无效。
- 使用 C 指针
->
无效。
我看到有人谈论合成 属性 和扩展 class,但我真的不知道它是如何工作的,也想不通。还有其他想法吗?
这段代码可以编译,但我还没有测试过...
首先,我将 WKWebView
子类化,用我自己的 WKBackForwardList
子类覆盖 backForwardList
。
然后,在我的 WKBackForwardList
子类中,我可以覆盖 backItem
和 forwardItem
使它们 return 为零,而不是让它们查看各自的列表(这很可能是默认实现)。
或者我可以像在 WKWebView
和 backForwardList
中那样覆盖 backList
和 forwardList
。我这样做是为了添加 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 以外的开发人员非常严格,您有两个选择:
最简单的方法(当然也是 Apple 的意图)是创建一个新的 WKWebView
并用它替换当前的。这并不是一个很难实施的解决方案,因为您可能需要从旧实例复制到新实例的配置数量非常有限。您甚至可以创建一个包装器视图来为您完成此操作,这样您只需调用 clearHistory()
,包装器视图就会为您进行基础交换。
如果由于某种原因,你无法替换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))
}
WKWebView 的 backForwardList
似乎是只读的,但我见过人们用一些非常神奇的方法来解决这个问题。我需要想办法清除 WKWebView 的历史记录。任何想法我怎么会这样?到目前为止,我尝试了一些失败的技巧:
- 使用 keyValue:forKey 无效。
- 使用 C 指针
->
无效。
我看到有人谈论合成 属性 和扩展 class,但我真的不知道它是如何工作的,也想不通。还有其他想法吗?
这段代码可以编译,但我还没有测试过...
首先,我将 WKWebView
子类化,用我自己的 WKBackForwardList
子类覆盖 backForwardList
。
然后,在我的 WKBackForwardList
子类中,我可以覆盖 backItem
和 forwardItem
使它们 return 为零,而不是让它们查看各自的列表(这很可能是默认实现)。
或者我可以像在 WKWebView
和 backForwardList
中那样覆盖 backList
和 forwardList
。我这样做是为了添加 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 以外的开发人员非常严格,您有两个选择:
最简单的方法(当然也是 Apple 的意图)是创建一个新的
WKWebView
并用它替换当前的。这并不是一个很难实施的解决方案,因为您可能需要从旧实例复制到新实例的配置数量非常有限。您甚至可以创建一个包装器视图来为您完成此操作,这样您只需调用clearHistory()
,包装器视图就会为您进行基础交换。如果由于某种原因,你无法替换
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))
}