WKWebView 是 iOS 8.0 以后用于替代 UIWebView 的浏览器组件。和 UIWebView 相比,具有渲染性能更好、支持更多的 HTML5 特性、控制更加细致等诸多优点。一直没沉下心来学习,我们一起好好看看里面都有什么吧🤓
WKWebView 主要的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 @interface WKWebView : UIView @property (nonatomic , readonly , copy ) WKWebViewConfiguration *configuration;@property (nullable , nonatomic , weak ) id <WKNavigationDelegate > navigationDelegate;@property (nullable , nonatomic , weak ) id <WKUIDelegate > UIDelegate ;@property (nonatomic , readonly , strong ) WKBackForwardList *backForwardList;@property (nonatomic , readonly , nullable ) SecTrustRef serverTrust;@property (nullable , nonatomic , copy ) NSString *customUserAgent;@property (nonatomic ) BOOL allowsLinkPreview;@property (nonatomic , readonly , strong ) UIScrollView *scrollView;... - (nullable WKNavigation *)loadRequest:(NSURLRequest *)request; - (nullable WKNavigation *)loadFileURL:(NSURL *)URL allowingReadAccessToURL:(NSURL *)readAccessURL; ... - (nullable WKNavigation *)goBack; - (nullable WKNavigation *)goForward; - (nullable WKNavigation *)reload; - (nullable WKNavigation *)reloadFromOrigin; + (BOOL )handlesURLScheme:(NSString *)urlScheme; - (void )evaluateJavaScript:(NSString *)javaScriptString completionHandler:(void (^ _Nullable)(_Nullable id , NSError * _Nullable error))completionHandler; - (void )evaluateJavaScript:(NSString *)javaScriptString inFrame:(nullable WKFrameInfo *)frame inContentWorld:(WKContentWorld *)contentWorld completionHandler:(void (^ _Nullable)(_Nullable id , NSError * _Nullable error))completionHandler; - (void )callAsyncJavaScript:(NSString *)functionBody arguments:(nullable NSDictionary <NSString *, id > *)arguments inFrame:(nullable WKFrameInfo *)frame inContentWorld:(WKContentWorld *)contentWorld completionHandler:(void (^ _Nullable)(_Nullable id , NSError * _Nullable error))completionHandler; @end
WKWebView 初始化 一个简单用于展示的 WebView 可以是这样的:
1 2 3 4 5 6 7 8 9 10 WKWebViewConfiguration *configuration = [[WKWebViewConfiguration alloc] init];WKWebView *webView = ({ webView = [[WKWebView alloc] initWithFrame:self .view.bounds configuration:configuration]; webView.scrollView.bounces = NO ; webView.backgroundColor = [UIColor whiteColor]; webView; }); [self .view addSubview:webView]; [webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"http://baidu.com" ]]];
这样一个单纯用于展示网页、没有任何配置项的 WebView 就算完成了。相较于 UIWebView
,我们看到多了一个 configuration
配置类型,那我们就来 Read the fucking source code……
WKWebViewConfiguration 官方文档是这样描述这个类型的:
A WKWebViewConfiguration
object is a collection of properties with which to initialize a web view.
一个用于初始化 web view 属性的集合。
我们可以用它做什么呢?
设置用于网站的初始cookie
处理自定义的 URL schemes
设置如何处理媒体内容
管理网页中选中的信息
自定义注入网页的脚本
自定义内容的展示规则
……
我们可以通过创建一个 WKWebViewConfiguration
对象来设置网页的属性,并且在 WebView 初始化的时候传递给它。注意的是只能在初始化的时候配置 configuration
中的属性,后面是没办法动态再去修改这些配置的。
我们通过这个类看看 WebKit 里面有哪些主要的内容:
WKProcessPool 1 2 3 4 5 6 7 @property (nonatomic , strong ) WKProcessPool *processPool;
1 2 @interface WKProcessPool : NSObject <NSSecureCoding >@end
官方文档解释为一个可以在单个进程中运行多个 web 视图的不可见 token?令牌?。进程池。可以看到 WKProcessPool 类没有暴漏任何接口,这意味着我们只能创建和读取该对象,通过对象地址判断是否在相同进程。
WKWebView 为了安全和稳定性考虑,会为每一个 WKWebView 实例分配独立的进程(而不是直接使用APP的进程空间),系统会有一个设定的进程个数上线。相同 WKProcessPool 对象的 WKWebView 共享相同的进程空间。这也是与 UIWebView
最大不同的一点:NSHTTPCookieStorage
中的 cookie ,UIWebView
是可以自动携带使用的,但 WKWebView
无法获取 Storage中 的 cookie。
诶,那是不是放在同一个进程池中的 web view 就可以共享 cookie 了呢?带着这个问题,稍后我们会提到 cookie 有关的处理。
WKPreferences 1 2 3 @property (nonatomic , strong ) WKPreferences *preferences;
针对 web 视图的偏好设置,如果是针对 web 内容的设置还是使用 WKWebViewConfiguration
,感觉这个类还在完善、扩充中,内容很少。比较值得注意的是与 JavaScript 有关的两个属性。
1 2 3 4 5 6 7 8 @property (nonatomic ) CGFloat minimumFontSize;@property (nonatomic ) BOOL javaScriptCanOpenWindowsAutomatically;@property (nonatomic ) BOOL javaScriptEnabled;@property (nonatomic ) BOOL fraudulentWebsiteWarningEnabled;
WKUserContentController 1 2 3 @property (nonatomic , strong ) WKUserContentController *userContentController;
这个类提供了一个 JavaScript 向 web view 发送消息的途径,可以增删用户脚本。
JavaScript 与原生做交互,比较多的一个场景是需要调用原生的某些能力。在 UIWebView
中简单的方式是拦截请求,根据特定的 scheme 或者参数来区分,那在 WKWebView
中则是通过 WKUserContentController
添加消息处理器。例如打开相机功能:
1 2 3 4 WKUserContentController *userContentController = [[WKUserContentController alloc] init];[userContentController addScriptMessageHandler:self name:@"OpenCamera" ]; WKWebViewConfiguration *configuration = [[WKWebViewConfiguration alloc] init];configuration.userContentController = userContentController;
实现 WKScriptMessageHandler 代理的方法
1 2 3 4 5 6 #pragma mark - WKScriptMessageHandler - (void )userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message { if ([message.name caseInsensitiveCompare:@"OpenCamera" ] == NSOrderedSame ) { } }
1 window .webkit.messageHandlers.OpenCamera.postMessage();
我们看到接收 JavaScript 的消息是通过一个 WKScriptMessage
类型,我们再来简单看下这个类
WKScriptMessage
A WKScriptMessage object contains information about a message sent from a webpage.
他的注释就很简单了:一个包含网页发来的消息内容的对象。
1 2 3 4 5 6 7 8 9 10 @property (nonatomic , readonly , copy ) id body;@property (nullable , nonatomic , readonly , weak ) WKWebView *webView;@property (nonatomic , readonly , copy ) WKFrameInfo *frameInfo;@property (nonatomic , readonly , copy ) NSString *name;@property (nonatomic , readonly ) WKContentWorld *world API_AVAILABLE(macos(11.0 ), ios(14.0 ));
WKUserScript
A script that the web view injects into a webpage.
当需要将自定义脚本代码注入 Web 页面时,可以创建一个 WKUserScript 对象。使用此对象指定要注入的 JavaScript 代码,以及与注入该代码的时间和方式相关的参数。通过前面提到的 WKUserContentController 调用 addUserScript:
完成注入。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 @interface WKUserScript : NSObject <NSCopying >@property (nonatomic , readonly , copy ) NSString *source;@property (nonatomic , readonly ) WKUserScriptInjectionTime injectionTime;@property (nonatomic , readonly , getter =isForMainFrameOnly) BOOL forMainFrameOnly;- (instancetype )initWithSource:(NSString *)source injectionTime:(WKUserScriptInjectionTime )injectionTime forMainFrameOnly:(BOOL )forMainFrameOnly; - (instancetype )initWithSource:(NSString *)source injectionTime:(WKUserScriptInjectionTime )injectionTime forMainFrameOnly:(BOOL )forMainFrameOnly inContentWorld:(WKContentWorld *)contentWorld WK_API_AVAILABLE (macos(11.0 ), ios(14.0 )); @end
WKContentWorld
An object that defines a scope of execution for JavaScript code, and which you use to prevent conflicts between different scripts.
WKContentWorld 是 iOS 14 的新增内容,可以理解为不同的命名空间、不同的运行环境。显而易见的,在逻辑上,原生的 JS 环境和 web JS 运行环境存在命名冲突的可能。WKContentWorld 有两个类属性 defaultClientWorld
、pageWorld
,分别代表原生和 web 容器的 JS 运行空间。开发者也可以通过:
1 + (WKContentWorld *)worldWithName:(NSString *)name;
工厂方法创建一个独立的 JS 运行环境。
WKWebsiteDataStore 这个类貌似包含了一个 web view 的所有数据,我看完这个类的介绍,第一感觉是,哇,我可以窥探一切了。然而,除了 cookie,一毛钱都拿不到……不讲了,有兴趣自己试吧~~
1 2 3 4 5 6 7 8 9 10 11 12 ( WKWebsiteDataTypeDiskCache , WKWebsiteDataTypeOfflineWebApplicationCache , WKWebsiteDataTypeMemoryCache , WKWebsiteDataTypeLocalStorage , WKWebsiteDataTypeFetchCache , WKWebsiteDataTypeCookies , WKWebsiteDataTypeSessionStorage , WKWebsiteDataTypeIndexedDBDatabases , WKWebsiteDataTypeWebSQLDatabases , WKWebsiteDataTypeServiceWorkerRegistrations )
WKHTTPCookieStore
A WKHTTPCookieStore object allows managing the HTTP cookies associated with a particular WKWebsiteDataStore.
用来管理与特定 WKWebsiteDataStore
相关联的 HTTP cookie。
API 看上去很简单…但获得 cookie 是异步操作,与 NSHTTPCookieStorage
的同步操作不同,处理起来可能要注意下。
1 2 3 4 5 - (void )getAllCookies:(void (^)(NSArray <NSHTTPCookie *> *))completionHandler; - (void )setCookie:(NSHTTPCookie *)cookie completionHandler:(nullable void (^)(void ))completionHandler; - (void )deleteCookie:(NSHTTPCookie *)cookie completionHandler:(nullable void (^)(void ))completionHandler; - (void )addObserver:(id <WKHTTPCookieStoreObserver >)observer; - (void )removeObserver:(id <WKHTTPCookieStoreObserver >)observer;
对应观察者的协议方法:
在 cookie 发生变化时,可以异步通知,但经测试是有一点延迟的,有兴趣可以测一测?
1 2 3 4 @protocol WKHTTPCookieStoreObserver <NSObject >@optional - (void )cookiesDidChangeInCookieStore:(WKHTTPCookieStore *)cookieStore; @end
WKBackForwardList 访问过的 web 页面历史记录。
WKNavigation WKNavigation 对象可以用来了解网页的加载进度。通过 loadRequest、goBack 等方法加载页面时,将返回一个 WKNavigation 对象。通过 WKNavigationDelegate 代理的以下几个方法,可以知道页面的加载情况。WKNavigationDelegate 稍后我们再说。
WKNavigationAction 包含网页导航信息,需要据此显示对应的操作界面。
WKFrameInfo 标识当前网页内容信息的对象。
关于 WKWebView 的几篇文章: WKWebView 基础篇 WKWebView 协议篇 WKWebView 实战篇 WKWebView Cookie 试错 WKWebView - WKScriptMessageHandler 循环引用
Demo WebView 的 Demo