iOS中WKWebView互動使用總結

蒼翼-墨羽發表於2019-03-26

前言

現在多數專案中會有使用webView的情況,過去往往使用UIWebView解決問題,但是由於其各種不便,給開發者帶來了很多麻煩。現在專案中有所使用,所以寫一篇總結,方便以後用到了查詢和使用也為了方便其他同行。

正文

基礎使用

構建和配置

WKWebView是繼承自UIView的,因此構建方式還是很老套的,通常

- (instancetype)initWithFrame:(CGRect)frame configuration:(WKWebViewConfiguration *)configuration
複製程式碼

這個方法就夠用了,第一個引數不多說,按照通常的使用就可以,第二個引數是對webView的配置物件,裡面有很多屬性可以使用,但是這裡我只進行最簡單使用的說明。

WKWebViewConfiguration *configuration = [[WKWebViewConfiguration alloc]init];
configuration.preferences.minimumFontSize = 10;//設定最小字型
configuration.preferences.javaScriptEnabled = YES;//是否可以使用JavaScript
configuration.preferences.javaScriptCanOpenWindowsAutomatically = NO;//JS是否可以自動開啟頁面
複製程式碼

以上配置就足夠正常使用,其他的如果專案還有需要,請自己根據需要新增。 然後是對WKWebView的基本設定,

self.webView.scrollView.bounces = NO;
self.webView.navigationDelegate = self;
複製程式碼

設定了取消彈性和代理,需要說明的是由於我們使用的是需要和JS進行互動的webView,所以需要在ViewController中宣告兩個代理WKNavigationDelegate,WKScriptMessageHandler,前者是用來處理webView載入檢視的各種情況的,後者是主要用來處理互動事件的。 最後通過addSubView新增檢視到父檢視上面就可以了,這個時候應該是沒有載入任何頁面的webView。而主要功能載入web網頁,需要使用以下方法:

@property (nonatomic, strong) NSURLRequest *resetUrlRequest;

self.resetUrlRequest = [NSURLRequest requestWithURL:[NSURL URLWithString:@"你所需要載入的網址"]];
複製程式碼

當然考慮專案中可能會對網址進行拼接,如拼接token,因此強烈建議,將後面的URL構建部分挪到頂上分出來寫。

基本代理相關

常用的有:

//開始載入
-(void)webView:(WKWebView *)webView didStartProvisionalNavigation:(WKNavigation *)navigation
//載入完成
-(void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation
//頁面跳轉失敗
- (void)webView:(WKWebView *)webView didFailNavigation:(WKNavigation *)navigation withError:(NSError *)error
//載入報錯,通常來說如果頁面出現不存在等問題,會走這裡,如果需要對空白頁面進行處理,在這裡處理
- (void)webView:(WKWebView *)webView didFailProvisionalNavigation:(WKNavigation *)navigation withError:(NSError *)error
//請求之前,決定是否要跳轉:使用者點選網頁上的連結,需要開啟新頁面時,將先呼叫這個方法。
- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler {
    
    //允許頁面跳轉
//    NSLog(@"%@=========tw============",navigationAction.request.URL);
    //如果是跳轉一個新頁面
    if (navigationAction.targetFrame == nil) {
        [webView loadRequest:navigationAction.request];
    }
    
    decisionHandler(WKNavigationActionPolicyAllow);
}
//接收到相應資料後,決定是否跳轉
- (void)webView:(WKWebView *)webView decidePolicyForNavigationResponse:(WKNavigationResponse *)navigationResponse decisionHandler:(void (^)(WKNavigationResponsePolicy))decisionHandler{
    
    if (((NSHTTPURLResponse *)navigationResponse.response).statusCode == 200) {
        decisionHandler (WKNavigationResponsePolicyAllow);
    }else {
        decisionHandler(WKNavigationResponsePolicyCancel);
    }
}
複製程式碼

還有這些可能需要的

// 主機地址被重定向時呼叫
- (void)webView:(WKWebView *)webView didReceiveServerRedirectForProvisionalNavigation:(null_unspecified WKNavigation *)navigation;
// 當內容開始返回時呼叫
- (void)webView:(WKWebView *)webView didCommitNavigation:(null_unspecified WKNavigation *)navigation;
// 如果需要證照驗證,與使用AFN進行HTTPS證照驗證是一樣的
- (void)webView:(WKWebView *)webView didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *__nullable credential))completionHandler;
//9.0才能使用,web內容處理中斷時會觸發
- (void)webViewWebContentProcessDidTerminate:(WKWebView *)webView NS_AVAILABLE(10_11, 9_0);
複製程式碼

到此,基礎的使用結束。

限制使用者選擇以及長按操作

有時候,我們會遇到一個比較頭疼的問題,我們不想讓使用者長按選擇或者有彈窗,那麼這時我們需要新增兩行程式碼來禁止這一系列行為。

//WKWebview 禁止長按(超連結、圖片、文字...)彈出效果
[self.webView evaluateJavaScript:@"document.documentElement.style.webkitTouchCallout='none';" completionHandler:nil];
[self.webView evaluateJavaScript:@"document.documentElement.style.webkitUserSelect='none';" completionHandler:nil];
複製程式碼

值得注意的是,這裡其實是通過呼叫webView直接使用JS程式碼實現的操作,如果有需要還可以實現別的功能,而且這個方法最後有一個執行完畢之後的block,可以實現很多操作。

新增進度條

構建

@property (nonatomic, strong)UIProgressView *progressView;

//新增進度條
self.progressView = [[UIProgressView alloc]initWithFrame:CGRectMake(0, 2, self.view.frame.size.width, self.view.frame.size.height)];
self.progressView.tintColor = UIColorWithRGB(254, 79, 109);
[self.webView addSubview:self.progressView];
[self.webView addObserver:self forKeyPath:@"estimatedProgress" options:NSKeyValueObservingOptionOld | NSKeyValueObservingOptionNew context:nil];
複製程式碼

監聽

#pragma mark - 進度條
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context{
    
    if ([keyPath isEqual:@"estimatedProgress"] && object == self.webView) {
        [self.progressView setAlpha:1.0f];
        [self.progressView setProgress:self.webView.estimatedProgress animated:YES];
        if (self.webView.estimatedProgress  >= 1.0f) {
            [UIView animateWithDuration:0.3 delay:0.3 options:UIViewAnimationOptionCurveEaseOut animations:^{
                [self.progressView setAlpha:0.0f];
            } completion:^(BOOL finished) {
                [self.progressView setProgress:0.0f animated:YES];
                self.progressView.hidden = YES;
            }];
        }
    }else{
        [super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
    }
}
複製程式碼

代理中操作

-(void)webView:(WKWebView *)webView didStartProvisionalNavigation:(WKNavigation *)navigation{
    
    self.progressView.hidden = NO;
    self.progressView.transform = CGAffineTransformMakeScale(1.0, 1.5);
    [self.view bringSubviewToFront:self.progressView]; // 將progress放到最前面
}

-(void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation{
    self.progressView.hidden = YES;
    self.title = self.webView.title;
    
    //WKWebview 禁止長按(超連結、圖片、文字...)彈出效果
    [self.webView evaluateJavaScript:@"document.documentElement.style.webkitTouchCallout='none';" completionHandler:nil]; 
    //    [self.webView evaluateJavaScript:@"document.documentElement.style.webkitUserSelect='none';" completionHandler:nil];
    
}
- (void)webView:(WKWebView *)webView didFailNavigation:(WKNavigation *)navigation withError:(NSError *)error{
    NSLog(@"%@---------------",error);
    self.progressView.hidden = YES;
    
}
- (void)webView:(WKWebView *)webView didFailProvisionalNavigation:(WKNavigation *)navigation withError:(NSError *)error{
    NSLog(@"%@-------------------",error);
    //載入本地的一個空頁面的操作
    //[self sk_loadErrorPage];
}
複製程式碼

返回上級以及popViewController

	if ([[self.webView.backForwardList currentItem].title isEqualToString:@"首頁"]) {
        [self.navigationController popViewControllerAnimated:YES];
    }
    if ([self.webView canGoBack]) {
        //控制訂單列表中的較多介面折回
        if ([[self.webView.backForwardList currentItem].title isEqualToString:@"訂單列表"] &&
            [[self.webView.backForwardList backItem].title isEqualToString:@"訂單列表"]) {
            [self.webView goToBackForwardListItem:[self.webView.backForwardList backList].firstObject];
        }else{
            [self.webView goBack];
        }
        
    }else{
        [self.navigationController popViewControllerAnimated:YES];
    }
複製程式碼

可以對H5頁面的標題進行判斷來決定是否跳轉。

重點:JS互動

WKWebView的互動方法和之前的UIWebView其實本質上沒有什麼太大的差別,都是通過傳送方法名找到對應的方法執行對應的操作。我的具體操作如下:

-(void)viewWillAppear:(BOOL)animated{
    //    self.navigationController.navigationBar.hidden = NO;
    if (self.webView) {
        if (self.webView.configuration.userContentController.userScripts.count>0) {
            //移除所有的監聽
            [self removeAllScriptMsgHandle];
        }
        //對JS呼叫的方法進行監聽,最好集中處理
        [self.webView.configuration.userContentController addScriptMessageHandler:self name:@"mjxLogin"];
    }
}
-(void)viewWillDisappear:(BOOL)animated{
    [self removeAllScriptMsgHandle];
}
- (void)dealloc{
	//移除監聽和代理
    [self.webView removeObserver:self forKeyPath:@"estimatedProgress"];
    [self.webView setNavigationDelegate:nil];
    [self.webView setUIDelegate:nil];
}
-(void)removeAllScriptMsgHandle{
    
    //移除監聽,不移除一定會報錯
    [self.webView.configuration.userContentController removeScriptMessageHandlerForName:@"mjxLogin"];
   
}

複製程式碼

對監聽的處理的代理

//用來接收js呼叫本地方法的攔截器
-(void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message{
//    NSLog(@"%@------mes------",message.body);
    //分享,多引數的情況
    if ([message.name isEqualToString:@"mjxShare"]) {
        NSDictionary *messageDic = message.body;
        [self shareGoodsWithTitle:messageDic[@"title"] withContent:messageDic[@"content"] withUrl:messageDic[@"url"] withImage:messageDic[@"img"]];
    }
    //申請試用,帶一個引數的情況
    if ([message.name isEqualToString:@"mjxApply"]) {
        NSDictionary *messageDic = message.body;
        [self requestApplyGoods:messageDic[@"trade_sn"]];
        
    }
    //彈出錯誤資訊
    if ([message.name isEqualToString:@"errorAlert"]) {
        [SKHUD showErrorWithStatus:message.body];
    }
  
    //儲存二維碼
    if ([message.name isEqualToString:@"codeImg"]) {
        [SVProgressHUD show];
        NSDictionary *messageDic = message.body;
        
        NSURL *url = [NSURL URLWithString:messageDic[@"codeImgUrl"]];
        
        NSData *data = [NSData dataWithContentsOfURL:url];
        UIImage *img;
        img = [UIImage imageWithData:data];
        
        
        UIImageWriteToSavedPhotosAlbum( img, self,@selector(imageSavedToPhotosAlbum:didFinishSavingWithError:contextInfo:) , NULL);
    }
    
}
複製程式碼

到此,相關使用全部結束,希望喜歡活有用的話,能夠點贊或者評論支援,謝謝。

相關文章