一直以來項目中都習慣使用UIWebview
,其實WK功能更增強大,包括與JS的交互。WK的加載速度比UIWebView
提高差很少一倍,內存使用上面反而還少了一半。而且蘋果推薦使用WK。因此,今天就整理一下WK的相關知識。html
iOS8.0
以後,蘋果推薦使用WebKit框架中的WKWebView
來加載網頁,使用WKWebViewConfiguration
來配置JS交互。java
首先導入#import <WebKit/WebKit.h> 建立UIgit
_webView = [[WKWebView alloc] initWithFrame:CGRectMake(0, 0, SCREEN_W, SCREEN_H) configuration:config]; //config爲建立好的配置對象
_webView.UIDelegate = self; // UI代理
_webView.navigationDelegate = self; // 導航代理
_webView.allowsBackForwardNavigationGestures = YES; // 左滑返回
//可返回的頁面列表, 存儲已打開過的網頁
WKBackForwardList * backForwardList = [_webView backForwardList];
複製代碼
打開網頁github
NSString *path = [[NSBundle mainBundle] pathForResource:@"JStoOC.html" ofType:nil];
NSString *htmlString = [[NSString alloc]initWithContentsOfFile:path encoding:NSUTF8StringEncoding error:nil];
//加載本地html文件
[_webView loadHTMLString:htmlString baseURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] bundlePath]]];
複製代碼
網頁切換web
[_webView goBack]; //頁面後退
[_webView goForward]; //頁面前進
[_webView reload]; //刷新當前頁面
複製代碼
網頁執行JS方法緩存
[_webView evaluateJavaScript:@"h5method()" completionHandler:^(id _Nullable response, NSError * _Nullable error) {
}];
複製代碼
主要依靠WKScriptMessageHandler
協議類、WKUserContentController
其中: WKUserContentController
對象負責註冊JS方法,設置處理接收JS方法的代理,代理遵照WKScriptMessageHandler
,實現捕捉到JS消息的回調方法。服務器
一、配置與JS的交互 用WKWebViewConfiguration
來配置JS交互markdown
//一、建立網頁配置對象
WKWebViewConfiguration *config = [[WKWebViewConfiguration alloc] init];
// 二、添加設置對象WKPreferences
WKPreferences *preference = [[WKPreferences alloc]init];
//2.1最小字體大小
// 當將javaScriptEnabled屬性設置爲NO時,能夠看到明顯的效果
preference.minimumFontSize = 0;
//2.2是否支持javaScript 默認是支持的
preference.javaScriptEnabled = YES;
//2.3是否容許不通過用戶交互由javaScript自動打開窗口
preference.javaScriptCanOpenWindowsAutomatically = YES; // 默認爲NO
// 2.4添加
config.preferences = preference;
//三、通常配置
// 是使用h5的視頻播放器在線播放, 仍是使用原生播放器全屏播放
config.allowsInlineMediaPlayback = YES;
//設置視頻是否須要用戶手動播放,設置爲NO則會容許自動播放
config.requiresUserActionForMediaPlayback = YES;
//設置是否容許畫中畫技術 (在特定設備上有效
config.allowsPictureInPictureMediaPlayback = YES;
//設置請求的User-Agent信息中應用程序名稱( iOS9後可用
config.applicationNameForUserAgent = @"ChinaDailyForiPad";
複製代碼
二、使用WKUserContentController
,用來作 原生與JavaScript的交互管理cookie
WKUserContentController * wkUController = [[WKUserContentController alloc] init];
//註冊一個name爲 jsToOcNoPrams 的js方法
[wkUController addScriptMessageHandler:weakScriptMessageDelegate name:@"jsToOcNoPrams"];
[wkUController addScriptMessageHandler:weakScriptMessageDelegate name:@"jsToOcWithPrams"];
// 設置
config.userContentController = wkUController;
// 用完移除
[[_webView configuration].userContentController removeScriptMessageHandlerForName:@"jsToOcNoPrams"];
[[_webView configuration].userContentController removeScriptMessageHandlerForName:@"jsToOcWithPrams"];
複製代碼
三、使用協議類WKScriptMessageHandler
,用來處理監聽JavaScript方法從而調用原生OC方法。(和WKUserContentController
搭配使用)app
// 建立代理
WeakWebViewScriptMessageDelegate *weakScriptMessageDelegate =
[[WeakWebViewScriptMessageDelegate alloc] initWithDelegate:self];
複製代碼
四、經過 接收JS傳出消息的name 進行捕捉的回調方法 ps:遵照WKScriptMessageHandler
協議,代理是由WKUserContentControl
設置
- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message{
NSLog(@"name:%@\\\\n body:%@\\\\n frameInfo:%@\\\\n",message.name,message.body,message.frameInfo);
//用message.body得到JS傳出的參數體
NSDictionary * parameter = message.body;
//JS調用OC
if([message.name isEqualToString:@"jsToOcNoPrams"]){
NSLog("----->js調用到了oc , 不帶參數");
}else if([message.name isEqualToString:@"jsToOcWithPrams"]){
NSLog("----->js調用到了oc , 帶參數");
}
}
複製代碼
使用WKUserScript
,執行自定義的JavaScript代碼
// js代碼字符串
NSString *jSString = @"var meta = document.createElement('meta'); meta.setAttribute('name', 'viewport'); meta.setAttribute('content', 'width=device-width'); document.getElementsByTagName('head')[0].appendChild(meta);";
// 建立WKUserScript
WKUserScript *wkUScript = [[WKUserScript alloc] initWithSource:jSString injectionTime:WKUserScriptInjectionTimeAtDocumentEnd forMainFrameOnly:YES];
// 設置
[config.userContentController addUserScript:wkUScript];
複製代碼
動態加載並運行JS代碼,用於在客戶端內部加入JS代碼,並執行,如:
// 圖片縮放的js代碼
NSString *js = @"var count = document.images.length;for (var i = 0; i < count; i++) {var image = document.images[i];image.style.width=320;};window.alert('找到' + count + '張圖');";
// 根據JS字符串初始化WKUserScript對象
WKUserScript *script = [[WKUserScript alloc] initWithSource:js injectionTime:WKUserScriptInjectionTimeAtDocumentEnd forMainFrameOnly:YES];
// 根據生成的WKUserScript對象,初始化WKWebViewConfiguration
WKWebViewConfiguration *config = [[WKWebViewConfiguration alloc] init];
[config.userContentController addUserScript:script];
_webView = [[WKWebView alloc] initWithFrame:self.view.bounds configuration:config];
NSString *html = @"<head></head><imgea src='http://www.nsu.edu.cn/v/2014v3/img/background/3.jpg' />";
[_webView loadHTMLString:html baseURL:nil];
[self.view addSubview:_webView];
複製代碼
一、WKNavigationDelegate
協議 主要處理一些跳轉、加載處理操做
- (void)webView:(WKWebView *)webView didStartProvisionalNavigation:(WKNavigation *)navigation {
}
複製代碼
- (void)webView:(WKWebView *)webView didFailProvisionalNavigation:(null_unspecified WKNavigation *)navigation withError:(NSError *)error {
[self.progressView setProgress:0.0f animated:NO];
}
複製代碼
- (void)webView:(WKWebView *)webView didCommitNavigation:(WKNavigation *)navigation {
}
複製代碼
- (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation {
// [self getCookie];
}
複製代碼
- (void)webView:(WKWebView *)webView didFailNavigation:(WKNavigation *)navigation withError:(NSError *)error {
[self.progressView setProgress:0.0f animated:NO];
}
複製代碼
- (void)webView:(WKWebView *)webView didReceiveServerRedirectForProvisionalNavigation:(WKNavigation *)navigation {
}
複製代碼
來決定是否跳轉
- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler {
NSString * urlStr = navigationAction.request.URL.absoluteString;
NSString *htmlHeadString = @"github://"; //本身定義的協議頭
if([urlStr hasPrefix:htmlHeadString]){
// 經過截取URL調用OC
NSURL * url = [NSURL URLWithString:[urlStr stringByReplacingOccurrencesOfString:@"github://callName_?" withString:@""]];
[[UIApplication sharedApplication] openURL:url];
decisionHandler(WKNavigationActionPolicyCancel); //
}else{
decisionHandler(WKNavigationActionPolicyAllow); // 容許直接跳轉
}
}
複製代碼
- (void)webView:(WKWebView *)webView decidePolicyForNavigationResponse:(WKNavigationResponse *)navigationResponse decisionHandler:(void (^)(WKNavigationResponsePolicy))decisionHandler{
NSString * urlStr = navigationResponse.response.URL.absoluteString;
NSLog(@"當前跳轉地址:%@",urlStr);
//容許跳轉
decisionHandler(WKNavigationResponsePolicyAllow);
//不容許跳轉
//decisionHandler(WKNavigationResponsePolicyCancel);
}
複製代碼
- (void)webView:(WKWebView *)webView didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential * _Nullable credential))completionHandler{
//用戶身份信息
NSURLCredential * newCred = [[NSURLCredential alloc] initWithUser:@"user123" password:@"123" persistence:NSURLCredentialPersistenceNone];
//爲 challenge 的發送方提供 credential
[challenge.sender useCredential:newCred forAuthenticationChallenge:challenge];
completionHandler(NSURLSessionAuthChallengeUseCredential,newCred);
}
複製代碼
2.WKUIDelegate
協議 主要處理JS腳本,確認框,警告框等
- (void)webView:(WKWebView *)webView runJavaScriptAlertPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(void))completionHandler {
NSLog("HTML彈出框");
completionHandler();
}
複製代碼
@param webView : 實現該代理的webview
@param message : 警告框中的內容
@param completionHandler : 警告框消失回調
複製代碼
confirm是js中的肯定框,須要在block中把用戶選擇的狀況傳遞進去
- (void)webView:(WKWebView *)webView runJavaScriptConfirmPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(BOOL))completionHandler{
completionHandler(NO);
}
複製代碼
prompt是js中的輸入框,須要在block中把用戶輸入的信息傳入
- (void)webView:(WKWebView *)webView runJavaScriptTextInputPanelWithPrompt:(NSString *)prompt defaultText:(NSString *)defaultText initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(NSString * _Nullable))completionHandler{
UIAlertController *alertController = [UIAlertController alertControllerWithTitle:prompt message:@"" preferredStyle:UIAlertControllerStyleAlert];
[alertController addTextFieldWithConfigurationHandler:^(UITextField * _Nonnull textField) {
textField.text = defaultText;
}];
[alertController addAction:([UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
completionHandler(alertController.textFields[0].text?:@"");
}])];
[self presentViewController:alertController animated:YES completion:nil];
}
複製代碼
- (WKWebView *)webView:(WKWebView *)webView createWebViewWithConfiguration:(WKWebViewConfiguration *)configuration forNavigationAction:(WKNavigationAction *)navigationAction windowFeatures:(WKWindowFeatures *)windowFeatures {
if (!navigationAction.targetFrame.isMainFrame) {
[webView loadRequest:navigationAction.request];
}
return nil;
}
複製代碼
0.添加進度條
// 屬性
@property (nonatomic, strong) UIProgressView *progressView;
// 懶加載
- (UIProgressView *)progressView
{
if (!_progressView)
{
_progressView = [[UIProgressView alloc] initWithFrame:CGRectMake(0, 0, SCREEN_W, 2)];
_progressView.backgroundColor = [UIColor blueColor];
_progressView.transform = CGAffineTransformMakeScale(1.0f, 1.5f);
_progressView.progressTintColor = [UIColor app_color_yellow_eab201];
[self.view addSubview:self.progressView];
}
return _progressView;
}
複製代碼
1.添加觀察者
//添加 監測網頁加載進度 的觀察者
[self.webView addObserver:self
forKeyPath:@"estimatedProgress"
options:0
context:nil];
//添加 監測網頁標題title 的觀察者
[self.webView addObserver:self
forKeyPath:@"title"
options:NSKeyValueObservingOptionNew
context:nil];
複製代碼
2.監聽方法
//---kvo 監聽進度 必須實現此方法
-(void)observeValueForKeyPath:(NSString *)keyPath
ofObject:(id)object
change:(NSDictionary<NSKeyValueChangeKey,id> *)change
context:(void *)context
{
if ([keyPath isEqualToString:@"estimatedProgress"])
{
self.progressView.progress = self.webView.estimatedProgress;
if (self.progressView.progress == 1)
{
WeakSelfDeclare
[UIView animateWithDuration:0.25f delay:0.3f options:UIViewAnimationOptionCurveEaseOut animations:^
{
weakSelf.progressView.transform = CGAffineTransformMakeScale(1.0f, 1.4f);
}
completion:^(BOOL finished)
{
weakSelf.progressView.hidden = YES;
}];
}
}else if([keyPath isEqualToString:@"title"]
&& object == _webView){
self.navigationItem.title = _webView.title;
}else{
[super observeValueForKeyPath:keyPath
ofObject:object
change:change
context:context];
}
}
複製代碼
3.顯示/隱藏進度條
- (void)webView:(WKWebView *)webView didStartProvisionalNavigation:(WKNavigation *)navigation
{
self.progressView.hidden = NO;
self.progressView.transform = CGAffineTransformMakeScale(1.0f, 1.5f);
[self.view bringSubviewToFront:self.progressView];
}
- (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation
{
self.progressView.hidden = YES;
}
- (void)webView:(WKWebView *)webView didFailNavigation:(WKNavigation *)navigation withError:(NSError *)error
{
if(error.code==NSURLErrorCancelled)
{
[self webView:webView didFinishNavigation:navigation];
}
else
{
self.progressView.hidden = YES;
}
}
- (void)webView:(WKWebView *)webView didFailProvisionalNavigation:(WKNavigation *)navigation withError:(NSError *)error
{
self.progressView.hidden = YES;
[self.navigationItem setTitleWithCustomLabel:@"加載失敗"];
}
複製代碼
4.移除觀察者
[_webView removeObserver:self
forKeyPath:NSStringFromSelector(@selector(estimatedProgress))];
[_webView removeObserver:self
forKeyPath:NSStringFromSelector(@selector(title))];
複製代碼
- (void)cleanCacheAndCookie
{
//清除cookies
NSHTTPCookie *cookie;
NSHTTPCookieStorage *storage = [NSHTTPCookieStorage sharedHTTPCookieStorage];
for (cookie in [storage cookies])
{
[storage deleteCookie:cookie];
}
[[NSURLCache sharedURLCache] removeAllCachedResponses];
NSURLCache * cache = [NSURLCache sharedURLCache];
[cache removeAllCachedResponses];
[cache setDiskCapacity:0];
[cache setMemoryCapacity:0];
WKWebsiteDataStore *dateStore = [WKWebsiteDataStore defaultDataStore];
[dateStore fetchDataRecordsOfTypes:[WKWebsiteDataStore allWebsiteDataTypes]
completionHandler:^(NSArray<WKWebsiteDataRecord *> * __nonnull records)
{
for (WKWebsiteDataRecord *record in records)
{
[[WKWebsiteDataStore defaultDataStore] removeDataOfTypes:record.dataTypes
forDataRecords:@[record]
completionHandler:^
{
NSLog(@"Cookies for %@ deleted successfully",record.displayName);
}];
}
}];
}
複製代碼
- (void)dealloc
{
[_webView stopLoading];
[_webView setNavigationDelegate:nil];
[self clearCache];
[self cleanCacheAndCookie];
}
複製代碼
#pragma mark WKNavigationDelegate
-(WKWebView *)webView:(WKWebView *)webView createWebViewWithConfiguration:(WKWebViewConfiguration *)configuration forNavigationAction:(WKNavigationAction *)navigationAction windowFeatures:(WKWindowFeatures *)windowFeatures {
if (!navigationAction.targetFrame.isMainFrame) {
[webView loadRequest:navigationAction.request];
}
return nil;
}
複製代碼
添加KVO
[_webView.scrollView addObserver:selfforKeyPath:@"contentSize"options:NSKeyValueObservingOptionNewcontext:nil];
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
if ([keyPath isEqualToString:@"contentSize"]) {
dispatch_async(dispatch_get_global_queue(0,0), ^{
//document.documentElement.scrollHeight
//document.body.offsetHeight
[_webView evaluateJavaScript:@"document.documentElement.offsetHeight"completionHandler:^(id_Nullable result, NSError * _Nullable error) {
CGRect frame =_webView.frame;
frame.size.height = [result doubleValue] + 50;
_webView.frame = frame;
_scrollViewHeight =220 + _webView.height;
_scrollView.contentSize =CGSizeMake(kScreenWidth,_scrollViewHeight);
}];
});
}
}
複製代碼
先攔截特色scheme,而後執行撥打電話的代碼
- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler{
// 攔截
NSURL *URL = navigationAction.request.URL;
NSString *scheme = [URL scheme];
if ([scheme isEqualToString:@"tel"]) {
NSString *resourceSpecifier = [URL resourceSpecifier];
NSString *callPhone = [NSString stringWithFormat:@"telprompt://%@", resourceSpecifier];
decisionHandler(WKNavigationActionPolicyCancel);
// 撥打
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:callPhone]];
return ;
}
decisionHandler(WKNavigationActionPolicyAllow);
}
複製代碼
www.jianshu.com/p/20cfd4f8c… www.jianshu.com/p/5cf0d241a… www.jianshu.com/p/6ba250744… * www.jianshu.com/p/4d12d593b…