今年的WWDC之后,有一条关于UIWebview的弃用消息出来了,UIWebview会在iOS12之后弃用,全面普及WKWebview。所以还在使用UIWebview的话需要考虑一下迁移到WKWebview了。下面是把项目WKWebview脱敏之后的一些基础功能的封装。
注意:ios H5页面web页面 上下滑动卡顿问题解决方案
原因1:只需删除如下代码:
html,body{
height: 100%;
}
原因2:增加如下代码:
*{
-webkit-overflow-scrolling: touch;
}
HTML5网页内都有快速滚动和回弹的效果,看上去和原生app的效率都有得一拼。要实现这个效果很简单,只需要加一行css代码即可:
-webkit-overflow-scrolling : touch;
-webkit-overflow-scrolling控制元素在移动设备上面是否有滚动回弹效果,它可以设置成auto和touch。
auto: 使用普通滚动, 当手指从触摸屏上移开,滚动会立即停止。
touch: 使用具有回弹效果的滚动, 当手指从触摸屏上移开,内容会继续保持一段时间的滚动效果。继续滚动的速度和持续的时间和滚动手势的强烈程度成正比。
使用touch可以让滚动条有滚动回弹效果,就像是ios原生的滚动体验一样
可用以下网页测试:
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<meta charset="utf-8" />
<title>scroll</title>
<style type="text/css">
.container {
width : 300px;
height : 50%;
-webkit-box-sizing : border-box;
position : relative;
overflow-y : auto;
background-color : cyan;
-webkit-overflow-scrolling : touch; /* liuhx:可以把这整行注释掉对比差别 */
}
ul {
height: 50px;
}
</style>
</head>
<body>
<div align="center">
<nav class="container">
<ul>1</ul>
<ul>2</ul>
<ul>3</ul>
<ul>4</ul>
<ul>5</ul>
<ul>6</ul>
<ul>7</ul>
<ul>8</ul>
<ul>9</ul>
<ul>10</ul>
<ul>11</ul>
<ul>12</ul>
<ul>13</ul>
<ul>14</ul>
<ul>15</ul>
<ul>16</ul>
<ul>17</ul>
<ul>18</ul>
<ul>19</ul>
<ul>20</ul>
</nav>
</div>
</body>
</html>
但是从前端开发的角度讲,只需要知道CSS的属性-webkit-overflow-scrolling是真的创建了带有硬件加速的系统级控件,所以效率很高。但是这相对是耗更多内存的,最好在产生了非常大面积的overflow时才应用。
正文
在iOS开发中,调用web页面时,最常用的就是UIWebView控件。但在使用过程中,也让大家觉得很不爽,就是内存爆增。
自从iOS8.0以后有了WKWebView,这种情况就好转了。因为在使用WKWebView的过程中基本上内存保持不变。
WKWebView 是现代 WebKit API在 iOS 8和 OS X Yosemite应用中的核心部分。
WKWebView 代替了 UIKit中的 UIWebView和 AppKit中的 WebView,提供了统一的跨双平台 API。
WKWebView的特点:
- 拥有 60fps滚动刷新率
- 内置手势
- 高效的 app和 web信息交换通道
- 和 Safari相同的 JavaScript引擎
- 性能高,稳定性好,占用的内存比较小
WKWebView的用法:
1、导入头文件
#if __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_8_0
#import <WebKit/WebKit.h>
#endif
2、添加协议及定义WKWebView
在这里我定义成属性使用,同时使用getter方法进行实例化,实例化后记得要添加到父视图。
@interface WKWebViewVC () <WKUIDelegate, WKNavigationDelegate>
@property (nonatomic, strong) WKWebView *webview;
@end
- (WKWebView *)webview
{
if (_webview == nil)
{
_webview = [[WKWebView alloc] init];
_webview.frame = self.view.bounds;
_webview.backgroundColor = [UIColor clearColor];
_webview.UIDelegate = self;
_webview.navigationDelegate = self;
}
return _webview;
}
[self.view addSubview:self.webview];
3、实现代理方法
3-1、WKUIDelegate代理方法
UI界面相关,原生控件支持,三种提示框:输入、确认、警告。首先将web提示框拦截然后再做处理
/// 创建一个新的WebView
- (WKWebView *)webView:(WKWebView *)webView createWebViewWithConfiguration:(WKWebViewConfiguration *)configuration forNavigationAction:(WKNavigationAction *)navigationAction windowFeatures:(WKWindowFeatures *)windowFeatures
{
NSLog(@"加载:打开内部链接");
// 打开内部链接
return self.webview;
// 不打开
// return nil;
}
/// 输入框
- (void)webView:(WKWebView *)webView runJavaScriptTextInputPanelWithPrompt:(NSString *)prompt defaultText:(nullable NSString *)defaultText initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(NSString * __nullable result))completionHandler
{
NSLog(@"加载:输入框提示");
completionHandler(@"Client Not handler");
}
/// 确认框
- (void)webView:(WKWebView *)webView runJavaScriptConfirmPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(BOOL result))completionHandler
{
NSLog(@"加载:确认框提示");
// js 里面的alert实现,如果不实现,网页的alert函数无效
UIAlertController *alertController = [UIAlertController alertControllerWithTitle:message
message:nil
preferredStyle:UIAlertControllerStyleAlert];
[alertController addAction:[UIAlertAction actionWithTitle:@"确定"
style:UIAlertActionStyleDefault
handler:^(UIAlertAction *action) {
completionHandler(YES);
}]];
[alertController addAction:[UIAlertAction actionWithTitle:@"取消"
style:UIAlertActionStyleCancel
handler:^(UIAlertAction *action){
completionHandler(NO);
}]];
[self presentViewController:alertController animated:YES completion:^{}];
}
/// 警告框
- (void)webView:(WKWebView *)webView runJavaScriptAlertPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(void))completionHandler
{
NSLog(@"加载:警告框提示");
// js 里面的alert实现,如果不实现,网页的alert函数无效
UIAlertController *alertController = [UIAlertController alertControllerWithTitle:message
message:nil
preferredStyle:UIAlertControllerStyleAlert];
[alertController addAction:[UIAlertAction actionWithTitle:@"确定"
style:UIAlertActionStyleCancel
handler:^(UIAlertAction *action) {
completionHandler();
}]];
[self presentViewController:alertController animated:YES completion:^{}];
}
3-2、WKNavigationDelegate
追踪加载过程,有是否允许加载、开始加载、加载完成、加载失败
/// 接收到服务器跳转请求之后调用 (服务器端redirect),不一定调用
- (void)webView:(WKWebView *)webView didReceiveServerRedirectForProvisionalNavigation:(WKNavigation *)navigation
{
[UIApplication sharedApplication].networkActivityIndicatorVisible = NO ;
NSLog(@"加载:服务器跳转");
}
/// 1 在发送请求之前,决定是否跳转(注:不加上decisionHandler回调会造成闪退)
- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler
{
[UIApplication sharedApplication].networkActivityIndicatorVisible = YES ;
NSLog(@"加载:发送请求是否跳转");
WKNavigationActionPolicy actionPolicy = WKNavigationActionPolicyAllow;
// 这句是必须加上的,不然会异常
decisionHandler(actionPolicy);
}
/// 2 页面开始加载
- (void)webView:(WKWebView *)webView didStartProvisionalNavigation:(WKNavigation *)navigation
{
[UIApplication sharedApplication].networkActivityIndicatorVisible = YES ;
NSLog(@"加载:开始");
}
/// 3 在收到服务器的响应头,根据response相关信息,决定是否跳转。decisionHandler必须调用,来决定是否跳转,参数WKNavigationActionPolicyCancel取消跳转,WKNavigationActionPolicyAllow允许跳转(注:不加上decisionHandler回调会造成闪退)
- (void)webView:(WKWebView *)webView decidePolicyForNavigationResponse:(WKNavigationResponse *)navigationResponse decisionHandler:(void (^)(WKNavigationResponsePolicy))decisionHandler
{
NSLog(@"加载:服务器响应头");
WKNavigationResponsePolicy responsePolicy = WKNavigationResponsePolicyAllow;
// 这句是必须加上的,不然会异常
decisionHandler(responsePolicy);
}
/// 4 开始获取到网页内容时返回
- (void)webView:(WKWebView *)webView didCommitNavigation:(WKNavigation *)navigation
{
[UIApplication sharedApplication].networkActivityIndicatorVisible = YES ;
NSLog(@"加载:开始获取信息");
}
/// 5 页面加载完成之后调用
- (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation
{
[UIApplication sharedApplication].networkActivityIndicatorVisible = NO ;
NSLog(@"加载:完成");
}
/// 页面加载失败时调用
- (void)webView:(WKWebView *)webView didFailProvisionalNavigation:(WKNavigation *)navigation
{
[UIApplication sharedApplication].networkActivityIndicatorVisible = NO ;
NSLog(@"加载:失败");
}
4、打开web页面,或停止加载web页面
- (void)startClick
{
if ([_webview isLoading])
{
return;
}
NSURL *url = [NSURL URLWithString:@"http://www.hao123.com"];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
[_webview loadRequest:request];
NSLog(@"加载:启动");
}
- (void)stopClick
{
if ([_webview isLoading])
{
[_webview stopLoading];
[UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
NSLog(@"加载:停止");
}
}
注意:
1、视图控制器释放时,注意内存管理。
- (void)dealloc
{
_webview.UIDelegate = nil;
_webview.navigationDelegate = nil;
[_webview loadHTMLString:@"" baseURL:nil];
[_webview stopLoading];
[_webview removeFromSuperview];
[[NSURLCache sharedURLCache] removeAllCachedResponses];
[UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
_webview = nil;
}
WKWebView有个监视加载进度的属性"estimatedProgress"
注意:kvo使用时path名称必须是"estimatedProgress",或者"NSStringFromSelector(@selector(estimatedProgress))
",否则会造成异常闪退。
#pragma mark - 通知方法
- (void)observeProgress
{
// 进度条监视
CGFloat progress = self.webview.estimatedProgress;
NSLog(@"加载进度:%.2f", progress);
[self.webview addObserver:self forKeyPath:@"estimatedProgress" options:NSKeyValueObservingOptionNew context:nil];
}
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context
{
// 进度条
if ([@"estimatedProgress" isEqualToString:keyPath])
{
CGFloat progress = self.webview.estimatedProgress;
NSLog(@"正在加载中,加载进度:%.2f", progress);
// 初始和终止状态
if (progress == 0.0)
{
NSLog(@"加载开始,进度条值:0.0");
}
else if (progress == 1.0)
{
NSLog(@"加载结束,进度条值:1.0");
// 1秒后隐藏
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
// 再次判断,防止正在加载时有其他操作
if (progress == 1.0)
{
NSLog(@"加载结束,进度条值:1.0(异步)");
}
});
}
}
}
注意:
代理方法使用说明
// 1 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;
// 2 WKNavigtionDelegate来进行页面跳转
// 接收到服务器跳转请求之后再执行
- (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;
// 3 WKUIDelegate(PS:WKUIDelegate主要是做跟网页交互的,可以显示javascript的一些alert或者Action,看起来跟自己做的一样的.)
// 创建一个新的WebVeiw
- (nullable WKWebView *)webView:(WKWebView *)webView createWebViewWithConfiguration:(WKWebViewConfiguration *)configuration forNavigationAction:(WKNavigationAction *)navigationAction windowFeatures:(WKWindowFeatures *)windowFeatures;
// WebVeiw关闭(9.0中的新方法)
- (void)webViewDidClose:(WKWebView *)webView NS_AVAILABLE(10_11, 9_0);
// 显示一个JS的Alert(与JS交互)
- (void)webView:(WKWebView *)webView runJavaScriptAlertPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(void))completionHandler;
// 弹出一个输入框(与JS交互的)
- (void)webView:(WKWebView *)webView runJavaScriptTextInputPanelWithPrompt:(NSString *)prompt defaultText:(nullable NSString *)defaultText initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(NSString * __nullable result))completionHandler;
// 显示一个确认框(JS的)
- (void)webView:(WKWebView *)webView runJavaScriptConfirmPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(BOOL result))completionHandler;