iOS UIWebView 与 WKWebView
RPJKimberly
8年前
<h2>一、 <strong>概述</strong></h2> <p>UIWebView自iOS2就有,WKWebView从iOS8才有,毫无疑问WKWebView将逐步取代笨重的UIWebView。WKWebView只能用代码创建,而且自身就支持了右滑返回手势allowsBackForwardNavigationGestures和加载进度estimatedProgress等一些UIWebView不具备却非常好用的属性。通过简单的测试即可发现UIWebView占用过多内存,且内存峰值更是夸张。WKWebView网页加载速度也有提升,但是并不像内存那样提升那么多。下面列举一些其它的优势:</p> <ul> <li>更多的支持HTML5的特性</li> <li>官方宣称的高达60fps的滚动刷新率以及内置手势</li> <li>Safari相同的JavaScript引擎</li> <li>将UIWebViewDelegate与UIWebView拆分成了14类与3个协议( <a href="/misc/goto?guid=4959738129511405773" rel="nofollow,noindex">官方文档说明</a> )</li> <li>另外用的比较多的,增加加载进度属性:estimatedProgress</li> </ul> <h2>二、UIWebView的用法</h2> <h3>1、 加载网页或本地文件</h3> <pre> <code class="language-objectivec">// 网页url NSURL *url = [NSURLURLWithString:@"https://www.baidu.com"]; // 网络请求 NSURLRequest *request =[NSURLRequestrequestWithURL:url]; // 加载网页 [self.webviewloadRequest:request]; </code></pre> <p>注意,如果上述的:</p> <pre> <code class="language-objectivec">// 网页url NSURL *url = [NSURLURLWithString:@"https://www.baidu.com"]; </code></pre> <p>改为:</p> <pre> <code class="language-objectivec">// 网页url NSURL *url = [NSURLURLWithString:@"http://www.baidu.com"]; </code></pre> <p>会无法加载网页并有如下提示:</p> <pre> <code class="language-objectivec">2017-02-07 15:29:46.768 WebViewTest[12469:1020441] AppTransportSecurityhasblocked a cleartextHTTP (http://) resource load since it is insecure. Temporary exceptions can be configured via your app's Info.plist file. </code></pre> <p>原因为ATS禁止了HTTP的明文传输,因为它不安全。可以修改Info.plist文件,让它临时允许明文传输。</p> <p>解决办法:</p> <p>在Info.plist文件中添加”App Transport SecuritySettings”,Type为”Dictionary”,再添加一个item为”Allow Arbitray Loads”,Type 为”Boolean”,“Value”为“YES”即可。</p> <h3>2、 网页导航刷新有关函数</h3> <pre> <code class="language-objectivec">// 刷新 - (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; </code></pre> <h3>3、 相关代理协议</h3> <pre> <code class="language-objectivec">#pragma mark - UIWebViewDelegate // 是否允许加载网页 - (BOOL)webView:(UIWebView *)webViewshouldStartLoadWithRequest:(NSURLRequest *)requestnavigationType:(UIWebViewNavigationType)navigationType { NSLog(@"允许加载网页"); return YES; } // 开始加载网页时调用 - (void)webViewDidStartLoad:(UIWebView *)webView { NSLog(@"开始加载网页"); } // 网页加载完成时调用 - (void)webViewDidFinishLoad:(UIWebView *)webView { NSLog(@"网页加载完成"); } // 网页加载错误时调用 - (void)webView:(UIWebView *)webViewdidFailLoadWithError:(NSError *)error { NSLog(@"网页加载错误时调用"); } </code></pre> <h3>4、 与JS交互</h3> <p>(1) OC调用JS</p> <p>OC调用JS主要通过下面方法:</p> <pre> <code class="language-objectivec">- (nullableNSString )stringByEvaluatingJavaScriptFromString:(NSString )script; </code></pre> <p>我们只需要传入要执行的JS代码块即可,如果有返回值,可以接收NSString类型返回值。</p> <p>例如,我们获取网页Title并赋值给导航控制器Title:</p> <pre> <code class="language-objectivec">/** "调用JS"按钮点击事件 */ -(void)rightAction{ self.navigationItem.title = [self.webViewstringByEvaluatingJavaScriptFromString:@"document.title"]; } </code></pre> <p>运行结果:</p> <p><img src="https://simg.open-open.com/show/b723b9ebc5ceb82883ecf250e76f8649.png"></p> <p>点击“调用JS”按钮后:</p> <p><img src="https://simg.open-open.com/show/ad782b0808d3ceb526d0f5ac146c88ce.png"></p> <p>(2) JS调用OC</p> <p>JS是不能执行OC代码的,但是可以变相的执行,JS可以将要执行的操作封装到网络请求里面,然后OC拦截这个请求,获取URL里面的字符串解析即可,这里用到代理协议的如下方法:</p> <pre> <code class="language-objectivec">- (BOOL)webView:(UIWebView *)webViewshouldStartLoadWithRequest:(NSURLRequest *)requestnavigationType:(UIWebViewNavigationType)navigationType </code></pre> <p>例如:</p> <pre> <code class="language-objectivec">- (BOOL)webView:(UIWebView *)webViewshouldStartLoadWithRequest:(NSURLRequest *)requestnavigationType:(UIWebViewNavigationType)navigationType { // 获取请求路径 NSString *url = request.URL.absoluteString; // 定义的协议 NSString *scheme = @"ios://"; if ([urlhasPrefix:scheme]) { // 获得协议后面的路径 NSString *path = [urlsubstringFromIndex:scheme.length]; // 利用?切割路径 分割方法与参数 NSArray *subpaths = [pathcomponentsSeparatedByString:@"?"]; // 方法名 methodName == sendMessage:number2: NSString *methodName = [subpathsfirstObject]; // 参数 如:200&300 NSArray *params = nil; if (subpaths.count == 2) { params = [[subpathslastObject] componentsSeparatedByString:@"&"]; } // 调用本地函数 [self performSelector:NSSelectorFromString(methodName) withObjects:params]; return NO; } NSLog(@"想加载其他请求,不是想调用OC的方法"); return YES; } </code></pre> <h2>三、 WKWebView的用法</h2> <p>WKWebView 和 UIWebView 的基本使用方法相类似,但是需要导入头文件 #import <WebKit/WebKit.h>。</p> <p>1、 加载网页</p> <p>加载网页方法与UIWebView相同:</p> <pre> <code class="language-objectivec">// 网页url NSURL *url = [NSURLURLWithString:@"https://www.baidu.com"]; // 网络请求 NSURLRequest *request =[NSURLRequestrequestWithURL:url]; // 加载网页 [self.webviewloadRequest:request]; </code></pre> <p>2、 加载文件</p> <pre> <code class="language-objectivec">// 创建url(可以随便从桌面拉张图片) NSURL *url = [NSURLfileURLWithPath:@"/Users/ios/Desktop/图片/xxx.jpg"]; // 加载文件 [webViewloadFileURL:urlallowingReadAccessToURL:url]; </code></pre> <p>其他几个加载方法:</p> <pre> <code class="language-objectivec">// 其它三个加载函数 - (WKNavigation *)loadRequest:(NSURLRequest *)request; - (WKNavigation *)loadHTMLString:(NSString *)string baseURL:(nullableNSURL *)baseURL; - (WKNavigation *)loadData:(NSData *)dataMIMEType:(NSString *)MIMETypecharacterEncodingName:(NSString *)characterEncodingNamebaseURL:(NSURL *)baseURL; </code></pre> <p>3、 网页导航刷新有关函数</p> <pre> <code class="language-objectivec">@property (nonatomic, readonly) BOOL canGoBack; @property (nonatomic, readonly) BOOL canGoForward; - (WKNavigation *)goBack; - (WKNavigation *)goForward; - (WKNavigation *)reload; - (WKNavigation *)reloadFromOrigin; // 增加的函数 - (WKNavigation *)goToBackForwardListItem:(WKBackForwardListItem *)item; // 增加的函数 - (void)stopLoading; </code></pre> <ul> <li>reloadFromOrigin会比较网络数据是否有变化,没有变化则使用缓存,否则从新请求。</li> <li>goToBackForwardListItem:比向前向后更强大,可以跳转到某个指定历史页面</li> </ul> <p>4、 常用属性</p> <ul> <li>allowsBackForwardNavigationGestures:BOOL类型,是否允许左右划手势导航,默认不允许</li> <li>estimatedProgress:加载进度,取值范围0~1</li> <li>title:页面title</li> <li>scrollView.scrollEnabled:是否允许上下滚动,默认允许</li> <li>backForwardList:WKBackForwardList类型,访问历史列表,可以通过前进后退按钮访问,或者通过goToBackForwardListItem函数跳到指定页面</li> </ul> <p>5、 相关代理协议</p> <p>几个常用代理协议:</p> <p>(1) WKNavigationDelegate</p> <p>最常用,和UIWebViewDelegate功能类似,追踪加载过程,有是否允许加载、开始加载、加载完成、加载失败。下面会对方法做简单的说明,并用数字标出调用的先后次序:1-2-3-4-5</p> <p>三个是否允许加载方法:</p> <pre> <code class="language-objectivec">// 接收到服务器跳转请求之后调用 (服务器端redirect),不一定调用 - (void)webView:(WKWebView *)webViewdidReceiveServerRedirectForProvisionalNavigation:(WKNavigation *)navigation; // 3 在收到服务器的响应头,根据response相关信息,决定是否跳转。decisionHandler必须调用,来决定是否跳转,参数WKNavigationActionPolicyCancel取消跳转,WKNavigationActionPolicyAllow允许跳转 - (void)webView:(WKWebView *)webViewdecidePolicyForNavigationResponse:(WKNavigationResponse *)navigationResponsedecisionHandler:(void (^)(WKNavigationResponsePolicy))decisionHandler; // 1 在发送请求之前,决定是否跳转 - (void)webView:(WKWebView *)webViewdecidePolicyForNavigationAction:(WKNavigationAction *)navigationActiondecisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler; </code></pre> <p>追踪加载过程方法:</p> <pre> <code class="language-objectivec">// 2 页面开始加载 - (void)webView:(WKWebView *)webViewdidStartProvisionalNavigation:(WKNavigation *)navigation; // 4 开始获取到网页内容时返回 - (void)webView:(WKWebView *)webViewdidCommitNavigation:(WKNavigation *)navigation; // 5 页面加载完成之后调用 - (void)webView:(WKWebView *)webViewdidFinishNavigation:(WKNavigation *)navigation; // 页面加载失败时调用 - (void)webView:(WKWebView *)webViewdidFailProvisionalNavigation:(WKNavigation *)navigation; </code></pre> <p>(2) WKUIDelegate</p> <p>UI界面相关,原生控件支持,三种提示框:输入、确认、警告。首先将web提示框拦截然后再做处理。</p> <pre> <code class="language-objectivec">// 创建一个新的WebView - (WKWebView *)webView:(WKWebView *)webViewcreateWebViewWithConfiguration:(WKWebViewConfiguration *)configurationforNavigationAction:(WKNavigationAction *)navigationActionwindowFeatures:(WKWindowFeatures *)windowFeatures; // 输入框 - (void)webView:(WKWebView *)webViewrunJavaScriptTextInputPanelWithPrompt:(NSString *)promptdefaultText:(nullableNSString *)defaultTextinitiatedByFrame:(WKFrameInfo *)framecompletionHandler:(void (^)(NSString * __nullableresult))completionHandler; // 确认框 - (void)webView:(WKWebView *)webViewrunJavaScriptConfirmPanelWithMessage:(NSString *)messageinitiatedByFrame:(WKFrameInfo *)framecompletionHandler:(void (^)(BOOL result))completionHandler; // 警告框 - (void)webView:(WKWebView *)webViewrunJavaScriptAlertPanelWithMessage:(NSString *)messageinitiatedByFrame:(WKFrameInfo *)framecompletionHandler:(void (^)(void))completionHandler; </code></pre> <p>6、 与JS交互</p> <p>(1) WKWebView加载JS</p> <pre> <code class="language-objectivec">//JS文件路径 NSString *jsPath = [[NSBundlemainBundle] pathForResource:@"demo" ofType:@"js"]; //读取JS文件内容 NSString *jsContent = [NSStringstringWithContentsOfFile:jsPathencoding:NSUTF8StringEncodingerror:nil]; //创建用户脚本对象, //WKUserScriptInjectionTimeAtDocumentStart :HTML文档创建后,完成加载前注入,类似于<head>中 //WKUserScriptInjectionTimeAtDocumentEnd :HTML文件完成加载后注入,类似于<body>中 WKUserScript *script = [[WKUserScriptalloc] initWithSource:jsContentinjectionTime:WKUserScriptInjectionTimeAtDocumentStartforMainFrameOnly:YES]; //添加用户脚本 [webView.configuration.userContentControlleraddUserScript:script]; </code></pre> <p>(2) WKWebView执行JS方法</p> <pre> <code class="language-objectivec">//执行JS方法 [webViewevaluateJavaScript:@"test()" completionHandler:^(id_Nullableresult, NSError * _Nullableerror) { //result为执行js方法的返回值 if(error){ NSLog(@"Success"); }else{ NSLog(@"Fail"); } }]; </code></pre> <p> </p> <p>来自:http://www.imlifengfeng.com/blog/?p=528</p> <p> </p> <p> </p>