一 先來看下UIWebView 基本用法 以及與JS交互過程
- 舉例:簡單的使用
//1.創(chuàng)建webview,并設(shè)置大小
_webView = [[UIWebView alloc] initWithFrame:self.view.bounds];
//2.創(chuàng)建請求
NSURL *url = [NSURL URLWithString:self.urlString];
NSURLRequest *urlRequest = [[NSURLRequest alloc] initWithURL:url cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:30];
//3.加載網(wǎng)頁 將webView添加到界面
[_webView loadRequest:urlRequest];
[self.view addSubview:_webView];
- 一些實用函數(shù)
- (void)loadRequest:(NSURLRequest *)request;
- (void)loadHTMLString:(NSString *)string baseURL:(nullable NSURL *)baseURL;
- (void)loadData:(NSData *)data MIMEType:(NSString *)MIMEType textEncodingName:(NSString *)textEncodingName baseURL:(NSURL *)baseURL;
- 網(wǎng)頁導(dǎo)航刷新有關(guān)函數(shù)
// 刷新
- (void)reload;
//停止加載
- (void)stopLoading;
//后退函數(shù)
- (void)goBack;
//前進函數(shù)
- (void)goForward;
//是否可以后退
@property (nonatomic,readonly, getter=canGoBack)BOOL canGoBack;
//是否可以向前
@property (nonatomic,readonly, getter=canGoForward)BOOL canGoForward;
//是否正在加載
@property (nonatomic,readonly, getter=isLoading)BOOL loading;
- 代理協(xié)議使用:UIWebViewDelegate
#pragma mark - UIWebViewDelegate
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType{
// 攔截scheme為aaa時,是javascript要調(diào)用native代碼
if ([[request.URL scheme] isEqualToString:@"aaa"]) {
// Host
NSString *host = [[request.URL host] trimBothEnd];
// 取得參數(shù)
NSDictionary *parameters = [request.URL.absoluteString urlQueryToDictionary];
[self handleLinkTapWithUrl:request.URL.absoluteString withHost:host withParams:parameters];
}
return YES;
}
- (void)handleLinkTapWithUrl:(NSString *)url withHost:(NSString *)host withParams:(NSDictionary *)param{
if(host && [[self getHostDictionary] objectForKey:host]){
[self pushScheme:url];
}
}
- (void)webViewDidFinishLoad:(UIWebView*)webView {
// 在最后執(zhí)行一次,以保證標題正確設(shè)置,loading被正確隱藏
self.title = [_webView stringByEvaluatingJavaScriptFromString:@"document.title"];
[UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
[self hideLoading];
[self registerJSContextFunction];
}
- (void)webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error
{
[self hideLoading];
if ([error code] != NSURLErrorCancelled){
[self showWarning:@"加載失敗,請重試"];
}
}
- 與js交互
1 在viewDidLoad 中 初始化 給JS注冊方法
// 初始化以做后來交互使用
_jsContext = [_webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];
//在js自己刷新頁面,不通過本地刷新的時候,需要重新綁定clint,寫成成員變量,避免多次實例化。
_client = [ClientH5Object new];
_client.delegate = self;
// 給JS注冊方法
[self registerJSContextFunction];
2 注冊方法
#pragma mark - JSContext
- (void)registerJSContextFunction {
__weak __typeof(self)weakSelf = self;
_jsContext[@"jsObjLoadDomFinished"] = ^() {
dispatch_async(dispatch_get_main_queue(), ^{
[weakSelf loadDomFinished];
});
};
_jsContext[@"jsObjPageDataLoaded"] = ^() {
dispatch_async(dispatch_get_main_queue(), ^{
[weakSelf globalObjectLoaded];
});
};
// 使用這種方式可以避免循環(huán)引用。
// 注意:不能把self賦給_jsContext,即使是weakSelf也不行
_jsContext[@"client"] = _Client;
for (NSString *key in _jsCallers) {
_jsContext[@"client"][key] = _jsCallers[key];
}
}
- (void)addJSCall:(NSString *)jsFuncName implemention:(id)block {
_jsCallers[jsFuncName] = block;
}
/**
* @brief js調(diào)用該方法以hideLoading
*/
- (void)loadDomFinished{
[self hideLoading];
// 因為此時_jsGlobalObject還不能從H5中獲取,所以只先設(shè)置一個title,與數(shù)據(jù)相關(guān)的分享功能先不展示
self.title = [_webView stringByEvaluatingJavaScriptFromString:@"document.title"];
}
- (void)globalObjectLoaded{
// 從H5中取得全局變量
@try {
_jsGlobalObject = [[self.jsContext evaluateScript:@"window.GLOBAL.pagedata"] toDictionary];
} @catch (NSException *exception) {
}
if (_jsGlobalObject == nil) {
return;
}
//分享數(shù)據(jù)源
_shareObject = [[WMShareObject alloc]initWithDictionary:_jsGlobalObject[@"share_data"] error:nil];
//同時不為空的時候才顯示微信快照和朋友圈快照
_isShowScreenshot = [_shareObject.wechatScreenshot.content isNonEmpty] && [_shareObject.wechatScreenshot.content isNonEmpty];
if (![_jsGlobalObject[@"hide_share"] boolValue] && (_shareObject != nil)) {
[self setNavigateTitle:[_webView stringByEvaluatingJavaScriptFromString:@"document.title"] rightButtonOption:OCBarButtonImageTypeShare];
}
}
二 WKWebView使用說明
1 簡單使用
與UIWebview一樣,僅需三步:記住導(dǎo)入
// 1.創(chuàng)建webview,并設(shè)置大小
WKWebView *webView = [[UIWebView alloc] initWithFrame:self.view.bounds];
// 2.創(chuàng)建請求
NSMutableURLRequest *request =[NSMutableURLRequest requestWithURL:[NSURL URLWithString:@"http://www.cnblogs.com/mddblog/"]];
// 3.加載網(wǎng)頁
[webView loadRequest:request];
//最后將webView添加到界面
[self.view addSubview:webView];
2 一些實用函數(shù)
//加載網(wǎng)頁函數(shù)
//相比UIWebview,WKWebView也支持各種文件格式,并新增了loadFileURL函數(shù),顧名思義加載本地文件。
///模擬器調(diào)試加載mac本地文件
- (void)loadLocalFile {
// 1.創(chuàng)建webview,并設(shè)置大小,"20"為狀態(tài)欄高度
WKWebView *webView = [[WKWebView alloc] initWithFrame:CGRectMake(0,20, self.view.frame.size.width,self.view.frame.size.height -20)];
// 2.創(chuàng)建url userName:電腦用戶名
NSURL *url = [NSURL fileURLWithPath:@"/Users/userName/Desktop/bigIcon.png"];
// 3.加載文件
[webView loadFileURL:url allowingReadAccessToURL:url];
//最后將webView添加到界面
[self.view addSubview:webView];
}
/// 其它三個加載函數(shù)
- (WKNavigation *)loadRequest:(NSURLRequest *)request;
- (WKNavigation *)loadHTMLString:(NSString *)string baseURL:(nullable NSURL *)baseURL;
- (WKNavigation *)loadData:(NSData *)data MIMEType:(NSString *)MIMEType characterEncodingName:(NSString *)characterEncodingName baseURL:(NSURL *)baseURL;
網(wǎng)頁導(dǎo)航刷新相關(guān)函數(shù)
和UIWebview幾乎一樣,不同的是有返回值,WKNavigation(已更新),另外增加了函數(shù)reloadFromOrigin和goToBackForwardListItem。
reloadFromOrigin會比較網(wǎng)絡(luò)數(shù)據(jù)是否有變化,沒有變化則使用緩存,否則從新請求。
goToBackForwardListItem:比向前向后更強大,可以跳轉(zhuǎn)到某個指定歷史頁面
@property (nonatomic,readonly) BOOL canGoBack;
@property (nonatomic,readonly) BOOL canGoForward;
- (WKNavigation *)goBack;
- (WKNavigation *)goForward;
- (WKNavigation *)reload;
- (WKNavigation *)reloadFromOrigin; //增加的函數(shù)
- (WKNavigation *)goToBackForwardListItem:(WKBackForwardListItem *)item;// 增加的函數(shù)
- (void)stopLoading;
一些常用屬性
allowsBackForwardNavigationGestures:BOOL類型,是否允許左右劃手勢導(dǎo)航,默認不允許
estimatedProgress:加載進度,取值范圍0~1
title:頁面title
.scrollView.scrollEnabled:是否允許上下滾動,默認允許
backForwardList:WKBackForwardList類型,訪問歷史列表,可以通過前進后退按鈕訪問,或者通過goToBackForwardListItem函數(shù)跳到指定頁面
3 代理協(xié)議使用
一共有三個代理協(xié)議:
WKNavigationDelegate:最常用,和UIWebViewDelegate功能類似,追蹤加載過程,有是否允許加載、開始加載、加載完成、加載失敗。下面會對函數(shù)做簡單的說明,并用數(shù)字標出調(diào)用的先后次序:1-2-3-4-5
三個是否允許跳轉(zhuǎn)加載的函數(shù):
/// 接收到服務(wù)器跳轉(zhuǎn)請求之后調(diào)用 (服務(wù)器端redirect),不一定調(diào)用
- (void)webView:(WKWebView *)webView didReceiveServerRedirectForProvisionalNavigation:(WKNavigation *)navigation;
/// 3 在收到服務(wù)器的響應(yīng)頭,根據(jù)response相關(guān)信息,決定是否跳轉(zhuǎn)。decisionHandler必須調(diào)用,來決定是否跳轉(zhuǎn),參數(shù)WKNavigationActionPolicyCancel取消跳轉(zhuǎn),WKNavigationActionPolicyAllow允許跳轉(zhuǎn)
- (void)webView:(WKWebView *)webView decidePolicyForNavigationResponse:(WKNavigationResponse *)navigationResponse decisionHandler:(void (^)(WKNavigationResponsePolicy))decisionHandler;
/// 1 在發(fā)送請求之前,決定是否跳轉(zhuǎn)
- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler;
追蹤加載過程函數(shù):
/// 2 頁面開始加載
- (void)webView:(WKWebView *)webView didStartProvisionalNavigation:(WKNavigation *)navigation;
/// 4 開始獲取到網(wǎng)頁內(nèi)容時返回
- (void)webView:(WKWebView *)webView didCommitNavigation:(WKNavigation *)navigation;
/// 5 頁面加載完成之后調(diào)用
- (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation;
/// 頁面加載失敗時調(diào)用
- (void)webView:(WKWebView *)webView didFailProvisionalNavigation:(WKNavigation *)navigation;
WKScriptMessageHandler:必須實現(xiàn)的函數(shù),是APP與js交互,提供從網(wǎng)頁中收消息的回調(diào)方法
/// message: 收到的腳本信息.
- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message;
WKUIDelegate:UI界面相關(guān),原生控件支持,三種提示框:輸入、確認、警告。首先將web提示框攔截然后再做處理。
/// 創(chuàng)建一個新的WebView
- (WKWebView *)webView:(WKWebView *)webView createWebViewWithConfiguration:(WKWebViewConfiguration *)configuration forNavigationAction:(WKNavigationAction *)navigationAction windowFeatures:(WKWindowFeatures *)windowFeatures;
/// 輸入框
- (void)webView:(WKWebView *)webView runJavaScriptTextInputPanelWithPrompt:(NSString *)prompt defaultText:(nullable NSString *)defaultText initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(NSString *__nullable result))completionHandler {
NSLog(@"%s",__FUNCTION__);
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];
}
/// 確認框
- (void)webView:(WKWebView *)webView runJavaScriptConfirmPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(BOOL result))completionHandler {
NSLog(@"%s", __FUNCTION__);
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 runJavaScriptAlertPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(void))completionHandler {
NSLog(@"%s", __FUNCTION__);
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);
}
4 與js交互
/// 5 頁面加載完成之后調(diào)用
- (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation;
{
// 直接調(diào)用js
[self.webViewevaluateJavaScript:@"document.documentElement.style.webkitUserSelect='none';"completionHandler:nil];
// 調(diào)用js參數(shù)
[self.webViewevaluateJavaScript:@"document.documentElement.style.webkitTouchCallout='none';"completionHandler:nil];
[self.showViewremoveFromSuperview];
NSString *jsToGetHTMLSource =@"document.getElementsByTagName('html')[0].innerHTML";
[self.activityViewstopAnimating];
// 調(diào)用js獲取返回值
[self.webViewevaluateJavaScript:jsToGetHTMLSourcecompletionHandler:^(id_Nullable HTMLSource, NSError * _Nullable error) {
NSRange range = [HTMLSourcerangeOfString:@"Bad Gateway"];//判斷字符串是否包含
bool urlIsTrue = (range.location ==NSNotFound);
if (urlIsTrue ==true ) {
range = [HTMLSource rangeOfString:@"Network is unreachable"];
urlIsTrue = (range.location ==NSNotFound);
}
if (urlIsTrue ==true ) {
range = [HTMLSource rangeOfString:@"頁面不存在"];
urlIsTrue = (range.location ==NSNotFound);
}
if (urlIsTrue ==true) {
range = [HTMLSource rangeOfString:@"網(wǎng)頁無法訪問"];
urlIsTrue = (range.location ==NSNotFound);
}
if (urlIsTrue ==true) {
range = [HTMLSource rangeOfString:@"Not Found"];
urlIsTrue = (range.location ==NSNotFound);
}
//網(wǎng)頁可訪問隱藏我們自己的返回按鈕
if (urlIsTrue==true)
{
self.topView.hidden =YES;
}
else
{
[webView stopLoading];
[selfaddShowView];
}
}];
[webView stopLoading];
}
如果想在加載的頁面中繼續(xù)操作
UIWebView打開一個頁面之后,點擊里面的內(nèi)容就直接能跳轉(zhuǎn)。而WKWebView一開始點里面的東西沒反應(yīng),只要加了這個方法就好了。
//在發(fā)送請求之前,決定是否跳轉(zhuǎn)
- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler {
if (navigationAction.targetFrame ==nil) {
[webView loadRequest:navigationAction.request];
}
// 沒有這一句頁面就不會顯示
decisionHandler(WKNavigationActionPolicyAllow);
}