preface

Recently UIWebView in the project has been replaced with WKWebView, so to summarize. This article will introduce WKWebView from the following aspects: 1, WKWebView involved some classes 2, WKWebView involved proxy method 3, web content loading progress bar and title implementation 4, JS and OC interaction 5, local HTML file implementation 6, WKWebView+UITableView mix 7. WKWebView offline cache functionCopy the code

WKWebView involves some classes

WKWebView: Rendering and presentation of web pages

Note: #import <WebKit/ webkit. h> // initialize _webView = [[WKWebView alloc] initWithFrame:CGRectMake(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT) configuration:config]; // UI agent _webview. UIDelegate = self; / / navigation agent _webView. NavigationDelegate = self; / / whether to allow gestures left slip back at the next higher level, similar to the left of the navigation control slide returns _webView. AllowsBackForwardNavigationGestures = YES; WKBackForwardList * backForwardList = [_webView backForwardList]; // NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:@"http://www.chinadaily.com.cn"]]; // [request addValue:[self readCurrentCookieWithDomain:@"http://www.chinadaily.com.cn"] forHTTPHeaderField:@"Cookie"]; // [_webView loadRequest:request]; [_webView goBack]; [_webView goForward]; // Refresh the current page [_webView reload]; NSString *path = [[NSBundle mainBundle] pathForResource:@"JStoOC.html" ofType:nil]; NSString *htmlString = [[NSString alloc]initWithContentsOfFile:path encoding:NSUTF8StringEncoding error:nil]; / / load the local HTML file [_webView loadHTMLString: htmlString baseURL: [NSURL fileURLWithPath: [[NSBundle mainBundle] bundlePath]]].Copy the code

WKWebViewConfiguration: Adds WKWebView configuration information

WKWebViewConfiguration *config = [[WKWebViewConfiguration alloc] init]; WKPreferences *preference = [[WKPreferences alloc]init]; / / the minimum font size When set javaScriptEnabled attribute to NO, apparent effect can see preference. MinimumFontSize = 0; / / set whether to support javaScript is support by default preference. JavaScriptEnabled = YES; / / on the iOS default to NO, said whether to allow opened a window without user interaction by javaScript automatic preference. JavaScriptCanOpenWindowsAutomatically = YES; config.preferences = preference; / / is a streaming video players, the use of h5 or use native player full-screen playback config. AllowsInlineMediaPlayback = YES; / / set if video requires the user to manually set to NO would be allowed to play automatically config. RequiresUserActionForMediaPlayback = YES; / / set whether to allow picture in picture technology on a specific device config. The efficient allowsPictureInPictureMediaPlayback = YES; / / set the request of the user-agent information in the application name after iOS9 available config. ApplicationNameForUserAgent = @ "ChinaDailyForiPad"; / / custom WKScriptMessageHandler is in order to solve the problem of memory does not release WeakWebViewScriptMessageDelegate * weakScriptMessageDelegate = [[WeakWebViewScriptMessageDelegate alloc] initWithDelegate:self]; WKUserContentController * wkUController = [[WKUserContentController alloc] init]; / / register a name for jsToOcNoPrams js method [wkUController addScriptMessageHandler: weakScriptMessageDelegate name: @ "jsToOcNoPrams"]. [wkUController addScriptMessageHandler:weakScriptMessageDelegate name:@"jsToOcWithPrams"]; config.userContentController = wkUController;Copy the code

WKUserScript: Used for JavaScript injection

// The following code ADAPTS to the text size, changes from UIWebView to WKWebView, will find the font is much smaller, this should be WKWebView and HTML compatibility problem, the solution is to modify the original page, NSString *jSString = @"var Meta = document.createElement('meta'); meta.setAttribute('name', 'viewport'); meta.setAttribute('content', 'width=device-width'); document.getElementsByTagName('head')[0].appendChild(meta);" ; WKUserScript *wkUScript = [[WKUserScript alloc] initWithSource:jSString injectionTime:WKUserScriptInjectionTimeAtDocumentEnd forMainFrameOnly:YES]; [config.userContentController addUserScript:wkUScript];Copy the code

WKUserContentController: This class is used to manage interaction between Native and JavaScript

WKUserContentController * wkUController = [[WKUserContentController alloc] init]; / / register a name for jsToOcNoPrams js method, processing method receives the js set agent [wkUController addScriptMessageHandler: self name: @ "jsToOcNoPrams"]. [wkUController addScriptMessageHandler:self name:@"jsToOcWithPrams"]; config.userContentController = wkUController; // Remove the registered js method [[_webView Configuration]. UserContentController removeScriptMessageHandlerForName:@"jsToOcNoPrams"]; [[_webView configuration].userContentController removeScriptMessageHandlerForName:@"jsToOcWithPrams"];Copy the code

WKScriptMessageHandler: This protocol class deals specifically with listening on JavaScript methods to call native OC methods, used in conjunction with WKUserContentController.

Note: Follow the WKScriptMessageHandler protocol, The proxy is a callback method captured by the WKUserContentControl setting // by the name that receives the JS outgoing message - (void)userContentController:(WKUserContentController) *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message{ NSLog(@"name:%@\\\\n body:%@\\\\n frameInfo:%@\\\\n",message.name,message.body,message.frameInfo); NSDictionary * parameter = message.body; NSDictionary * parameter = message.body; OC if([message.name isEqualToString:@"jsToOcNoPrams"]){UIAlertController *alertController = [UIAlertController AlertControllerWithTitle: @ "js calls to the oc message:" @ "with no arguments" preferredStyle: UIAlertControllerStyleAlert]; [alertController addAction:([UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleDefault handler:^(UIAlertAction  * _Nonnull action) { }])]; [self presentViewController:alertController animated:YES completion:nil]; }else if([message.name isEqualToString:@"jsToOcWithPrams"]){ UIAlertController *alertController = [UIAlertController AlertControllerWithTitle: @ "js calls to the oc" message: parameter [@ "params"] preferredStyle: UIAlertControllerStyleAlert]; [alertController addAction:([UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleDefault handler:^(UIAlertAction  * _Nonnull action) { }])]; [self presentViewController:alertController animated:YES completion:nil]; }}Copy the code

WKWebView involves proxy methods

WKNavigationDelegate: Handles jump and load operations

/ / start page load when the - (void) webView: (WKWebView *) webView didStartProvisionalNavigation: (WKNavigation *) navigation / / {} Page loading failure when the - (void) webView: (WKWebView *) webView didFailProvisionalNavigation: (null_unspecified WKNavigation *) navigation WithError :(NSError *)error {[self.progressView setProgress:0.0f animated:NO]; } // called when the content starts to return - (void)webView:(WKWebView *)webView didCommitNavigation:(WKNavigation *)navigation {} // called after the page is loaded - (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation { [self getCookie]; } // call when commit error occurs - (void)webView:(WKWebView *)webView didFailNavigation:(WKNavigation *)navigation withError:(NSError *)error {[self.progressView setProgress:0.0f animated:NO]; } // call - (void)webView:(WKWebView *)webView DidReceiveServerRedirectForProvisionalNavigation: (WKNavigation *) navigation {} / / according to the WebView for the jump of the HTTP request header information and related information to decide whether to jump  - (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler { NSString * urlStr = navigationAction.request.URL.absoluteString; NSLog(@" send jump request: %@",urlStr); NSString *htmlHeadString = @"github://"; if([urlStr hasPrefix:htmlHeadString]){ UIAlertController *alertController = [UIAlertController AlertControllerWithTitle: @ "by intercepting the URL call OC message:" @ "do you want to get to my homepage making?" preferredStyle: UIAlertControllerStyleAlert]; [alertController addAction ([UIAlertAction actionWithTitle: @ "cancel" style: UIAlertActionStyleCancel handler: ^ (UIAlertAction * _Nonnull action) { }])]; [alertController addAction ([UIAlertAction actionWithTitle: @ "open" style: UIAlertActionStyleDefault handler: ^ (UIAlertAction  * _Nonnull action) { NSURL * url = [NSURL URLWithString:[urlStr stringByReplacingOccurrencesOfString:@"github://callName_?" withString:@""]]; [[UIApplication sharedApplication] openURL:url]; }])]; [self presentViewController:alertController animated:YES completion:nil]; decisionHandler(WKNavigationActionPolicyCancel); }else{ decisionHandler(WKNavigationActionPolicyAllow); - (void)webView:(WKWebView *)webView decidePolicyForNavigationResponse:(WKNavigationResponse *)navigationResponse decisionHandler:(void (^)(WKNavigationResponsePolicy))decisionHandler{ NSString * urlStr = navigationResponse.response.URL.absoluteString; NSLog(@" current address: %@",urlStr); / / allow jump decisionHandler (WKNavigationResponsePolicyAllow); / / is not allowed to jump / / decisionHandler (WKNavigationResponsePolicyCancel); } // the user's identity is passed in the block as well - (void)webView:(WKWebView *)webView didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential * _Nullable credential))completionHandler{// NSURLCredential * newCred = [[NSURLCredential alloc]  initWithUser:@"user123" password:@"123" persistence:NSURLCredentialPersistenceNone]; / / to challenge the sender the credential [challenge. The sender useCredential: newCred forAuthenticationChallenge: challenge]; completionHandler(NSURLSessionAuthChallengeUseCredential,newCred); } / / process is terminated when the - (void) webViewWebContentProcessDidTerminate: (webView WKWebView *) {}Copy the code

WKUIDelegate: mainly handles JS scripts, confirmation boxes, warning boxes, etc

/** * webView implements the proxy webView ** @param message The contents of the warning box * @param completionHandler Calls the warning box disappear */ - (void)webView:(WKWebView *)webView runJavaScriptAlertPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(void))completionHandler { UIAlertController *alertController = [UIAlertController AlertControllerWithTitle: @ "HTML pop-up message:" message? : @ "" preferredStyle: UIAlertControllerStyleAlert]; [alertController addAction:([UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleDefault handler:^(UIAlertAction  * _Nonnull action) { completionHandler(); }])]; [self presentViewController:alertController animated:YES completion:nil]; } // Confirm method is called by JavaScript. Need to pass in the user's choice of situation in the block - (void) webView: (WKWebView *) webView runJavaScriptConfirmPanelWithMessage message: (nsstrings *) initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(BOOL))completionHandler{ UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"" message:message?:@"" preferredStyle:UIAlertControllerStyleAlert]; [alertController addAction:([UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) { completionHandler(NO); }])]; [alertController addAction:([UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleDefault handler:^(UIAlertAction  * _Nonnull action) { completionHandler(YES); }])]; [self presentViewController:alertController animated:YES completion:nil]; - (void)webView (WKWebView *)webView - (void)webView (WKWebView * runJavaScriptTextInputPanelWithPrompt:(NSString *)prompt defaultText:(NSString *)defaultText initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(NSString * _Nullable))completionHandler{ UIAlertController *alertController = [UIAlertController alertControllerWithTitle:prompt message:@"" preferredStyle:UIAlertControllerStyleAlert]; [alertController addTextFieldWithConfigurationHandler:^(UITextField * _Nonnull textField) { textField.text = defaultText; }]; [alertController addAction:([UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleDefault handler:^(UIAlertAction  * _Nonnull action) { completionHandler(alertController.textFields[0].text?:@""); }])]; [self presentViewController:alertController animated:YES completion:nil]; _blank - (WKWebView *)webView:(WKWebView *)webView createWebViewWithConfiguration:(WKWebViewConfiguration *)configuration forNavigationAction:(WKNavigationAction *)navigationAction windowFeatures:(WKWindowFeatures *)windowFeatures { if (! navigationAction.targetFrame.isMainFrame) { [webView loadRequest:navigationAction.request]; } return nil; }Copy the code

Third, webpage content loading progress bar and title implementation

// addObserver to monitor page loading progress [self.webView addObserver:self forKeyPath:@"estimatedProgress" options:0 context:nil]; / / add monitoring page title title observer [self. WebView addObserver: self forKeyPath: @ "title" options: NSKeyValueObservingOptionNew context:nil]; //kvo monitor progress must implement this method -(void) observeforkeypath :(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context{ if ([keyPath IsEqualToString: NSStringFromSelector (@ the selector (estimatedProgress)] && object = = {NSLog _webView) (@ "page load = progress %f",_webView.estimatedProgress); self.progressView.progress = _webView.estimatedProgress; If (_webView estimatedProgress > = 1.0 f) {dispatch_after (dispatch_time (DISPATCH_TIME_NOW, (int64_t) (0.3 * NSEC_PER_SEC)), dispatch_get_main_queue (), ^ {self. ProgressView. Progress = 0; }); } }else if([keyPath isEqualToString:@"title"] && object == _webView){ self.navigationItem.title = _webView.title; }else{ [super observeValueForKeyPath:keyPath ofObject:object change:change context:context]; }} / / remove the observer [_webView removeObserver: self forKeyPath: NSStringFromSelector (@ the selector (estimatedProgress)]; [_webView removeObserver:self forKeyPath:NSStringFromSelector(@selector(title))];Copy the code

4. Interaction between JS and OC

JS calls OC

This implementation mainly relies on the WKScriptMessageHandler protocol class and the WKUserContentController class: The WKUserContentController object is responsible for registering the JS method, setting up the agent that receives the JS method, complying with the WKScriptMessageHandler, and implementing the callback method that captures the JS message. See the description of these two classes in step 1.

WKUserContentController * wkUController = [[WKUserContentController alloc] init]; / / register a name for jsToOcNoPrams js method, processing method receives the js set agent [wkUController addScriptMessageHandler: self name: @ "jsToOcNoPrams"]. [wkUController addScriptMessageHandler:self name:@"jsToOcWithPrams"]; config.userContentController = wkUController; Note: Follow the WKScriptMessageHandler protocol, The proxy is captured by the WKUserContentControl setting // the callback method JS calls OC - (void)userContentController:(WKUserContentController) *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message{ NSLog(@"name:%@\\\\n body:%@\\\\n frameInfo:%@\\\\n",message.name,message.body,message.frameInfo); }Copy the code

OC call JS

ChangeColor () is the name of the JS method, CompletionHandler is an asynchronous callback block NSString *jsString = [NSString stringWithFormat:@"changeColor('%@')", @"Js parameter "]; [_webView evaluateJavaScript:jsString completionHandler:^(id _Nullable data, NSError * _Nullable error) {NSLog(@" change HTML background color ");}]; // Change the font size to call the native JS method NSString *jsFont = [NSString stringWithFormat:@"document.getElementsByTagName('body')[0].style.webkitTextSizeAdjust= '%d%%'", arc4random()%99 + 100]; [_webView evaluateJavaScript:jsFont completionHandler:nil];Copy the code