背景
通常在 WKWebView 打開一個頁面,收到頁面數(shù)據(jù)時纵朋,代理方法可感知這個時機:
-webView:didCommitNavigation:
但若是改變頁面 hash (也就是位置標識符"#") 打開另一頁面時,這個代理方法不會調(diào)用,也沒有合適的回調(diào)接口滓鸠。
從表現(xiàn)上說拾碌,改變 hash 會產(chǎn)生網(wǎng)頁歷史棧吐葱,safari 也會產(chǎn)生歷史記錄,這種場景應(yīng)該是有和 -webView:didCommitNavigation:
相對應(yīng)的回調(diào)校翔,官方?jīng)]做好一致性弟跑,需通過 WebKit 源碼進一步探索感知方式。
源碼分析
斷點查看觸發(fā)-webView:didCommitNavigation:
調(diào)用棧如下:
-> UIProcess 進程
-[AnyInstance webView:didCommitNavigation:]
WebKit::NavigationState::NavigationClient::didCommitNavigation
(IPC) WebKit::WebPageProxy::didCommitLoadForFrame
-> WebContent 進程
(IPC) Messages::WebPageProxy::DidCommitLoadForFrame
WebKit::WebFrameLoaderClient::dispatchDidCommitLoad
WebCore::FrameLoader::dispatchDidCommitLoad
WebCore::FrameLoader::receivedFirstData
在第一次收到頁面數(shù)據(jù)后防症,會進行網(wǎng)頁歷史棧等狀態(tài)的處理孟辑,最后拋給公開代理。對于改變頁面 hash 打開另一頁面場景蔫敲,是在同一個 Document饲嗽,FrameLoader
作為專門處理頁面加載的地方,應(yīng)該是有處理目標頁面是否是同一 Document 的代碼分支奈嘿,掃描一下就找到了一個可疑的函數(shù):
void FrameLoader::loadItem(...) {
...
if (sameDocumentNavigation)
loadSameDocumentItem(item);
else
loadDifferentDocumentItem(item, fromItem, loadType, MayAttemptCacheOnlyLoadForFormSubmissionItem, shouldTreatAsContinuingLoad);
}
追溯函數(shù)調(diào)用鏈貌虾,發(fā)現(xiàn)-webView:didCommitNavigation:
調(diào)用棧正是來自else
分支,查看if
分支的處理裙犹,最終會通過一個 IPC 消息發(fā)送到 APP 進程尽狠,在 APP 進程代碼實現(xiàn)如下:
void WebPageProxy::didSameDocumentNavigationForFrame(...) {
...
m_navigationClient->didSameDocumentNavigation(...);
...
}
void NavigationState::NavigationClient::didSameDocumentNavigation(...) {
...
[static_cast<id <WKNavigationDelegatePrivate>>(navigationDelegate.get()) _webView:m_navigationState->m_webView navigation:wrapper(navigation) didSameDocumentNavigation:toWKSameDocumentNavigationType(navigationType)];
}
這里的navigationDelegate
就關(guān)聯(lián)了 WKWebView 的公開代理 navigationDelegate
衔憨,而這個代理方法在私有代理方法列表躺著:
typedef NS_ENUM(NSInteger, _WKSameDocumentNavigationType) {
_WKSameDocumentNavigationTypeAnchorNavigation,
_WKSameDocumentNavigationTypeSessionStatePush,
_WKSameDocumentNavigationTypeSessionStateReplace,
_WKSameDocumentNavigationTypeSessionStatePop,
} WK_API_AVAILABLE(macos(10.10), ios(8.0));
@protocol WKNavigationDelegatePrivate <WKNavigationDelegate>
...
- (void)_webView:(WKWebView *)webView navigation:(WKNavigation *)navigation didSameDocumentNavigation:(_WKSameDocumentNavigationType)navigationType;
...
@end
粗略分析下源碼,當 navigationType
這個枚舉是 _WKSameDocumentNavigationTypeAnchorNavigation
時就表示完成了這次改變 hash 的頁面切換袄膏。實現(xiàn)這個私有代理從源碼來看是無副作用的践图,MR 記錄在這里:https://bugs.webkit.org/show_bug.cgi?id=134855 。
結(jié)論
所以只需要在 WKWebView 的 navigationDelegate
所屬類下面實現(xiàn) _webView:navigation:didSameDocumentNavigation:
方法就能捕獲到改變 hash 的頁面切換的操作了沉馆,和 -webView:didCommitNavigation:
配對可完整感知 WKWebView 的頁面切換完成時機码党。