一、WKWebview的初始化
首先来一段常用的初始化webview的代码
WKWebView *webView = [[WKWebView alloc]initWithFrame:self.view.bounds configuration:configuration];
webView.navigationDelegate = self;
webView.UIDelegate = self;
self.webView = webView;
[self.view addSubview:webView];
[self.webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"https://www.baidu.com"]]];
1. 创建webView最重要的参数configuration介绍
WKWebViewConfiguration *configuration = [[WKWebViewConfiguration alloc]init];
self.jsImplement = [[JSImplement alloc]init];
self.jsImplement.delegagte = self;
// 这个类主要用于native和js进行交互管理
WKUserContentController *userContentController = [[WKUserContentController alloc]init];
//有添加就有移除,一般在ViewDidDisappear中移除,不然JS_ScriptMessageReceiver会被强引用而无法释放(内存泄露),个人猜测是被WebKit里面某个单例强引用。
[userContentController addScriptMessageHandler:self.jsImplement name:@"jsCallNativeName"];
NSString *myJsCode = @"window.jsCallNativeName = window.webkit.messageHandlers.jsCallNativeName;\
window.jsCallNativeName.post = window.webkit.messageHandlers.jsCallNativeName.postMessage;\
"; //可兼容安卓
// myJsCode = @"alert('woaini')";
// myJsCode = @"<button onclick = \"login()\" style =\"font-size: 18px;\">登录</button>";
WKUserScript *userScript = [[WKUserScript alloc]initWithSource:myJsCode injectionTime:WKUserScriptInjectionTimeAtDocumentEnd forMainFrameOnly:YES];
[userContentController addUserScript:userScript];
configuration.userContentController = userContentController;
WKPreferences *preference = [[WKPreferences alloc]init];
preference.minimumFontSize = 30;
// preference.javaScriptEnabled = NO;
preference.javaScriptCanOpenWindowsAutomatically = YES;
configuration.preferences = preference;
创建WKWebViewConfiguration对象,比较重要的就是, WKUserContentController、WKUserScript、WKPreferences三个信息配置很重要的类。
WKUserContentController, 用户交互的类,其中最重要的就是,原生和JS的交互。建议单独创建一个和js交互的类如JSImplement。
2. 原生和JS交互的类JSImplement介绍。
JSImplement实现类的介绍
@implementation JSImplement
- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message{
id body = message.body;
if (![body isKindOfClass:[NSString class]]) {
return;
}
NSString *bodyString = (NSString *)body;
[self jsCallNative:bodyString];
}
- (void)jsCallNative:(NSString *)bodyString{
NSData *jsonData = [bodyString dataUsingEncoding:NSUTF8StringEncoding];//jsonString
NSError *err = nil;
NSDictionary *dict;
id obj = [NSJSONSerialization JSONObjectWithData:jsonData
options:NSJSONReadingMutableContainers
error:&err];
if (obj && [obj isKindOfClass:[NSDictionary class]]) {
dict = (NSDictionary*)obj;
}
if (!dict) {
//给出提示
return;
}
NSString *jsCallBack = [dict objectForKey:@"callBack"];
NSString *methodName = [dict objectForKey:@"methodName"];
SEL selector1 = NSSelectorFromString(methodName);
SEL selector2 = NSSelectorFromString([NSString stringWithFormat:@"%@:", methodName]);
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
if ([self respondsToSelector:selector1]) {
[self performSelector:selector1 withObject:dict];
}else if ([self respondsToSelector:selector2]) {
[self performSelector:selector2 withObject:dict];
}
if (self.delegagte && [self.delegagte respondsToSelector:@selector(evaluatingJavaScript:params:)]) {
[self.delegagte evaluatingJavaScript:jsCallBack params:@{@"result":@"1"}];
}
}
JSImplement头文件的介绍
@protocol JSImplementDelegate <NSObject>
/**
向webView注入一段js脚本
@param funcName 运行js脚本函数名字
@param params js函数所带的参数
*/
- (void)evaluatingJavaScript:(NSString*)funcName params:(NSDictionary *)params;
@end
@interface JSImplement : NSObject<WKScriptMessageHandler>
@property (nonatomic, weak) id<JSImplementDelegate> delegagte;
并在实现JSImplementDelegate的类中实现该代码,一般为创建了webview的控制器中去实现。
#pragma mark -- JSImplementDelegate
- (void)evaluatingJavaScript:(NSString*)funcName params:(NSDictionary *)params{
NSString *args = @"";
if ([NSJSONSerialization isValidJSONObject:params]) {
NSData *jsonData = [NSJSONSerialization dataWithJSONObject:params
options:NSJSONWritingPrettyPrinted
error:nil];
if (jsonData) {
args = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
}
}
/*
本来native调用js是在主线程,js调用native是在webThread线程,不会死锁,
但是js那边做了处理,阻塞了自己,导致死锁,这里加setTimeout保护
*/
NSString *string = [NSString stringWithFormat:@"if (window.%@){setTimeout(function(){window.%@(%@)},0)}", funcName, funcName, args];
[self evaluatingJavaScript:string completionHandler:nil];
}
- (void)evaluatingJavaScript:(NSString*)jsCode
completionHandler:(void (^)(id param, NSError *error))completionHandler{
if (jsCode && [jsCode isKindOfClass:[NSString class]]) {
[self.webView evaluateJavaScript:jsCode completionHandler:^(id _Nullable param, NSError * _Nullable error) {
if (completionHandler) {
completionHandler(param, error);
}
}];
}
}
设置WKWebview的UserAgent
设置UserAgent是异步操作,存在代理还未设置成功,但是H5内部已加载url并根据UserAgent判断出错的问题。故在设置成功UserAgent之后再进行loadUrl
- (void)ios9AndLaterSetUserAgent{
[self.webView evaluateJavaScript:@"navigator.userAgent" completionHandler:^(id result, NSError *error) {
NSString *userAgent = result;
if (!userAgent || error) {
userAgent = @"";
}
if ([userAgent rangeOfString:@"xd_Plugin"].location == NSNotFound) {
NSString *newUserAgent = [userAgent stringByAppendingString:@";xd_Plugin/0.1.0"];
// [[NSUserDefaults standardUserDefaults] registerDefaults:@{@"UserAgent":newUserAgent}]; //设置全局的UserAgent
// [[NSUserDefaults standardUserDefaults] synchronize];
if (weakSelf) {
weakSelf.webView.customUserAgent = newUserAgent; //设置当前webView的UserAgent
}
}
[weakSelf loadRequest];
}];
}
在给WebView对象设置UA时,在iOS12.0和12.1
的系统上使用webView.customUserAgent = newUserAgent会失效
,也就是无法设置UA,这里好像是苹果系统的一个bug。这里需要通过一个中间WebView来处理。
WS(weakSelf)
self.tempWebView = [[WKWebView alloc]init];
[self.tempWebView evaluateJavaScript:@"navigator.userAgent" completionHandler:^(id result, NSError *error) {
NSString *userAgent = result;
if (!userAgent || error) {
userAgent = @"";
}
if ([userAgent rangeOfString:@"xd_Plugin"].location == NSNotFound) {
NSString *newUserAgent = [userAgent stringByAppendingString:@";xd_Plugin/0.1.0"];
if (weakSelf) {
weakSelf.webView.customUserAgent = newUserAgent;
[[NSUserDefaults standardUserDefaults] registerDefaults:@{@"UserAgent":newUserAgent}]; //这里是设置全局UA,根据自己实际应用选择设置的UA场景
[[NSUserDefaults standardUserDefaults] synchronize]
}
}
weakSelf.tempWebView = nil;
}];
- (void)loadRequest{
if (self.webView) {
if ([self.webView isLoading]) {
[self.webView stopLoading];
}
if (self.url ) {
NSURLRequest *request = [NSURLRequest requestWithURL:self.url cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:10];
[self.webView loadRequest:request];
}
}
}
网友评论