美文网首页工作生活
WKWebView使用简析

WKWebView使用简析

作者: 中秋梧桐语 | 来源:发表于2019-10-24 13:51 被阅读0次

一、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];
        }
    }

}

相关文章

网友评论

    本文标题:WKWebView使用简析

    本文链接:https://www.haomeiwen.com/subject/nnvthctx.html