推薦使用WKWebView
WKWebView 是蘋果在 iOS 8 中引入的新組件,目的是給出一個新的高性能的 Web View 解決方案,擺脫過去 UIWebView 的老舊笨重特別是內(nèi)存占用量巨大的問題。蘋果將 UIWebViewDelegate 與 UIWebView 重構(gòu)成了 14 個類和 3 個協(xié)議,引入了不少新的功能和接口。
WKWebView準備工作
#import <WebKit/WebKit.h>
@interface ViewController () <WKScriptMessageHandler, WKNavigationDelegate, WKUIDelegate>
@end
在創(chuàng)建WKWebView之前,我們先做配置操作
這邊就需要用到WKWebViewConfiguration
WKWebViewConfiguration *config =[[WKWebViewConfiguration alloc] init];
交互需要利用WKUserContentController
這個類是用來給JS注入對象的,對象是和網(wǎng)頁端一起約定好的。
config.userContentController = [[WKUserContentController alloc] init];
例如我們現(xiàn)在約定使用sendInfoModel這個對象,那么:
- OC中,給JS注入對象:
[config.userContentController addScriptMessageHandler:self name:@"sendInfoModel"];
- JS中,使用對象:
這是JS的一個傳值操作,通過body來傳值。
window.webkit.messageHandlers.sendInfoModel.postMessage({body: 'JS要傳值'});
- 當(dāng)JS通過sendInfoModel傳值的時候,在iOS端,我們在下面的這個代理中接受結(jié)果:
- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message {
//首先判斷一下是哪個對象(我們可以注入多個不同的對象,來進行不同的操作)
if ([message.name isEqualToString:@"sendInfoModel"]) {
// 打印所傳過來的參數(shù),只支持NSNumber, NSString, NSDate, NSArray,NSDictionary, and NSNull類
NSLog(@%@", message.body);
}
}
下面利用上面的WKWebViewConfiguration *config配置構(gòu)造器來創(chuàng)建KWWebView
self.webView = [[WKWebView alloc] initWithFrame:self.view.bounds configuration:config];
NSURL *path = [[NSBundle mainBundle] URLForResource:@"test" withExtension:@"html"];
[self.webView loadRequest:[NSURLRequest requestWithURL:path]];
[self.view addSubview:self.webView];
- WKWebView的title(標題), loading(BOOL,是否在加載), estimatedProgress(加載進度),可以用KVO來監(jiān)聽,進行一些細節(jié)操作(進度條啥的)。
WKWebView在請求開始前會調(diào)用下面這個代理方法:
-webView:webView decidePolicyForNavigationAction:navigationAction decisionHandler:decisionHandler
- navigationAction決定是否讓一個網(wǎng)頁被加載,我們檢查它的navigationType和request這兩個屬性;
- navigationType 枚舉值,UIWebViewNavigationTypeLinkClicked為點擊鏈接操作;
- request用來確定它是否是外部鏈接。
- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler
{
NSString *hostname = navigationAction.request.URL.host.lowercaseString;
if (navigationAction.navigationType == WKNavigationTypeLinkActivated
&& ![hostname containsString:@".lanou.com"])
{
// 對于跨域,需要手動跳轉(zhuǎn)
[[UIApplication sharedApplication] openURL:navigationAction.request.URL];
// 不允許web內(nèi)跳轉(zhuǎn)
decisionHandler(WKNavigationActionPolicyCancel);
} else
{
//允許web內(nèi)跳轉(zhuǎn)
self.progressView.alpha = 1.0;//(顯示進度條)
decisionHandler(WKNavigationActionPolicyAllow);
}
}
KWWebView完成響應(yīng)
-(void)webView:(WKWebView *)webView
decidePolicyForNavigationResponse:(WKNavigationResponse *)navigationResponse
decisionHandler:(void (^)(WKNavigationResponsePolicy))decisionHandler
{
//允許響應(yīng)
decisionHandler(WKNavigationResponsePolicyAllow);
//若為不允許響應(yīng),那么web內(nèi)容就傳不過來
}
針對HTTPS協(xié)議的鏈接,加載時都會觸發(fā)以下方法來驗證證書(操作與使用AFN進行HTTPS驗證證書一樣)
-(void)webView:(WKWebView *)webView didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:
(void (^)(NSURLSessionAuthChallengeDisposition disposition,
NSURLCredential *__nullable credential))completionHandler
{
//如不需要驗證傳默認的就可以
completionHandler(NSURLSessionAuthChallengePerformDefaultHandling, nil);
}
WKUIDelegate
與JS原生的alert,confirm,prompt進行交互:
- alert :
JS中:
function callJsAlert() {
alert('這個是OC調(diào)用JS的方法,并且通過Alert()進行顯示出來!');
window.webkit.messageHandlers.sendInfoModel.postMessage({body: '在JS中調(diào)用JS alert中方法'});
}
OC中: 當(dāng)JS調(diào)用alert時會觸發(fā)此方法
- (void)webView:(WKWebView *)webView runJavaScriptAlertPanelWithMessage:(NSString *)message
initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(void))completionHandler
{
UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"alert" message:@"JS調(diào)用alert" preferredStyle:UIAlertControllerStyleAlert];
[alert addAction:[UIAlertAction actionWithTitle:@"確定" style: UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
completionHandler();
}]];
[self presentViewController:alert animated:YES completion:NULL];
NSLog(@"%@", message);
}
- confirm
- prompt
與alert相似
JS中:
function callJsConfirm() {
if (confirm('confirm', 'Objective-C call js to show confirm')) {
document.getElementById('jsParamFuncSpan').innerHTML
= 'true';
// sendInfoModel是我們所注入的對象
window.webkit.messageHandlers.sendInfoModel.postMessage({body: '我是JS里面的內(nèi)容'});//傳值
} else {
document.getElementById('jsParamFuncSpan').innerHTML
= 'false';
}
}
function callJsInput() {
var response = prompt('Hello', '請輸入你的名字:');
document.getElementById('jsParamFuncSpan').innerHTML = response;
// sendInfoModel是我們所注入的對象
window.webkit.messageHandlers.sendInfoModel.postMessage({body: response});
}
OC中:
- (void)webView:(WKWebView *)webView
runJavaScriptConfirmPanelWithMessage:(NSString *)message
initiatedByFrame:(WKFrameInfo *)frame
completionHandler:(void (^)(BOOL result))completionHandler
{
UIAlertController *alert = [UIAlertController alertControllerWithTitle:
@"confirm" message:@"JS調(diào)用confirm"
preferredStyle:UIAlertControllerStyleAlert];
[alert addAction:[UIAlertAction actionWithTitle:@"確定"
style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action){
completionHandler(YES);
}]];
[alert addAction:[UIAlertAction actionWithTitle:@"取消"
style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) {
completionHandler(NO);
}]];
[self presentViewController:alert animated:YES completion:NULL];
NSLog(@"%@", message);
}
-(void)webView:(WKWebView *)webView
runJavaScriptTextInputPanelWithPrompt:(NSString *)prompt
defaultText:(nullable NSString *)defaultText
initiatedByFrame:(WKFrameInfo *)frame
completionHandler:(void (^)(NSString * __nullable result))completionHandler
{
NSLog(@"%@", prompt);
UIAlertController *alert = [UIAlertController alertControllerWithTitle:
@"textinput" message:@"JS調(diào)用輸入框"
preferredStyle:UIAlertControllerStyleAlert];
[alert addTextFieldWithConfigurationHandler:^(UITextField * _Nonnull textField) {
textField.textColor = [UIColor redColor];
}];
[alert addAction:[UIAlertAction actionWithTitle:@"確定"
style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
completionHandler([[alert.textFields lastObject] text]);
}]];
[self presentViewController:alert animated:YES completion:NULL];
}