需求:APP开发中 帮助与反馈页面 有一个链接需要跳转到网页,但是需要点击网页上面的返回按钮返回APP中
解决方案:(小编亲测好使)
- 使用
WKWebView
的WKUserContentController
- 为OC与js交互注册通用交互方法
js_obj
,代码如下:
[userContent addScriptMessageHandler:self name:@"js_obj"];
- 在
WKScriptMessageHandler
协议userContentController
方法里面编写OC接受js回调信息,然后判断约定的字段是否一致,如果一致的话,就执行相应的代码,代码如下:
// 判断是否是调用原生的
if([@"js_obj" isEqualToString:message.name]) {
if ([@"close" isEqualToString:message.body]) {
[self back];
}
}
- 在代码最后,一定要把注册的东西移除掉
- (void)dealloc {
[self.webView.configuration.userContentController removeScriptMessageHandlerForName:@"js_obj"];
}
- 前段所要做的工作只有:
在点击按钮返回的时候调用
window.webkit.messageHandlers.js_obj.postMessage("close") // js向OC发送返回消息
- 注意,可能会造成循环引用,页面销毁时不调用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];
}
// 导航栏返回
- (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;
// 创建UserContentController(提供JavaScript向webView发送消息的方法)
// WKUserContentController 是JavaScript与原生进行交互的桥梁
WKUserContentController *userContent = [[WKUserContentController alloc] init];
// 添加消息处理,注意:self指代的对象需要遵守 WKScriptMessageHandler 协议,结束时需要移除
[userContent addScriptMessageHandler:self name:@"js_obj"];
// 将UserConttentController设置到配置文件
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];
// 设置内边距
self.webView.navigationDelegate = self;
self.webView.UIDelegate = self;
[self.webView loadRequest:request];
}
#pragma mark - WKScriptMessageHandler
// JS 端可通过 window.webkit.messageHandlers.js_obj.postMessage("123") 发送消息
/* JS 调用原生 */
- (void)userContentController:(nonnull WKUserContentController *)userContentController didReceiveScriptMessage:(nonnull WKScriptMessage *)message {
// 判断是否是调用原生的
if([@"js_obj" isEqualToString:message.name]) {
if ([@"close" isEqualToString:message.body]) {
[self back];
}
}
}
#pragma mark - WKNavigationDelegate
/* 页面开始加载 */
- (void)webView:(WKWebView *)webView didStartProvisionalNavigation:(WKNavigation *)navigation {
NSLog(@"----页面开始加载");
}
/* 页面返回内容 */
- (void)webView:(WKWebView *)webView didCommitNavigation:(WKNavigation *)navigation {
NSLog(@"----页面返回内容");
}
/* 页面加载完成 */
- (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation {
NSLog(@"----页面加载完成");
}
/* 页面加载失败 */
- (void)webView:(WKWebView *)webView didFailProvisionalNavigation:(WKNavigation *)navigation withError:(NSError *)error {
NSLog(@"----页面加载失败");
}
/* 在发送请求之前,决定是否跳转 */
- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler {
NSURL *url = navigationAction.request.URL;
NSString *urlStr = url.absoluteString;
NSLog(@"【load url】=== %@", urlStr);
//不允许跳转
//decisionHandler(WKNavigationActionPolicyCancel);
//return;
//允许跳转
decisionHandler(WKNavigationActionPolicyAllow);
}
/* 在收到响应之后,决定是否跳转 */
- (void)webView:(WKWebView *)webView decidePolicyForNavigationResponse:(WKNavigationResponse *)navigationResponse decisionHandler:(void (^)(WKNavigationResponsePolicy))decisionHandler {
NSLog(@"===%@",navigationResponse.response.URL.absoluteString);
//允许跳转
decisionHandler(WKNavigationResponsePolicyAllow);
//不允许跳转
//decisionHandler(WKNavigationResponsePolicyCancel);
}
/* 接受到服务器跳转请求之后调用 */
- (void)webView:(WKWebView *)webView didReceiveServerRedirectForProvisionalNavigation:(WKNavigation *)navigation {
NSLog(@"----接收到服务器跳转请求之后调用");
}
/* 加载数据发生错误时调用 */
- (void)webView:(WKWebView *)webView didFailNavigation:(WKNavigation *)navigation withError:(NSError *)error {
NSLog(@"----数据加载发生错误时调用");
}
/* 需要响应身份验证时调用,同样在block中需要传入用户身份凭证 */
- (void)webView:(WKWebView *)webView didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition, NSURLCredential * _Nullable))completionHandler {
//用户身份信息
NSLog(@"----需要响应身份验证时调用 同样在block中需要传入用户身份凭证");
NSURLCredential *newCard = [NSURLCredential credentialWithUser:@"" password:@"" persistence:NSURLCredentialPersistenceNone];
// 为 challenge 的发送方提供 credential
[[challenge sender] useCredential:newCard forAuthenticationChallenge:challenge];
completionHandler(NSURLSessionAuthChallengeUseCredential,newCard);
}
/* 进程被终止时调用 */
- (void)webViewWebContentProcessDidTerminate:(WKWebView *)webView {
NSLog(@"----进程被终止时调用");
}
#pragma mark - WKUIDelegate
// WKUIDelegate是web界面中有弹出警告框时调用这个代理方法,主要是用来处理使用系统的弹框来替换JS中的一些弹框的,比如: 警告框, 选择框, 输入框等
/**
webView中弹出警告框时调用, 只能有一个按钮
@param webView webView
@param message 提示信息
@param frame 可用于区分哪个窗口调用的
@param completionHandler 警告框消失的时候调用, 回调给JS
*/
- (void)webView:(WKWebView *)webView runJavaScriptAlertPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(void))completionHandler {
NSLog(@"----web界面中有弹出警告框时调用");
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调用confirm方法后回调的方法 confirm是js中的确定框,需要在block中把用户选择的情况传递进去
/** 对应js的confirm方法
webView中弹出选择框时调用, 两个按钮
@param webView webView description
@param message 提示信息
@param frame 可用于区分哪个窗口调用的
@param completionHandler 确认框消失的时候调用, 回调给JS, 参数为选择结果: 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调用prompt方法后回调的方法 prompt是js中的输入框 需要在block中把用户输入的信息传入
/** 对应js的prompt方法
webView中弹出输入框时调用, 两个按钮 和 一个输入框
@param webView webView description
@param prompt 提示信息
@param defaultText 默认提示文本
@param frame 可用于区分哪个窗口调用的
@param completionHandler 输入框消失的时候调用, 回调给JS, 参数为输入的内容
*/
- (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];
*/
}
/* 关闭webView时调用的方法 */
- (void)webViewDidClose:(WKWebView *)webView {
NSLog(@"----关闭webView时调用的方法");
}
#pragma mark - dealloc
- (void)dealloc {
[self.webView.configuration.userContentController removeScriptMessageHandlerForName:@"js_obj"];
}
写在最后:
WKWebView和UIWebView的区别:
主要是性能问题,UIWebView占用过多内存,而且WKWebView有诸多新特性
1.更多的支持HTML5的特性
2.官方宣称的高达60fps的滚动刷新率以及内置手势
3.与Safari相同的JavaScript引擎
4.将UIWebViewDelegate与UIWebView拆分成了14类与3个协议
5.增加加载进度属性:estimatedProgress