一、UIWebView 介绍
1.UIWebView网页加载展现的几种方法
// 使用 NSURLRequest 的方式加载网页 (url可以是远程也可以是本地)
- (void)loadRequest:(NSURLRequest *)request;
/*! @brief 加载HTML字符串
*
* @param string 为要加载的本地HTML字符串
* @param baseURL 用来确定htmlString的基准地址,相当于HTML的<base>标签的作用,定义页面中所有链接的默认地址
*/
- (void)loadHTMLString:(NSString *)string
baseURL:(nullable NSURL *)baseURL;
/*! @brief 以二进制数据的形式加载文件
*
* @param Data 文件数据
* @param MIMEType 文件类型
* @param textEncodingName 编码类型
* @param baseURL 素材资源路径
*/
- (void)loadData:(NSData *)data
MIMEType:(NSString *)MIMEType
textEncodingName:(NSString *)textEncodingName
baseURL:(NSURL *)baseURL;
2.UIWebView的一些属性和方法
#pragma mark - 判断属性
// 是否可以后退
@property (nonatomic, readonly, getter=canGoBack) BOOL canGoBack;
// 是否可以向前
@property (nonatomic, readonly, getter=canGoForward) BOOL canGoForward;
// 是否正在加载
@property (nonatomic, readonly, getter=isLoading) BOOL loading;
#pragma mark - 操作方法
// 刷新网页
- (void)reload;
// 停止加载网页
- (void)stopLoading;
// 后退
- (void)goBack;
// 前进
- (void)goForward;
3.UIWebView的代理方法
//是否允许加载网页,也可获取js要打开的url,通过截取此url可与js交互
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request
navigationType:(UIWebViewNavigationType)navigationType;
//开始加载网页
- (void)webViewDidStartLoad:(UIWebView *)webView;
//网页加载完成
- (void)webViewDidFinishLoad:(UIWebView *)webView;
//网页加载错误
- (void)webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error;
4.UIWebView和Javascript之间的交互
主要有两方面 : js执行OC代码、OC调取写好的js代码
(1).OC调取写好的js代码
这里用到UIwebview的一个方法。stringByEvaluatingJavaScriptFromString
示例代码
// 实现自动定位js代码, htmlLocationID为定位的位置 (由js开发人员给出)
NSString *javascriptStr = [NSString stringWithFormat:@"window.location.href = '#%@'",htmlLocationID];
[self.webView stringByEvaluatingJavaScriptFromString:javascriptStr];
// 获取网页的title
title = [self.webView stringByEvaluatingJavaScriptFromString:@"document.title"]
// 获取当前页面的url
NSString *url = [webview stringByEvaluatingJavaScriptFromString:@"document.location.href"];
(2).js执行OC代码
2.1 js是不能执行OC代码的,但是可以变相的执行,js可以将要执行的操作封装到网络请求里面,然后oc拦截这个请求,获取url里面的字符串解析即可,这里用到代理协议的shouldStartLoadWithRequest函数。
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request
navigationType:(UIWebViewNavigationType)navigationType
示例代码
#pragma mark - iOS 拦截UIWebView 内容的点击事件>
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request
navigationType:(UIWebViewNavigationType)navigationType {
//判断是否是单击
if (navigationType == UIWebViewNavigationTypeLinkClicked){
NSString *url = [request.URL absoluteString];
//拦截链接跳转
if ([url rangeOfString:@"http:"].location != NSNotFound){
return NO;
}
}
return YES;
}
#pragma mark - UIWebViewNavigationType 类型
UIWebViewNavigationTypeLinkClicked,用户触击了一个链接。
UIWebViewNavigationTypeFormSubmitted,用户提交了一个表单。
UIWebViewNavigationTypeBackForward,用户触击前进或返回按钮。
UIWebViewNavigationTypeReload,用户触击重新加载的按钮。
UIWebViewNavigationTypeFormResubmitted,用户重复提交表单
UIWebViewNavigationTypeOther,发生其它行为。
#pragma mark - 捕获内部webView点击事件
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request
navigationType:(UIWebViewNavigationType)navigationType {
// 在此捕获当前打开的URL的地址;
NSLog(@"%@",request.URL.absoluteString);
// 此内测试在此用webView打开百度页面;点击--新闻--点击新闻分类,当点击新闻为国际时,模拟为注册成功,关闭注册页面;就是关闭当前webView页面
if ([request.URL.absoluteString hasSuffix:@"internews"]) {
NSLog(@"line<%d> %s",__LINE__,__func__);
[self closeView];
return NO;
}
return YES;
}
2.2利用NSURLProtocol拦截UIWebView的网络请求
NSURLProtocol它是干什么的呢,是一个挺牛逼的类,它是一个抽象类,不能去实例化它,只能子类化NSURLProtocol,每次在对一个 URL 进行请求的时候 URL Loading System 都会向 已经注册的 Protocol 询问是否可以处理该请求。这里就看出他的作用来了. 比如: 拦截UIWebView的请求,忽略请求,重定向… …
iOS - UIWebView (NSURLProtocol)拦截js、css
二、为什么需要 WKWebView
用 Web 方式承载业务引入 app 的混合开发已成为一个硬性需求,并且随着业务的扩展和审核的限制,该比例会愈加增大,这样对承载的平台就有严格要求,而老旧的 UIWebView 存在严重的性能和内存消耗问题,限制了业务方的自由度。而自 iOS 8 之后,苹果提供的 WebKit 库包含了 WKWebView,WKWebView 采用跨进程方案,Nitro JS 解析器,高达 60fps 的刷新率,理论上性能和 Safari 比肩,而且对 H5 的高度支持,还提供了一个准确的加载进度值属性,我们没有理由拒绝这样的一个新事物。
那么,WKWebview上如何加载进度条呢?
1、添加UIProgressView属性
@property (nonatomic, strong) WKWebView *wkWebView;
@property (nonatomic, strong) UIProgressView *progressView;
2、初始化UIProgressView
- (UIProgressView *)progressView {
if(!_progressView) {
_progressView = [[UIProgressView alloc] initWithFrame:CGRectMake(0, self.navBar.maxY, MSWIDTH, 0)];
_progressView.progressViewStyle = UIProgressViewStyleBar;
_progressView.tintColor = RGB(218, 187, 63);
_progressView.trackTintColor = [UIColor clearColor];
[self.view addSubview:_progressView];
}
return _progressView;
}
3、添加kvo监听
[self.wkWebView addObserver:self forKeyPath:@"estimatedProgress" options:NSKeyValueObservingOptionNew context:nil];
4、计算wkWebView进度条
- (void)observeValueForKeyPath:(NSString *)keyPath
ofObject:(id)object
change:(NSDictionary<NSString *,id> *)change
context:(void *)context {
if ([keyPath isEqualToString:@"estimatedProgress"]) {
self.progressView.progress = self.wkWebView.estimatedProgress;
if (self.progressView.progress == 1) {
__weak typeof (self)weakSelf = self;
/*添加一个简单的动画,将progressView的Height变为1.4倍,在开始加载网页的代理中会恢复为1.5倍.动画时长0.25s,延时0.3s后开始动画,动画结束后将progressView隐藏*/
[UIView animateWithDuration:0.25f delay:0.3f options:UIViewAnimationOptionCurveEaseOut animations:^{
weakSelf.progressView.transform = CGAffineTransformMakeScale(1.0f, 1.4f);
} completion:^(BOOL finished) {
weakSelf.progressView.hidden = YES;
}];
}
} else {
[super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
}
}
5、页面开始加载时
- (void)webView:(WKWebView *)webView didStartProvisionalNavigation:(null_unspecified WKNavigation *)navigation {
DLOG(@"%@", webView.URL);
[self.progressView setProgress:.15 animated:YES];
}
6、移除监听
[self.wkWebView removeObserver:self forKeyPath:@"estimatedProgress"];
三、对比 UIWebView 介绍WKWebView
WKWebView 和 UIWebView 二者在使用上差不多(如下代码所示),如果仅做页面呈现和简单交互,不需要考虑太多的投入成本。 对比代码,二者在创建和使用上基本一致,通过一个 URL 字符串构建 Request 对象,然后使用WebView 内置接口载入 Request 对象即可。流程上二者有一定的区别,在节点上,WKWebView 比 UIWebView 多了一个询问过程,在服务器响应请求之后会询问是否载入内容到当前 Frame,在控制上会比UIWebView 粒度更细一些
1.相比UIWebview
1、相比UIWebview支持各种文件格式,WKWebView也支持各种文件格式,并新增了loadFileURL函数,顾名思义加载本地文件。
2、相比UIWebView的一些属性和方法,几乎一样,不同的是有返回值。另外增加了函数reloadFromOrigin和goToBackForwardListItem。
// 会比较网络数据是否有变化,没有变化则使用缓存,否则从新请求。
- (WKNavigation *)reloadFromOrigin;
// 比向前向后更强大,可以跳转到某个指定历史页面
- (WKNavigation *)goToBackForwardListItem:(WKBackForwardListItem *)item;
2.一些常用属性
- allowsBackForwardNavigationGestures : BOOL类型,是否允许左右划手势导航,默认不允许
- estimatedProgress: 加载进度,取值范围0~1
- title : 页面title
- .scrollView.scrollEnabled : 是否允许上下滚动,默认允许
- backForwardList : WKBackForwardList类型,访问历史列表,可以通过前进后退按钮访问,或者通过goToBackForwardListItem函数跳到指定页面
3.WKWebView 的代理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 didReceiveServerRedirectForProvisionalNavigation:(WKNavigation *)navigation;
// 在收到响应后,决定是否跳转
- (void)webView:(WKWebView *)webView decidePolicyForNavigationResponse:(WKNavigationResponse *)navigationResponse
decisionHandler:(void(^)(WKNavigationResponsePolicy))decisionHandler;
// 在发送请求之前,决定是否跳转
- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction
decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler;
而除此之外,WKWebView 对比 UIWebView 有较大差异的地方有几点。
(1).WKWebView 多了一个重定向通知,在收到服务器重定向消息并且跳转询问允许之后,会回调重定向方法,这点是 UIWebView 没有的,在 UIWebView之上需要验证是否重定向,得在询问方法验证 head 信息。
- (void)webView:(WKWebView *)webView didReceiveServerRedirectForProvisionalNavigation:(null_unspecified WKNavigation *)navigation;
(2).新增 WKUIDelegate 协议,该协议包含一些UI相关的内容。
在 UIWebView中,Alert、Confirm、Prompt 等视图是直接可执行的,但在WKWebView上,需要通过 WKUIDelegate协议接收通知,然后通过 iOS 原生执行。
// 创建一个新的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 * result))completionHandler;
/// 确认框
- (void)webView:(WKWebView *)webView runJavaScriptConfirmPanelWithMessage:(NSString *)message
initiatedByFrame:(WKFrameInfo *)frame
completionHandler:(void (^)(BOOL result))completionHandler;
/// 警告框
- (void)webView:(WKWebView *)webView runJavaScriptAlertPanelWithMessage:(NSString *)message
initiatedByFrame:(WKFrameInfo *)frame
completionHandler:(void (^)(void))completionHandler;
4.WKWebView 和Javascript之间的交互
主要有两方面 : js执行OC代码、OC调取写好的js代码
(1).OC调取写好的js代码
这个完全依靠 WebView 提供的接口实现,WKWebView 提供的接口和 UIWebView 命名上较为类似,区别是 WKWebView 的这个接口是异步的,而 UIWebView 是同步接口.
#pragma mark - UIWebView
NSString *title = [webView stringByEvaluatingJavaScriptFromString:@"document.title"];
#pragma mark - WKWebView
[wkWebView evaluateJavaScript:@"document.title"
completionHandler:^(id _Nullable ret, NSError * _Nullable error) {
NSString *title = ret;
}];
(2).js执行OC代码
这地方需要两个配置,一个是OC代码的配置,另一个是JS代码的配置,下面先说一下OC代码的配置,细心的小伙伴可能已经发现了,创建WKWebView的时候,除了有 initWithFrame:方法外,还有一个高端的方法 : initWithFrame:configuration:方法。
(2.1).配置 WKWebView
// 创建配置
WKWebViewConfiguration *config = [[WKWebViewConfiguration alloc] init];
// 创建UserContentController(提供JavaScript向webView发送消息的方法)
WKUserContentController* userController = [[WKUserContentController alloc] init];
// 添加消息处理
//(注意:self指代的对象需要遵守WKScriptMessageHandler协议,结束时需要移除//NativeMethod 这个方法一会要与JS里面的方法写的一样)
[userController addScriptMessageHandler:self name:@"NativeMethod"];
// 将UserConttentController设置到配置文件
config.userContentController = userController ;
// 高端的自定义配置创建WKWebView
WKWebView *webView = [[WKWebView alloc] initWithFrame:
[UIScreen mainScreen].bounds configuration:config];
// 设置访问的URL
NSURL *url = [NSURL URLWithString:@"http://www.jianshu.com"];
// 根据URL创建请求
NSURLRequest *request = [NSURLRequest requestWithURL:url];
// WKWebView加载请求
[webView loadRequest:request];
// 将WKWebView添加到视图
[self.view addSubview:webView];
(2.2).实现协议方法
如果JavaScript执行已经写好的:
window.webkit.messageHandlers.NativeMethod.postMessage("就是一个消息啊");
这行代码WKScriptMessageHandler的协议里面userContentController:didReceiveScriptMessage:这个代理方法就会走,并且会有WKScriptMessage的对象,这个WKScriptMessage对象有个name属性,拿到之后你会发现,就是我们注册的NativeMethod这个字符串,这时候你就可以手动调用OC的方法了。如果有多个方法需要调用的话怎么办,看到JavaScript中postMessage()方法有一个参数了没有,可以根据这里的参数来区分调用原生App的哪个方法。
- (void)userContentController:(WKUserContentController *)userContentController
didReceiveScriptMessage:(WKScriptMessage *)message {
// 判断是否是调用原生的
if ([@"NativeMethod" isEqualToString:message.name]) {
// 判断message的内容,然后做相应的操作
if ([@"close" isEqualToString:message.body]) {
}
}
}
注意:上面将当前ViewController设置为MessageHandler之后需要在当前ViewController销毁前将其移除(dealloc方法),否则会造成内存泄漏。
[self.webView.configuration.userContentController removeScriptMessageHandlerForName:@"NativeMethod"];
5.WKWebView 和Javascript之间的交互注意事项
(1).WKWebview 默认是不弹出js的alert 要想可以弹出alert 需要手动的设置代理实现
- (void)webView:(WKWebView *)webView runJavaScriptAlertPanelWithMessage:(NSString *)message
initiatedByFrame:(WKFrameInfo *)frame
completionHandler:(void (^)(void))completionHandler
具体的实现方法是,我们采用源生的UIAlertController 来实现弹出框,获取js里面的内容并显示出来
- (void)webView:(WKWebView *)webView runJavaScriptAlertPanelWithMessage:(NSString *)message
initiatedByFrame:(WKFrameInfo *)frame
completionHandler:(void (^)(void))completionHandler {
UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"提示" message:message?:@"" preferredStyle:UIAlertControllerStyleAlert];
[alertController addAction:([UIAlertAction actionWithTitle:@"取消" style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) {
completionHandler();
NSLog(@"点击了取消按钮==%@",message);
}])];
[alertController addAction:([UIAlertAction actionWithTitle:@"确认" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
completionHandler();
NSLog(@"点击了确定按钮==%@",message);
}])];
[self presentViewController:alertController animated:YES completion:nil];
}
(2).WKWebview 默认是不能识别电话号的,这里需要通过实现一个协议来实现拨打电话的功能
- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction
decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler {
NSURL *URL = navigationAction.request.URL;
NSLog(@"获取到URL========%@",URL);
NSString *scheme = [URL scheme];
UIApplication *app = [UIApplication sharedApplication];
// 打电话
if ([scheme isEqualToString:@"tel"]) {
if ([app canOpenURL:URL]) {
[app openURL:URL];
// 一定要加上这句,否则会打开新页面
decisionHandler(WKNavigationActionPolicyCancel);
return;
}
}
decisionHandler(WKNavigationActionPolicyAllow);
}
(3).WKWebView 默认拦截open.window() 打开新的页面
// WKUIDelegate协议中
- (WKWebView )webView:(WKWebView )webView createWebViewWithConfiguration:(WKWebViewConfiguration )configuration
forNavigationAction:(WKNavigationAction )navigationAction
windowFeatures:(WKWindowFeatures *)windowFeatures {
// 会拦截到window.open()事件.只需要我们在在方法内进行处理
if (!navigationAction.targetFrame.isMainFrame) {
[webView loadRequest:navigationAction.request];
}
}
(4).WKWebView解决显示字体太小的问题
在使用WKWebView的时候,常常会碰到显示内容比实际css设置的样式不能正常显示,内容普遍的偏小。其实导致这样问题的根源是少了HTML5的meta标签。解决的办法可以在iOS端添加以下的内容,当然也可以让后台添加完整的HTML5的格式。如果要在iOS端指定字体的大小((颜色)也是可以的
(但不推荐在客户端修改字体大小)。
- (void)webView:(WKWebView *)webView didFinishNavigation:(null_unspecified WKNavigation *)navigation {
// 修改字体大小 200%
[webView evaluateJavaScript:@"document.getElementsByTagName('body')[0].style.webkitTextSizeAdjust= '200%'" completionHandler:nil];
// 修改字体颜色 #9098b8
[webView evaluateJavaScript:@"document.getElementsByTagName('body')[0].style.webkitTextFillColor= '#9098b8'" completionHandler:nil];
}
WKWebView替换UIWebView全攻略
本文详细介绍了UIWebView的加载方法、属性、代理及与JavaScript的交互,并探讨了为何需要迁移至WKWebView,对比了两者性能差异。还阐述了WKWebView的特性,包括加载进度条实现、WKNavigationDelegate代理、WKUIDelegate协议,以及如何处理JavaScript与OC的交互,如弹窗、电话识别等常见问题。
3万+

被折叠的 条评论
为什么被折叠?



