WKWebView 和UIWebView、網頁快取、網路請求快取
//
// ViewController.m
// WebView
//
// Created by lambo on 2017/1/17.
// Copyright © 2017年 cn.lr. All rights reserved.
//
/*webview 自動計算內容高度----------
//第一種方法
- (void)webViewDidFinishLoad:(UIWebView *)webView
{
CGFloat webViewHeight=[webView.scrollView contentSize].height;
CGRect newFrame = webView.frame;
newFrame.size.height = webViewHeight;
webView.frame = newFrame;
_webTablewView.contentSize = CGSizeMake(320, newFrame.size.height + 64 + KWIDTH - 100);
}
//2.執行js語句 直接獲取html文件的dom高度
- (void)webViewDidFinishLoad:(UIWebView *)webView{
CGFloatwebViewHeight =[[webViewstringByEvaluatingJavaScriptFromString:@document.body.offsetHeight]floatValue];
// CGFloat webViewHeight= [[webViewstringByEvaluatingJavaScriptFromString:@document.body.scrollHeight]floatValue];
CGRectnewFrame = webView.frame;
newFrame.size.height= webViewHeight;
webView.frame= newFrame;
}
//方法3.先將UIWebView的高度設為最小,然後再使用sizeThatFits就會返回剛好合適的大小
-(void)webViewDidFinishLoad:(UIWebView*)webVie{
CGSize actualSize = [webView sizeThatFits:CGSizeZero];
CGRect newFrame = webView.frame;
newFrame.size.height = actualSize.height;
webView.frame = newFrame;
}
//方法4.遍歷webview子檢視 獲取UIWebDocumentView高度即實際高度
-(void)webViewDidFinishLoad:(UIWebView *)webView{
CGFloat webViewHeight = 0.0f;
if([webView.subviews count] > 0)
{
UIView *scrollerView = webView.subviews[0];
if([scrollerView.subviews count] >
0)
{
UIView *webDocView = scrollerView.subviews.lastObject;
if ([webDocView isKindOfClass:[NSClassFromString(@UIWebDocumentView)class]])
{
webViewHeight = webDocView.frame.size.height;//獲取文件的高度
webView.frame=webDocView.frame;
//更新UIWebView 的高度
}
}
}
}
*/
#import "ViewController.h"
#import <webkit/WebKit.h>//MKWebKit要使用這個框架
@interface ViewController ()<UIWebViewDelegate,WKUIDelegate,WKNavigationDelegate,WKScriptMessageHandler>
@end
@implementation ViewController
/**webView中的屬性==========================
UIWebview 會自動儲存上一次的cookie,WKWebview不會。
代理屬性重點需要知道代理方法的使用
@property (nullable, nonatomic, assign) id <UIWebViewDelegate> 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);
==============================*/
/**webView的代理方法==========================
// 載入Data資料建立一個webView
- (void)loadData:(NSData *)data MIMEType:(NSString *)MIMEType textEncodingName:(NSString *)encodingName baseURL:(NSURL *)baseURL
// 載入本地HTML字串(字串中的內容是html網頁程式碼)用webView呼叫這個方法
- (void)loadHTMLString:(NSString *)string baseURL:(NSURL *)baseURL
// 載入一個請求建立一個webView,吧請求加在到webview網頁中
- (void)loadRequest:(NSURLRequest *)request
// 重新整理網頁
- (void)reload;
// 停止網頁載入內容
- (void)stopLoading;
// 後退
- (void)goBack;
// 前進
- (void)goForward;
// 執行JS方法,這個方法可以返回整個網頁html程式碼,
- (NSString *)stringByEvaluatingJavaScriptFromString:(NSString *)script
======================================*/
- (void)viewDidLoad {
[super viewDidLoad];
UIWebView *web=[[UIWebViewalloc]initWithFrame:self.view.frame];
[self.viewaddSubview:web];
[web loadHTMLString:@"<p>sssssssssss</p>"baseURL:nil];//載入html
// //==========/webView的使用===============
// UIWebView *webView = [[UIWebView alloc] initWithFrame:[UIScreen mainScreen].bounds];
// self.view = webView;
// webView.delegate=self;
// NSURL *url = [[NSBundle mainBundle] URLForResource:@"Untitled.html" withExtension:nil];//從本地獲取的URL
//
//// NSURL *url = [NSURL URLWithString:@"https:/baidu.com"];//從網路獲取的URL
// NSURLRequest *request = [NSURLRequest requestWithURL:url];//預設超時60秒
NSURLRequest *request=[NSURLRequestrequestWithURL:urlcachePolicy:NSURLRequestUseProtocolCachePolicytimeoutInterval:3];//帶超時時間的請求
// [webView loadRequest:request];
//===================================
//=====WKWebview===直接載入js,不是通過URL請求載入//方式4============
// 圖片縮放的js程式碼myFunction()
NSString *js =@"return document.getElementsByTagName('p')[0].innerHTML";
// NSString *js = @"myFunction();";
// 根據JS字串初始化WKUserScript物件
WKUserScript *script = [[WKUserScript alloc]initWithSource:js injectionTime:WKUserScript InjectionTimeAtDocumentStartforMainFrameOnly:YES];
// WKUserContentController *userContentController = [[WKUserContentController alloc] init];
////建立網頁配置物件, 根據生成的WKUserScript物件,初始化WKWebViewConfiguration
WKWebViewConfiguration *config = [[WKWebViewConfiguration alloc]init];
[config.userContentController addUserScript:script];
// 建立設定物件
WKPreferences *preference = [[WKPreferencesalloc]init];
// 設定網頁字型大小
preference.minimumFontSize =30;
// 設定偏好設定物件
config.preferences = preference;
//建立WKWebView
WKWebView* webView = [[WKWebViewalloc]initWithFrame:self.view.bounds configuration:config];
webView.navigationDelegate=self;
self.view=webView;
// NSURL *url = [[NSBundle mainBundle] URLForResource:@"Untitled.html" withExtension:nil];//從本地獲取的URL
//====== 這個也可以獲取本地路徑 //網頁檔案全路徑
// NSString*filePath = [[NSBundle mainBundle] pathForResource:@"Untitled.html" ofType:nil];
// // 建立URL物件:指定要載入資源的路徑
// ======= NSURL *URL = [NSURL fileURLWithPath:filePath];
//直接加在html==============
[webView loadHTMLString:@"<p>這是愛魚app,iOS版本我的老家就住在這個屯我是這個屯裡土生圖章的人啊這是愛魚app,iOS版本我的老家就住在這個屯我是這個屯裡土生圖章的人啊這是愛魚app,iOS版本我的老家就住在這個屯我是這個屯裡土生圖章的人啊這是愛魚app,iOS版本我的老家就住在這個屯我是這個屯裡土生圖章的人啊這是愛魚app,iOS版本我的老家就住在這個屯我是這個屯裡土生圖章的人啊這是愛魚app,iOS版本我的老家就住在這個屯我是這個屯裡土生圖章的人啊這是愛魚app,iOS版本我的老家就住在這個屯我是這個屯裡土生圖章的人啊</p></body></html>"baseURL:nil];
//=========WKWebview========//方式3============
// WKWebViewConfiguration * configuration = [[WKWebViewConfiguration alloc] init];
// WKWebView* webView = [[WKWebView alloc] initWithFrame:self.view.bounds configuration:configuration];
// NSURL *url = [[NSBundle mainBundle] URLForResource:@"Untitled.html" withExtension:nil];//從本地獲取的URL
// self.view = webView;
// webView.UIDelegate=self;
// webView.navigationDelegate=self;
// [webView loadRequest:[NSURLRequest requestWithURL:url]];
// //====22======WKWebview 的使用=============
// // js配置
// WKUserContentController *userContentController = [[WKUserContentController alloc] init];
// [userContentController addScriptMessageHandler:self name:@"webViewLoadStart"];
// [userContentController addScriptMessageHandler:self name:@"webViewLoadFinish"];
// [userContentController addScriptMessageHandler:self name:@"webViewLogout"];
// [userContentController addScriptMessageHandler:self name:@"webViewSuccess"];
//
// // WKWebView的配置
// WKWebViewConfiguration *configuration = [[WKWebViewConfiguration alloc] init];
// configuration.userContentController = userContentController;
//
// // 顯示WKWebView
// WKWebView * webView = [[WKWebView alloc] initWithFrame:self.view.frame configuration:configuration];
// webView.UIDelegate = self; // 設定WKUIDelegate代理
// webView.navigationDelegate = self; // 設定WKNavigationDelegate代理
// //====11======WKWebview 的使用=============
//
// WKWebView *webView = [[WKWebView alloc] initWithFrame:[UIScreen mainScreen].bounds];
// self.view = webView;
// webView.UIDelegate=self;
// webView.navigationDelegate=self;
// NSURL *url = [[NSBundle mainBundle] URLForResource:@"Untitled.html" withExtension:nil];//從本地獲取的URL
// NSURLRequest *request = [NSURLRequest requestWithURL:url];
// [webView loadRequest:request];
}
//==================webview 的代理方法=================
//// Sent before a web view begins loading a frame.請求傳送前都會呼叫該方法,返回NO則不處理這個請求
//- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType{
//
// return YES;
//}
//
//// 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{
//// NSString *str= [webView stringByEvaluatingJavaScriptFromString:@"document.documentElement.innerHTML"];//獲取整個網頁的內容
//// NSLog(@"-sss--%@-",str);
//// NSString *str= [webView stringByEvaluatingJavaScriptFromString:@"document.getElementsByTagName('p')[0].innerHTML"];//獲取第一個P標籤的內容
// NSString *str= [webView stringByEvaluatingJavaScriptFromString:@"myFunction();"];//通過執行JS程式碼獲取第一個P標籤的內容
// NSLog(@"-p標籤--%@-",str);
//}
//
//// Sent if a web view failed to load a frame. 網頁請求失敗則會呼叫該方法
//- (void)webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error{
//
//}
/***=============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 <WKNavigationDelegate> navigationDelegate;
// The WKUIDelegate class provides methods for presenting native user interface
elements on behalf of a webpage.
// WKWebView中,加入了網站視窗的概念,這個物件決了webView視窗的一些方法協議。
@property (nullable, nonatomic, weak) id <WKUIDelegate> 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;
*/
/***========MKWebview的物件方法========
// 這是載入網頁最常用的一種方式,通過一個網頁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);
*/
//- (nullable WKNavigation *)goToBackForwardListItem:(WKBackForwardListItem *)item;
/*! @abstract The page title.
@discussion @link WKWebView @/link is key-value observing (KVO) compliant
for this property.
*/
//@property (nullable, nonatomic, readonly, copy) NSString *title;
/*! @abstract The active URL.
@discussion This is the URL that should be reflected in the user
interface.
@link WKWebView @/link is key-value observing (KVO) compliant for this
property.
*/
//@property (nullable, nonatomic, readonly, copy) NSURL *URL;
/*! @abstract A Boolean value indicating whether the view is currently
loading content.
@discussion @link WKWebView @/link is key-value observing (KVO) compliant
for this property.
*/
//@property (nonatomic, readonly, getter=isLoading) BOOL loading;
/*! @abstract An estimate of what fraction of the current navigation has been completed.
@discussion This value ranges from 0.0 to 1.0 based on the total number of
bytes expected to be received, including the main document and all of its
potential subresources. After a navigation completes, the value remains at 1.0
until a new navigation starts, at which point it is reset to 0.0.
@link WKWebView @/link is key-value observing (KVO) compliant for this
property.
*/
//@property (nonatomic, readonly) double estimatedProgress;
/*! @abstract A Boolean value indicating whether all resources on the page
have been loaded over securely encrypted connections.
@discussion @link WKWebView @/link is key-value observing (KVO) compliant
for this property.
*/
//@property (nonatomic, readonly) BOOL hasOnlySecureContent;
/*! @abstract A SecTrustRef for the currently committed navigation.
@discussion @link WKWebView @/link is key-value observing (KVO) compliant
for this property.
*/
//@property (nonatomic, readonly, nullable) SecTrustRef serverTrust API_AVAILABLE(macosx(10.12), ios(10.0));
/*! @abstract A Boolean value indicating whether there is a back item in
the back-forward list that can be navigated to.
@discussion @link WKWebView @/link is key-value observing (KVO) compliant
for this property.
@seealso backForwardList.
*/
//@property (nonatomic, readonly) BOOL canGoBack;
/*! @abstract A Boolean value indicating whether there is a forward item in
the back-forward list that can be navigated to.
@discussion @link WKWebView @/link is key-value observing (KVO) compliant
for this property.
@seealso backForwardList.
*/
//@property (nonatomic, readonly) BOOL canGoForward;
/*! @abstract Navigates to the back item in the back-forward list.
@result A new navigation to the requested item, or nil if there is no back
item in the back-forward list.
*/
//- (nullable WKNavigation *)goBack;
/*! @abstract Navigates to the forward item in the back-forward list.
@result A new navigation to the requested item, or nil if there is no
forward item in the back-forward list.
*/
//- (nullable WKNavigation *)goForward;
/*! @abstract Reloads the current page.
@result A new navigation representing the reload.
*/
//- (nullable WKNavigation *)reload;
/*! @abstract Reloads the current page, performing end-to-end revalidation
using cache-validating conditionals if possible.
@result A new navigation representing the reload.
*/
//- (nullable WKNavigation *)reloadFromOrigin;
/*! @abstract Stops loading all resources on the current page.
*/
//- (void)stopLoading;
/* @abstract Evaluates the given JavaScript string.
@param javaScriptString The JavaScript string to evaluate.
@param completionHandler A block to invoke when script evaluation completes or fails.
@discussion The completionHandler is passed the result of the script evaluation or an error.
*/
//- (void)evaluateJavaScript:(NSString *)javaScriptString completionHandler:(void (^ _Nullable)(_Nullable id, NSError * _Nullable error))completionHandler;
/*! @abstract A Boolean value indicating whether horizontal swipe gestures
will trigger back-forward list navigations.
@discussion The default value is NO.
*/
//@property (nonatomic) BOOL allowsBackForwardNavigationGestures;
/*! @abstract The custom user agent string or nil if no custom user agent string has been set.
*/
//@property (nullable, nonatomic, copy) NSString *customUserAgent API_AVAILABLE(macosx(10.11), ios(9.0));
/*! @abstract A Boolean value indicating whether link preview is allowed for any
links inside this WKWebView.
@discussion The default value is YES on Mac and iOS.
*/
//@property (nonatomic) BOOL allowsLinkPreview API_AVAILABLE(macosx(10.11), ios(9.0));
//#if TARGET_OS_IPHONE
/*! @abstract The scroll view associated with the web view.
*/
//@property (nonatomic, readonly, strong) UIScrollView *scrollView;
//#endif
//#if !TARGET_OS_IPHONE
/* @abstract A Boolean value indicating whether magnify gestures will
change the web view's magnification.
@discussion It is possible to set the magnification property even if
allowsMagnification is set to NO.
The default value is NO.
*/
//@property (nonatomic) BOOL allowsMagnification;
/* @abstract The factor by which the page content is currently scaled.
@discussion The default value is 1.0.
*/
//@property (nonatomic) CGFloat magnification;
/* @abstract Scales the page content by a specified factor and centers the
result on a specified point.
* @param magnification The factor by which to scale the content.
* @param point The point (in view space) on which to center magnification.
*/
//- (void)setMagnification:(CGFloat)magnification centeredAtPoint:(CGPoint)point;
//==================MKWebview 的代理方法=================
//每當載入一個請求之前會呼叫該方法,通過該方法決定是否允許或取消請求的傳送
- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler{
// // 獲得協議頭(可以自定義協議頭,根據協議頭判斷是否要執行跳轉)
// NSString *scheme = navigationAction.request.URL.scheme;
// if ([scheme isEqualToString:@"zhixing"]) {
// // decisionHandler 對請求處理回撥
// //WKNavigationActionPolicyCancel:取消請求
// //WKNavigationActionPolicyAllow:允許請求
// decisionHandler(WKNavigationActionPolicyCancel);
// return;
// }
NSLog(@"請求前會先進入這個方法 webView:decidePolicyForNavigationActiondecisionHandler: %@ \n\n ",navigationAction.request);
decisionHandler(WKNavigationActionPolicyAllow);
}
//開始傳送請求時呼叫
- (void)webView:(WKWebView *)webView didStartProvisionalNavigation:(null_unspecifiedWKNavigation *)navigation
{
[UIApplicationsharedApplication].networkActivityIndicatorVisible =YES;
NSLog(@"webView:didStartProvisionalNavigation: 開始請求 \n\n");
}
//返回響應前呼叫。並且已經能接收到響應,在收到響應後,決定是否跳轉
//每當接收到伺服器返回的資料時呼叫,通過該方法可以決定是否允許或取消導航
- (void)webView:(WKWebView *)webView decidePolicyForNavigationResponse:(WKNavigationResponse *)navigationResponse decisionHandler:(void (^)(WKNavigationResponsePolicy))decisionHandler{
NSLog(@"返回響應前先會呼叫這個方法 並且已經能接收到響應webView:decidePolicyForNavigationResponse:decisionHandler: Response?%@ \n\n",navigationResponse.response);
//decisionHandler 對響應的處理
//WKNavigationActionPolicyCancel:取消響應
//WKNavigationActionPolicyAllow:允許響應
decisionHandler(WKNavigationResponsePolicyAllow);
}
//響應的內容到達主頁面的時候響應,剛準備開始渲染頁面應用,頁面開始返回
- (void)webView:(WKWebView *)webView didCommitNavigation:(WKNavigation *)navigation {
NSLog(@"webView:didCommitNavigation: 響應的內容到達主頁面的時候響應,剛準備開始渲染頁面應用 \n\n");
}
//響應渲染完成後呼叫該方法,頁面載入完成
- (void)webView:(WKWebView *)webView didFinishNavigation:(null_unspecifiedWKNavigation *)navigation
{
[UIApplicationsharedApplication].networkActivityIndicatorVisible =NO;
NSLog(@"webView:didFinishNavigation: 響應渲染完成後呼叫該方法 webView : %@ -- navigation : %@ \n\n",webView,navigation);
[webView evaluateJavaScript:[NSStringstringWithFormat:@"myFunction();"]completionHandler:^(id_Nullable response,NSError *_Nullable error) {
NSLog(@"-response-%@-",response);//response就是請求的結果;
}];//從載入過來的網頁html檔案中的JS
}
//當Web檢視的網頁內容被終止時呼叫。
- (void)webViewWebContentProcessDidTerminate:(WKWebView *)webView{
NSLog(@"webViewWebContentProcessDidTerminate: 當Web檢視的網頁內容被終止時呼叫。");
}
// 啟動時載入資料發生錯誤就會呼叫這個方法
- (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 didReceiveServerRedirectForProvisionalNavigation:(WKNavigation *)navigation{
NSLog(@"webView:didReceiveServerRedirectForProvisionalNavigation:重定向的時候就會呼叫 \n\n");
}
/**
* 當收到伺服器返回的受保護空間(證書)時呼叫
* @param challenge 安全質詢-->包含受保護空間和證書
* @param completionHandler 完成回撥-->告訴伺服器如何處置證書
*/
- (void)webView:(WKWebView*)webView didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge*)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition,NSURLCredential *__nullablecredential))completionHandler {
// 建立憑據物件
NSURLCredential *credential = [NSURLCredentialcredentialForTrust:challenge.protectionSpace.serverTrust];
// 告訴伺服器信任證書
completionHandler(NSURLSessionAuthChallengeUseCredential,credential);
}
//WKScriptMessageHandler
//這個協議中包含一個必須實現的方法,這個方法是提高App與web端互動的關鍵,它可以直接將接收到的JS指令碼轉化為OC或Swift物件。(當然,在UIWebView也可以通過“曲線救國”的方式與web進行互動,著名的Cordova框架就是這種機制)
//// 從web介面中接收到一個指令碼時呼叫
- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message{//方法中主要是收到JS的回執指令碼就會執行一次
NSLog(@"可以和js互動");
// //可以在這個方法裡面進行app和js的互動
}
//
/*
WKUIDelegate
以下三個代理方法全都是與介面彈出提示框相關的,針對web介面的三種提示框(警告框,提示框,輸入框)分別對應三種代理方法
*/
//為了響應html的alert和confirm事件,需要新增WKWebView的如下代理方法,
//提示框,如果JS中有提示框alert(),則必須實現下面的代理方法,否則不會彈出提示框
- (void)webView:(WKWebView *)webView runJavaScriptAlertPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(void))completionHandler {
NSLog(@"%s",__FUNCTION__);
UIAlertController *alert = [UIAlertControlleralertControllerWithTitle:@"alert"message:@"JS呼叫alert"preferredStyle:UIAlertControllerStyleAlert];
[alert addAction:[UIAlertActionactionWithTitle:@"確定"style:UIAlertActionStyleDefaulthandler:^(UIAlertAction *_Nonnull action) {
completionHandler();//這個必須執行,否則會崩潰
}]];
[selfpresentViewController:alertanimated:YEScompletion:NULL];
NSLog(@"%@", message);
}
//選擇框
- (void)webView:(WKWebView *)webView runJavaScriptConfirmPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(BOOL result))completionHandler {
NSLog(@"%s",__FUNCTION__);
UIAlertController *alert = [UIAlertControlleralertControllerWithTitle:@"confirm"message:@"JS呼叫confirm"preferredStyle:UIAlertControllerStyleAlert];
[alert addAction:[UIAlertActionactionWithTitle:@"確定"style:UIAlertActionStyleDefaulthandler:^(UIAlertAction *_Nonnull action) {
completionHandler(YES);//這個必須執行,否則會崩潰
}]];
[alert addAction:[UIAlertActionactionWithTitle:@"取消"style:UIAlertActionStyleCancelhandler:^(UIAlertAction *_Nonnull action) {
completionHandler(NO);//這個必須執行,否則會崩潰
}]];
[selfpresentViewController:alertanimated:YEScompletion:NULL];
NSLog(@"%@", message);
}
//輸入框
- (void)webView:(WKWebView *)webView runJavaScriptTextInputPanelWithPrompt:(NSString *)prompt defaultText:(nullableNSString *)defaultText initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(NSString *__nullable result))completionHandler {
NSLog(@"%s",__FUNCTION__);
NSLog(@"%@", prompt);
UIAlertController *alert = [UIAlertControlleralertControllerWithTitle:@"textinput"message:@"JS呼叫輸入框"preferredStyle:UIAlertControllerStyleAlert];
[alert addTextFieldWithConfigurationHandler:^(UITextField *_Nonnull textField) {
textField.textColor = [UIColorredColor];
}];
[alert addAction:[UIAlertActionactionWithTitle:@"確定"style:UIAlertActionStyleDefaulthandler:^(UIAlertAction *_Nonnull action) {
completionHandler([[alert.textFieldslastObject]text]);//這個必須執行,否則會崩潰
}]];
[selfpresentViewController:alertanimated:YEScompletion:NULL];
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
@end
======================截獲js中的URL,然後呼叫OC方法實現
使用WKWebView的時候,如果想要實現JS呼叫OC方法,除了攔截URL之外,還有一種簡單的方式。那就是利用WKWebView的新特性MessageHandler來實現JS呼叫原生方法。
MessageHandler 是什麼?
WKWebView 初始化時,有一個引數叫configuration,它是WKWebViewConfiguration型別的引數,而WKWebViewConfiguration有一個屬性叫userContentController,它又是WKUserContentController型別的引數。WKUserContentController物件有一個方法- addScriptMessageHandler:name:,我把這個功能簡稱為MessageHandler。
- addScriptMessageHandler:name:有兩個引數,第一個引數是userContentController的代理物件,第二個引數是JS裡傳送postMessage的物件。
所以要使用MessageHandler功能,就必須要實現WKScriptMessageHandler協議。
我們在該API的描述裡可以看到在JS中的使用方法:
window.webkit.messageHandlers.<name>.postMessage(<messageBody>)
//其中<name>,就是上面方法裡的第二個引數`name`。
//例如我們呼叫API的時候第二個引數填@"Share",那麼在JS裡就是:
//window.webkit.messageHandlers.Share.postMessage(<messageBody>)
//<messageBody>是一個鍵值對,鍵是body,值可以有多種型別的引數。
// 在`WKScriptMessageHandler`協議中,我們可以看到mssage是`WKScriptMessage`型別,有一個屬性叫body。
// 而註釋裡寫明瞭body的型別:
Allowed types are NSNumber, NSString, NSDate, NSArray, NSDictionary, and NSNull.
怎麼使用MessageHandler?
1.建立WKWebViewConfiguration物件,配置各個API對應的MessageHandler。
WKUserContentController物件可以新增多個scriptMessageHandler。
看了示例程式碼,會很容易理解。示例程式碼如下:
WKWebViewConfiguration *configuration = [[WKWebViewConfiguration alloc] init];
configuration.userContentController = [WKUserContentController new];
[configuration.userContentController addScriptMessageHandler:self name:@"ScanAction"];
[configuration.userContentController addScriptMessageHandler:self name:@"Location"];
[configuration.userContentController addScriptMessageHandler:self name:@"Share"];
[configuration.userContentController addScriptMessageHandler:self name:@"Color"];
[configuration.userContentController addScriptMessageHandler:self name:@"Pay"];
[configuration.userContentController addScriptMessageHandler:self name:@"Shake"];
[configuration.userContentController addScriptMessageHandler:self name:@"GoBack"];
[configuration.userContentController addScriptMessageHandler:self name:@"PlaySound"];
WKPreferences *preferences = [WKPreferencesnew];
preferences.javaScriptCanOpenWindowsAutomatically = YES;
preferences.minimumFontSize = 40.0;
configuration.preferences = preferences;
2.建立WKWebView。
這裡沒什麼好說的,直接看示例程式碼吧:
self.webView = [[WKWebView alloc] initWithFrame:self.view.frame configuration:configuration];
NSString *urlStr = [[NSBundlemainBundle]pathForResource:@"index.html"ofType:nil];
NSURL *fileURL = [NSURLfileURLWithPath:urlStr];
[self.webView loadFileURL:fileURL allowingReadAccessToURL:fileURL];
self.webView.navigationDelegate =self;
self.webView.UIDelegate = self;
[self.view addSubview:self.webView];
3.實現協議方法。
我這裡實現了兩個協議<WKUIDelegate,WKScriptMessageHandler>,WKUIDelegate是因為我在JS中彈出了alert。WKScriptMessageHandler是因為我們要處理JS呼叫OC方法的請求。
先看實現協議方法的示例程式碼:
#pragma mark - WKScriptMessageHandler
- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message
{
// message.body -- Allowed types are NSNumber, NSString, NSDate, NSArray,NSDictionary, and NSNull.
if ([message.name isEqualToString:@"ScanAction"]) {
NSLog(@"掃一掃");
} elseif ([message.name isEqualToString:@"Location"]) {
[self getLocation];
} elseif ([message.name isEqualToString:@"Share"]) {
[self shareWithParams:message.body];
} elseif ([message.name isEqualToString:@"Color"]) {
[self changeBGColor:message.body];
} elseif ([message.name isEqualToString:@"Pay"]) {
[self payWithParams:message.body];
} elseif ([message.name isEqualToString:@"Shake"]) {
[self shakeAction];
} elseif ([message.name isEqualToString:@"GoBack"]) {
[self goBack];
} elseif ([message.name isEqualToString:@"PlaySound"]) {
[self playSound:message.body];
}
}
WKScriptMessage有兩個關鍵屬性name和 body。
因為我們給每一個OC方法取了一個name,那麼我們就可以根據name來區分執行不同的方法。body中存著JS要給OC傳的引數。
關於引數body的解析,我就舉一個body中放字典的例子,其他的稍後可以看demo。
解析JS 呼叫OC 實現分享的引數:
- (void)shareWithParams:(NSDictionary *)tempDic
{
if (![tempDic isKindOfClass:[NSDictionary class]]) {
return;
}
NSString *title = [tempDic objectForKey:@"title"];
NSString *content = [tempDic objectForKey:@"content"];
NSString *url = [tempDic objectForKey:@"url"];
// 在這裡執行分享的操作
// 將分享結果返回給js
NSString *jsStr = [NSString stringWithFormat:@"shareResult('%@','%@','%@')",title,content,url];
[self.webView evaluateJavaScript:jsStr completionHandler:^(id_Nullable result, NSError *_Nullable error) {
NSLog(@"%@----%@",result, error);
}];
}
message.boby 就是JS裡傳過來的引數。我們不同的方法先做一下容錯性判斷。然後正常取值就可以了。
4.處理HTML中JS呼叫。
HMTL的原始碼跟之前的HTML內容差不多,只有JS的呼叫部分改變了。
// 傳null
function scanClick() {
window.webkit.messageHandlers.ScanAction.postMessage(null);
}
// 傳字典
function shareClick() {
window.webkit.messageHandlers.Share.postMessage({title:'測試分享的標題',content:'測試分享的內容',url:'http://www.baidu.com'});
}
// 傳字串
function playSound() {
window.webkit.messageHandlers.PlaySound.postMessage('shake_sound_male.wav');
}
// 傳陣列
function colorClick() {
window.webkit.messageHandlers.Color.postMessage([67,205,128,0.5]);
}
==========讀取cookie
開發過程中,使用UIWebView載入電腦版網頁,登入出錯,
請求頭有誤,webView請求頭為:
User-Agent: Mozilla/5.0 (iPad; CPU OS 7_0_2 like Mac OS X) AppleWebKit/537.51.1 (KHTML, like Gecko) Mobile/11A501
safari瀏覽器請求頭為:
User-Agent: Mozilla/5.0 (iPad; CPU OS 7_0_2 like Mac OS X) AppleWebKit/537.51.1 (KHTML, like Gecko) Version/7.0Mobile/11A501 Safari/9537.53
解決辦法:
在webView載入之前執行下面程式碼
NSString *userAgent = [_webView stringByEvaluatingJavaScriptFromString:@"navigator.userAgent"];
userAgent = [userAgent stringByAppendingString:@" Version/7.0 Safari/9537.53"];
[[NSUserDefaults standardUserDefaults] registerDefaults:@{@"UserAgent": userAgent}];
讀取cookie方法:
NSHTTPCookieStorage *sharedHTTPCookieStorage = [NSHTTPCookieStorage sharedHTTPCookieStorage];
NSArray *cookies = [sharedHTTPCookieStorage cookiesForURL:request.URL];
NSEnumerator *enumerator = [cookies objectEnumerator];
NSHTTPCookie *cookie;
while (cookie = [enumerator nextObject]) {
NSLog(@"COOKIE{name: %@, value: %@}", [cookie name], [cookie value]);
}
=========WKNavigationDelegate====
@protocol WKNavigationDelegate <NSObject>
@optional
//請求之前,決定是否要跳轉:使用者點選網頁上的連結,需要開啟新頁面時,將先呼叫這個方法。
- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler;
//接收到相應資料後,決定是否跳轉
- (void)webView:(WKWebView *)webView decidePolicyForNavigationResponse:(WKNavigationResponse *)navigationResponse decisionHandler:(void (^)(WKNavigationResponsePolicy))decisionHandler;
- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler{
// WKNavigationActionPolicyCancel,
// WKNavigationActionPolicyAllow,
// navigationAction.sourceFrame;
// navigationAction.targetFrame;
// navigationAction.request;
if (navigationAction.navigationType == WKNavigationTypeLinkActivated){
decisionHandler(WKNavigationActionPolicyCancel);
}else{
decisionHandler(WKNavigationActionPolicyAllow);
}
}
//接收到相應後,決定是否跳轉
- (void)webView:(WKWebView *)webView decidePolicyForNavigationResponse:(WKNavigationResponse *)navigationResponse decisionHandler:(void (^)(WKNavigationResponsePolicy))decisionHandler{
if (!navigationResponse.isForMainFrame){
decisionHandler(WKNavigationResponsePolicyCancel);
}else{
decisionHandler(WKNavigationResponsePolicyAllow);
}
NSLog(@"2");
}
}
//頁面開始載入時呼叫
- (void)webView:(WKWebView *)webView didStartProvisionalNavigation:(null_unspecifiedWKNavigation *)navigation;
// 主機地址被重定向時呼叫
- (void)webView:(WKWebView *)webView didReceiveServerRedirectForProvisionalNavigation:(null_unspecifiedWKNavigation *)navigation;
//