WKWebView的使用(iOS8.0+)替代UIWebView

今年的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;
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,047评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,807评论 3 386
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,501评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,839评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,951评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,117评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,188评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,929评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,372评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,679评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,837评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,536评论 4 335
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,168评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,886评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,129评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,665评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,739评论 2 351

推荐阅读更多精彩内容

  • Swift1> Swift和OC的区别1.1> Swift没有地址/指针的概念1.2> 泛型1.3> 类型严谨 对...
    cosWriter阅读 11,094评论 1 32
  • iOS 的 Cookie 存取 https://juejin.im/entry/58d4c4cc44d904006...
    Farmers阅读 5,913评论 0 16
  • WkWebView是IOS8中引入的新组件,苹果将UIWebViewDelegate 与 UIWebView 重构...
    i_belive阅读 5,024评论 1 25
  • 发现 关注 消息 iOS 第三方库、插件、知名博客总结 作者大灰狼的小绵羊哥哥关注 2017.06.26 09:4...
    肇东周阅读 12,067评论 4 62
  • 我笑 非常笑 漫天飞舞纷纷撒撒 我狂 自高傲 负手直指苍穹年少 我望 遥相望 春来草绿红花衬旁 我醉 逍遥醉 手掌...
    不争不抢不忧阅读 288评论 5 8