从UIWebView到WKWebView

8.0之前都是UIWebView,以后苹果官方鼓励用WKWebView,本人建议也是用WKWebView,因为除了功能更加强大,性能和内存都会好很多,网上也有兼容两者的三方比如IMYWebView等。

1.UIWebView:

@interface QXWebViewController ()

@property (weak, nonatomic) IBOutlet UILabel *lable;

@property (weak, nonatomic) IBOutlet UIWebView *webView;
@property(nonatomic,assign)BOOL isToRequest;

@end

@implementation QXWebViewController
/**

// 代理属性 重点需要知道代理方法的使用
@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);

以后的新特性
// 这个属性用来设置一种模式,当网页的大小超出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);

*/

  • (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view.

    _webView.dataDetectorTypes=UIDataDetectorTypeAll;//监测所有数据

    _webView.delegate=self;
    [self oc2Js];

    [self loadPDF];
    }

-(void)loadhttpStr{

NSString *strUrl=@"<html><head><title>Test WebView</title></head><body><h1>hello webview</h1><ul><li>1</li><li>2</li><li>3</li></body></html>";

[_webView loadHTMLString:strUrl baseURL:nil];

}

-(void)loadWWW{

NSURL *url=[NSURL URLWithString:@"https://www.hao123.com/"];

NSURLRequest *request=[NSURLRequest requestWithURL:url];

[_webView loadRequest:request];

}

//获取指定URL mimeType

-(NSString*)getMimeTypeOfUrl:(NSURL*)url{

NSURLRequest *request=[NSURLRequest requestWithURL:url];
//使用同步方法获取mimeType

NSHTTPURLResponse *reponse=nil;
NSError *error=nil;

[NSURLConnection sendSynchronousRequest:request returningResponse:&reponse error:&error];

if(!error){
    return reponse.MIMEType;
}else{

    return nil;

}

}
-(void)loadTxt{//类似的还可以加载本地html
NSString *path=[[NSBundle mainBundle] pathForResource:@”out” ofType:@”txt”];

NSURLRequest *request=[NSURLRequest requestWithURL:[NSURL fileURLWithPath:path]];
[_webView loadRequest:request];

}

-(void)loadPDF{//加载本地PDF

NSString* path=[[NSBundle mainBundle] pathForResource:@"Groovy" ofType:@"pdf"];


NSURLRequest *request=[NSURLRequest requestWithURL:[NSURL fileURLWithPath:path]];
[_webView loadRequest:request];

}

-(void)goBack{

[_webView goBack];

}

-(void)goForward{

[_webView goForward];

}

-(void)stopLoading{
[_webView stopLoading];

}

-(void)refreshing{

[_webView reload];

}

-(BOOL)canGoBack{
return [_webView canGoBack];
}

-(BOOL)canGoForward{

return [_webView canGoForward];

}

/**
与js交互

主要有两方面:js执行OC代码、oc调取写好的js代码

js执行OC代码:js是不能执行oc代码的,但是可以变相的执行,js可以将要执行的操作封装到网络请求里面,然后oc拦截这个请求,获取url里面的字符串解析即可,这里用到代理协议的- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType函数。

oc调取写好的js代码:这里用到UIwebview的一个方法。示例代码一个是网页定位,一个是获取网页title:
*/

-(void)oc2Js{
NSString *title=[_webView stringByEvaluatingJavaScriptFromString:@”document.title”];

NSLog(@"当前页面title=%@",title);

NSString *url=[_webView stringByEvaluatingJavaScriptFromString:@"document.location.href"];

NSLog(@"当前页面的URL=%@",url);

//修改利用js的知识修改html的属性
NSString *jsStr1=@"document.getElementByTagName('body')[0].style.webkitTextSizeAdjust='60%'";

[_webView stringByEvaluatingJavaScriptFromString:jsStr1];

//创建一个元素加入到html中
NSString *jsStr2=[NSString stringWithFormat:@"var meta=document.createElement(meta);meta.name='viewport';meta.content= 'width=%f,initial-scale=%f,minimum-scale=0.1,maximum-scale=1.0,user-scalable=yes'",_webView.frame.size.width,_webView.frame.size.width/1000];

[_webView setScalesPageToFit:YES];

[_webView stringByEvaluatingJavaScriptFromString:jsStr2];

// 实现自动定位js代码, htmlLocationID为定位的位置(由js开发人员给出),实现自动定位代码,应该在网页加载完成之后再调用

// NSString *javascriptStr = [NSString stringWithFormat:@”window.location.href = ‘#%@’”,htmlLocationID];
// // webview执行代码
// [self.webView stringByEvaluatingJavaScriptFromString:javascriptStr];

}
//是否对这个请求进行过滤处理 开始请求之前
-(BOOL)webView:(UIWebView )webView shouldStartLoadWithRequest:(NSURLRequest )request navigationType:(UIWebViewNavigationType)navigationType{

//比如对特殊的scheme或者url进行过滤 或者是解析js构成的商量好的协议,然后解析出来调用相应的OC代码,然后再处理 return YES;
NSString *urlStr=[[request URL] absoluteString];

urlStr=[urlStr stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];

NSArray<NSString*> *compentArray=[urlStr componentsSeparatedByString:@"://"];

NSURL *url=[request URL];

NSString *scheme= url.scheme;

NSString *host=url.host;

NSString *path=url.path;

NSString *pathExtention=url.pathExtension;

NSString *query=url.query;
//上面的东西都有了你是想过滤什么就过滤什么啊 比如
if([host isEqualToString:@"main"]){

    //跳转到mainViewController
}

if([pathExtention isEqualToString:@"goBack"]){

    [webView goBack];
}

// typedef NS_ENUM(NSInteger, UIWebViewNavigationType) {
// UIWebViewNavigationTypeLinkClicked,
// UIWebViewNavigationTypeFormSubmitted,
// UIWebViewNavigationTypeBackForward,
// UIWebViewNavigationTypeReload,
// UIWebViewNavigationTypeFormResubmitted,
// UIWebViewNavigationTypeOther
// } __TVOS_PROHIBITED;

switch (navigationType) {
        //进来的前的触发行为 然后针对性的过滤处理

    case UIWebViewNavigationTypeLinkClicked:
        //点击的是链接

        break;

    case UIWebViewNavigationTypeReload:
        //reload  可以做reload记录数加1

        break;
    case UIWebViewNavigationTypeBackForward:
        //点击的是向前

        break;
    case UIWebViewNavigationTypeFormSubmitted:
        //提交表单

        break;
    case UIWebViewNavigationTypeFormResubmitted:
        //重新提交表达

        break;

    default://UIWebViewNavigationTypeOther


        break;
}

//也可以预先处理403和404状态码

NSHTTPURLResponse *response=nil;

NSData *data=[NSURLConnection sendSynchronousRequest:request returningResponse:&response error:nil];


if(!_isToRequest){//代表这个请求是第一次或者是以前给isToRequest赋值过NO

    if(response.statusCode==403){

//处理403
}else if(response.statusCode==404){
 //处理404

}else{

    [webView loadData:data MIMEType:@"text/html" textEncodingName:@"NSUTF8StringEncoding" baseURL:nil];
    _isToRequest=YES;//已经经历过403和404的过滤 这次直接去请求吧 再走一次本方法

    return NO;//代表本次请求到这里结束下面系统方法不走了  我拦截了
}
}else{

    _isToRequest=NO;

    return YES;

}



return YES;//我是拦截了修改了 些东西 但是本次请求还是照样走

}

-(void)webViewDidStartLoad:(UIWebView *)webView{//已经开始请求

NSURLRequest *request=[webView request];

NSString *requestMthod=[request HTTPMethod];

NSData *bodyData= [request HTTPBody];

BOOL b=[request HTTPShouldHandleCookies];

}

//请求完毕 服务端响应回调 没有错误
-(void)webViewDidFinishLoad:(UIWebView *)webView{

NSURLRequest *request=[webView request];

NSURL *url=[request URL];

if([url.path isEqualToString:@"/wr/qx"]){
    //做点什么什么事儿

}

}

//请求完毕 但是服务端响应错误,没有成功

-(void)webView:(UIWebView )webView didFailLoadWithError:(NSError )error{
NSURLRequest *request=[webView request];

NSURL *url=[request URL];


NSLog(@"请求url=%@,错误=%@",[url absoluteString],error);

}

  • (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
    }

/*

pragma mark - Navigation

// In a storyboard-based application, you will often want to do a little preparation before navigation
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
// Get the new view controller using [segue destinationViewController].
// Pass the selected object to the new view controller.
}
*/

@end

WKWebView

@interface QXWKWebViewController ()

pragma mark -KVO 系统调用方法

-(void)observeValueForKeyPath:(NSString )keyPath ofObject:(id)object change:(NSDictionary

pragma mark -获取手机的相关信息

-(void)deviceInfos{

NSDictionary *infoDictionary = [[NSBundle mainBundle] infoDictionary];

// app名称
NSString *app_Name = [infoDictionary objectForKey:@"CFBundleDisplayName"];
// app版本
NSString *app_Version = [infoDictionary objectForKey:@"CFBundleShortVersionString"];
// app build版本
NSString *app_build = [infoDictionary objectForKey:@"CFBundleVersion"];

NSString * appXcodeStr =[infoDictionary objectForKey:@"DTXcode"];//Xcode 版本
NSString * appSDKNameStr = [infoDictionary objectForKey:@"DTSDKName"];//SDK 的版本

//手机别名: 用户定义的名称
NSString* userPhoneName = [[UIDevice currentDevice] name];
NSLog(@"手机别名: %@", userPhoneName);
//设备名称
NSString* deviceName = [[UIDevice currentDevice] systemName];
NSLog(@"设备名称: %@",deviceName );
//手机系统版本
NSString* phoneVersionStr = [[UIDevice currentDevice] systemVersion];

CGFloat  phoneVersionFloat=[phoneVersionStr floatValue];

//    //手机序列号

// if(phoneVersionFloat<7.0){7.0以后这个方法给禁了
// NSString* identifierNumber = [UIDevice currentDevice] uniqueIdentifier];
// NSLog(@”手机序列号: %@”,identifierNumber);
// }
//可以用OpenUDID替代手机序列号的唯一性:https://github.com/ylechelle/OpenUDID

NSLog(@"手机系统版本: %@", phoneVersionStr);
//手机型号
NSString* phoneModel = [[UIDevice currentDevice] model];
NSLog(@"手机型号: %@",phoneModel );
//地方型号  (国际化区域名称)
NSString* localPhoneModel = [[UIDevice currentDevice] localizedModel];
NSLog(@"国际化区域名称: %@",localPhoneModel );

}

pragma mark -WKNavigationDelegate 代理方法的开始

/**
request 来了前段先过滤一下 看看是否确定要真的去Request
*/

-(void)webView:(WKWebView )webView decidePolicyForNavigationAction:(WKNavigationAction )navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler{

//发送请求之前决定是否开启跳转 相当于didShuldeStart
  //无论你是否拦截 都有调用block 否则会报错
NSLog(@"decidePolicyForNavigationAction");

NSURL *url=navigationAction.request.URL;

if(navigationAction.navigationType==WKNavigationTypeLinkActivated){
    //&&![url.host containsString:@"我们公司规定的host"]一般还要加上不是我们公司内部的host
    //这样都是属于外域链接 需要手动跳转

    [[UIApplication sharedApplication] openURL:url options:@{} completionHandler:nil];

    //不允许web内跳 不继续request

    decisionHandler(WKNavigationActionPolicyCancel);

}else{

      //跳转 去继续request

      decisionHandler(WKNavigationActionPolicyAllow);
}

}
/**
允许request后开始这一步 向服务器发起request
*/

-(void)webView:(WKWebView )webView didStartProvisionalNavigation:(WKNavigation )navigation{
//开启跳转

NSLog(@"didStartProvisionalNavigation");

}

/**
服务器收到发起的request 先过滤一下 确定是否搭理这个request 给个回应 算是第一次握手
*/

-(void)webView:(WKWebView )webView decidePolicyForNavigationResponse:(WKNavigationResponse )navigationResponse decisionHandler:(void (^)(WKNavigationResponsePolicy))decisionHandler{
//收到服务端的握手回应 决定是否提交跳转
NSURL *url=navigationResponse.response.URL;
NSLog(@”decidePolicyForNavigationResponse”);
//无论你是否拦截 都有调用block 否则会报错

// if([url.host containsString:@”本公司的host”]){//就搭理它 接收request 给个回应
// decisionHandler(WKNavigationActionPolicyAllow);
// return;
// }else{
//
// decisionHandler(WKNavigationActionPolicyCancel);
// }

 decisionHandler(WKNavigationActionPolicyAllow);

}

-(void)webView:(WKWebView )webView didReceiveServerRedirectForProvisionalNavigation:(WKNavigation )navigation{
//这个不一定会被调用

NSLog(@"didReceiveServerRedirectForProvisionalNavigation");

}
/**
第一次握手成功 服务端给了回应开始真正提交request
*/

-(void)webView:(WKWebView )webView didCommitNavigation:(WKNavigation )navigation{
//握手成功确定提交跳转

NSLog(@"didCommitNavigation");

}
/**
request成功 web加载成功 完成
*/

-(void)webView:(WKWebView )webView didFinishNavigation:(WKNavigation )navigation{
//完成跳转

NSLog(@"didFinishNavigation");
//模拟三秒加载时间
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
      [webView.scrollView.mj_header endRefreshing];
});

}

/**
request失败 web加载失败
*/

-(void)webView:(WKWebView )webView didFailNavigation:(WKNavigation )navigation withError:(NSError *)error{

//跳转失败

NSLog(@"didFailNavigation");
//模拟三秒加载时间
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
    [webView.scrollView.mj_header endRefreshing];
});

}

pragma mark - WKUIDelegate 接口的两个方法

if __IPHONE_OS_VERSION_MAX_ALLOWED > __IPHONE_9_0

  • (void)webViewDidClose:(WKWebView *)webView {
    NSLog(@”%s”, FUNCTION);
    }

endif

// 解决创建一个新的WebView(标签带有 target=’_blank’ 时,导致WKWebView无法加载点击后的网页的问题。)

-(WKWebView )webView:(WKWebView )webView createWebViewWithConfiguration:(WKWebViewConfiguration )configuration forNavigationAction:(WKNavigationAction )navigationAction windowFeatures:(WKWindowFeatures *)windowFeatures{

WKFrameInfo *frameInfo=navigationAction.targetFrame;//打开新窗口的委托

if(![frameInfo isMainFrame]){//不是主窗口

    [webView loadRequest:navigationAction.request];
}
return nil;

}
-(void)webView:(WKWebView )webView didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge )challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition, NSURLCredential * _Nullable))completionHandler{
//SSL验证 如果没有就走默认的
completionHandler(NSURLSessionAuthChallengePerformDefaultHandling,nil);

 NSLog(@"didReceiveAuthenticationChallenge");

}

-(void)webViewWebContentProcessDidTerminate:(WKWebView *)webView{

//页面内容完全加载完毕 不一定会被调用

NSLog(@"webViewWebContentProcessDidTerminate");

}

pragma mark WKScriptMessageHandler接口的方法

-(void)userContentController:(WKUserContentController )userContentController didReceiveScriptMessage:(WKScriptMessage )message{

//我们通过[userContentController addScriptMessageHandler:self name:@"AppModel"];给JS注册了一个别名类
//当JS通过这个别名对象QXApp调用OC代码的时候 这个方法就会接收到信息

NSString *name=message.name;//就是我们注册给js的别名

if([name isEqualToString:@"AppModel"]){


    NSLog(@"js 消息内容:%@", message.body);
}

}

  • (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
    }

  • (void)viewWillAppear:(BOOL)animated
    {
    [super viewWillAppear:animated];

    [self.navigationController setNavigationBarHidden:YES animated:YES];

    [self setNeedsStatusBarAppearanceUpdate];
    }

  • (void)viewWillDisappear:(BOOL)animated
    {
    [super viewWillDisappear:animated];

    [self.navigationController setNavigationBarHidden:NO animated:YES];
    }

  • (BOOL)prefersStatusBarHidden
    {
    return YES;
    }

/*

pragma mark - Navigation

// In a storyboard-based application, you will often want to do a little preparation before navigation
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
// Get the new view controller using [segue destinationViewController].
// Pass the selected object to the new view controller.
}
*/

@end

猜你喜欢

转载自blog.csdn.net/watertekhqx/article/details/72731849