WKWebView 新特性
加載 web 頁(yè)面
加載本地資源
-
- loadHTMLString:baseURL:
同步方式加載本地資源业栅,數(shù)據(jù)可以來(lái)源于本地文件或者硬編碼的 HTML 字符串
// 設(shè)定主頁(yè)文件的基本路徑咖耘,通過(guò)一個(gè)HTML字符串加載主頁(yè)數(shù)據(jù)
- (IBAction)loadHTMLString:(id)sender {
// 主頁(yè)文件名
NSString *htmlPath = [[NSBundle mainBundle] pathForResource:@"Baidu001"
ofType:@"html"];
// 主頁(yè)文件的基本路徑
NSURL *bundleUrl = [NSURL fileURLWithPath:[[NSBundle mainBundle] bundlePath]];
// 將 index.html 文件的內(nèi)容讀取到 NSString 對(duì)象中
NSError *error = nil;
NSString *html = [[NSString alloc] initWithContentsOfFile:htmlPath
encoding:NSUTF8StringEncoding
error:&error];
//數(shù)據(jù)加載沒(méi)有錯(cuò)誤的情況下
if (error == nil) {
[self.webView loadHTMLString:html baseURL:bundleUrl];
}
}
-
- loadData:MIMEType:characterEncodingName:baseURL:
指定 MIME 類型、編碼集和NSDate
對(duì)象加載一個(gè)主頁(yè)數(shù)據(jù)菲驴,并設(shè)定主頁(yè)文件基本路徑
// NSData 是一種二進(jìn)制的字節(jié)數(shù)組類型悄但。它沒(méi)有字符集的概念汤纸,但用它來(lái)裝載 webView 的時(shí)候必須指定字符集织狐。
- (IBAction)loadDATA:(id)sender {
NSString *htmlPath = [[NSBundle mainBundle] pathForResource:@"index"
ofType:@"html"];
NSData *htmlData = [[NSData alloc] initWithContentsOfFile:htmlPath];
NSURL *bundleUrl = [NSURL fileURLWithPath:[[NSBundle mainBundle] bundlePath]];
[self.webView loadData:htmlData
MIMEType:@"text/html"
textEncodingName:@"UTF-8"
baseURL:bundleUrl];
}
加載本地HTML:iOS9以上的系統(tǒng)可以使用
WKWebView loadFileURL:allowingReadAccessToURL:
,iOS9以下的版本沒(méi)有這個(gè)方法筹煮,需要先將本地HTML文件的復(fù)制到tmp目錄中遮精,然后使用loadRequest:
方法來(lái)加載。如果在HTML中引入其他資源文件,例如js本冲,css准脂,image等必須一同復(fù)制到temp目錄中
加載網(wǎng)絡(luò)資源
// 創(chuàng)建 WKWebView 對(duì)象
CGRect rect = CGRectMake(0, 0, self.view.width, self.view.height);
WKWebView *webView = [[WKWebView alloc] initWithFrame:rect];
// 設(shè)置導(dǎo)航代理,監(jiān)聽(tīng)網(wǎng)頁(yè)加載進(jìn)程
_webView.navigationDelegate = self;
// 將 webView 對(duì)象添加到視圖
[self.view addSubview:webView];
// 根據(jù)URL發(fā)起網(wǎng)絡(luò)請(qǐng)求
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"www.google.com"]];
[webView loadRequest:request];
創(chuàng)建可與 JavaScript 交互的 WKWebView
1. Objective-C 調(diào)用 JavaScript 方法
-
JS 方法:
<!-- JavaScript 方法檬洞, 接收一個(gè)參數(shù) --> function alertAction(message) { alert(message); }
-
Objective-C 原生調(diào)用
// OC 調(diào)用 JS 的方法 [self.mainWebView evaluateJavaScript:@"alertAction('OC調(diào)用JS方法時(shí)傳入的參數(shù)')" completionHandler:^(id _Nullable item, NSError * _Nullable error) { NSLog(@"alert"); }];
2. JavaScript 調(diào)用 Objective-C 方法
參考:WKWebView的使用和各種坑的解決方法(OC+Swift)
- 第一步:
- (WKWebViewConfiguration *)webViewConfiguration {
if (!_webViewConfiguration) {
// 創(chuàng)建 WKWebViewConfiguration 對(duì)象
_webViewConfiguration = [[WKWebViewConfiguration alloc] init];
// 創(chuàng)建 WKUserContentController 對(duì)象狸膏,提供 JavaScript 向 webView 發(fā)送消息的方法
WKUserContentController *userContentColtroller = [[WKUserContentController alloc] init];
// 添加消息處理,注意:self指代的對(duì)象需要遵守 WKScriptMessageHandler 協(xié)議添怔,結(jié)束時(shí)需要移除
[userContentColtroller addScriptMessageHandler:self name:@"close"];
// 將 userConttentController 設(shè)置到配置文件
_webViewConfiguration.userContentController = userContentColtroller;
}
return _webViewConfiguration;
}
?? ScriptMessageHandler 的 name 必須和 web 中的 JavaScript 方法名稱一致:
window.webkit.messageHandlers.close.postMessage("message");
<script type="text/javascript">
function doPrint() {
if (checkiOS() == true) {
// iOS 注入代碼
window.webkit.messageHandlers.scriptName.postMessage("script message湾戳!");
} else {
// Android 注入代碼
Android.startLHYL();
}
}
// Android/iOS設(shè)備判斷
function checkiOS() {
var u = navigator.userAgent;
var isAndroid = u.indexOf('Android') > -1 || u.indexOf('Adr') > -1; // Android
var isiOS = !!u.match(/\(i[^;]+;( U;)? CPU.+Mac OS X/); // iOS
if (isiOS) {
return true;
} else {
return false;
}
}
</script>
- 第二步:
#pragma mark - WKScriptMessageHandler
- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message {
NSLog(@"%s",__FUNCTION__);
if ([message.name isEqualToString:@"close"]) {
NSLog(@"WKScriptMessage:%@",message.body);
}
}
- 第三步:
- (void)dealloc {
// 移除 ScriptMessageHandler
[self.webView.configuration.userContentController removeScriptMessageHandlerForName:@"close"];
}
顯示加載進(jìn)度條
對(duì)于 WKWebView ,有三個(gè)屬性支持 KVO澎灸,因此我們可以監(jiān)聽(tīng)其值的變化,分別是:
loading
遮晚、title
性昭、estimatedProgress
,對(duì)應(yīng)功能表示為:是否正在加載中、頁(yè)面的標(biāo)題县遣、頁(yè)面內(nèi)容加載進(jìn)度(值為0.0~1.0)
剛開(kāi)始用的方法:WKWebView添加進(jìn)度條 糜颠,參照著寫(xiě)完發(fā)現(xiàn)如果網(wǎng)速好的話,會(huì)出現(xiàn)進(jìn)度條走不完就被隱藏的現(xiàn)象萧求。
解決方法:添加延時(shí)animation動(dòng)畫(huà)其兴,參考:iOS WKWebView添加網(wǎng)頁(yè)加載進(jìn)度條
代碼如下:
#import "OSCViewController.h"
#import <WebKit/WebKit.h>
@interface OSCViewController () <WKNavigationDelegate>
@property (nonatomic, strong) NSURL *URL;
@property (nonatomic, strong) WKWebViewConfiguration *webViewConfiguration;
@property (nonatomic, strong) WKWebView *webView;
/** 1?? 添加 progressView 屬性*/
@property (nonatomic, strong) UIProgressView *progressView;
@end
@implementation OSCViewController
#pragma mark - Lifecycle
- (void)loadView {
[super loadView];
self.view = self.webView;
}
- (void)viewDidLoad {
[super viewDidLoad];
// 3?? 將 progressView 添加到父視圖
[self.view addSubview:self.progressView];
// 4?? 使用 KVO 注冊(cè)觀察者
// 監(jiān)聽(tīng) WKWebView 對(duì)象的 estimatedProgress 屬性,就是當(dāng)前網(wǎng)頁(yè)加載的進(jìn)度
[self.webView addObserver:self
forKeyPath:@"estimatedProgress"
options:NSKeyValueObservingOptionNew
context:nil];
NSURLRequest *request = [NSURLRequest requestWithURL:self.URL];
[self.webView loadRequest:request];
}
- (void)dealloc {
// 6?? 移除觀察者
[self.view removeObserver:self forKeyPath:@"estimatedProgress"];
}
#pragma mark - Custom Accessors
- (NSURL *)URL {
if (!_URL) {
_URL = [NSURL URLWithString:@"http://www.oschina.net/"];
}
return _URL;
}
- (WKWebViewConfiguration *)webViewConfiguration {
if (!_webViewConfiguration) {
_webViewConfiguration = [[WKWebViewConfiguration alloc] init];
}
return _webViewConfiguration;
}
- (WKWebView *)webView {
if (!_webView) {
_webView = [[WKWebView alloc] initWithFrame:[[UIScreen mainScreen] bounds] configuration:self.webViewConfiguration];
// 設(shè)置導(dǎo)航代理夸政,監(jiān)聽(tīng)網(wǎng)頁(yè)加載進(jìn)程
_webView.navigationDelegate = self;
}
return _webView;
}
/**
2?? 初始化progressView
@return 返回初始化后的進(jìn)度條視圖
*/
- (UIProgressView *)progressView {
if (!_progressView) {
CGRect sreenBounds = [[UIScreen mainScreen] bounds];
CGRect progressViewFrame = CGRectMake(0, 64, sreenBounds.size.width, 1);
_progressView = [[UIProgressView alloc] initWithFrame:progressViewFrame];
// 設(shè)置進(jìn)度條色調(diào)
_progressView.tintColor = [UIColor blueColor];
// 設(shè)置進(jìn)度條跟蹤色調(diào)
_progressView.trackTintColor = [UIColor whiteColor];
}
return _progressView;
}
#pragma mark - KVO
// 5?? 接收變更后的通知元旬,計(jì)算 webView 的進(jìn)度條
- (void)observeValueForKeyPath:(NSString *)keyPath
ofObject:(id)object
change:(NSDictionary<NSKeyValueChangeKey,id> *)change
context:(void *)context {
if ([keyPath isEqualToString:@"estimatedProgress"]) {
self.progressView.progress = self.webView.estimatedProgress;
if (self.progressView.progress == 1) {
/*
* 添加一個(gè)簡(jiǎn)單的動(dòng)畫(huà),將 progressView 的 Height 變?yōu)?.5倍
* 動(dòng)畫(huà)時(shí)長(zhǎng)0.25s守问,延時(shí)0.3s后開(kāi)始動(dòng)畫(huà)
* 動(dòng)畫(huà)結(jié)束后將 progressView 隱藏
*/
__weak __typeof(self)weakSelf = self;
[UIView animateWithDuration:0.25f delay:0.3f options:UIViewAnimationOptionCurveEaseInOut animations:^{
weakSelf.progressView.transform = CGAffineTransformMakeScale(1.0f, 1.5f);
} completion:^(BOOL finished) {
weakSelf.progressView.hidden = YES;
}];
}
}else {
[super observeValueForKeyPath:keyPath
ofObject:object
change:change
context:context];
}
}
#pragma mark - WKNavigationDelegate
// 頁(yè)面開(kāi)始加載web內(nèi)容時(shí)調(diào)用
- (void)webView:(WKWebView *)webView didStartProvisionalNavigation:(WKNavigation *)navigation {
self.progressView.hidden = NO;
// 防止 progressView 被網(wǎng)頁(yè)擋住
[self.view bringSubviewToFront:self.progressView];
}
// 當(dāng)web內(nèi)容開(kāi)始返回時(shí)調(diào)用
- (void)webView:(WKWebView *)webView didCommitNavigation:(WKNavigation *)navigation {
}
// 頁(yè)面加載完成之后調(diào)用
- (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation {
}
// 頁(yè)面加載失敗時(shí)調(diào)用 ( 【web視圖加載內(nèi)容時(shí)】發(fā)生錯(cuò)誤)
- (void)webView:(WKWebView *)webView didFailProvisionalNavigation:(WKNavigation *)navigation withError:(NSError *)error {
NSLog(@"Error:%@",error.localizedDescription);
self.progressView.hidden = YES;
}
// 【web視圖導(dǎo)航過(guò)程中發(fā)生錯(cuò)誤】時(shí)調(diào)用匀归。
- (void)webView:(WKWebView *)webView didFailNavigation:(WKNavigation *)navigation withError:(NSError *)error {
NSLog(@"Error:%@",error.localizedDescription);
self.progressView.hidden = YES;
// 如果請(qǐng)求被取消
if (error.code == NSURLErrorCancelled) {
return;
}
}
// 當(dāng)Web視圖的Web內(nèi)容進(jìn)程終止時(shí)調(diào)用。
- (void)webViewWebContentProcessDidTerminate:(WKWebView *)webView {
self.progressView.hidden = YES;
}
@end
監(jiān)聽(tīng) webView 的 title 屬性以動(dòng)態(tài)設(shè)置標(biāo)題
- (void)viewDidLoad {
[super viewDidLoad];
// 添加觀察者耗帕,監(jiān)聽(tīng) WKWebView 對(duì)象的 title 屬性
[self.webView addObserver:self forKeyPath:@"title" options:NSKeyValueObservingOptionNew context:NULL];
NSURLRequest *request = [NSURLRequest requestWithURL:self.URL];
[self.webView loadRequest:request];
}
- (void)dealloc {
// 移除觀察者
[self.view removeObserver:self forKeyPath:@"title"];
}
#pragma mark - KVO
- (void)observeValueForKeyPath:(NSString *)keyPath
ofObject:(id)object
change:(NSDictionary<NSKeyValueChangeKey,id> *)change
context:(void *)context {
if ([keyPath isEqualToString:@"title"]) {
self.title = change[@"new"];
}else {
[super observeValueForKeyPath:keyPath
ofObject:object
change:change
context:context];
}
}
URL 中文處理
- (NSURL *)URL {
if (!_URL) {
#pragma clang diagnostic push
#pragma clang diagnostic ignored"-Wdeprecated-declarations"
_URL = [NSURL URLWithString:(NSString *)CFBridgingRelease(CFURLCreateStringByAddingPercentEscapes(kCFAllocatorDefault, (CFStringRef)@"https://www.oschina.net/ios/home", (CFStringRef)@"!$&'()*+,-./:;=?@_~%#[]", NULL,kCFStringEncodingUTF8))];
#pragma clang diagnostic pop
}
return _URL;
}
導(dǎo)航欄右側(cè)添加刷新按鈕
#pragma mark - Lifecycle
- (void)viewDidLoad {
[super viewDidLoad];
// 刷新按鈕
UIBarButtonItem *rightBarButtonItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemRefresh target:self action:@selector(rightBarButtonDidClicked:)];
self.navigationItem.rightBarButtonItem = rightBarButtonItem;
}
#pragma mark - IBActions
- (void)rightBarButtonDidClicked:(id)sender {
[self.webView reload];
}
禁用長(zhǎng)按選中文字效果
- (void)webView:(WKWebView *)webView didFinishNavigation:(null_unspecified WKNavigation *)navigation {
// 禁用選中效果
[self.webView evaluateJavaScript:@"document.documentElement.style.webkitUserSelect='none'" completionHandler:nil];
[self.webView evaluateJavaScript:@"document.documentElement.style.webkitTouchCallout='none'" completionHandler:nil];
}
加載 HTML 圖片大小自適應(yīng)
參考 WebView加載HTML圖片大小自適應(yīng)與文章自動(dòng)換行
在 HTML 代碼中設(shè)置內(nèi)容樣式穆端,一般使用 css 或者 js ,根據(jù)加載優(yōu)先級(jí)以及加載效果仿便,可以自行選擇体啰。
- js在頁(yè)面加載完之后加載,所以設(shè)置圖片樣式的時(shí)候嗽仪,會(huì)先加載大圖荒勇,然后突然變小闻坚;
- css在引入時(shí)加載枕屉,直接加載縮小的圖片(實(shí)際占用內(nèi)存不會(huì)縮小)鲤氢;
一:使用 CSS 進(jìn)行圖片的自適應(yīng)
#define HTML_NO_HEAD @"<br/><br/>"
#define HTML_HAD_HEAD @"<head></head><body><br/><br/></body>"
- (void)viewDidLoad {
[super viewDidLoad];
// 沒(méi)有<head>標(biāo)簽
// [self.webView loadHTMLString:[self reSizeImageWithHTMLNoHead:HTML_NO_HEAD] baseURL:nil];
// 有<head>標(biāo)簽
[self.webView loadHTMLString:[self reSizeImageWithHTMLHadHead:HTML_HAD_HEAD] baseURL:nil];
}
/ ----------------------------------------------------------------
// 1.圖片樣式:不管用戶以前設(shè)置的圖片尺寸是多大搀擂,都縮放到寬度為320px大小西潘。
// 如果后臺(tái)返回的HTML代碼中,不包含 <head> 標(biāo)簽哨颂,則可以直接在HTML字符串前拼接代碼喷市。
- (NSString *)reSizeImageWithHTMLNoHead:(NSString *)html {
return [NSString stringWithFormat:@"<head><style>img{width:320px !important;}</style></head>%@", html];
}
// 如果包含 <head> 標(biāo)簽,則在<head>標(biāo)簽內(nèi)部替換添加
- (NSString *)reSizeImageWithHTMLHadHead:(NSString *)html {
return [HTML_HAD_HEAD stringByReplacingOccurrencesOfString:@"<head>" withString:@"<head><style>img{width:320px !important;}</style>"];
}
/ ----------------------------------------------------------------
// 2.圖片樣式:若需要根據(jù)圖片原本大小威恼,寬度小于320px的不縮放品姓,大于320px的縮小到320px,那么在HTML字符串前加上一下代碼:
- (NSString *)reSizeImageWithHTML:(NSString *)html {
return [NSString stringWithFormat:@"<head><style>img{max-width:320px !important;}</style></head>%@", html];
}
二:使用 JavaScript 進(jìn)行圖片的自適應(yīng)
在 webview 的代理中箫措,執(zhí)行 JavaScript 代碼腹备。(下面這段代碼是必須有 <head>
標(biāo)簽的)
如果沒(méi)有 <head>
標(biāo)簽,也很簡(jiǎn)單斤蔓,只需要給返回的 HTML 字符串前面拼接一個(gè) <head></head>
即可植酥。
- (void)webViewDidFinishLoad:(UIWebView *)webView
{
[webView stringByEvaluatingJavaScriptFromString:
@"var script = document.createElement('script');"
"script.type = 'text/javascript';"
"script.text = \"function ResizeImages() { "
"var myimg,oldwidth,oldheight;"
"var maxwidth=320;"http:// 圖片寬度
"for(i=0;i <document.images.length;i++){" "myimg = document.images[i];" "if(myimg.width > maxwidth){"
"myimg.width = maxwidth;"
"}"
"}"
"}\";"
"document.getElementsByTagName('head')[0].appendChild(script);"];
[webView stringByEvaluatingJavaScriptFromString:@"ResizeImages();"];
}
文章內(nèi)容自動(dòng)換行
文章的自動(dòng)換行也是通過(guò)css實(shí)現(xiàn)的,書(shū)寫(xiě)方式圖片縮放類似弦牡。在沒(méi)有<body>標(biāo)簽的情況下友驮,在HTML代碼前,直接拼接以下代碼即可(若包含<body>驾锰,則將代碼添加到body標(biāo)簽內(nèi)部)卸留,意思是全部?jī)?nèi)容自動(dòng)換行。
<body width=320px style=\"word-wrap:break-word; font-family:Arial\">
參考
- WKWebview 使用攻略
- WKWebViewTips
- 淺談 WKWebView 使用椭豫、JS 的交互
- iOS 新聞?lì)?App 內(nèi)容頁(yè)技術(shù)探索
- WKWebView 在實(shí)際開(kāi)發(fā)中的使用匯總 【URL中文處理耻瑟、監(jiān)聽(tīng)estimatedProgress、title赏酥、H5通過(guò)原生方法調(diào)用相冊(cè)】
- WKWebView @nshipster 【 Swift】
- WKWebView的新特性與使用 @saitjr
- 使用safari對(duì)webview進(jìn)行調(diào)試 @saitjr
- WebView加載HTML圖片大小自適應(yīng)與文章自動(dòng)換行 @saitjr
還在用UIWebView?何不試試WKWebView @吳白- WKWebview開(kāi)發(fā)筆記 @Qin's Blog 【清理緩存匆赃?】
- WKWebView特性及使用 @360doc 【Swift】
- 帶你走進(jìn)WKWebView的世界 @SOI
- WKWebView的使用和各種坑的解決方法(OC+Swift)
- js(javascript)與ios(Objective-C)相互通信交互 @天狐博客
- WKWebView 使用及注意點(diǎn)(keng) @伯樂(lè)在線
- ShingoFukuyama /WKWebViewTips
- IOS進(jìn)階之WKWebView @ 翻滾的牛寶寶
iOS8 WebKit庫(kù)之——WKWebView篇- iOS WebViewJavascriptBridge簡(jiǎn)單運(yùn)用方法 @iSuAbner
- iOS下JS與OC互相調(diào)用(六)--WKWebView + WebViewJavascriptBridge
- WK 與 JS 的那些事
- API翻譯:UIWebView (Swift版)
- API翻譯:WebKit
- API翻譯:WKWebView (Swift版)