This article will introduce WKWebView from the following aspects:

• Some classes involved in WKWebView

• Agent methods involved in WKWebView

• Web content loading progress bar and title implementation

• Interaction between JS and OC

• Implementation of local HTML files

WKWebView involves some classes

• WKWebView: Rendering and presentation of web pages

#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: for adding 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 is adapted to the size of the text. After changing UIWebView to WKWebView, you will find that the font size is much smaller. This should be a compatible problem between WKWebView and HTML. Either we manually inject JS 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 interactions 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 is designed to handle listening to JavaScript methods to call native OC methods. It is used in conjunction with WKUserContentController.

Note: Following the WKScriptMessageHandler protocol, the agent is set by WKUserContentControl ==

// callback method to catch the JS message by receiving the name of the 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. //JS call 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 agent methods

• WKNavigationDelegate: Handles some jump and load handling 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]; } // call when content starts returning - (void)webView:(WKWebView *)webView didCommitNavigation:(WKNavigation *)navigation {} // call after page loading is complete (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation { [self getCookie]; } // call when a submission 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 :(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); } // you need to pass in the user credentials in the block - (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 script, confirmation box, warning box, etc

/** * @param webView @param Message Content in the warning box * @param completionHandler Warning box disappear call */ - (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]; } //JavaScript calls the confirm method, and the callback method confirm is the confirm box in 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]; < span style = "box-sizing: block; color: # box-sizing: block; color: block; color: block; line-height: 20px; font-size: 14px! Important; word-break: normal; 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]; } // the page is a popup window processing - (WKWebView *)webView:(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
Three, page content loading progress bar and title implementation
[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]; -(void)observeValueForKeyPath:(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 relies on the WKScriptMessageHandler protocol class and the WKUserContentController class: The WKUserContentController object is responsible for registering THE JS methods, setting up the agent that handles the receiving OF the JS methods, and implementing the callback methods that catch the JS messages. See step 1 for more details on these two classes. = =

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;Copy the code

Note: Following the WKScriptMessageHandler protocol, the agent is set by WKUserContentControl ==

OC - (void)userContentController:(WKUserContentController *)userContentController (WKUserContentController * didReceiveScriptMessage:(WKScriptMessage *)message{ NSLog(@"name:%@\\\\n body:%@\\\\n frameInfo:%@\\\\n",message.name,message.body,message.frameInfo); }Copy the code

• OC calls JS

//OC calls JS changeColor() which is the JS method name, The 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 ");}]; // To change the font size 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