preface

UIWebview has always been used in projects, but WK is more powerful, including the interaction with JS. WK loads almost twice as fast as UIWebView and uses half as much memory. And Apple recommends WK. So today, let’s sort out the relevant knowledge of WK.

Introduction to the

After iOS8.0, Apple recommended the use of WebKit framework WKWebView to load web pages, use WKWebViewConfiguration to configure JS interaction.

use

First import #import <WebKit/ webkit.h > to create the UI

_webView = [[WKWebView alloc] initWithFrame:CGRectMake(0, 0, SCREEN_W, SCREEN_H) configuration:config]; //config for the created configuration object _webview. UIDelegate = self; / / UI agent _webView. NavigationDelegate = self; / / navigation agent _webView allowsBackForwardNavigationGestures = YES; WKBackForwardList * backForwardList = [_webView backForwardList]; WKBackForwardList * backForwardList = [_webView backForwardList];Copy the code

Commonly used method

Open the web page

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

Web page to switch

[_webView goBack]; // Back [_webView goForward]; [_webView reload]; // Refresh the current pageCopy the code

The page executes JS methods

[_webView evaluateJavaScript:@"h5method()" completionHandler:^(id _Nullable response, NSError * _Nullable error) {
                              
}];
Copy the code

Interaction between OC and JS

  • Js calls OC

We mainly rely on the WKScriptMessageHandler protocol class, WKUserContentController where: The WKUserContentController object is responsible for registering JS methods, setting up the agent that handles receiving JS methods, and following the WKScriptMessageHandler to implement the callback method that catches the JS message.

Use WKWebViewConfiguration to configure JS interaction

WKWebViewConfiguration *config = [[WKWebViewConfiguration alloc] init]; WKPreferences *preference = [[WKPreferences alloc]init]; 2.1 minimum font size / / / / when the javaScriptEnabled attribute is set to NO, apparent effect can see preference. MinimumFontSize = 0; / / 2.2 support javaScript is support by default preference. JavaScriptEnabled = YES; / / 2.3 whether to allow opened a window without user interaction by javaScript automatic preference. JavaScriptCanOpenWindowsAutomatically = YES; Config. preferences = preference; / / / / 3, the general configuration is a streaming video player, the use of h5 or use native player full-screen playback config. AllowsInlineMediaPlayback = YES; / / set the video if you need the user manual, set to NO would be allowed to play automatically config. RequiresUserActionForMediaPlayback = YES; / / set whether to allow picture in picture (valid config on specific equipment. AllowsPictureInPictureMediaPlayback = YES; / / set the request of the user-agent information in the application name (available after iOS9 config. ApplicationNameForUserAgent = @ "ChinaDailyForiPad";Copy the code

2. Use WKUserContentController for native/JavaScript interaction management

WKUserContentController * wkUController = [[WKUserContentController alloc] init]; / / register a name for jsToOcNoPrams js method [wkUController addScriptMessageHandler: weakScriptMessageDelegate name: @ "jsToOcNoPrams"].  [wkUController addScriptMessageHandler:weakScriptMessageDelegate name:@"jsToOcWithPrams"]; / / set the config. UserContentController = wkUController; / / remove finished [[_webView configuration]. UserContentController removeScriptMessageHandlerForName: @ "jsToOcNoPrams"]. [[_webView configuration].userContentController removeScriptMessageHandlerForName:@"jsToOcWithPrams"];Copy the code

Use WKScriptMessageHandler to handle listening JavaScript methods to call native OC methods. (Used with WKUserContentController)

/ / create a proxy WeakWebViewScriptMessageDelegate * weakScriptMessageDelegate = [[WeakWebViewScriptMessageDelegate alloc] initWithDelegate:self];Copy the code

Ps: follow WKScriptMessageHandler protocol, agent is set by WKUserContentControl

- (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"]){NSLog("-----> JS called OC with no argument "); }else if([message.name isEqualToString:@"jsToOcWithPrams"]){NSLog("----->js called oc, with argument "); }}Copy the code
  • OC call js

Execute custom JavaScript code using WKUserScript

NSString *jSString = @"var meta = document.createElement('meta'); meta.setAttribute('name', 'viewport'); meta.setAttribute('content', 'width=device-width'); document.getElementsByTagName('head')[0].appendChild(meta);" ; WKUserScript WKUserScript *wkUScript = [[WKUserScript alloc] initWithSource:jSString injectionTime:WKUserScriptInjectionTimeAtDocumentEnd forMainFrameOnly:YES]; / / set [config userContentController addUserScript: wkUScript];Copy the code

Dynamic load and run JS code, used to add JS code inside the client and execute, such as:

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 (' find '+ count +' graph ');" ; // Initialize the WKUserScript object according to the JS string. WKUserScript *script = [[WKUserScript alloc] initWithSource: JS injectionTime:WKUserScriptInjectionTimeAtDocumentEnd forMainFrameOnly:YES]; // Based on the generated WKUserScript object, WKWebViewConfiguration *config = [[WKWebViewConfiguration alloc] init]; [config.userContentController addUserScript:script]; _webView = [[WKWebView alloc] initWithFrame:self.view.bounds configuration:config]; NSString *html = @"<head></head><imgea src='http://www.nsu.edu.cn/v/2014v3/img/background/3.jpg' />"; [_webView loadHTMLString:html baseURL:nil]; [self.view addSubview:_webView];Copy the code

WKWebView involves proxy methods

1, WKNavigationDelegate protocol mainly handles some jump, load processing operations

  • Called when the page starts to load
- (void)webView:(WKWebView *)webView didStartProvisionalNavigation:(WKNavigation *)navigation {

}
Copy the code
  • Called when the page fails to load
- (void)webView:(WKWebView *)webView didFailProvisionalNavigation:(null_unspecified WKNavigation *)navigation withError:(NSError *)error {

    [self.progressView setProgress:0.0f animated:NO];
}
Copy the code
  • Called when content starts to return
- (void)webView:(WKWebView *)webView didCommitNavigation:(WKNavigation *)navigation {
}
Copy the code
  • Called after the page has loaded
- (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation {
    // [self getCookie];
}
Copy the code
  • Called when a commit error occurs
- (void)webView:(WKWebView *)webView didFailNavigation:(WKNavigation *)navigation withError:(NSError *)error { [self. ProgressView setProgress: 0.0 f animated: NO]; }Copy the code
  • Called after a server jump request, that is, a service redirect, is received
- (void)webView:(WKWebView *)webView didReceiveServerRedirectForProvisionalNavigation:(WKNavigation *)navigation {
}
Copy the code
  • According to the WebView for the HTTP request header to jump 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; NSString *htmlHeadString = @"github://"; OC NSURL * URL = [NSURL URLWithString:[urlStr stringByReplacingOccurrencesOfString:@"github://callName_?" withString:@""]]; [[UIApplication sharedApplication] openURL:url]; decisionHandler(WKNavigationActionPolicyCancel); // }else{ decisionHandler(WKNavigationActionPolicyAllow); // Allow direct jump}}Copy the code
  • According to the server response header received by the client, and the information related to response, to decide whether to jump
- (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); }Copy the code
  • Calls that need to respond to authentication also need to pass in 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); }Copy the code

2.WKUIDelegate protocol mainly handles JS script, confirmation box, warning box and so on

  • When a warning box is displayed on the Web interface
- (void)webView:(WKWebView *)webView runJavaScriptAlertPanelWithMessage:(NSString *)message InitiatedByFrame :(WKFrameInfo *)frame completionHandler:(void (^)(void))completionHandler {NSLog("HTML "); completionHandler(); }Copy the code

@param webView: The webView that implements the agent

@param Message: The content in the warning box. @param completionHandler: the warning box disappears callbackCopy the code
  • Confirmation box. Callback method after JavaScript calls confirm method

Confirm is the confirm box in JS that needs to be passed in the block to the user selection

- (void)webView:(WKWebView *)webView runJavaScriptConfirmPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(BOOL))completionHandler{
    completionHandler(NO);
}
Copy the code
  • Input box. The method called back after JavaScript calls the Prompt method

Prompt is the input box in JS, which needs to pass the user input information into the block

- (void)webView:(WKWebView *)webView 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]; }Copy the code
  • The page is handled by a popup window of _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

Page content loading progress bar

0. Add a progress bar

// @property (nonatomic, strong) UIProgressView *progressView; // Lazy load - (UIProgressView *)progressView {if (! _progressView) { _progressView = [[UIProgressView alloc] initWithFrame:CGRectMake(0, 0, SCREEN_W, 2)]; _progressView.backgroundColor = [UIColor blueColor]; _progressView. Transform = CGAffineTransformMakeScale (1.5 1.0 f, f); _progressView.progressTintColor = [UIColor app_color_yellow_eab201]; [self.view addSubview:self.progressView]; } return _progressView; }Copy the code

1. Add observers

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

2. Listening method

(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context { if ([keyPath isEqualToString:@"estimatedProgress"]) { self.progressView.progress = self.webView.estimatedProgress; If (self. ProgressView. Progress = = 1) {WeakSelfDeclare [UIView animateWithDuration: 0.25 f delay: 0.3 f options:UIViewAnimationOptionCurveEaseOut animations:^ { weakSelf.progressView.transform = CGAffineTransformMakeScale (1.0 f to 1.4 f);} completion: ^ (BOOL finished) {weakSelf. ProgressView. Hidden = YES;}]; } }else if([keyPath isEqualToString:@"title"] && object == _webView){ self.navigationItem.title = _webView.title; }else{ [super observeValueForKeyPath:keyPath ofObject:object change:change context:context]; }}Copy the code

3. Show or hide the progress bar

- (void)webView:(WKWebView *)webView didStartProvisionalNavigation:(WKNavigation *)navigation { self.progressView.hidden  = NO; Self. ProgressView. Transform = CGAffineTransformMakeScale (1.0 f to 1.5 f); [self.view bringSubviewToFront:self.progressView]; } - (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation { self.progressView.hidden = YES; } - (void)webView:(WKWebView *)webView didFailNavigation:(WKNavigation *)navigation withError:(NSError *)error { if(error.code==NSURLErrorCancelled) { [self webView:webView didFinishNavigation:navigation]; } else { self.progressView.hidden = YES; } } - (void)webView:(WKWebView *)webView didFailProvisionalNavigation:(WKNavigation *)navigation withError:(NSError *)error { self.progressView.hidden = YES; [self.navigationItem setTitleWithCustomLabel:@" Failed to load "]; }Copy the code

4. Remove observers

    [_webView removeObserver:self
                  forKeyPath:NSStringFromSelector(@selector(estimatedProgress))];
    [_webView removeObserver:self
                  forKeyPath:NSStringFromSelector(@selector(title))];
Copy the code

Clear the WK cache

- (void)cleanCacheAndCookie {// Clear cookies NSHTTPCookie *cookie; NSHTTPCookieStorage *storage = [NSHTTPCookieStorage sharedHTTPCookieStorage]; for (cookie in [storage cookies]) { [storage deleteCookie:cookie]; } [[NSURLCache sharedURLCache] removeAllCachedResponses]; NSURLCache * cache = [NSURLCache sharedURLCache]; [cache removeAllCachedResponses]; [cache setDiskCapacity:0]; [cache setMemoryCapacity:0]; WKWebsiteDataStore *dateStore = [WKWebsiteDataStore defaultDataStore]; [dateStore fetchDataRecordsOfTypes:[WKWebsiteDataStore allWebsiteDataTypes] completionHandler:^(NSArray<WKWebsiteDataRecord *> * __nonnull records) { for (WKWebsiteDataRecord *record in records) {  [[WKWebsiteDataStore defaultDataStore] removeDataOfTypes:record.dataTypes forDataRecords:@[record] completionHandler:^ { NSLog(@"Cookies for %@ deleted successfully",record.displayName); }]; } }]; }Copy the code
- (void)dealloc
{
    [_webView stopLoading];
    [_webView setNavigationDelegate:nil];
    [self clearCache];
    [self cleanCacheAndCookie];
}
Copy the code

Clicking on the link does not respond

#pragma mark WKNavigationDelegate -(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

WKWebView calculates the content height

Add KVO

[_webView.scrollView addObserver:selfforKeyPath:@"contentSize"options:NSKeyValueObservingOptionNewcontext:nil]; - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {if ([keyPath isEqualToString:@"contentSize"]) {dispatch_async(dispatch_get_global_queue(0,0), ^{ //document.documentElement.scrollHeight //document.body.offsetHeight [_webView evaluateJavaScript:@"document.documentElement.offsetHeight"completionHandler:^(id_Nullable result, NSError * _Nullable error) { CGRect frame =_webView.frame; frame.size.height = [result doubleValue] + 50; _webView.frame  = frame; _scrollViewHeight =220 + _webView.height; _scrollView.contentSize =CGSizeMake(kScreenWidth,_scrollViewHeight); }]; }); }}Copy the code

H5 calls the call function

Intercepts the feature scheme and executes the code to make the call

- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction DecisionHandler (void (^) (WKNavigationActionPolicy)) decisionHandler {/ / intercept NSURL * URL = navigationAction. Request. URL; NSString *scheme = [URL scheme]; if ([scheme isEqualToString:@"tel"]) { NSString *resourceSpecifier = [URL resourceSpecifier]; NSString *callPhone = [NSString stringWithFormat:@"telprompt://%@", resourceSpecifier]; decisionHandler(WKNavigationActionPolicyCancel); // Call [[UIApplication sharedApplication] openURL:[NSURL URLWithString:callPhone]]; return ; } decisionHandler(WKNavigationActionPolicyAllow); }Copy the code

The resources

www.jianshu.com/p/20cfd4f8c… www.jianshu.com/p/5cf0d241a… www.jianshu.com/p/6ba250744… * www.jianshu.com/p/4d12d593b…