WKWebView的应用

WKWebView的应用详细使用使用方法,包括自定义UA,动态注入js,以及闪退问题"

Posted by gongkuihua on May 14, 2019

WKWebView的应用

在ios12.2后UIWebView系统不在给予支持了,要求更新wkwebview,12.2后image.png 而且在12.0后一些网页视频不能播放了,找了很久原因也没有找到,最终的解决方法就是把UIwebView换成WkwebView解决视频播放问题,今天就将自己在集成的时候遇到的问题和迁移方法进行提供,供大家参考. 对于系统在11.0到11.3之间,网页打开地图闪退,在viewDidLoad添加以下代码解决

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
//解决在11.0到11.3之间的系统打开地图闪退问题
if (iOS_SYSTEM >= 11.0 && iOS_SYSTEM <= 11.3) {
        setenv("JSC_useJIT", "false", 0);
    } # 1.初始化wkweb ### 初始化

#import <WebKit/WebKit.h>
WKWebView *cq_newWkWeb (void) {
    
    WKWebViewConfiguration *configuration = [[WKWebViewConfiguration alloc] init];
    configuration.allowsAirPlayForMediaPlayback = YES;//是否运行airplay自动播放媒体功能
    configuration.allowsInlineMediaPlayback = YES;// 是使用h5的视频播放器在线播放, 还是使用原生播放器全屏播放
    configuration.selectionGranularity = WKSelectionGranularityCharacter;//解决WKWebView的内存泄露
    configuration.dataDetectorTypes = UIDataDetectorTypePhoneNumber;
    configuration.suppressesIncrementalRendering = YES;
    configuration.processPool = [[WKProcessPool alloc] init];
    
    NSString *version= [UIDevice currentDevice].systemVersion;
    if(version.doubleValue >= 10.0) {
        // 该属性只支持10+系统
        configuration.dataDetectorTypes = WKDataDetectorTypePhoneNumber;
        configuration.mediaTypesRequiringUserActionForPlayback = WKAudiovisualMediaTypeNone;//允许网页自动播放视频和自动播放音频
    }else if (version.doubleValue <= 10.0 && version.doubleValue >=9.0){
        configuration.requiresUserActionForMediaPlayback = YES;//允许网页自动播放视频和自动播放音频
    }
    
    // 创建设置对象
    WKPreferences *preference = [[WKPreferences alloc]init];
    //最小字体大小 当将javaScriptEnabled属性设置为NO时,可以看到明显的效果
    preference.minimumFontSize = 0;
    //设置是否支持javaScript 默认是支持的
    preference.javaScriptEnabled = YES;
    // 在iOS上默认为NO,表示是否允许不经过用户交互由javaScript自动打开窗口
    preference.javaScriptCanOpenWindowsAutomatically = YES;
    configuration.preferences = preference;
    
    //设置是否允许画中画技术 在特定设备上有效
    configuration.allowsPictureInPictureMediaPlayback = YES;

    
    WKUserContentController *userContentController = [[WKUserContentController alloc] init];
    configuration.userContentController = userContentController;
    
    WKWebView *web = [[WKWebView alloc] initWithFrame:CGRectZero configuration:configuration];
    web.userInteractionEnabled = YES;
    web.multipleTouchEnabled = true;
    web.backgroundColor = [UIColor whiteColor];
    web.opaque = NO;
//    web.scalesPageToFit = true;
    
    return web;
}

- (WKWebView *)baseWebView {
    if (!_baseWebView) {
        _baseWebView = cq_newWkWeb();
        _baseWebView.UIDelegate = self;
        _baseWebView.navigationDelegate = self;
        [_baseWebView addObserver:self forKeyPath:@"title" options:NSKeyValueObservingOptionNew context:nil];
        [_baseWebView addObserver:self forKeyPath:@"estimatedProgress" options:NSKeyValueObservingOptionNew context:nil];
        [self.view addSubview:_baseWebView];
    }
    return _baseWebView;
} ### 协议遵守
WKNavigationDelegate, WKUIDelegate,

#pragma mark - WKNavigationDelegate
/* 页面开始加载 */
- (void)webView:(WKWebView *)webView didStartProvisionalNavigation:(WKNavigation *)navigation{
}
/* 开始返回内容 */
- (void)webView:(WKWebView *)webView didCommitNavigation:(WKNavigation *)navigation{
     
}
/* 页面加载完成 */
- (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation{
     
}
/* 页面加载失败 */
- (void)webView:(WKWebView *)webView didFailProvisionalNavigation:(WKNavigation *)navigation{
     
}
/* 在发送请求之前,决定是否跳转 */
- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler{
    //允许跳转
    decisionHandler(WKNavigationActionPolicyAllow);
    //不允许跳转
    //decisionHandler(WKNavigationActionPolicyCancel);
}
/* 在收到响应后,决定是否跳转 */
- (void)webView:(WKWebView *)webView decidePolicyForNavigationResponse:(WKNavigationResponse *)navigationResponse decisionHandler:(void (^)(WKNavigationResponsePolicy))decisionHandler{
     
    NSLog(@"%@",navigationResponse.response.URL.absoluteString);
    //允许跳转
    decisionHandler(WKNavigationResponsePolicyAllow);
    //不允许跳转
    //decisionHandler(WKNavigationResponsePolicyCancel);
} # 2.设置监听 ### 监听标题
[_baseWebView addObserver:self forKeyPath:@"title" options:NSKeyValueObservingOptionNew context:nil]; ### 监听进度条
[_baseWebView addObserver:self forKeyPath:@"estimatedProgress" options:NSKeyValueObservingOptionNew context:nil];

监听处理
#pragma mark kov
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context {
    if([keyPath isEqualToString:@"title"]) {
        NSString *n_new = [change dicStringForKey:@"new"];
        [self setJs_Title:n_new];
    } else if ([keyPath isEqualToString:@"estimatedProgress"]) {
        self.webProgress.progress = self.baseWebView.estimatedProgress;
        if (self.webProgress.progress == 1) {
            __weak typeof (self)weakSelf = self;
            [UIView animateWithDuration:0.25f delay:0.1f options:UIViewAnimationOptionCurveEaseOut animations:^{
                weakSelf.webProgress.transform = CGAffineTransformMakeScale(1.0f, 1.2f);
            } completion:^(BOOL finished) {
                weakSelf.webProgress.hidden = YES;
            }];
        }
    }
} # 3.wkweb调用原生弹框
- (void)webView:(WKWebView *)webView runJavaScriptAlertPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(void))completionHandler {
    UIAlertAction *alertActionCancel = [UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) {
        // 返回用户选择的信息
        completionHandler();
    }];
    UIAlertAction *alertActionOK = [UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
        completionHandler();
    }];
    // alert弹出框
    
    UIAlertController *alterVC =[UIAlertController alertControllerWithTitle:message message:nil preferredStyle:UIAlertControllerStyleAlert];
    [alterVC addAction:alertActionCancel];
    [alterVC addAction:alertActionOK];
    [self presentViewController:alterVC animated:YES completion:nil];
} # 4.js注入 ### 对于交互的js注入
  //注册所有js
    NSString *filePath = [[NSBundle mainBundle] pathForResource:@"AppJsObj" ofType:@"js"];
    NSString *js = [NSString stringWithContentsOfFile:filePath encoding:NSUTF8StringEncoding error:nil];
    
    WKUserScript *jsFile = [[WKUserScript alloc] initWithSource:js injectionTime:WKUserScriptInjectionTimeAtDocumentStart forMainFrameOnly:true];
    [self.configuration.userContentController addUserScript:jsFile]; ### 对于需要改变值的js,例如登陆的js需要在登陆后改变js的值的注入方法
 //注册用户信息js方法
    NSString *jsFunc = [NSString stringWithFormat:@"var New_AppJsObj = {function() {var userinfo;}, getUserInfo: function(){return userinfo},setUserInfo: function(user){userinfo = user},getNetworkType: function(){return '%@'}}",@([CQNetworkManager status])];
    WKUserScript *jsFileUserInfo = [[WKUserScript alloc] initWithSource:jsFunc injectionTime:WKUserScriptInjectionTimeAtDocumentStart forMainFrameOnly:true];
    [self.configuration.userContentController addUserScript:jsFileUserInfo];
    
   更新注入
    NSString *par = [[CQUser getUserInfo] getSessionToken];
NSString *js = [NSString stringWithFormat:@"New_AppJsObj.setUserInfo('%@')",par];
[self evaluateJavaScript:js completionHandler:^(id _Nullable data, NSError * _Nullable error) {
    if (!error) {
        NSLog(@"更新用户信息成功");
    }
}];
[self evaluateJavaScript:@"New_AppJsObj.getUserInfo()" completionHandler:^(id _Nullable data, NSError * _Nullable error) {
    NSLog(@"用户信息:。。%@",data);
}];


使用下面这个代理进行js拦截处理

- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler {
    NSString *absoluteString = navigationAction.request.URL.absoluteString;
    NSLog(@"%@",absoluteString);
    
     NSString *hostString = navigationAction.request.URL.host;
    if ([hostString containsString:@"itunes.apple.com"]) {//跳转appstore
        if ([[UIApplication sharedApplication] canOpenURL:[NSURL URLWithString:absoluteString]]) {
            [[UIApplication sharedApplication] openURL:[NSURL URLWithString:absoluteString]];
             decisionHandler(WKNavigationActionPolicyCancel);
            return;
        }
    }
    NSString *scheme = navigationAction.request.URL.scheme;
    if ([scheme isEqualToString:@"tel"]) {
        NSString *absoluteString = navigationAction.request.URL.absoluteString;
        NSString *telNo = [absoluteString componentsSeparatedByString:@":"].lastObject;
        if (!CQ_stringIsEmpty(telNo)) {
            [Utils callPhone:telNo];
            decisionHandler(WKNavigationActionPolicyCancel);
            return;
        }
    }
    
    if (![webView decidePolicyForNavigationAction:navigationAction decisionHandler:decisionHandler]) {
         decisionHandler(WKNavigationActionPolicyCancel);
        return ;
    }
    if (![self decidePolicyForNavigationAction:navigationAction decisionHandler:decisionHandler]) {
        decisionHandler(WKNavigationActionPolicyCancel);
        return ;
    }
    
    //如果是跳转一个新页面
    if (navigationAction.targetFrame == nil) {
        [webView loadRequest:navigationAction.request];
        decisionHandler(WKNavigationActionPolicyCancel);
        return;
    }
     decisionHandler(WKNavigationActionPolicyAllow);
} # 5自定义userAgent ### 首先在AppDelegate文件中获取系统UA并进行记录
// 获取 userAgent
    self.wkWeb = [[WKWebView alloc] init];
    __weak typeof(self) weakSelf = self;
    [self.wkWeb evaluateJavaScript:@"navigator.userAgent" completionHandler:^(id _Nullable data, NSError * _Nullable error) {
        NSUserDefaults *userD = [NSUserDefaults standardUserDefaults];
        NSString *dataString = String_nil(data);
        [userD registerDefaults:@{kUserAgentKey : dataString}];
        [userD synchronize];
        weakSelf.wkWeb = nil;
    }]; ### 重写UA,并将原来的拼在后面进行更新
/**
 重置UA 
 */
+ (NSString *)cq_resetUA {
    
    NSString *oldAgent = String_nil([kCQUserDefaults objectForKey:kUserAgentKey]);
    if ([oldAgent containsString:prefixString]) {
        // 截取之前的UA
        oldAgent = [oldAgent componentsSeparatedByString:prefixString].firstObject;
    }
    
    CQUser *u = [CQUser getUserInfo];
    //loginType(0:未登录  1:第三方登录  2:手机号登录)
    NSString *loginType = @"0";
    if (u.isLogged) {
        loginType = CQ_stringIsEmpty(u.phone) ? @"1" : @"2";
    }
    NSString *newAgent = [NSString stringWithFormat:@"%@(%@;iOS;%@;%@;%@;%@)",prefixString,[Utils getAppBuild],kCQAPPID,u.sessionId,u.token,loginType];
    newAgent = [oldAgent stringByAppendingString:newAgent];
    [[NSUserDefaults standardUserDefaults] cq_registerUserAgent:newAgent];
    return newAgent;
}

在需要更新的地方调用进行UA更新

1
self.baseweb.customUserAgent = [WKWebView cq_resetUA];

在这里整个WkWebView就算迁移或者集成完毕了,希望大家bug少一点,工资多一点,写的不好,请大家指正