WKWebView问题小结

来源:互联网 发布:三金冠淘宝店值多少钱 编辑:程序博客网 时间:2024/06/05 09:13

内存泄漏

当需要拦截Web页面的Javascript函数时会使用以下方法

- (void)addScriptMessageHandler:(id <WKScriptMessageHandler>)scriptMessageHandler name:(NSString *)name;

在Web页面需要调用以下方法

window.webkit.messageHandlers.name.postMessage();

这两个方法的name必须一样才能被WKWebView拦截到。
但是如果不及时移除Handler的话WebView不会被释放。所以只要调用了addScriptMessageHandler方法就要调用相对应的方法移除

- (void)removeScriptMessageHandlerForName:(NSString *)name;

内嵌在Cell里显示不全

在iOS10及以上系统里在TableViewCell中嵌入WKWebView时,滚动TableView时WebView渲染会出错。往往内容显示不出来。

具体解决办法在TableView的代理方法中调用WKWebView的setNeedsLayout方法:

- (void)scrollViewDidScroll:(UIScrollView *)scrollView{    [webView setNeedsLayout];}

WKWebView不能打开新窗口<a target=_blank />

如果Web页面的链接是<a target=_blank />时,正常结果是新建窗口打开这个页面。但在WKWebView中,如果没有实现相关的代理方法,那么这个方法失效,点击网页中任何外部链接都没有反应。期望的结果是通过Safari来打开这个外部链接。原来的WebView不进行任何处理。如果这样需要在WebView的代理方法中进行如下处理:

- (void)webView:(WKWebView*)webView decidePolicyForNavigationAction:(WKNavigationAction*)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler{    if (!navigationAction.targetFrame) {    //处理<a target=_blank />这种情况        NSURL *url = navigationAction.request.URL;        UIApplication *app = [UIApplication sharedApplication];        if ([app canOpenURL:url]) {            [app openURL:url];        }    }}

在WKWebView中动态更改UserAgent

往往在访问我们自己的域名时会修改相关UserAgent上传。而访问其他域名时会将UserAgent修改成默认的。这样就需要选择性的修改UserAgent
首先在程序加载时我们先获取系统默认的UserAgent并将它保存在本地:

+(void)load{    UIWebView *webView = [[UIWebView alloc] initWithFrame:CGRectZero];    NSString *oldAgent = [webView stringByEvaluatingJavaScriptFromString:@"navigator.userAgent"];    NSDictionary *dictionnary = [[NSDictionary alloc] initWithObjectsAndKeys:oldAgent, @"originAgent", nil];    [[NSUserDefaults standardUserDefaults] registerDefaults:dictionnary];    }

然后在加载新页面时来判断是否为我们自己的域名,调用如下方法针对性的修改UserAgent

- (void)setUserAgent:(NSString *)userAgent{    if ([URL.host hasSuffix:baseDomain] {         //如果是我们自己的域名将传进来的userAgent设置到http的请求header中        NSDictionary *dictionnary = [[NSDictionary alloc] initWithObjectsAndKeys:userAgent, @"UserAgent", nil];        [[NSUserDefaults standardUserDefaults] registerDefaults:dictionnary];        }    else{        //否则设置成系统默认的UserAgent        NSString *string = [[NSUserDefaults standardUserDefaults] objectForKey:@"originAgent"];        NSDictionary *dictionnary = [[NSDictionary alloc] initWithObjectsAndKeys:OutNull(string), @"UserAgent", nil];        [[NSUserDefaults standardUserDefaults] registerDefaults:dictionnary];    }}

本地Request的Cookies与WKWebView的Cookies共享

往往我们想将本地URL请求的Cookies同步到WKWebView中,在使用UIWebView时会自动同步我们请求URL的Cookies,但在WKWebView中会失效,因为WKWebView自己独立管理Cookies,不与NSURLRequest的公用。因此要通过一下方法进行设置WKWebview的Cookies。

首先在初始化是执行一下脚本来设置Cookies

-(void)initWebView{    WKWebViewConfiguration *webViewconfiguration = [[WKWebViewConfiguration alloc] init];    WKUserContentController *wkUController = [[WKUserContentController alloc] init];    if(URL.host hasSuffix:baseDomain){        //在此处要判断域名是否是自己网站。        NSString *jScript = [self setCookies];        WKUserScript *wkUScript = [[WKUserScript alloc] initWithSource:jScript injectionTime:WKUserScriptInjectionTimeAtDocumentStart forMainFrameOnly:NO];                        [wkUController addUserScript:wkUScript];    }    webViewconfiguration.userContentController = wkUController;    WKWebView *webView = [[WKWebView alloc] initWithFrame:CGRectMake(0, 0, width, height) configuration:webViewconfiguration];}//执行的脚本,可能略有不同+(NSString *)setCookies{    NSString *script = [NSString string];    for (NSHTTPCookie *httpCookie in [[NSHTTPCookieStorage sharedHTTPCookieStorage] cookies])    {        NSString *cookie = [NSString stringWithFormat:@"%@=%@;domain=%@;path=%@",httpCookie.name,httpCookie.value,httpCookie.domain,httpCookie.path?:@"/"];        script = [script stringByAppendingString:[NSString stringWithFormat:@"document.cookie='%@';",cookie]];    }    return script;}

然后在创建NSURLRequest对象时手动添加cookies到HTTP的Header中:

- (void)loadRequest:(NSURLRequest *)request{    //此处也要判断要加载的url是否是我们自己的域名    if ([request.URL.host hasSuffix:baseDomain]){        NSMutableURLRequest *mutableRequest = [[NSMutableURLRequest alloc] initWithURL:request.URL];        NSString *cookies = @""; //设置Cookies        [mutableRequest addValue:cookies forHTTPHeaderField:@"Cookie"];    }    // do request  }

WKWebView在iOS10及以上系统设置新的Cookies不能覆盖旧的Cookies

在使用WKWebView时发现在iOS10以下的系统中如果按照上面的方法设置完Cookies后会生效,并且会自动覆盖旧的Cookies。而在iOS10及以上系统时设置新的Cookies并不会生效,WKWebView还会使用之前的设置过的Cookies。所以我们在切换用户时需要调用以下方法手动删除Cookies

-(void)clearCookies{    WKWebsiteDataStore *dateStore = [WKWebsiteDataStore defaultDataStore];            [dateStore fetchDataRecordsOfTypes:[WKWebsiteDataStore allWebsiteDataTypes]                             completionHandler:^(NSArray<WKWebsiteDataRecord *> * __nonnull records) {                                for (WKWebsiteDataRecord *record  in records){                                    if ([record.displayName isEqualToString:baseDomain]){                                           //判断如果是我们自己的域名那么删除该域名下的Cookies                                        [[WKWebsiteDataStore defaultDataStore] removeDataOfTypes:record.dataTypes forDataRecords:@[record] completionHandler:^{                                            }];                                    }                                }                             }];}

WKWebView在在iOS10及以上系统清除Cookies失效

使用上面的方法清除Cookies时会发现有时会清除失败。加载新的页面时还会使用旧的Cookies。这是因为清除Cookies的时机不对,我们应该在清除完成的回调内再加载新的页面。

- (void)loadRequest:(NSURLRequest *)request{    WKWebsiteDataStore *dateStore = [WKWebsiteDataStore defaultDataStore];            [dateStore fetchDataRecordsOfTypes:[WKWebsiteDataStore allWebsiteDataTypes]                             completionHandler:^(NSArray<WKWebsiteDataRecord *> * __nonnull records) {                                BOOL isExit = NO;                                for (WKWebsiteDataRecord *record  in records){                                    if ([record.displayName isEqualToString:baseDomain]){                                           //判断如果是我们自己的域名那么删除该域名下的Cookies                                        isExit = YES;                                        [[WKWebsiteDataStore defaultDataStore] removeDataOfTypes:record.dataTypes forDataRecords:@[record] completionHandler:^{                                                [webView loadRequest:request];                                            }];                                            break;                                    }                                }                                if (!isExit) {                                     [webView loadRequest:[mutableRequest copy]];                                 }                             }];}

注意:如果在退出页面时清除Cookies,也会出现清除失败的情况。

Native页面内嵌WKWebView高度计算问题

计算WKWebView的高度是我们往往调用document.body.scrollHeight这个方法来计算。如果在下面的代理方法里调用那么计算的高度是不准确的:

- (void)webView:(WKWebView*)webView decidePolicyForNavigationAction:(WKNavigationAction*)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler{}

因为页面内部有很多图片还没有加载出来。所以此时调用document.body.scrollHeight这个方法计算的高度是未加载完的页面高度。
如果在加载结束的代理方法里面调用document.body.scrollHeight,那么页面只能在全部资源下载后才会显示。如果我们想要边加载资源边动态的改变页面高度需要做如下处理:

我们在初始化WebView时监听WebView的加载进度

-(void)initWebView{    WKWebView *webView = [[WKWebView alloc] initWithFrame:CGRectMake(0, 0, width, height)];    [webView addObserver:self forKeyPath:@"estimatedProgress" options:NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOld context:NULL];}

然后边加载边计算WebView的高度,这样就不必等页面全部加载完再计算WebView的高度了。

-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context{    if ([keyPath isEqualToString:@"estimatedProgress"]) {        self.estimatedProgress = [change[NSKeyValueChangeNewKey] doubleValue];        if ([change[NSKeyValueChangeOldKey] doubleValue]) {            [self evaluateJavaScript:@"document.body.scrollHeight" completionHandler:^(id heitht, NSError *error) {                if (!error) {                    self.pageHeight = [heitht floatValue];                }            }];        }    }}
原创粉丝点击