最近項目中的UIWebView被替換爲了WKWebView,所以來總結一下。 示例Demo:WKWebView的使用 本文將從如下幾方面介紹WKWebView:html
- 一、WKWebView涉及的一些類
- 二、WKWebView涉及的代理方法
- 三、網頁內容加載進度條和title的實現
- 四、JS和OC的交互
- 五、本地HTML文件的實現
注意: #import <WebKit/WebKit.h>
//初始化
_webView = [[WKWebView alloc] initWithFrame:CGRectMake(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT) configuration:config];
// UI代理
_webView.UIDelegate = self;
// 導航代理
_webView.navigationDelegate = self;
// 是否容許手勢左滑返回上一級, 相似導航控制的左滑返回
_webView.allowsBackForwardNavigationGestures = YES;
//可返回的頁面列表, 存儲已打開過的網頁
WKBackForwardList * backForwardList = [_webView backForwardList];
// NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:@"http://www.chinadaily.com.cn"]];
// [request addValue:[self readCurrentCookieWithDomain:@"http://www.chinadaily.com.cn"] forHTTPHeaderField:@"Cookie"];
// [_webView loadRequest:request];
//頁面後退
[_webView goBack];
//頁面前進
[_webView goForward];
//刷新當前頁面
[_webView reload];
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]]];
複製代碼
//建立網頁配置對象
WKWebViewConfiguration *config = [[WKWebViewConfiguration alloc] init];
// 建立設置對象
WKPreferences *preference = [[WKPreferences alloc]init];
//最小字體大小 當將javaScriptEnabled屬性設置爲NO時,能夠看到明顯的效果
preference.minimumFontSize = 0;
//設置是否支持javaScript 默認是支持的
preference.javaScriptEnabled = YES;
// 在iOS上默認爲NO,表示是否容許不通過用戶交互由javaScript自動打開窗口
preference.javaScriptCanOpenWindowsAutomatically = YES;
config.preferences = preference;
// 是使用h5的視頻播放器在線播放, 仍是使用原生播放器全屏播放
config.allowsInlineMediaPlayback = YES;
//設置視頻是否須要用戶手動播放 設置爲NO則會容許自動播放
config.requiresUserActionForMediaPlayback = YES;
//設置是否容許畫中畫技術 在特定設備上有效
config.allowsPictureInPictureMediaPlayback = YES;
//設置請求的User-Agent信息中應用程序名稱 iOS9後可用
config.applicationNameForUserAgent = @"ChinaDailyForiPad";
//自定義的WKScriptMessageHandler 是爲了解決內存不釋放的問題
WeakWebViewScriptMessageDelegate *weakScriptMessageDelegate = [[WeakWebViewScriptMessageDelegate alloc] initWithDelegate:self];
//這個類主要用來作native與JavaScript的交互管理
WKUserContentController * wkUController = [[WKUserContentController alloc] init];
//註冊一個name爲jsToOcNoPrams的js方法
[wkUController addScriptMessageHandler:weakScriptMessageDelegate name:@"jsToOcNoPrams"];
[wkUController addScriptMessageHandler:weakScriptMessageDelegate name:@"jsToOcWithPrams"];
config.userContentController = wkUController;
複製代碼
//如下代碼適配文本大小,由UIWebView換爲WKWebView後,會發現字體小了不少,這應該是WKWebView與html的兼容問題,解決辦法是修改原網頁,要麼咱們手動注入JS
NSString *jSString = @"var meta = document.createElement('meta'); meta.setAttribute('name', 'viewport'); meta.setAttribute('content', 'width=device-width'); document.getElementsByTagName('head')[0].appendChild(meta);";
//用於進行JavaScript注入
WKUserScript *wkUScript = [[WKUserScript alloc] initWithSource:jSString injectionTime:WKUserScriptInjectionTimeAtDocumentEnd forMainFrameOnly:YES];
[config.userContentController addUserScript:wkUScript];
複製代碼
//這個類主要用來作native與JavaScript的交互管理
WKUserContentController * wkUController = [[WKUserContentController alloc] init];
//註冊一個name爲jsToOcNoPrams的js方法,設置處理接收JS方法的代理
[wkUController addScriptMessageHandler:self name:@"jsToOcNoPrams"];
[wkUController addScriptMessageHandler:self name:@"jsToOcWithPrams"];
config.userContentController = wkUController;
//用完記得移除
//移除註冊的js方法
[[_webView configuration].userContentController removeScriptMessageHandlerForName:@"jsToOcNoPrams"];
[[_webView configuration].userContentController removeScriptMessageHandlerForName:@"jsToOcWithPrams"];
複製代碼
注意:遵照WKScriptMessageHandler協議,代理是由WKUserContentControl設置
//經過接收JS傳出消息的name進行捕捉的回調方法
- (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"]){
UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"js調用到了oc" message:@"不帶參數" preferredStyle:UIAlertControllerStyleAlert];
[alertController addAction:([UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
}])];
[self presentViewController:alertController animated:YES completion:nil];
}else if([message.name isEqualToString:@"jsToOcWithPrams"]){
UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"js調用到了oc" message:parameter[@"params"] preferredStyle:UIAlertControllerStyleAlert];
[alertController addAction:([UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
}])];
[self presentViewController:alertController animated:YES completion:nil];
}
}
複製代碼
// 頁面開始加載時調用
- (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 {
}
// 根據WebView對於即將跳轉的HTTP請求頭信息和相關信息來決定是否跳轉
- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler {
NSString * urlStr = navigationAction.request.URL.absoluteString;
NSLog(@"發送跳轉請求:%@",urlStr);
//本身定義的協議頭
NSString *htmlHeadString = @"github://";
if([urlStr hasPrefix:htmlHeadString]){
UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"經過截取URL調用OC" message:@"你想前往個人Github主頁?" preferredStyle:UIAlertControllerStyleAlert];
[alertController addAction:([UIAlertAction actionWithTitle:@"取消" style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) {
}])];
[alertController addAction:([UIAlertAction actionWithTitle:@"打開" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
NSURL * url = [NSURL URLWithString:[urlStr stringByReplacingOccurrencesOfString:@"github://callName_?" withString:@""]];
[[UIApplication sharedApplication] openURL:url];
}])];
[self presentViewController:alertController animated:YES completion:nil];
decisionHandler(WKNavigationActionPolicyCancel);
}else{
decisionHandler(WKNavigationActionPolicyAllow);
}
}
// 根據客戶端受到的服務器響應頭以及response相關信息來決定是否能夠跳轉
- (void)webView:(WKWebView *)webView decidePolicyForNavigationResponse:(WKNavigationResponse *)navigationResponse decisionHandler:(void (^)(WKNavigationResponsePolicy))decisionHandler{
NSString * urlStr = navigationResponse.response.URL.absoluteString;
NSLog(@"當前跳轉地址:%@",urlStr);
//容許跳轉
decisionHandler(WKNavigationResponsePolicyAllow);
//不容許跳轉
//decisionHandler(WKNavigationResponsePolicyCancel);
}
//須要響應身份驗證時調用 一樣在block中須要傳入用戶身份憑證
- (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);
}
//進程被終止時調用
- (void)webViewWebContentProcessDidTerminate:(WKWebView *)webView{
}
複製代碼
/**
* web界面中有彈出警告框時調用
*
* @param webView 實現該代理的webview
* @param message 警告框中的內容
* @param completionHandler 警告框消失調用
*/
- (void)webView:(WKWebView *)webView runJavaScriptAlertPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(void))completionHandler {
UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"HTML的彈出框" message:message?:@"" preferredStyle:UIAlertControllerStyleAlert];
[alertController addAction:([UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
completionHandler();
}])];
[self presentViewController:alertController animated:YES completion:nil];
}
// 確認框
//JavaScript調用confirm方法後回調的方法 confirm是js中的肯定框,須要在block中把用戶選擇的狀況傳遞進去
- (void)webView:(WKWebView *)webView runJavaScriptConfirmPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(BOOL))completionHandler{
UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"" message:message?:@"" preferredStyle:UIAlertControllerStyleAlert];
[alertController addAction:([UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) {
completionHandler(NO);
}])];
[alertController addAction:([UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
completionHandler(YES);
}])];
[self presentViewController:alertController animated:YES completion:nil];
}
// 輸入框
//JavaScript調用prompt方法後回調的方法 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];
}
// 頁面是彈出窗口 _blank 處理
- (WKWebView *)webView:(WKWebView *)webView createWebViewWithConfiguration:(WKWebViewConfiguration *)configuration forNavigationAction:(WKNavigationAction *)navigationAction windowFeatures:(WKWindowFeatures *)windowFeatures {
if (!navigationAction.targetFrame.isMainFrame) {
[webView loadRequest:navigationAction.request];
}
return nil;
}
複製代碼
//添加監測網頁加載進度的觀察者
[self.webView addObserver:self
forKeyPath:@"estimatedProgress"
options:0
context:nil];
//添加監測網頁標題title的觀察者
[self.webView addObserver:self
forKeyPath:@"title"
options:NSKeyValueObservingOptionNew
context:nil];
//kvo 監聽進度 必須實現此方法
-(void)observeValueForKeyPath:(NSString *)keyPath
ofObject:(id)object
change:(NSDictionary<NSKeyValueChangeKey,id> *)change
context:(void *)context{
if ([keyPath isEqualToString:NSStringFromSelector(@selector(estimatedProgress))]
&& object == _webView) {
NSLog(@"網頁加載進度 = %f",_webView.estimatedProgress);
self.progressView.progress = _webView.estimatedProgress;
if (_webView.estimatedProgress >= 1.0f) {
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
self.progressView.progress = 0;
});
}
}else if([keyPath isEqualToString:@"title"]
&& object == _webView){
self.navigationItem.title = _webView.title;
}else{
[super observeValueForKeyPath:keyPath
ofObject:object
change:change
context:context];
}
}
//移除觀察者
[_webView removeObserver:self
forKeyPath:NSStringFromSelector(@selector(estimatedProgress))];
[_webView removeObserver:self
forKeyPath:NSStringFromSelector(@selector(title))];
複製代碼
這個實現主要是依靠WKScriptMessageHandler協議類和WKUserContentController兩個類:WKUserContentController對象負責註冊JS方法,設置處理接收JS方法的代理,代理遵照WKScriptMessageHandler,實現捕捉到JS消息的回調方法,詳情能夠看第一步中對這兩個類的介紹。java
//這個類主要用來作native與JavaScript的交互管理
WKUserContentController * wkUController = [[WKUserContentController alloc] init];
//註冊一個name爲jsToOcNoPrams的js方法,設置處理接收JS方法的代理
[wkUController addScriptMessageHandler:self name:@"jsToOcNoPrams"];
[wkUController addScriptMessageHandler:self name:@"jsToOcWithPrams"];
config.userContentController = wkUController;
注意:遵照WKScriptMessageHandler協議,代理是由WKUserContentControl設置
//經過接收JS傳出消息的name進行捕捉的回調方法 js調OC
- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message{
NSLog(@"name:%@\\\\n body:%@\\\\n frameInfo:%@\\\\n",message.name,message.body,message.frameInfo);
}
複製代碼
//OC調用JS changeColor()是JS方法名,completionHandler是異步回調block
NSString *jsString = [NSString stringWithFormat:@"changeColor('%@')", @"Js參數"];
[_webView evaluateJavaScript:jsString completionHandler:^(id _Nullable data, NSError * _Nullable error) {
NSLog(@"改變HTML的背景色");
}];
//改變字體大小 調用原生JS方法
NSString *jsFont = [NSString stringWithFormat:@"document.getElementsByTagName('body')[0].style.webkitTextSizeAdjust= '%d%%'", arc4random()%99 + 100];
[_webView evaluateJavaScript:jsFont completionHandler:nil];
複製代碼
因爲示例Demo的須要以及知識有限,我用僅知的HTML、CSS、JavaScript的一點皮毛寫了一個HTML文件,比較業餘,大神勿噴😁😁 小白想學習這方面的知識能夠看這裏: www.w3school.com.cn/index.htmlgit
我用MAC自帶的文本編輯工具,生成一個文件,改後綴名,強轉爲.html文件,同時還須要設置文本編輯打開HTML文件時顯示代碼(以下圖),而後編輯代碼。github
詳情請前往個人Github:WKWebView的使用web
若是我WKWebView使用的總結沒幫到你,你也能夠看看下面幾篇文: www.jianshu.com/p/833448c30… www.jianshu.com/p/4fa8c4eb1… www.jianshu.com/p/91cfe58c0…bash
若是須要跟我交流的話: ※ Github: github.com/wsl2ls ※ 我的博客:wsl2ls.github.io ※ 簡書:www.jianshu.com/u/e15d1f644… ※ 微信公衆號:iOS2679114653 ※ QQ:1685527540服務器