需求:APP開發(fā)中 幫助與反饋頁面 有一個鏈接需要跳轉(zhuǎn)到網(wǎng)頁澜建,但是需要點擊網(wǎng)頁上面的返回按鈕返回APP中
解決方案:(小編親測好使)
- 使用
WKWebView
的WKUserContentController
- 為OC與js交互注冊通用交互方法
js_obj
,代碼如下:
[userContent addScriptMessageHandler:self name:@"js_obj"];
- 在
WKScriptMessageHandler
協(xié)議userContentController
方法里面編寫OC接受js回調(diào)信息米丘,然后判斷約定的字段是否一致,如果一致的話舌镶,就執(zhí)行相應(yīng)的代碼柱彻,代碼如下:
// 判斷是否是調(diào)用原生的
if([@"js_obj" isEqualToString:message.name]) {
if ([@"close" isEqualToString:message.body]) {
[self back];
}
}
- 在代碼最后豪娜,一定要把注冊的東西移除掉
- (void)dealloc {
[self.webView.configuration.userContentController removeScriptMessageHandlerForName:@"js_obj"];
}
- 前段所要做的工作只有:
在點擊按鈕返回的時候調(diào)用
window.webkit.messageHandlers.js_obj.postMessage("close") // js向OC發(fā)送返回消息
- 注意,可能會造成循環(huán)引用哟楷,頁面銷毀時不調(diào)用dealloc方法瘤载,所以要在返回方法里面注銷一下,加入如下代碼:
[self.webView.configuration.userContentController removeScriptMessageHandlerForName:@"js_obj"];
- 全部代碼如下:
#import "MineHelpViewController.h"
#import "UserManager.h"
#import <WebKit/WebKit.h>
@interface MineHelpViewController ()<WKScriptMessageHandler,WKUIDelegate,WKNavigationDelegate>
@property(nonatomic,strong)WKWebView *webView;
@property(nonatomic,weak)NSURLRequest *request;
@end
@implementation MineHelpViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = [UIColor whiteColor];
[self setupFromWebView];
}
// 導(dǎo)航欄返回
- (void)back {
[self.webView.configuration.userContentController removeScriptMessageHandlerForName:@"js_obj"];
[self.navigationController popViewControllerAnimated:YES];
}
- (void)setupFromWebView {
// 1. 配置Configuration信息
WKWebViewConfiguration *config = [[WKWebViewConfiguration alloc] init];
config.selectionGranularity = WKSelectionGranularityDynamic;
config.allowsInlineMediaPlayback = YES;
WKPreferences *preferences = [WKPreferences new];
// 是否支持JavaScript
preferences.javaScriptEnabled = YES;
// 不通過用戶交互,是否可以打開窗口
preferences.javaScriptCanOpenWindowsAutomatically = YES;
config.preferences = preferences;
// 創(chuàng)建UserContentController(提供JavaScript向webView發(fā)送消息的方法)
// WKUserContentController 是JavaScript與原生進行交互的橋梁
WKUserContentController *userContent = [[WKUserContentController alloc] init];
// 添加消息處理卖擅,注意:self指代的對象需要遵守 WKScriptMessageHandler 協(xié)議鸣奔,結(jié)束時需要移除
[userContent addScriptMessageHandler:self name:@"js_obj"];
// 將UserConttentController設(shè)置到配置文件
config.userContentController = userContent;
//
self.webView = [[WKWebView alloc] initWithFrame:self.view.frame configuration:config];
self.webView.backgroundColor = [UIColor whiteColor];
[self.view addSubview:self.webView];
NSURL *url = [NSURL URLWithString:[self.webURL stringByAppendingFormat:@"?token=%@",[UserManager defaultManager].token]];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
// 設(shè)置內(nèi)邊距
self.webView.navigationDelegate = self;
self.webView.UIDelegate = self;
[self.webView loadRequest:request];
}
#pragma mark - WKScriptMessageHandler
// JS 端可通過 window.webkit.messageHandlers.js_obj.postMessage("123") 發(fā)送消息
/* JS 調(diào)用原生 */
- (void)userContentController:(nonnull WKUserContentController *)userContentController didReceiveScriptMessage:(nonnull WKScriptMessage *)message {
// 判斷是否是調(diào)用原生的
if([@"js_obj" isEqualToString:message.name]) {
if ([@"close" isEqualToString:message.body]) {
[self back];
}
}
}
#pragma mark - WKNavigationDelegate
/* 頁面開始加載 */
- (void)webView:(WKWebView *)webView didStartProvisionalNavigation:(WKNavigation *)navigation {
NSLog(@"----頁面開始加載");
}
/* 頁面返回內(nèi)容 */
- (void)webView:(WKWebView *)webView didCommitNavigation:(WKNavigation *)navigation {
NSLog(@"----頁面返回內(nèi)容");
}
/* 頁面加載完成 */
- (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation {
NSLog(@"----頁面加載完成");
}
/* 頁面加載失敗 */
- (void)webView:(WKWebView *)webView didFailProvisionalNavigation:(WKNavigation *)navigation withError:(NSError *)error {
NSLog(@"----頁面加載失敗");
}
/* 在發(fā)送請求之前,決定是否跳轉(zhuǎn) */
- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler {
NSURL *url = navigationAction.request.URL;
NSString *urlStr = url.absoluteString;
NSLog(@"【load url】=== %@", urlStr);
//不允許跳轉(zhuǎn)
//decisionHandler(WKNavigationActionPolicyCancel);
//return;
//允許跳轉(zhuǎn)
decisionHandler(WKNavigationActionPolicyAllow);
}
/* 在收到響應(yīng)之后,決定是否跳轉(zhuǎn) */
- (void)webView:(WKWebView *)webView decidePolicyForNavigationResponse:(WKNavigationResponse *)navigationResponse decisionHandler:(void (^)(WKNavigationResponsePolicy))decisionHandler {
NSLog(@"===%@",navigationResponse.response.URL.absoluteString);
//允許跳轉(zhuǎn)
decisionHandler(WKNavigationResponsePolicyAllow);
//不允許跳轉(zhuǎn)
//decisionHandler(WKNavigationResponsePolicyCancel);
}
/* 接受到服務(wù)器跳轉(zhuǎn)請求之后調(diào)用 */
- (void)webView:(WKWebView *)webView didReceiveServerRedirectForProvisionalNavigation:(WKNavigation *)navigation {
NSLog(@"----接收到服務(wù)器跳轉(zhuǎn)請求之后調(diào)用");
}
/* 加載數(shù)據(jù)發(fā)生錯誤時調(diào)用 */
- (void)webView:(WKWebView *)webView didFailNavigation:(WKNavigation *)navigation withError:(NSError *)error {
NSLog(@"----數(shù)據(jù)加載發(fā)生錯誤時調(diào)用");
}
/* 需要響應(yīng)身份驗證時調(diào)用,同樣在block中需要傳入用戶身份憑證 */
- (void)webView:(WKWebView *)webView didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition, NSURLCredential * _Nullable))completionHandler {
//用戶身份信息
NSLog(@"----需要響應(yīng)身份驗證時調(diào)用 同樣在block中需要傳入用戶身份憑證");
NSURLCredential *newCard = [NSURLCredential credentialWithUser:@"" password:@"" persistence:NSURLCredentialPersistenceNone];
// 為 challenge 的發(fā)送方提供 credential
[[challenge sender] useCredential:newCard forAuthenticationChallenge:challenge];
completionHandler(NSURLSessionAuthChallengeUseCredential,newCard);
}
/* 進程被終止時調(diào)用 */
- (void)webViewWebContentProcessDidTerminate:(WKWebView *)webView {
NSLog(@"----進程被終止時調(diào)用");
}
#pragma mark - WKUIDelegate
// WKUIDelegate是web界面中有彈出警告框時調(diào)用這個代理方法,主要是用來處理使用系統(tǒng)的彈框來替換JS中的一些彈框的,比如: 警告框, 選擇框, 輸入框等
/**
webView中彈出警告框時調(diào)用, 只能有一個按鈕
@param webView webView
@param message 提示信息
@param frame 可用于區(qū)分哪個窗口調(diào)用的
@param completionHandler 警告框消失的時候調(diào)用, 回調(diào)給JS
*/
- (void)webView:(WKWebView *)webView runJavaScriptAlertPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(void))completionHandler {
NSLog(@"----web界面中有彈出警告框時調(diào)用");
UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"警告" message:message preferredStyle:(UIAlertControllerStyleAlert)];
UIAlertAction *ok = [UIAlertAction actionWithTitle:@"我知道了" style:(UIAlertActionStyleDefault) handler:^(UIAlertAction * _Nonnull action) {
completionHandler();
}];
[alert addAction:ok];
[self presentViewController:alert animated:YES completion:nil];
}
// JavaScript調(diào)用confirm方法后回調(diào)的方法 confirm是js中的確定框,需要在block中把用戶選擇的情況傳遞進去
/** 對應(yīng)js的confirm方法
webView中彈出選擇框時調(diào)用, 兩個按鈕
@param webView webView description
@param message 提示信息
@param frame 可用于區(qū)分哪個窗口調(diào)用的
@param completionHandler 確認框消失的時候調(diào)用, 回調(diào)給JS, 參數(shù)為選擇結(jié)果: YES or NO
*/
- (void)webView:(WKWebView *)webView runJavaScriptConfirmPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(BOOL))completionHandler {
NSLog(@"%@",message);
completionHandler(YES);
/*
UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"請選擇" message:message preferredStyle:(UIAlertControllerStyleAlert)];
UIAlertAction *ok = [UIAlertAction actionWithTitle:@"同意" style:(UIAlertActionStyleDefault) handler:^(UIAlertAction * _Nonnull action) {
completionHandler(YES);
}];
UIAlertAction *cancel = [UIAlertAction actionWithTitle:@"不同意" style:(UIAlertActionStyleCancel) handler:^(UIAlertAction * _Nonnull action) {
completionHandler(NO);
}];
[alert addAction:ok];
[alert addAction:cancel];
[self presentViewController:alert animated:YES completion:nil];
*/
}
// JavaScript調(diào)用prompt方法后回調(diào)的方法 prompt是js中的輸入框 需要在block中把用戶輸入的信息傳入
/** 對應(yīng)js的prompt方法
webView中彈出輸入框時調(diào)用, 兩個按鈕 和 一個輸入框
@param webView webView description
@param prompt 提示信息
@param defaultText 默認提示文本
@param frame 可用于區(qū)分哪個窗口調(diào)用的
@param completionHandler 輸入框消失的時候調(diào)用, 回調(diào)給JS, 參數(shù)為輸入的內(nèi)容
*/
- (void)webView:(WKWebView *)webView runJavaScriptTextInputPanelWithPrompt:(NSString *)prompt defaultText:(NSString *)defaultText initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(NSString * _Nullable))completionHandler {
NSLog(@"%@",prompt);
completionHandler(@"123");
/*
UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"請輸入" message:prompt preferredStyle:(UIAlertControllerStyleAlert)];
[alert addTextFieldWithConfigurationHandler:^(UITextField * _Nonnull textField) {
textField.placeholder = @"請輸入";
}];
UIAlertAction *ok = [UIAlertAction actionWithTitle:@"確定" style:(UIAlertActionStyleDefault) handler:^(UIAlertAction * _Nonnull action) {
UITextField *tf = [alert.textFields firstObject];
completionHandler(tf.text);
}];
UIAlertAction *cancel = [UIAlertAction actionWithTitle:@"取消" style:(UIAlertActionStyleCancel) handler:^(UIAlertAction * _Nonnull action) {
completionHandler(defaultText);
}];
[alert addAction:ok];
[alert addAction:cancel];
[self presentViewController:alert animated:YES completion:nil];
*/
}
/* 關(guān)閉webView時調(diào)用的方法 */
- (void)webViewDidClose:(WKWebView *)webView {
NSLog(@"----關(guān)閉webView時調(diào)用的方法");
}
#pragma mark - dealloc
- (void)dealloc {
[self.webView.configuration.userContentController removeScriptMessageHandlerForName:@"js_obj"];
}
寫在最后:
WKWebView和UIWebView的區(qū)別:
主要是性能問題磨镶,UIWebView占用過多內(nèi)存溃蔫,而且WKWebView有諸多新特性
1.更多的支持HTML5的特性
2.官方宣稱的高達60fps的滾動刷新率以及內(nèi)置手勢
3.與Safari相同的JavaScript引擎
4.將UIWebViewDelegate與UIWebView拆分成了14類與3個協(xié)議
5.增加加載進度屬性:estimatedProgress