IOS 进阶之 WKWebView

来源:互联网 发布:js删除指定class 编辑:程序博客网 时间:2024/05/16 11:51

来源:o翻滚的牛宝宝o   

链接:http://www.jianshu.com/p/4fa8c4eb1316



前言




Xcode8发布以后,编译器开始不支持IOS7,所以很多应用在适配IOS10之后都不在适配IOS7了,其中包括了很多大公司,网易新闻,滴滴出行等。因此,我们公司的应用也打算淘汰IOS7。


支持到IOS8,第一个要改的自然是用WKWebView替换原来的UIWebView。WKWebView有很多明显优势:


  • 更多的支持HTML5的特性

  • 官方宣称的高达60fps的滚动刷新率以及内置手势

  • 将UIWebViewDelegate与UIWebView拆分成了14类与3个协议,以前很多不方便实现的功能得以实现。

    https://developer.apple.com/library/mac/documentation/Cocoa/Reference/WebKit/ObjC_classic/index.html

  • Safari相同的JavaScript引擎

  • 占用更少的内存


UIWebView



WKWebView



因此,使用WkWebview替换UIWebView还是很有必要的。


基本使用方法




WKWebView有两个delegate,WKUIDelegate 和 WKNavigationDelegate。WKNavigationDelegate主要处理一些跳转、加载处理操作,WKUIDelegate主要处理JS脚本,确认框,警告框等。因此WKNavigationDelegate更加常用。


比较常用的方法:


#pragma mark - lifeCircle

-(void)viewDidLoad{

    [superviewDidLoad];

    webView = [[WKWebViewalloc]init];

    [self.viewaddSubview:webView];

    [webViewmas_makeConstraints:^(MASConstraintMaker *make){

        make.left.equalTo(self.view);

        make.right.equalTo(self.view);

        make.top.equalTo(self.view);

        make.bottom.equalTo(self.view);

    }];

    webView.UIDelegate = self;

    webView.navigationDelegate = self;

    [webViewloadRequest:[NSURLRequestrequestWithURL:[NSURLURLWithString:@"http://www.baidu.com"]]];

 

}

 

#pragma mark - WKNavigationDelegate

// 页面开始加载时调用

-(void)webView:(WKWebView *)webViewdidStartProvisionalNavigation:(WKNavigation *)navigation{

 

}

// 当内容开始返回时调用

-(void)webView:(WKWebView *)webViewdidCommitNavigation:(WKNavigation *)navigation{

 

}

// 页面加载完成之后调用

-(void)webView:(WKWebView *)webViewdidFinishNavigation:(WKNavigation *)navigation{

 

}

// 页面加载失败时调用

-(void)webView:(WKWebView *)webViewdidFailProvisionalNavigation:(WKNavigation *)navigation{

 

}

// 接收到服务器跳转请求之后调用

-(void)webView:(WKWebView *)webViewdidReceiveServerRedirectForProvisionalNavigation:(WKNavigation *)navigation{

 

}

// 在收到响应后,决定是否跳转

-(void)webView:(WKWebView *)webViewdecidePolicyForNavigationResponse:(WKNavigationResponse *)navigationResponsedecisionHandler:(void(^)(WKNavigationResponsePolicy))decisionHandler{

 

    NSLog(@"%@",navigationResponse.response.URL.absoluteString);

    //允许跳转

    decisionHandler(WKNavigationResponsePolicyAllow);

    //不允许跳转

    //decisionHandler(WKNavigationResponsePolicyCancel);

}

// 在发送请求之前,决定是否跳转

-(void)webView:(WKWebView *)webViewdecidePolicyForNavigationAction:(WKNavigationAction *)navigationActiondecisionHandler:(void(^)(WKNavigationActionPolicy))decisionHandler{

 

    NSLog(@"%@",navigationAction.request.URL.absoluteString);

    //允许跳转

    decisionHandler(WKNavigationActionPolicyAllow);

    //不允许跳转

    //decisionHandler(WKNavigationActionPolicyCancel);

}

#pragma mark - WKUIDelegate

// 创建一个新的WebView

-(WKWebView *)webView:(WKWebView *)webViewcreateWebViewWithConfiguration:(WKWebViewConfiguration *)configurationforNavigationAction:(WKNavigationAction *)navigationActionwindowFeatures:(WKWindowFeatures *)windowFeatures{

    return[[WKWebViewalloc]init];

}

// 输入框

-(void)webView:(WKWebView *)webViewrunJavaScriptTextInputPanelWithPrompt:(NSString *)promptdefaultText:(nullable NSString *)defaultTextinitiatedByFrame:(WKFrameInfo *)framecompletionHandler:(void(^)(NSString *__nullableresult))completionHandler{

    completionHandler(@"http");

}

// 确认框

-(void)webView:(WKWebView *)webViewrunJavaScriptConfirmPanelWithMessage:(NSString *)messageinitiatedByFrame:(WKFrameInfo *)framecompletionHandler:(void(^)(BOOLresult))completionHandler{

    completionHandler(YES);

}

// 警告框

-(void)webView:(WKWebView *)webViewrunJavaScriptAlertPanelWithMessage:(NSString *)messageinitiatedByFrame:(WKFrameInfo *)framecompletionHandler:(void(^)(void))completionHandler{

    NSLog(@"%@",message);

    completionHandler();

}


OC与JS交互




WKWebview提供了API实现js交互 不需要借助JavaScriptCore或者webJavaScriptBridge。使用WKUserContentController实现js native交互。简单的说就是先注册约定好的方法,然后再调用。


JS调用OC方法


oc代码(有误,内存不释放):


@interfaceViewController(){

    WKWebView *webView;

    WKUserContentController*userContentController;

}

@end

@implementationViewController

#pragma mark - lifeCircle

-(void)viewDidLoad{

    [superviewDidLoad];

    //配置环境

    WKWebViewConfiguration *configuration = [[WKWebViewConfigurationalloc]init];

    userContentController =[[WKUserContentControlleralloc]init];

    configuration.userContentController = userContentController;

    webView = [[WKWebViewalloc]initWithFrame:CGRectMake(0,0,100,100)configuration:configuration];

    //注册方法

    [userContentControlleraddScriptMessageHandler:self  name:@"sayhello"];//注册一个name为sayhello的js方法

 

    [self.viewaddSubview:webView];

    [webViewmas_makeConstraints:^(MASConstraintMaker *make){

        make.left.equalTo(self.view);

        make.right.equalTo(self.view);

        make.top.equalTo(self.view);

        make.bottom.equalTo(self.view);

    }];

    webView.UIDelegate = self;

    webView.navigationDelegate = self;

    [webViewloadRequest:[NSURLRequestrequestWithURL:[NSURLURLWithString:@"http://www.test.com"]]];

}

-(void)dealloc{

    //这里需要注意,前面增加过的方法一定要remove掉。

    [userContentControllerremoveScriptMessageHandlerForName:@"sayhello"];

}

#pragma mark - WKScriptMessageHandler

-(void)userContentController:(WKUserContentController *)userContentControllerdidReceiveScriptMessage:(WKScriptMessage *)message{

    NSLog(@"name:%@\\\\n body:%@\\\\n frameInfo:%@\\\\n",message.name,message.body,message.frameInfo);

}

@end


上面的OC代码如果认证测试一下就会发现dealloc并不会执行,这样肯定是不行的,会造成内存泄漏。原因是[userContentController addScriptMessageHandler:self name:@"sayhello"];这句代码造成无法释放内存。(ps:试了下用weak指针还是不能释放,不知道是什么原因。)因此还需要进一步改进,正确的写法是用一个新的controller来处理,新的controller再绕用delegate绕回来。


oc代码(正确写法):


@interfaceViewController(){

    WKWebView *webView;

    WKUserContentController*userContentController;

}

@end

@implementationViewController

#pragma mark - lifeCircle

-(void)viewDidLoad{

    [superviewDidLoad];

    //配置环境

    WKWebViewConfiguration *configuration = [[WKWebViewConfigurationalloc]init];

    userContentController =[[WKUserContentControlleralloc]init];

    configuration.userContentController = userContentController;

    webView = [[WKWebViewalloc]initWithFrame:CGRectMake(0,0,100,100)configuration:configuration];

    //注册方法

    WKDelegateController *delegateController = [[WKDelegateControlleralloc]init];

    delegateController.delegate = self;

 

    [userContentControlleraddScriptMessageHandler:delegateController  name:@"sayhello"];

 

    [self.viewaddSubview:webView];

    [webViewmas_makeConstraints:^(MASConstraintMaker *make){

        make.left.equalTo(self.view);

        make.right.equalTo(self.view);

        make.top.equalTo(self.view);

        make.bottom.equalTo(self.view);

    }];

    webView.UIDelegate = self;

    webView.navigationDelegate = self;

    [webViewloadRequest:[NSURLRequestrequestWithURL:[NSURLURLWithString:@"http://www.test.com"]]];

}

-(void)dealloc{

    //这里需要注意,前面增加过的方法一定要remove掉。

    [userContentControllerremoveScriptMessageHandlerForName:@"sayhello"];

}

#pragma mark - WKScriptMessageHandler

-(void)userContentController:(WKUserContentController *)userContentControllerdidReceiveScriptMessage:(WKScriptMessage *)message{

    NSLog(@"name:%@\\\\n body:%@\\\\n frameInfo:%@\\\\n",message.name,message.body,message.frameInfo);

}

@end


WKDelegateController代码:


#import

#import

@protocolWKDelegate

 

-(void)userContentController:(WKUserContentController *)userContentControllerdidReceiveScriptMessage:(WKScriptMessage *)message;

 

@end

 

@interfaceWKDelegateController : UIViewController

 

@property(weak,nonatomic)iddelegate;

 

@end


.m代码:


#import "WKDelegateController.h"

 

@interfaceWKDelegateController()

 

@end

 

@implementationWKDelegateController

 

-(void)viewDidLoad{

    [superviewDidLoad];

}

 

-(void)userContentController:(WKUserContentController *)userContentControllerdidReceiveScriptMessage:(WKScriptMessage *)message{

    if([self.delegaterespondsToSelector:@selector(userContentController:didReceiveScriptMessage:)]){

        [self.delegateuserContentController:userContentControllerdidReceiveScriptMessage:message];

    }

}

 

 

@end


h5代码:


<html>

<head>

    <script>

functionsay()

{

//前端需要用 window.webkit.messageHandlers.注册的方法名.postMessage({body:传输的数据} 来给native发送消息

    window.webkit.messageHandlers.sayhello.postMessage({body:'hello world!'});

}

</script>

</head>

    <body>

        <h1>helloworld</h1>

        <buttononclick="say()">sayhello</button>

    </body>

 

</html>


打印出的log:


name:sayhello

body:{

    body = "hello world!";

}

frameInfo:{URL:http://www.test.com/ }>


注意点


  • addScriptMessageHandler要和removeScriptMessageHandlerForName配套出现,否则会造成内存泄漏。

  • h5只能传一个参数,如果需要多个参数就需要用字典或者json组装。




oc调用JS方法


代码如下:


-(void)webView:(WKWebView *)tmpWebViewdidFinishNavigation:(WKNavigation *)navigation{

 

    //say()是JS方法名,completionHandler是异步回调block

    [webViewevaluateJavaScript:@"say()"completionHandler:^(id _Nullable result,NSError *_Nullableerror){

        NSLog(@"%@",result);

    }];

 

}


h5代码同上。


WebViewJavascriptBridge




一般来说,一个好的UI总有一个大神会开发出一个好的第三方封装框架。WebViewJavascriptBridge的作者也做了一套支持WKWebView与JS交互的第三方框架:WKWebViewJavascriptBridge。


  • cocoaPods: pod ‘WebViewJavascriptBridge’, ‘~> 5.0.5’

  • github地址:https://github.com/marcuswestin/WebViewJavascriptBridge


主要方法如下:


//初始化方法

+(instancetype)bridgeForWebView:(WKWebView*)webView;

+(void)enableLogging;

 

//注册函数名

-(void)registerHandler:(NSString*)handlerNamehandler:(WVJBHandler)handler;

 

//调用函数名

-(void)callHandler:(NSString*)handlerName;

-(void)callHandler:(NSString*)handlerNamedata:(id)data;

-(void)callHandler:(NSString*)handlerNamedata:(id)dataresponseCallback:(WVJBResponseCallback)responseCallback;

 

//重置

-(void)reset;

 

//设置WKNavigationDelegate

-(void)setWebViewDelegate:(id)webViewDelegate;


基本的实现方法和上面写的差不多,就是封装了一下,有兴趣的童鞋可以自己pod下来使用。

0 0
原创粉丝点击