iOS WKWebView的使用

前言

最近項目中UIWebView被替換成WKWebView骏啰,因此來總結(jié)一下。
本文將從以下幾方面介紹WKWebView。

  • 1睦疫、WKWebView的創(chuàng)建
  • 2、WKWebView的代理方法
  • 3鞭呕、Html進度進度條的展示和title的實時獲取
  • 4蛤育、JS和OC交互

一、WKWebView的創(chuàng)建

  • WKWebView 創(chuàng)建主要涉及的類

WKUserScript:主要是用于JS的注入
WKPreferences:主要是設(shè)置WKWebview的屬性
WKWebPagePreferences:iOS13后推出設(shè)置是否支持JavaScript
WKWebViewConfiguration:為WKWebView添加配置信息
WKUserContentController:主要是管理JS與Native交互

  • WKWebView 初始化
注意: #import <WebKit/WebKit.h>
      
        //初始化
        _webView = [[WKWebView alloc] initWithFrame:CGRectMake(0, 0, SCREEN_WIDTH, SCREENH_HEIGHT) configuration:config];
        //UI 代理
        _webView.UIDelegate = self;
        //navigation delegate
        _webView.navigationDelegate = self;
        //是否允許手勢左滑返回上一級, 默認為NO
        _webView.allowsBackForwardNavigationGestures = YES;
        //可返回的頁面列表, 存儲已打開過的網(wǎng)頁
        WKBackForwardList *backList = [_webView backForwardList];
        NSLog(@"可返回頁面列表:%@",backList);
        //加載URL
//        NSURL *url = [NSURL URLWithString:@"https://www.baidu.com"];
//        NSURLRequest *request = [[NSURLRequest alloc] initWithURL:url];
//        [_webView loadRequest:request];
        
        //加載本地HTML
        NSString *htmlPath = [[NSBundle mainBundle] pathForResource:@"JStoOC" ofType:@"html"];
        NSString *htmlString = [[NSString alloc] initWithContentsOfFile:htmlPath encoding:NSUTF8StringEncoding error:nil];
        [_webView loadHTMLString:htmlString baseURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] bundlePath]]];
  • WKWebViewConfiguration 為WKWebView添加配置信息
  //創(chuàng)建網(wǎng)頁配置對象
        WKWebViewConfiguration *config = [[WKWebViewConfiguration alloc] init];
        //創(chuàng)建設(shè)置對象
        WKPreferences *preferences = [[WKPreferences alloc] init];
        //最小字體大小 當將javaScriptEnable
        preferences.minimumFontSize = 0;
        //設(shè)置是否支持javascript,默認是YES葫松。
        if (@available(iOS 14.0, *)) {
            //iOS13后新增的類,allowsContentJavaScript是iOS14后新增的方法瓦糕。
            WKWebpagePreferences *webpagePreferences = [[WKWebpagePreferences alloc] init];
            webpagePreferences.allowsContentJavaScript = YES;
            config.defaultWebpagePreferences = webpagePreferences;
        }else{
            preferences.javaScriptEnabled = YES;
        }
        //是否允許不經(jīng)過用戶交互由JavaScript自動打開窗口,默認為NO
        preferences.javaScriptCanOpenWindowsAutomatically = YES;
        config.preferences = preferences;
        //YES是使用h5的視頻播放器在線播放腋么,NO是使用原生播放器全屏播放咕娄,默認為NO
        config.allowsInlineMediaPlayback = YES;
        //設(shè)置視頻是否需要手動播放,設(shè)置為NO會自動播放党晋。
        config.requiresUserActionForMediaPlayback = YES;
        //設(shè)置是否允許畫中畫播放谭胚,默認為YES
        config.allowsPictureInPictureMediaPlayback = YES;
        //設(shè)置請求的User-Agent信息中的應(yīng)用程序名稱 iOS9后可用
        config.applicationNameForUserAgent = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleDisplayName"];
  • WKUserScript :用于進行JavaScript注入
   //適配字體大小
        NSString *jSString = @"var meta = document.createElement('meta'); meta.setAttribute('name', 'viewport'); meta.setAttribute('content', 'width=device-width'); document.getElementsByTagName('head')[0].appendChild(meta);";
        //用于JS注入
        WKUserScript *userScript = [[WKUserScript alloc] initWithSource:jSString injectionTime:WKUserScriptInjectionTimeAtDocumentEnd forMainFrameOnly:YES];
        [config.userContentController addUserScript:userScript];
  • WKUserContentController:用于管理native和JavaScript交互
 //解決WKWebView內(nèi)存不釋放問題
        WeakScriptMessageHandler *weakScriptMessageHandler = [[WeakScriptMessageHandler alloc] initWithDelegate:self];
        //主要用來管理native與JavaScript的交互管理
        WKUserContentController * userContentC = [[WKUserContentController alloc] init];
        //注冊name為JStoOCNoParams的js方法,設(shè)置處理接收JS方法的對象self
        [userContentC addScriptMessageHandler:weakScriptMessageHandler name:@"JStoOCNoParams"];
        //注冊name為JStoOCWithParams的js方法未玻,設(shè)置處理接收JS方法的對象self
        [userContentC addScriptMessageHandler:weakScriptMessageHandler name:@"JStoOCWithParams"];
       
        config.userContentController = userContentC;

  • WKScriptMessageHandler:該協(xié)議專門用來監(jiān)聽JavaScript調(diào)用OC方法灾而。與WKUserContentController搭配使用
#pragma mark - 處理JS調(diào)用Native方法的代理方法。通過message.name來區(qū)分扳剿。
//注意:遵守WKScriptMessageHandler協(xié)議旁趟,代理由WKUserContentController設(shè)置
- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message
{
    NSString *actionName = message.name;
    NSDictionary *params = message.body;
    if (actionName.length >0) {
        if ([actionName isEqualToString:@"JStoOCNoParams"]) {
            
            UIAlertController *alertC = [UIAlertController alertControllerWithTitle:@"提示" message:@"無參數(shù)" preferredStyle:UIAlertControllerStyleAlert];
            UIAlertAction *action = [UIAlertAction actionWithTitle:@"確定" style:UIAlertActionStyleCancel handler:nil];
            [alertC addAction:action];
            [self presentViewController:alertC animated:YES completion:nil];
            
            
        }else if ([actionName isEqualToString:@"JStoOCWithParams"]){
            
            UIAlertController *alertC = [UIAlertController alertControllerWithTitle:@"提示" message:params[@"params"] preferredStyle:UIAlertControllerStyleAlert];
            UIAlertAction *action = [UIAlertAction actionWithTitle:@"確定" style:UIAlertActionStyleCancel handler:nil];
            [alertC addAction:action];
            [self presentViewController:alertC animated:YES completion:nil];
            
            
        }
    }
}
  • 注意在dealloc方法中進行移除注冊的JS方法
-(void)dealloc
{
    //移除注冊的JS方法
    [self.webView.configuration.userContentController removeScriptMessageHandlerForName:@"JStoOCNoParams"];
    [self.webView.configuration.userContentController removeScriptMessageHandlerForName:@"JStoOCWithParams"];
}

二、WKWebView的代理方法

UIDelegate:主要處理JS腳本庇绽、確認框锡搜、警示框等

#pragma mark - UI Delegate

-(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))completionHandler
{
    
}

-(void)webView:(WKWebView *)webView runJavaScriptTextInputPanelWithPrompt:(NSString *)prompt defaultText:(NSString *)defaultText initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(NSString * _Nullable))completionHandler
{
    
}

-(WKWebView *)webView:(WKWebView *)webView createWebViewWithConfiguration:(WKWebViewConfiguration *)configuration forNavigationAction:(WKNavigationAction *)navigationAction windowFeatures:(WKWindowFeatures *)windowFeatures
{
    return nil;
}

WKNavigationDelegate:主要處理一些跳轉(zhuǎn)、加載處理操作

#pragma mark - NavigationDelegate

-(void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation
{
    //加載結(jié)束
}

-(void)webView:(WKWebView *)webView didCommitNavigation:(WKNavigation *)navigation
{
    //加載內(nèi)容開始返回時
}

-(void)webView:(WKWebView *)webView didStartProvisionalNavigation:(WKNavigation *)navigation
{
    //開始加載
}

-(void)webView:(WKWebView *)webView didFailProvisionalNavigation:(WKNavigation *)navigation withError:(NSError *)error
{
    //加載時發(fā)生錯誤
    [self.progressView setProgress:0.0 animated:YES];
}

-(void)webView:(WKWebView *)webView didReceiveServerRedirectForProvisionalNavigation:(WKNavigation *)navigation
{
    //接收到服務(wù)器跳轉(zhuǎn)請求即服務(wù)重定向時調(diào)用
}

-(void)webView:(WKWebView *)webView didFailNavigation:(WKNavigation *)navigation withError:(NSError *)error
{
    //提交時發(fā)生錯誤
    [self.progressView setProgress:0.0 animated:YES];
}

//根據(jù)webview對于即將跳轉(zhuǎn)的HTTP請求頭信息和相關(guān)信息來決定是否跳轉(zhuǎn)
-(void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler
{
    NSString *urlString = navigationAction.request.URL.absoluteString;
    
    NSString *htmlHeadString = @"github://callName_";
    
    if ([urlString hasPrefix:htmlHeadString]) {
        //進行客戶端代碼
        
        UIAlertController *alertC = [UIAlertController alertControllerWithTitle:@"提示" message:@"是否跳轉(zhuǎn)到該頁面瞧掺?" preferredStyle:UIAlertControllerStyleAlert];
        
        UIAlertAction *cancelAction = [UIAlertAction actionWithTitle:@"取消" style:UIAlertActionStyleCancel handler:nil];
        
        UIAlertAction *okAction = [UIAlertAction actionWithTitle:@"打開" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
            
            dispatch_async(dispatch_get_main_queue(), ^{
                //Safari打開url
                NSURL *url = [NSURL URLWithString:[urlString stringByReplacingOccurrencesOfString:@"github://callName_?" withString:@""]];
                
                [[UIApplication sharedApplication] openURL:url];
            });
           
            
        }];
        
        [alertC addAction:cancelAction];
        [alertC addAction:okAction];
        
        [self presentViewController:alertC animated:YES completion:nil];
        
        decisionHandler(WKNavigationActionPolicyCancel);
    }else{
        decisionHandler(WKNavigationActionPolicyAllow);
    }
}

//根據(jù)客戶端收到的服務(wù)器響應(yīng)頭信息和Response相關(guān)信息來決定是否跳轉(zhuǎn)
-(void)webView:(WKWebView *)webView decidePolicyForNavigationResponse:(WKNavigationResponse *)navigationResponse decisionHandler:(void (^)(WKNavigationResponsePolicy))decisionHandler
{
    NSString *urlString = navigationResponse.response.URL.absoluteString;
    NSLog(@"%@",urlString);
    //根據(jù)URL來進行攔截或者阻止跳轉(zhuǎn)
    decisionHandler(WKNavigationResponsePolicyAllow);//允許跳轉(zhuǎn)
    
//    decisionHandler(WKNavigationResponsePolicyCancel);//不允許跳轉(zhuǎn)
}

//需要相應(yīng)身份驗證是調(diào)用 在block中需要傳入用戶身份憑證
-(void)webView:(WKWebView *)webView didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition, NSURLCredential * _Nullable))completionHandler
{
    //用戶身份信息
    NSURLCredential *credential = [[NSURLCredential alloc] initWithUser:@"User" password:@"Password" persistence:NSURLCredentialPersistenceNone];
    //為challenge的發(fā)送方提供credential
    [challenge.sender useCredential:credential forAuthenticationChallenge:challenge];
completionHandler(NSURLSessionAuthChallengeUseCredential,credential);
    
}

三耕餐、進度條和title

  • 注冊觀察者
 //添加監(jiān)測網(wǎng)頁title變化的觀察者(self) 被觀察者(self.webView)
    [self.webView addObserver:self forKeyPath:@"title" options:NSKeyValueObservingOptionNew context:nil];
    //添加監(jiān)測網(wǎng)頁加載進度變化的觀察者
    [self.webView addObserver:self forKeyPath:@"estimatedProgress" options:NSKeyValueObservingOptionNew context:nil];
  • 監(jiān)聽回調(diào)方法
#pragma mark - 觀察者的監(jiān)聽方法
-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context
{
    if ([keyPath isEqualToString:@"title"] && object == _webView) {
        self.title = change[@"new"];
    }else if ([keyPath isEqualToString:@"estimatedProgress"] && object == _webView){
        self.progressView.progress = _webView.estimatedProgress;
        if (_webView.estimatedProgress >= 1.0f) {
            
            dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.2*NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
                self.progressView.progress = 0.0f;
            });
        }
    }
}
  • 注意 dealloc中移除觀察者
//移除觀察者
    [self.webView removeObserver:self forKeyPath:@"title"];
    [self.webView removeObserver:self forKeyPath:@"estimatedProgress"];

四、OC與JS交互

  • OC調(diào)用JS方法
#pragma mark - Native調(diào)用JS方法
-(void)OCtoJS
{
    //action:changeColor 更換背景顏色
    [_webView evaluateJavaScript:[[NSString alloc] initWithFormat:@"changeColor('')"] completionHandler:nil];
    
    //id:pictureId  action:changePicture path:圖片路徑 根據(jù)id更換圖片
    NSString *imgPath = [[NSBundle mainBundle] pathForResource:@"girl.png" ofType:nil];
    NSString *jsString = [[NSString alloc] initWithFormat:@"changePicture('pictureId','%@')",imgPath];
    [_webView evaluateJavaScript:jsString completionHandler:^(id _Nullable data, NSError * _Nullable error) {
        NSLog(@"完成更換圖片");
    }];
    
    //改變字體大小
    NSString *jsFont = [NSString stringWithFormat:@"document.getElementsByTagName('body')[0].style.webkitTextSizeAdjust= '%d%%'", arc4random()%99 + 20];
    [_webView evaluateJavaScript:jsFont completionHandler:nil];
}
  • JS調(diào)用OC方法
 WKUserContentController * userContentC = [[WKUserContentController alloc] init];
        //注冊name為JStoOCNoParams的js方法辟狈,設(shè)置處理接收JS方法的對象self
        [userContentC addScriptMessageHandler:weakScriptMessageHandler name:@"JStoOCNoParams"];
        //注冊name為JStoOCWithParams的js方法肠缔,設(shè)置處理接收JS方法的對象self
        [userContentC addScriptMessageHandler:weakScriptMessageHandler name:@"JStoOCWithParams"];
        
        config.userContentController = userContentC;
注意:遵守WKScriptMessageHandler協(xié)議夏跷,代理是由WKUserContentControl設(shè)置
 //通過接收JS傳出消息的name進行捕捉的回調(diào)方法  js調(diào)OC
- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message{
    NSLog(@"name:%@\\\\n body:%@\\\\n frameInfo:%@\\\\n",message.name,message.body,message.frameInfo);
}

參考文獻:
http://www.reibang.com/p/5cf0d241ae12

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市明未,隨后出現(xiàn)的幾起案子槽华,更是在濱河造成了極大的恐慌,老刑警劉巖趟妥,帶你破解...
    沈念sama閱讀 206,378評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件猫态,死亡現(xiàn)場離奇詭異,居然都是意外死亡披摄,警方通過查閱死者的電腦和手機亲雪,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,356評論 2 382
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來行疏,“玉大人匆光,你說我怎么就攤上這事∧鹆” “怎么了?”我有些...
    開封第一講書人閱讀 152,702評論 0 342
  • 文/不壞的土叔 我叫張陵夺巩,是天一觀的道長贞让。 經(jīng)常有香客問我,道長柳譬,這世上最難降的妖魔是什么喳张? 我笑而不...
    開封第一講書人閱讀 55,259評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮美澳,結(jié)果婚禮上销部,老公的妹妹穿的比我還像新娘。我一直安慰自己制跟,他們只是感情好舅桩,可當我...
    茶點故事閱讀 64,263評論 5 371
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著雨膨,像睡著了一般擂涛。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上聊记,一...
    開封第一講書人閱讀 49,036評論 1 285
  • 那天何乎,我揣著相機與錄音竣况,去河邊找鬼。 笑死,一個胖子當著我的面吹牛恃轩,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播该溯,決...
    沈念sama閱讀 38,349評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼雅倒,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起附鸽,我...
    開封第一講書人閱讀 36,979評論 0 259
  • 序言:老撾萬榮一對情侶失蹤脱拼,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后坷备,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體熄浓,經(jīng)...
    沈念sama閱讀 43,469評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,938評論 2 323
  • 正文 我和宋清朗相戀三年省撑,在試婚紗的時候發(fā)現(xiàn)自己被綠了赌蔑。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,059評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡竟秫,死狀恐怖娃惯,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情肥败,我是刑警寧澤趾浅,帶...
    沈念sama閱讀 33,703評論 4 323
  • 正文 年R本政府宣布,位于F島的核電站馒稍,受9級特大地震影響皿哨,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜纽谒,卻給世界環(huán)境...
    茶點故事閱讀 39,257評論 3 307
  • 文/蒙蒙 一证膨、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧鼓黔,春花似錦央勒、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,262評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至肆捕,卻和暖如春刷晋,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背慎陵。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評論 1 262
  • 我被黑心中介騙來泰國打工眼虱, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人席纽。 一個月前我還...
    沈念sama閱讀 45,501評論 2 354
  • 正文 我出身青樓捏悬,卻偏偏與公主長得像,于是被迫代替她去往敵國和親润梯。 傳聞我的和親對象是個殘疾皇子过牙,可洞房花燭夜當晚...
    茶點故事閱讀 42,792評論 2 345

推薦閱讀更多精彩內(nèi)容

  • 1.努力意義就是在你傾盡全力去做一件事成功后那種喜悅和發(fā)自內(nèi)心的高興的那種感覺最讓人瘋狂感動 2.要知道需要哪個方...
    莫忘小寒閱讀 44評論 0 0
  • 下午1點左右要對公司的網(wǎng)銀的賬甥厦,不能對,瀏覽器要升級寇钉,感覺很頭疼啊 不懂操作刀疙。問了個親戚,她也給了我建議扫倡,但是我覺...
    感性的我閱讀 122評論 0 2
  • 家庭關(guān)系營
    0650e60c19b5閱讀 60評論 0 0
  • ‘ 在教學(xué)中如何培養(yǎng)核心素養(yǎng) 主講:上海師范大學(xué)中文系教授 鄭桂華 2021.04.07上午 藍莓頤華 2014...
    09c078222944閱讀 113評論 0 0
  • 覺察日記:2021.4.26 今天一天都很忙谦秧,無暇寫分享,看到艷品和彥希的分享撵溃,感覺自責疚鲤、內(nèi)疚著急,缘挑。自責內(nèi)疚是因...
    有覺察的生活閱讀 104評論 0 1