[iOS]使用WKWebView遇到的问题总结
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[iOS]使用WKWebView遇到的问题总结相关的知识,希望对你有一定的参考价值。
参考技术A 在使用WKWebView获取userAgent的时候, 如果要全局配置, 使所有的WKWebView都能生效, 我们可能的做法是在AppDelegate中来配置, 但是需要一个WKWebView实例对象, 所以可能是这么写的:这样, 你会发现设置一直是无效的, 在回调里打印一下:
这时发现获取到的 info 字段为nil, 并且error信息如下:
个人猜测: 这是因为, WKWebView的evaluatejavascript方法是异步执行的, 当WKWebView回调这个方法的时候, 其实例对象已经从内存中释放了, 所以导致回调出错.
我做了如下验证, 在回调方法里输出webView实例对象:
会发现输出是info有值, 而error为nil, webView有值, 又正常了, 有人说了,看样子不是这个问题! 真的么? 仔细想一下会发现, 在webView的方法回调闭包里使用了webView实例, 会发生什么? 对, 循环引用! webView实例此时不为nil, 这也验证了, 如果webView实例正常的话, 获取结果是不会有误的! 继续上面的验证, 我们弱引用一下:
这时, 会发现: info和webView都为nil, error值为上面那个错误!!!
这样, 基本验证出现这个问题的原因是: webView 提前释放了!
但是为了添加这个设置, 而将webView 设为全局变量, 仿佛有点得不偿失, 这时可以在使用webView的页面进行设置, 或者使用UIWebView替换:
在做JS与原生交互的时候, 使用下面方法注入的协议无效:
然后在js端使用的时候: 这里不需要传参数, 直接这么写的
这样, 没有响应js端的事件!!!
在代理方法中:
一直没有收到回调!!!
其实, 并不是注入协议失败, 这么使用也没问题, 问题就出在postMessage的参数上, 如果是带参数的:
这么写, 是完全没有问题的, 所以如果不需传参数的话, 可以这么写:
给一个空的字典, 就能正常交互了!!!
在WKWebView加载的html页面上, 如果长按会弹出一些选择框, 在文字上长按, 会弹出UIMenuController选择框:
而在图片上长按, 会弹出一个alertSheet:
这里可以保存图片到系统相册(如果有权限), 或者复制到剪切板. 但是这些需求并不是我们需要, 如何禁止这些行为呢?需要从JS入手, 只需要执行下面两句js即可:
可以在创建WKWebView的时候注入:
也可以在页面加载完成后的代理方法中执行:
在加载的HTML页面中, 无端出现一个广告的悬浮框:
打开之后是这样的:
而且只会在移动4G网络下才会出现, 其实这是移动的流量劫持, 强加的广告推广,目前网上有一些解决方式,常用的有:
其他的可参考这篇文章 ios 客户端对于运营商劫持的一点点对抗方式
。
在联调的时候, 前端的同学改了一些东西, 例如页面的布局, 显示元素, 或者js方法, 而APP端没反应!!!
这是因为, WKWebView有缓存, 为了保证每次加载的都是最新的页面, 可以在加载的链接后面加上一个时间戳, 例如你的HTML地址为:
一般使用是这样的:
这样的话是有缓存, 加载一次之后, 再去加载也不是最新的页面, 可以这样使用:
这样每次加载的时候都会是最新的, 当然弊端就是, 每次都会耗费一些额外的流量.
在页面无导航的情况下,系统会自动调节滚动视图的contentInset,使其视图永远处于状态栏之下,但是如果我们想让滚动视图的Y坐标从屏幕顶端(状态栏)开始,我们都知道怎么修改,但是 WKWebView不是继承自UIScrollView 的,所以不能直接设置,可以这么写:
这个闪退发生在与 JS 进行交互,使用下面的方法注册协议时:
如果重复注册了相同名称的协议,就会发生闪退,所以在使用完webView的时候,一定要记得移除已注册的协议:
app首页使用 WKWebView 来承载的内容,在启动时,如果添加了引导页/广告页,这时如果有手势操作,例如在出现广告页时点击屏幕,就会闪退,并在控制台输出:
添加一个全局断点,调试发现崩溃信息为
查了些资料,没找到具体原因,但是了解到和 +load方法有关,我是在 +load 方法内初始化的广告页的信息,把这些放在 didFinishLaunchingWithOptions 进行初始化,就不会有这个问题;
解决:
将广告/引导页视图的初始化放在 didFinishLaunchingWithOptions 方法内。
WKWebView替换WebView体会总结
一.引言
最近应公司要求,把项目里用到的UIWebView全都用WKWebView替换掉。于是就去研究了WKWebview,加上在使用中遇到的一些问题加以总结,如有不足之处,还望指出,本人将会加以修改。
UIWebView
自iOS2就有,WKWebView
从iOS8才有,毫无疑问WKWebView
将逐步取代笨重的UIWebView
。通过简单的测试即可发现UIWebView
占用过多内存,且内存峰值更是夸张。WKWebView
网页加载速度也有提升,但是并不像内存那样提升那么多。下面列举一些其它的优势:
- 更多的支持HTML5的特性
- 官方宣称的高达60fps的滚动刷新率以及内置手势
- Safari相同的JavaScript引擎
- 将UIWebViewDelegate与UIWebView拆分成了14类与3个协议(官方文档说明)
- 另外用的比较多的,增加加载进度属性:
estimatedProgress
缺点: WKWebView 不支持缓存 和 NSURLProtocol 拦截了
二、UIWebView使用说明
1 举例:简单的使用
UIWebView
使用非常简单,可以分为三步,也是最简单的用法,显示网页
// 1.创建webview,并设置大小,"20"为状态栏高度
UIWebView *webView = [[UIWebView alloc] initWithFrame:CGRectMake(0,20, self.view.frame.size.width,self.view.frame.size.height -20)];
// 2.创建请求
NSMutableURLRequest *request =[NSMutableURLRequestrequestWithURL:[NSURLURLWithString:@"http://www.cnblogs.com/mddblog/"]];
// 3.加载网页
[webView loadRequest:request];
// 最后将webView添加到界面
[self.view addSubview:webView];
2 一些实用函数
- 加载函数。
- (void)loadRequest:(NSURLRequest *)request;
- (void)loadHTMLString:(NSString *)string baseURL:(nullable NSURL *)baseURL;
- (void)loadData:(NSData *)data MIMEType:(NSString *)MIMEType textEncodingName:(NSString *)textEncodingName baseURL:(NSURL *)baseURL;
UIWebView
不仅可以加载HTML页面,还支持pdf、word、txt、各种图片等等的显示。下面以加载mac桌面上的png图片:/Users/coohua/Desktop/bigIcon.png
为例// 1.获取url
NSURL *url = [NSURLfileURLWithPath:@"/Users/coohua/Desktop/bigIcon.png"];
// 2.创建请求
NSURLRequest *request=[NSURLRequestrequestWithURL:url];
// 3.加载请求
[self.webView loadRequest:request];
- 网页导航刷新有关函数
// 刷新
- (void)reload;
//停止加载
- (void)stopLoading;
//后退函数
- (void)goBack;
//前进函数
- (void)goForward;
//是否可以后退
@property (nonatomic,readonly, getter=canGoBack)BOOL canGoBack;
//是否可以向前
@property (nonatomic,readonly, getter=canGoForward)BOOL canGoForward;
//是否正在加载
@property (nonatomic,readonly, getter=isLoading)BOOL loading;
3 代理协议使用:UIWebViewDelegate
一共有四个方法
///是否允许加载网页,也可获取js要打开的url,通过截取此url可与js交互
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType
NSString *urlString = [[request URL] absoluteString];
urlString = [urlString stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
NSArray *urlComps = [urlString componentsSeparatedByString:@"://"];
NSLog(@"urlString=%@---urlComps=%@",urlString,urlComps);
returnYES;
///开始加载网页
- (void)webViewDidStartLoad:(UIWebView *)webView
NSURLRequest *request = webView.request;
NSLog(@"webViewDidStartLoad-url=%@--%@",[request URL],[request HTTPBody]);
///网页加载完成
- (void)webViewDidFinishLoad:(UIWebView *)webView
NSURLRequest *request = webView.request;
NSURL *url = [request URL];
if ([url.path isEqualToString:@"/normal.html"])
NSLog(@"isEqualToString");
NSLog(@"webViewDidFinishLoad-url=%@--%@",[request URL],[request HTTPBody]);
NSLog(@"%@",[self.webView stringByEvaluatingJavaScriptFromString:@"document.title"]);
///网页加载错误
- (void)webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error
NSURLRequest *request = webView.request;
NSLog(@"didFailLoadWithError-url=%@--%@",[request URL],[request HTTPBody]);
4 与js交互
///加载完成时调用
- (void)webViewDidFinishLoad:(UIWebView *)webView
[self.webView stringByEvaluatingJavaScriptFromString:@"document.documentElement.style.webkitUserSelect='none';"];
[self.webView stringByEvaluatingJavaScriptFromString:@"document.documentElement.style.webkitTouchCallout='none';"];
[self.showView removeFromSuperview];
NSString *jsToGetHTMLSource = @"document.getElementsByTagName('html')[0].innerHTML";
NSString *HTMLSource = [self.webView stringByEvaluatingJavaScriptFromString:jsToGetHTMLSource];
[self.activityView stopAnimating];
NSRange range = [HTMLSource rangeOfString:@"Bad Gateway"];//判断字符串是否包含
bool urlIsTrue = (range.location == NSNotFound);
if (urlIsTrue ==true )
range = [HTMLSource rangeOfString:@"Network is unreachable"];
urlIsTrue = (range.location == NSNotFound);
if (urlIsTrue ==true )
range = [HTMLSource rangeOfString:@"页面不存在"];
urlIsTrue = (range.location == NSNotFound);
if (urlIsTrue ==true)
range = [HTMLSource rangeOfString:@"网页无法访问"];
urlIsTrue = (range.location == NSNotFound);
if (urlIsTrue ==true)
range = [HTMLSource rangeOfString:@"Not Found"];
urlIsTrue = (range.location == NSNotFound);
//网页可访问隐藏我们自己的返回按钮
if (urlIsTrue==true)
self.topView.hidden =YES;
else
[webView stopLoading];
[self addShowView];
三、WKWebView使用说明
1 简单使用
与UIWebview一样,仅需三步:记住导入(#import <WebKit/WebKit.h>)
- (void)simpleExampleTest
// 1.创建webview,并设置大小,"20"为状态栏高度
WKWebView *webView = [[WKWebView alloc] initWithFrame:CGRectMake(0,20, self.view.frame.size.width,self.view.frame.size.height -20)];
// 2.创建请求
NSMutableURLRequest *request =[NSMutableURLRequest requestWithURL:[NSURL URLWithString:@"http://www.cnblogs.com/mddblog/"]];
// 3.加载网页
[webView loadRequest:request];
//最后将webView添加到界面
[self.view addSubview:webView];
2 一些实用函数
- 加载网页函数
相比UIWebview,WKWebView也支持各种文件格式,并新增了loadFileURL
函数,顾名思义加载本地文件。
///模拟器调试加载mac本地文件
- (void)loadLocalFile
// 1.创建webview,并设置大小,"20"为状态栏高度
WKWebView *webView = [[WKWebView alloc] initWithFrame:CGRectMake(0,20, self.view.frame.size.width,self.view.frame.size.height -20)];
// 2.创建url userName:电脑用户名
NSURL *url = [NSURL fileURLWithPath:@"/Users/userName/Desktop/bigIcon.png"];
// 3.加载文件
[webView loadFileURL:url allowingReadAccessToURL:url];
//最后将webView添加到界面
[self.view addSubview:webView];
/// 其它三个加载函数
- (WKNavigation *)loadRequest:(NSURLRequest *)request;
- (WKNavigation *)loadHTMLString:(NSString *)string baseURL:(nullable NSURL *)baseURL;
- (WKNavigation *)loadData:(NSData *)data MIMEType:(NSString *)MIMEType characterEncodingName:(NSString *)characterEncodingName baseURL:(NSURL *)baseURL;
- 网页导航刷新相关函数
reloadFromOrigin
和
goToBackForwardListItem
。
- reloadFromOrigin会比较网络数据是否有变化,没有变化则使用缓存,否则从新请求。
- goToBackForwardListItem:比向前向后更强大,可以跳转到某个指定历史页面
@property (nonatomic,readonly) BOOL canGoBack;
@property (nonatomic,readonly) BOOL canGoForward;
- (WKNavigation *)goBack;
- (WKNavigation *)goForward;
- (WKNavigation *)reload;
- (WKNavigation *)reloadFromOrigin; //增加的函数
- (WKNavigation *)goToBackForwardListItem:(WKBackForwardListItem *)item;// 增加的函数
- (void)stopLoading;
- 一些常用属性
- allowsBackForwardNavigationGestures:BOOL类型,是否允许左右划手势导航,默认不允许
- estimatedProgress:加载进度,取值范围0~1
- title:页面title
- .scrollView.scrollEnabled:是否允许上下滚动,默认允许
- backForwardList:WKBackForwardList类型,访问历史列表,可以通过前进后退按钮访问,或者通过goToBackForwardListItem函数跳到指定页面
3 代理协议使用
一共有三个代理协议:
- WKNavigationDelegate:最常用,和UIWebViewDelegate功能类似,追踪加载过程,有是否允许加载、开始加载、加载完成、加载失败。下面会对函数做简单的说明,并用数字标出调用的先后次序:1-2-3-4-5
三个是否允许跳转加载的函数:
/// 接收到服务器跳转请求之后调用 (服务器端redirect),不一定调用
- (void)webView:(WKWebView *)webView didReceiveServerRedirectForProvisionalNavigation:(WKNavigation *)navigation;
/// 3 在收到服务器的响应头,根据response相关信息,决定是否跳转。decisionHandler必须调用,来决定是否跳转,参数WKNavigationActionPolicyCancel取消跳转,WKNavigationActionPolicyAllow允许跳转
- (void)webView:(WKWebView *)webView decidePolicyForNavigationResponse:(WKNavigationResponse *)navigationResponse decisionHandler:(void (^)(WKNavigationResponsePolicy))decisionHandler;
/// 1 在发送请求之前,决定是否跳转
- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler;
追踪加载过程函数:
/// 2 页面开始加载
- (void)webView:(WKWebView *)webView didStartProvisionalNavigation:(WKNavigation *)navigation;
/// 4 开始获取到网页内容时返回
- (void)webView:(WKWebView *)webView didCommitNavigation:(WKNavigation *)navigation;
/// 5 页面加载完成之后调用
- (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation;
/// 页面加载失败时调用
- (void)webView:(WKWebView *)webView didFailProvisionalNavigation:(WKNavigation *)navigation;
- WKScriptMessageHandler:必须实现的函数,是APP与js交互,提供从网页中收消息的回调方法
/// message: 收到的脚本信息.
- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message;
- WKUIDelegate:UI界面相关,原生控件支持,三种提示框:输入、确认、警告。首先将web提示框拦截然后再做处理。
/// 创建一个新的WebView
- (WKWebView *)webView:(WKWebView *)webView createWebViewWithConfiguration:(WKWebViewConfiguration *)configuration forNavigationAction:(WKNavigationAction *)navigationAction windowFeatures:(WKWindowFeatures *)windowFeatures;
/// 输入框
- (void)webView:(WKWebView *)webView runJavaScriptTextInputPanelWithPrompt:(NSString *)prompt defaultText:(nullable NSString *)defaultText initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(NSString *__nullable result))completionHandler
NSLog(@"%s",__FUNCTION__);
NSLog(@"%@", prompt);
UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"textinput" message:@"JS调用输入框" preferredStyle:UIAlertControllerStyleAlert];
[alert addTextFieldWithConfigurationHandler:^(UITextField *_Nonnull textField)
textField.textColor = [UIColor redColor];
];
[alert addAction:[UIAlertAction actionWithTitle:@"确定" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action)
completionHandler([[alert.textFields lastObject] text]);
]];
[self presentViewController:alert animated:YES completion:NULL];
/// 确认框
- (void)webView:(WKWebView *)webView runJavaScriptConfirmPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(BOOL result))completionHandler
NSLog(@"%s", __FUNCTION__);
UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"confirm" message:@"JS调用confirm"preferredStyle:UIAlertControllerStyleAlert];
[alert addAction:[UIAlertAction actionWithTitle:@"确定" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action)
completionHandler(YES);
]];
[alert addAction:[UIAlertAction actionWithTitle:@"取消" style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action)
completionHandler(NO);
]];
[self presentViewController:alert animated:YES completion:NULL];
NSLog(@"%@", message);
/// 警告框
- (void)webView:(WKWebView *)webView runJavaScriptAlertPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(void))completionHandler
NSLog(@"%s", __FUNCTION__);
UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"alert"message:@"JS调用alert" preferredStyle:UIAlertControllerStyleAlert];
[alert addAction:[UIAlertAction actionWithTitle:@"确定以上是关于[iOS]使用WKWebView遇到的问题总结的主要内容,如果未能解决你的问题,请参考以下文章
在 iOS 9 中遇到 WKWebview 和 UIWebView 问题