前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >如何感知 WebKit 页面切换

如何感知 WebKit 页面切换

作者头像
波儿菜
修改2021-11-24 15:44:15
1.5K0
修改2021-11-24 15:44:15
举报
文章被收录于专栏:iOS技术iOS技术

背景

通常在 WKWebView 打开一个页面,收到页面数据时,代理方法可感知这个时机:

代码语言:javascript
复制
-webView:didCommitNavigation:

但若是改变页面 hash (也就是位置标识符"#") 打开另一页面时,这个代理方法不会调用,也没有合适的回调接口。

从表现上说,改变 hash 会产生网页历史栈,safari 也会产生历史记录,这种场景应该是有和 -webView:didCommitNavigation: 相对应的回调,官方没做好一致性,需通过 WebKit 源码进一步探索感知方式。

源码分析

断点查看触发-webView:didCommitNavigation:调用栈如下:

代码语言:javascript
复制
-> 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

在第一次收到页面数据后,会进行网页历史栈等状态的处理,最后抛给公开代理。对于改变页面 hash 打开另一页面场景,是在同一个 Document,FrameLoader 作为专门处理页面加载的地方,应该是有处理目标页面是否是同一 Document 的代码分支,扫描一下就找到了一个可疑的函数:

代码语言:javascript
复制
void FrameLoader::loadItem(...) {
    ...
    if (sameDocumentNavigation)
        loadSameDocumentItem(item);
    else
        loadDifferentDocumentItem(item, fromItem, loadType, MayAttemptCacheOnlyLoadForFormSubmissionItem, shouldTreatAsContinuingLoad);
}

追溯函数调用链,发现-webView:didCommitNavigation:调用栈正是来自else分支,查看if分支的处理,最终会通过一个 IPC 消息发送到 APP 进程,在 APP 进程代码实现如下:

代码语言:javascript
复制
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就关联了 WKWebView 的公开代理 navigationDelegate,而这个代理方法在私有代理方法列表躺着:

代码语言:javascript
复制
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 的页面切换。实现这个私有代理从源码来看是无副作用的,MR 记录在这里:https://bugs.webkit.org/show_bug.cgi?id=134855

结论

所以只需要在 WKWebView 的 navigationDelegate 所属类下面实现 _webView:navigation:didSameDocumentNavigation: 方法就能捕获到改变 hash 的页面切换的操作了,和 -webView:didCommitNavigation: 配对可完整感知 WKWebView 的页面切换完成时机。

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2021/10/24 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 背景
  • 源码分析
  • 结论
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档