欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

iOS开发中UIWebView 与 WKWebView的基本使用技巧

程序员文章站 2023-02-02 10:03:13
ios开发中uiwebview 与 wkwebview的基本使用技巧。最近有个需求,是将公司的一个内网的页面嵌套在app中作为一个模块.这不是很简单的webview请求一下就行了么?其实内里大有...

ios开发中uiwebview 与 wkwebview的基本使用技巧。最近有个需求,是将公司的一个内网的页面嵌套在app中作为一个模块.这不是很简单的webview请求一下就行了么?其实内里大有乾坤.自己也将思路整理一遍。

uiwebview

uiwebview的基本使用方法 :

就这样就已经整整个baidu的页面展示到app上
下面我们看一下webview的属性与方法

    uiwebview *webview = [[uiwebview alloc] initwithframe:[uiscreen mainscreen].bounds];
    self.view = webview;
    nsurl *url = [nsurl urlwithstring:@"https://www.baidu.com"];
    nsurlrequest *request = [nsurlrequest requestwithurl:url];
    [webview loadrequest:request];

uiwebview的层级结构 :

iOS开发中UIWebView 与 WKWebView的基本使用技巧
uiwebview的类继承关系

uiwebview的属性 :

// 代理属性 重点需要知道代理方法的使用
@property (nullable, nonatomic, assign) id  delegate;

// 这个是webview内部的scrollview 只读,但是利用这个属性,设置scrollview的代理,就可以控制整个webview的滚动事件
@property(nonatomic, readonly, strong) uiscrollview *scrollview;

// webview的请求,这个属性一般在整个加载完成后才能拿到
@property (nullable, nonatomic, readonly, strong) nsurlrequest *request;

// a boolean value indicating whether the receiver can move backward. (read-only)
// if yes, able to move backward; otherwise, no.
// 如果这个属性为yes,才能后退
@property (nonatomic, readonly, getter=cangoback) bool cangoback;

// a boolean value indicating whether the receiver can move forward. (read-only)
// if yes, able to move forward; otherwise, no.
// 如果这个属性为yes,才能前进
@property (nonatomic, readonly, getter=cangoforward) bool cangoforward;

// a boolean value indicating whether the receiver is done loading content. (read-only)
// if yes, the receiver is still loading content; otherwise, no.
// 这个属性很好用,如果为yes证明webview还在加载数据,所有数据加载完毕后,webview就会为no
@property (nonatomic, readonly, getter=isloading) bool loading;

//a boolean value determining whether the webpage scales to fit the view and the user can change the scale.
//if yes, the webpage is scaled to fit and the user can zoom in and zoom out. if no, user zooming is disabled. the default value is no.
// yes代表网页可以缩放,no代表不可以缩放
@property (nonatomic) bool scalespagetofit;

// 设置某些数据变为链接形式,这个枚举可以设置如电话号,地址,邮箱等转化为链接
@property (nonatomic) uidatadetectortypes datadetectortypes ns_available_ios(3_0);

// iphone safari defaults to no. ipad safari defaults to yes
// 设置是否使用内联播放器播放视频
@property (nonatomic) bool allowsinlinemediaplayback ns_available_ios(4_0); 

// iphone and ipad safari both default to yes
// 设置视频是否自动播放
@property (nonatomic) bool mediaplaybackrequiresuseraction ns_available_ios(4_0); 

// iphone and ipad safari both default to yes
// 设置音频播放是否支持ari play功能
@property (nonatomic) bool mediaplaybackallowsairplay ns_available_ios(5_0); 

// iphone and ipad safari both default to no
// 设置是否将数据加载入内存后渲染界面
@property (nonatomic) bool suppressesincrementalrendering ns_available_ios(6_0); 

// default is yes
// 设置用户是否能打开keyboard交互
@property (nonatomic) bool keyboarddisplayrequiresuseraction ns_available_ios(6_0); 

/* ios7 */ 以后的新特性
// 这个属性用来设置一种模式,当网页的大小超出view时,将网页以翻页的效果展示,枚举如下:
@property (nonatomic) uiwebpaginationmode paginationmode ns_available_ios(7_0);
typedef ns_enum(nsinteger, uiwebpaginationmode) { 
uiwebpaginationmodeunpaginated, //不使用翻页效果 
uiwebpaginationmodelefttoright, //将网页超出部分分页,从左向右进行翻页 
uiwebpaginationmodetoptobottom, //将网页超出部分分页,从上向下进行翻页 
uiwebpaginationmodebottomtotop, //将网页超出部分分页,从下向上进行翻页 
uiwebpaginationmoderighttoleft //将网页超出部分分页,从右向左进行翻页 
};

// this property determines whether certain css properties regarding column- and page-breaking are honored or ignored. 
// 这个属性决定css的属性分页是可用还是忽略。默认是uiwebpaginationbreakingmodepage 
@property (nonatomic) uiwebpaginationbreakingmode paginationbreakingmode ns_available_ios(7_0);

// 设置每一页的长度
@property (nonatomic) cgfloat pagelength ns_available_ios(7_0);

// 设置每一页的间距
@property (nonatomic) cgfloat gapbetweenpages ns_available_ios(7_0);

// 获取页数
@property (nonatomic, readonly) nsuinteger pagecount ns_available_ios(7_0);

还有一些属性请详细翻苹果文档

uiwebview的代理方法 :

uiwebview的代理方法是用的最多的方法,并且一般来说,相对web页面作处理都在这相应的4个方法中
分别解释一下方法的调用情况

    // sent before a web view begins loading a frame.请求发送前都会调用该方法,返回no则不处理这个请求
    - (bool)webview:(uiwebview *)webview shouldstartloadwithrequest:(nsurlrequest *)request navigationtype:(uiwebviewnavigationtype)navigationtype;

    // sent after a web view starts loading a frame. 请求发送之后开始接收响应之前会调用这个方法
    - (void)webviewdidstartload:(uiwebview *)webview;

    // sent after a web view finishes loading a frame. 请求发送之后,并且服务器已经返回响应之后调用该方法
    - (void)webviewdidfinishload:(uiwebview *)webview;

    // sent if a web view failed to load a frame. 网页请求失败则会调用该方法
    - (void)webview:(uiwebview *)webview didfailloadwitherror:(nullable nserror *)error;

uiwebview的对象方法

// 加载data数据创建一个webview
- (void)loaddata:(nsdata *)data mimetype:(nsstring *)mimetype textencodingname:(nsstring *)encodingname baseurl:(nsurl *)baseurl

// 加载本地html创建一个webview
- (void)loadhtmlstring:(nsstring *)string baseurl:(nsurl *)baseurl

// 加载一个请求创建一个webview
- (void)loadrequest:(nsurlrequest *)request

// 刷新网页
- (void)reload;

// 停止网页加载内容
- (void)stoploading;

// 后退
- (void)goback;

// 前进
- (void)goforward;

// 执行js方法
- (nsstring *)stringbyevaluatingjavascriptfromstring:(nsstring *)script






wkwebview

wkwebview的简介 :

从文档中可以看到,这个是ios8之后新增的一个类,也是苹果推崇的一个新的类

iOS开发中UIWebView 与 WKWebView的基本使用技巧
wkwebview的类层级结构

wkwebview的基本使用方法 :

其实和uiwebview的用法没什么区别
但是wkwebview相对于uiwebview强大了很多,内存的消耗相对少了,所提供的接口也丰富了。
推荐使用
多了一部操作就是需要包含webkit框架
@import webkit

    wkwebview *webview = [[wkwebview alloc] initwithframe:[uiscreen mainscreen].bounds];
    self.view = webview;
    nsurl *url = [nsurl urlwithstring:@"https://www.baidu.com"];
    nsurlrequest *request = [nsurlrequest requestwithurl:url];
    [webview loadrequest:request];

wkwebview的属性 :

// uiwebview 中会自动保存cookie,如果登录了一次下次再次进入的时候,会记住登录状态
// 在wkwebview中,新增一个configuration属性,  configuration 让wkwebview知道登录状态,
// configuration 可以通过已有的cookie进行设置,也可以通过保存上一次的configuration进行设置
// wkwebviewconfiguration类中也有一些相应的属性
@property (nonatomic, readonly, copy) wkwebviewconfiguration *configuration;

// the methods of the wknavigationdelegate protocol help you track the progress of the web site's main frame navigations and decide load policy for main frame and subframe navigations.
// wkwebview中,加入了网站导航的概念,这个对象决定主框架导航加载方法协议。
@property (nullable, nonatomic, weak) id  navigationdelegate;

// the wkuidelegate class provides methods for presenting native user interface 
elements on behalf of a webpage.
// wkwebview中,加入了网站窗口的概念,这个对象决了webview窗口的一些方法协议。
@property (nullable, nonatomic, weak) id  uidelegate;

a wkbackforwardlist object is a list of webpages previously visited in a web view that can be reached by going back or forward.
// wkwebview中,加入了网站列表的概念,这个webbackforwardlist对象是以前在web视图访问的网页,可以通过去后退或前进
@property (nonatomic, readonly, strong) wkbackforwardlist *backforwardlist;

还有很多方法,同样可以查文档看到

wkwebview的代理方法 :

有一些方法和uiwebview是基本一直的,但是因为返回了navigation,所能用到的属性多了很多,另外多了一些方法,将请求与相应的整个过程

- (void)webviewwebcontentprocessdidterminate:(wkwebview *)webview{
    nslog(@"webviewwebcontentprocessdidterminate:  当web视图的网页内容被终止时调用。");
}


- (void)webview:(wkwebview *)webview didfinishnavigation:(null_unspecified wknavigation *)navigation
{
    [uiapplication sharedapplication].networkactivityindicatorvisible = no;
    nslog(@"webview:didfinishnavigation:  响应渲染完成后调用该方法   webview : %@  -- navigation : %@  \n\n",webview,navigation);
}


- (void)webview:(wkwebview *)webview didstartprovisionalnavigation:(null_unspecified wknavigation *)navigation
{
    [uiapplication sharedapplication].networkactivityindicatorvisible = yes;
    nslog(@"webview:didstartprovisionalnavigation:  开始请求  \n\n");
}

- (void)webview:(wkwebview *)webview didcommitnavigation:(wknavigation *)navigation {
    nslog(@"webview:didcommitnavigation:   响应的内容到达主页面的时候响应,刚准备开始渲染页面应用 \n\n");
}


// error
- (void)webview:(wkwebview *)webview didfailprovisionalnavigation:(wknavigation *)navigation witherror:(nserror *)error {
    // 类似 uiwebview 的- webview:didfailloadwitherror:

    nslog(@"webview:didfailprovisionalnavigation:witherror: 启动时加载数据发生错误就会调用这个方法。  \n\n");
}



- (void)webview:(wkwebview *)webview didfailnavigation:(wknavigation *)navigation witherror:(nserror *)error{
    nslog(@"webview:didfailnavigation: 当一个正在提交的页面在跳转过程中出现错误时调用这个方法。  \n\n");
}



- (void)webview:(wkwebview *)webview decidepolicyfornavigationaction:(wknavigationaction *)navigationaction decisionhandler:(void (^)(wknavigationactionpolicy))decisionhandler{

    nslog(@"请求前会先进入这个方法  webview:decidepolicyfornavigationactiondecisionhandler: %@   \n\n  ",navigationaction.request);

    decisionhandler(wknavigationactionpolicyallow);

}

- (void)webview:(wkwebview *)webview decidepolicyfornavigationresponse:(wknavigationresponse *)navigationresponse decisionhandler:(void (^)(wknavigationresponsepolicy))decisionhandler{

    nslog(@"返回响应前先会调用这个方法  并且已经能接收到响应webview:decidepolicyfornavigationresponse:decisionhandler: response?%@  \n\n",navigationresponse.response);

    decisionhandler(wknavigationresponsepolicyallow);
}



- (void)webview:(wkwebview *)webview didreceiveserverredirectforprovisionalnavigation:(wknavigation *)navigation{

    nslog(@"webview:didreceiveserverredirectforprovisionalnavigation: 重定向的时候就会调用  \n\n");
}

wkwebview的对象方法 :

这些方法,基本上和uiwebview中的使用用法是一致的,所以

// 这是加载网页最常用的一种方式,通过一个网页url来加载一个wkwebview,这个url可以是远程的也可以是本地的,例如我加载百度的主页
- (nullable wknavigation *)loadrequest:(nsurlrequest *)request;

// 根据一个文件,加载一个wkwebview
- (nullable wknavigation *)loadfileurl:(nsurl *)url allowingreadaccesstourl:(nsurl *)readaccessurl ns_available(10_11, 9_0);

// 这个方法需要将html文件读取为字符串从而加载为wkwebview,其中baseurl是我们自己设置的一个路径,用于寻找html文件中引用的图片等素材。
- (nullable wknavigation *)loadhtmlstring:(nsstring *)string baseurl:(nullable nsurl *)baseurl;

// 这个方式使用的比较少,但也更加*,其中data是文件数据,mimetype是文件类型,characterencodingname是编码类型,baseurl是素材资源路径
- (nullable wknavigation *)loaddata:(nsdata *)data mimetype:(nsstring *)mimetype characterencodingname:(nsstring *)characterencodingname baseurl:(nsurl *)baseurl ns_available(10_11, 9_0);

基本使用

下面会总结一些我在开发过程中遇到的坑,和解决问题的一些思路,不过在此之前我发现,如果要webview玩得好,有以下几点的只是也需要掌握好,因为我认为在h5崛起的今天,源生app和h5的交互之间会产生比较大改变,而且源生与h5之间的混编,越来越被重视.所以 :

源生技术,特别是有关于webview这一块的api要非常熟练,js语法, js的语法需要熟练,特别是操作document的几个常用js,标签需要用得滚瓜烂熟.要非常了解网络请求 - 响应的机制,理解请求头,响应头,等等.http的整套协议

需求一 : 展示一个网页,但是需要隐藏一部分页面

首先看看百度的页面,这是用chrome打开的开发者模式
基本界面组成如下,基本使用用法请详情百度,这里不作介绍

iOS开发中UIWebView 与 WKWebView的基本使用技巧

假设现在想将这个logo由网页开始加载就去掉

iOS开发中UIWebView 与 WKWebView的基本使用技巧

百度的logo就是一个p套着一个image标签

iOS开发中UIWebView 与 WKWebView的基本使用技巧
- (void)webviewdidfinishload:(uiwebview *)webview
{
    // 在html标签都加载完成后,开始处理html标签,调用js,操作document
    [webview stringbyevaluatingjavascriptfromstring:@"document.getelementbyid('plus-card').remove();"];
}

就这样, logo标签就被去掉了,思路就是等html加载完成后,操作js从而操作document标签从而改变整个html页面的应用,下图是去掉整个body主题内容后的结果

iOS开发中UIWebView 与 WKWebView的基本使用技巧

另外还可以将一段函数封装到里面,执行函数,原理是通过stringbyevaluatingjavascriptfromstring将js函数写进head标签中,然后再调用该函数

// 自定义editmylogo函数
[webview stringbyevaluatingjavascriptfromstring:@"var script = document.createelement('script');"
     "script.type = 'text/javascript';"
     "script.text = \"function editmylogo() { "
     "var logo = document.getelementbyid('logo');"
     "logo.innerhtml= logo.innerhtml + '这是我自己定义的名字';"
     "var imglist = logo.getelementsbytagname('img');"
     "for (i=0 ; i < imglist.length ; i++ ){"
     "imglist[i].src = 'https://pic.to8to.com/attch/day_160218/20160218_d968438a2434b62ba59dh7q5kezts6oh.png';"
     "}"
     "}\";"
     "document.getelementsbytagname('head')[0].appendchild(script);"];

    // 执行editmylogo函数
    [webview stringbyevaluatingjavascriptfromstring:@"editmylogo();"];

效果如下 :

iOS开发中UIWebView 与 WKWebView的基本使用技巧

有几点问题,这种操作是在webviewdidfinishload方法下进行的,webviewdidfinishload方法是webview的document已经渲染好后,再去处理这个这个页面.

你会发现有时候会出现一些闪屏现象,原因是渲染过后,内部处理js代码后,页面会再渲染一次资源浪费,假设这边的需求只需要显示10%的内容,却要加载100%的内容,不过这一方面还需要网页端作出很好的适配某些时候,js会失效,不知道什么原因,有些时候自定义加载的js的方法并没有执行到.等于内容并没有屏蔽等等..

需求二 : 怎样处理403,404的情况 ?

iOS开发中UIWebView 与 WKWebView的基本使用技巧
@property (nonatomic, assign) bool ispost; // 定义一个变量

// 每一个请求开始发送前都会调用这个方法
// 1, 定义一个全局变量currentrequest,用作保存当前的请求
// 2, 将请求转换成data,然后处理data再将data作为请求数据再次请求
- (bool)webview:(uiwebview *)webview shouldstartloadwithrequest:(nsurlrequest *)request navigationtype:(uiwebviewnavigationtype)navigationtype{

    if (!_ispost) {
        nshttpurlresponse *response = nil;
        nsdata *data = [nsurlconnection sendsynchronousrequest:request returningresponse:&response error:nil];
        if (response.statuscode == 404) {
            // 这里处理 404 代码
        } else if (response.statuscode == 403) {
            // 这里处理 403 代码
        } else {

            _ispost = true;
            [webview loaddata:data mimetype:@"text/html" textencodingname:@"nsutf8stringencoding" baseurl:[request url]];

        }
        return no;
    }else{
        nslog(@"\n\n shouldstartloadwithrequest请求准备 --  %@ \n\n ",request);
        _ispost = no;
        return yes;
    }
}

需求一 : 进一步改进

iOS开发中UIWebView 与 WKWebView的基本使用技巧

在处理html这里,将你想隐藏的页面,加上 display:none 属性,
或者,将整段html标签去掉.