WKWebView的基本使用/攔截URL/Alert彈窗/禁止選擇操作/內容字體大小控制


WKWebView是iOS 8推出,輸入WebKit.framework, UIWebView屬於UIKit.framework; WKWebView相對UIWebView優化了很多,特別是內存的消耗.
經測試通過UIWebView和WKWebView分別訪問http:www.baidu.com,App正常啟動后內存平穩在42M,點擊按鈕使用UIWebView加載baidu內存最高峰達到131M,然后平穩在119M;
通過WKWebView記載baidu內存最高峰達到47.2M,然后平穩在46.6M.

 WKWebView特性:

  • 在性能、穩定性、功能方面有很大提升.
  • 允許JavaScript的Nitro庫加載並使用(UIWebView中限制).
  • 支持了更多的HTML5特性.
  • 高達60fps的滾動刷新率以及內置手勢
  • 將UIWebViewDelegate與UIWebView重構成了14類與3個協議

基本使用:

  1. 加載網頁
  2. 加載的狀態回調
  3. 新的WKUIDelegate協議
  4. 動態加載並運行JS代碼
  5. webView 執行JS代碼
  6. JS調用App注冊過的方法

 

加載頁面

- (nullable WKNavigation *)loadRequest:(NSURLRequest *)request;

- (nullable WKNavigation *)loadFileURL:(NSURL *)URL allowingReadAccessToURL:(NSURL *)readAccessURL API_AVAILABLE(macosx(10.11), ios(9.0));

- (nullable WKNavigation *)loadHTMLString:(NSString *)string baseURL:(nullable NSURL *)baseURL;

- (nullable WKNavigation *)loadData:(NSData *)data MIMEType:(NSString *)MIMEType characterEncodingName:(NSString *)characterEncodingName baseURL:(NSURL *)baseURL API_AVAILABLE(macosx(10.11), ios(9.0));

其中有2個方法是iOS 9才有的

注意:load方法中使用的baseURL參數,此參數是html基於的路徑,比如html中使用的資源等路徑,

[self loadHTMLString:string baseURL:[[NSBundle mainBundle] bundleURL]];

網頁中的資源圖片等可以寫相對路徑,相對bundelURL下的資源文件.

 

NSURL *url = [NSURL URLWithString:@"http://www.baidu.com"]; // [NSURL fileURLWithPath:path]
    
NSURLRequest *request = [NSURLRequest requestWithURL:url];
[self.webView loadRequest:request];

 

加載狀態回調

 追蹤加載過程代理方法:

// 頁面開始加載時調用
- (void)webView:(WKWebView *)webView didStartProvisionalNavigation:(null_unspecified WKNavigation *)navigation{
    
}

// 頁面加載失敗時調用
- (void)webView:(WKWebView *)webView didFailProvisionalNavigation:(null_unspecified WKNavigation *)navigation withError:(NSError *)error{
    
}

// 當內容開始返回時調用
- (void)webView:(WKWebView *)webView didCommitNavigation:(null_unspecified WKNavigation *)navigation{
    
}

// 頁面加載完成之后調用
- (void)webView:(WKWebView *)webView didFinishNavigation:(null_unspecified WKNavigation *)navigation{
    
}

// 加載失敗
- (void)webView:(WKWebView *)webView didFailNavigation:(null_unspecified WKNavigation *)navigation withError:(NSError *)error{
    
}

頁面跳轉代理方法:

// 在發送請求之前,決定是否跳轉
- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler{
    decisionHandler(WKNavigationActionPolicyAllow);
}

// 在收到響應后,決定是否跳轉
- (void)webView:(WKWebView *)webView decidePolicyForNavigationResponse:(WKNavigationResponse *)navigationResponse decisionHandler:(void (^)(WKNavigationResponsePolicy))decisionHandler{
    decisionHandler(WKNavigationResponsePolicyAllow);
}

// 接收到服務器跳轉請求之后調用
- (void)webView:(WKWebView *)webView didReceiveServerRedirectForProvisionalNavigation:(null_unspecified WKNavigation *)navigation{
    
}

// 接受認證
- (void)webView:(WKWebView *)webView didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential * _Nullable credential))completionHandler{
    
    if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) {
        
        NSURLCredential *card = [[NSURLCredential alloc]initWithTrust:challenge.protectionSpace.serverTrust];
        
        completionHandler(NSURLSessionAuthChallengeUseCredential,card);
        
    }
}

新的WKUIDelegate協議

這個協議主要用於WKWebView處理web界面的三種提示框(警告框、確認框、輸入框)

// 窗口已經關閉
- (void)webViewDidClose:(WKWebView *)webView API_AVAILABLE(macosx(10.11), ios(9.0)){
    
}


/**
 alert框

 @param webView WKWebView
 @param message 消息文本內容
 @param frame frame主窗口
 @param completionHandler 窗口消失回調
 */
- (void)webView:(WKWebView *)webView runJavaScriptAlertPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(void))completionHandler{
    
}

// 確認框
- (void)webView:(WKWebView *)webView runJavaScriptConfirmPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(BOOL result))completionHandler{
    
}

// 文本輸入框
- (void)webView:(WKWebView *)webView runJavaScriptTextInputPanelWithPrompt:(NSString *)prompt defaultText:(nullable NSString *)defaultText initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(NSString * _Nullable result))completionHandler{
    
}

 

動態加載並運行JS代碼

// 圖片縮放的js代碼
NSString *js = @"var count = document.images.length;for (var i = 0; i < count; i++) {var image = document.images[i];image.style.width=320;};window.alert('找到' + count + '張圖');";
// 根據JS字符串初始化WKUserScript對象
WKUserScript *script = [[WKUserScript alloc] initWithSource:js injectionTime:WKUserScriptInjectionTimeAtDocumentEnd forMainFrameOnly:YES];
// 根據生成的WKUserScript對象,初始化WKWebViewConfiguration
WKWebViewConfiguration *config = [[WKWebViewConfiguration alloc] init];
[config.userContentController addUserScript:script];
_webView = [[WKWebView alloc] initWithFrame:self.view.bounds configuration:config];
[_webView loadHTMLString:@"<head></head><imgea src='http://www.nsu.edu.cn/v/2014v3/img/background/3.jpg' />"baseURL:nil];
[self.view addSubview:_webView];

 

webView 執行JS代碼

//javaScriptString是JS方法名,completionHandler是異步回調block
[self.webView evaluateJavaScript:javaScriptString completionHandler:completionHandler];

 

JS調用App注冊過的方法

WKWebView里面注冊供JS調用的方法,是通過WKUserContentController類下面的方法:

- (void)addScriptMessageHandler:(id <WKScriptMessageHandler>)scriptMessageHandler name:(NSString *)name;
[self.webView.configuration.userContentController addScriptMessageHandler:self name:@"openYou"];

scriptMessageHandler是代理回調; JS調用name方法后,OC會調用scriptMessageHandler指定的對象

實現代理方法

- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message{
    NSLog(@"%s",__func__);
}

頁面點擊事件

function btnClick(){
    //JS調用
    window.webkit.messageHandlers.openYou.postMessage(null);
}

name(方法名)是放在中間的,messageBody只能是一個對象,如果要傳多個值,需要封裝成數組,或者字典.

當js方法btnClick調用時會觸發OC的代理方法

- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message;

 

WKWebView攔截URL請求

// 在發送請求之前,決定是否跳轉
- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler{
//    navigationAction.request.URL.absoluteString 根據請求的Url進行攔截請求處理
    decisionHandler(WKNavigationActionPolicyAllow);
}

 

Alert彈窗解決方案

/**
 alert框

 @param webView WKWebView
 @param message 消息文本內容
 @param frame frame主窗口
 @param completionHandler 窗口消失回調
 */
- (void)webView:(WKWebView *)webView runJavaScriptAlertPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(void))completionHandler{
    kFunLog
    UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"提示" message:message?:@"" preferredStyle:UIAlertControllerStyleAlert];
    [alertController addAction:([UIAlertAction actionWithTitle:@"確認" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
        completionHandler();
    }])];
    [self presentViewController:alertController animated:YES completion:nil];
}

// 確認框
- (void)webView:(WKWebView *)webView runJavaScriptConfirmPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(BOOL result))completionHandler{
    kFunLog
    UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"提示" message:message?:@"" preferredStyle:UIAlertControllerStyleAlert];
    [alertController addAction:([UIAlertAction actionWithTitle:@"取消" style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) {
        completionHandler(NO);
    }])];
    [alertController addAction:([UIAlertAction actionWithTitle:@"確認" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
        completionHandler(YES);
    }])];
    [self presentViewController:alertController animated:YES completion:nil];
}

// 文本輸入框
- (void)webView:(WKWebView *)webView runJavaScriptTextInputPanelWithPrompt:(NSString *)prompt defaultText:(nullable NSString *)defaultText initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(NSString * _Nullable result))completionHandler{
    kFunLog
    UIAlertController *alertController = [UIAlertController alertControllerWithTitle:prompt message:@"" preferredStyle:UIAlertControllerStyleAlert];
    [alertController addTextFieldWithConfigurationHandler:^(UITextField * _Nonnull textField) {
        textField.text = defaultText;
    }];
    [alertController addAction:([UIAlertAction actionWithTitle:@"完成" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
        completionHandler(alertController.textFields[0].text?:@"");
    }])];
    
    [self presentViewController:alertController animated:YES completion:nil];
}

 

禁止選擇操作

方式1.

// 禁止選擇CSS
        NSString *css = @"body{-webkit-user-select:none;}";
        
        // CSS選中樣式取消 此方式可能會影響dataDetectorTypes屬性
        NSMutableString *javascript = [NSMutableString string];
        [javascript appendString:@"var style = document.createElement('style');"];
        [javascript appendString:@"style.type = 'text/css';"];
        [javascript appendFormat:@"var cssContent = document.createTextNode('%@');", css];
        [javascript appendString:@"style.appendChild(cssContent);"];
        [javascript appendString:@"document.body.appendChild(style);"];
        
        // javascript注入
        WKUserScript *noneSelectScript = [[WKUserScript alloc] initWithSource:javascript injectionTime:WKUserScriptInjectionTimeAtDocumentEnd forMainFrameOnly:YES];
        WKUserContentController *userContentController = [[WKUserContentController alloc] init];
        [userContentController addUserScript:noneSelectScript];
        
        WKWebViewConfiguration *configuration = [[WKWebViewConfiguration alloc] init];
        configuration.userContentController = userContentController;
WKWebViewConfiguration *configuration = [[WKWebViewConfiguration alloc] init];if(greaterIOS10){
      configuration.dataDetectorTypes = WKDataDetectorTypePhoneNumber | WKDataDetectorTypeLink | WKDataDetectorTypeCalendarEvent;
  }

 

方式2.

- (void)webView:(WKWebView *)webView didFinishNavigation:(null_unspecified WKNavigation *)navigation{
    if([webView isLoading]){
        return;
    }
    // 禁用用戶菜單操作效果
    [webView evaluateJavaScript:@"document.documentElement.style.webkitUserSelect='none'" completionHandler:nil];
    [webView evaluateJavaScript:@"document.documentElement.style.webkitTouchCallout='none'" completionHandler:nil];
}

 

此方式1采用JavaScript注入CSS方式實現的過程經測試調用會影響頁面電話號碼,超鏈接等的識別. 本人調試過,去掉css注入就可以識別, 最后采用方式2解決禁止選中效果 

 

WKWebView滾動渲染時肯能出現空白問題解決方式

- (void)scrollViewDidScroll:(UIScrollView *)scrollView{
    
    // 渲染WKWebView ((void(*)(id,SEL,BOOL))objc_msgSend)(self.webView,@selector(_updateVisibleContentRects),NO);
//    objc_msgSend(person, @selector(run));
    
    if ([self.twebView respondsToSelector:@selector(_updateVisibleContentRects)]) {
        ((void(*)(id,SEL,BOOL))objc_msgSend)(self.twebView,@selector(_updateVisibleContentRects),NO);
    }
}

 

.WebView內容字體大小

根據app字體大小的調整,webView的字體大小調整

- (void)webView:(WKWebView *)webView didFinishNavigation:(null_unspecified WKNavigation *)navigation{
    if([webView isLoading]){
        return;
    }
    // 禁用用戶菜單操作效果
    [webView evaluateJavaScript:@"document.documentElement.style.webkitUserSelect='none'" completionHandler:nil];
    [webView evaluateJavaScript:@"document.documentElement.style.webkitTouchCallout='none'" completionHandler:nil];
    
    // 控制字體大小
    // (initial-scale是初始縮放比,minimum-scale=1.0最小縮放比,maximum-scale=5.0最大縮放比,user-scalable=yes是否支持縮放)
    NSString *metaJS = [NSString stringWithFormat:@"document.getElementsByName(\"viewport\")[0].content = \"width=self.view.frame.size.width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=yes\""];
    [webView evaluateJavaScript:metaJS  completionHandler:nil];
    
    CGFloat fontSize = [AppFontSizeUtil getMailContentFontProportionOfNormal];
    // 修改百分比即可 @"document.getElementsByTagName('body')[0].style.webkitTextSizeAdjust= '150%'"
    NSString *fontSizeJs = [NSString stringWithFormat:@"document.getElementsByTagName('body')[0].style.webkitTextSizeAdjust= '%f%%'",fontSize*100];
    [webView evaluateJavaScript:fontSizeJs  completionHandler:nil];
}

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM