WebKit框架解析(三)—— WKWebView替换UIWebView(一)

版本记录

版本号 时间
V1.0 2018.08.03

前言

iOS8OS X 10.10以后,苹果推出了新框架WebKit,提供了替换UIWebView的组件WKWebView。各种UIWebView的问题没有了,速度更快了,占用内存少了。接下来几篇我们就对WebKit框架进行全面深度的解析,还是老规矩,从简单到复杂,从面到点。感兴趣的可以看我写的上面几篇。
1. WebKit框架解析(一)—— 基本概览及WKWebView(一)
2. WebKit框架解析(二)—— 基本概览及WKWebView(二)

WKWebView优点

WKWebView是iOS 8.0就出现了,目前iOS系统已经出到11了,所以以支持最近三代系统的原则进行计算,很多App已经没必要支持iOS8了,而且iOS8用户非常之少了,同时考虑到WKWebViewUIWebView性能上带来的优越性,所以是时候考虑到替换webview的API了。

WKWebViewUIWebView性能上带来的优越性主要体现在如下几点:

  • 加载网页是占用的内存少,同样加载百度,WKWebView占用20M,而UIWebView占用85M。
  • 允许JavaScript的Nitro库加载并使用(UIWebView中限制)。
  • 支持了更多的HTML5特性。
  • 高达60fps的滚动刷新率以及内置手势。

加载网页

加载网页的方式和UIWebview是一样的,都是loadRequest

NSString *urlString = [self.urlStr stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
NSURL *url = [NSURL URLWithString:urlString];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url cachePolicy:NSURLRequestReloadIgnoringLocalCacheData timeoutInterval:60.0];
[self.webView loadRequest:request];

这里,self.webView就是WKWebView,self.urlStr 就是从外部传进来的URL了,具体如何传递可以根据业务逻辑进行处理,这里不是讨论的重点。


WKNavigationDelegate

这个是WKWebView三个代理中的一个,主要是负责加载的状态回调以及页面跳转。

/*! A class conforming to the WKNavigationDelegate protocol can provide
 methods for tracking progress for main frame navigations and for deciding
 policy for main frame and subframe navigations.
 */
@protocol WKNavigationDelegate <NSObject>

@optional

/*! @abstract Decides whether to allow or cancel a navigation.
 @param webView The web view invoking the delegate method.
 @param navigationAction Descriptive information about the action
 triggering the navigation request.
 @param decisionHandler The decision handler to call to allow or cancel the
 navigation. The argument is one of the constants of the enumerated type WKNavigationActionPolicy.
 @discussion If you do not implement this method, the web view will load the request or, if appropriate, forward it to another application.
 */
- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler;

/*! @abstract Decides whether to allow or cancel a navigation after its
 response is known.
 @param webView The web view invoking the delegate method.
 @param navigationResponse Descriptive information about the navigation
 response.
 @param decisionHandler The decision handler to call to allow or cancel the
 navigation. The argument is one of the constants of the enumerated type WKNavigationResponsePolicy.
 @discussion If you do not implement this method, the web view will allow the response, if the web view can show it.
 */
- (void)webView:(WKWebView *)webView decidePolicyForNavigationResponse:(WKNavigationResponse *)navigationResponse decisionHandler:(void (^)(WKNavigationResponsePolicy))decisionHandler;

/*! @abstract Invoked when a main frame navigation starts.
 @param webView The web view invoking the delegate method.
 @param navigation The navigation.
 */
- (void)webView:(WKWebView *)webView didStartProvisionalNavigation:(null_unspecified WKNavigation *)navigation;

/*! @abstract Invoked when a server redirect is received for the main
 frame.
 @param webView The web view invoking the delegate method.
 @param navigation The navigation.
 */
- (void)webView:(WKWebView *)webView didReceiveServerRedirectForProvisionalNavigation:(null_unspecified WKNavigation *)navigation;

/*! @abstract Invoked when an error occurs while starting to load data for
 the main frame.
 @param webView The web view invoking the delegate method.
 @param navigation The navigation.
 @param error The error that occurred.
 */
- (void)webView:(WKWebView *)webView didFailProvisionalNavigation:(null_unspecified WKNavigation *)navigation withError:(NSError *)error;

/*! @abstract Invoked when content starts arriving for the main frame.
 @param webView The web view invoking the delegate method.
 @param navigation The navigation.
 */
- (void)webView:(WKWebView *)webView didCommitNavigation:(null_unspecified WKNavigation *)navigation;

/*! @abstract Invoked when a main frame navigation completes.
 @param webView The web view invoking the delegate method.
 @param navigation The navigation.
 */
- (void)webView:(WKWebView *)webView didFinishNavigation:(null_unspecified WKNavigation *)navigation;

/*! @abstract Invoked when an error occurs during a committed main frame
 navigation.
 @param webView The web view invoking the delegate method.
 @param navigation The navigation.
 @param error The error that occurred.
 */
- (void)webView:(WKWebView *)webView didFailNavigation:(null_unspecified WKNavigation *)navigation withError:(NSError *)error;

/*! @abstract Invoked when the web view needs to respond to an authentication challenge.
 @param webView The web view that received the authentication challenge.
 @param challenge The authentication challenge.
 @param completionHandler The completion handler you must invoke to respond to the challenge. The
 disposition argument is one of the constants of the enumerated type
 NSURLSessionAuthChallengeDisposition. When disposition is NSURLSessionAuthChallengeUseCredential,
 the credential argument is the credential to use, or nil to indicate continuing without a
 credential.
 @discussion If you do not implement this method, the web view will respond to the authentication challenge with the NSURLSessionAuthChallengeRejectProtectionSpace disposition.
 */
- (void)webView:(WKWebView *)webView didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential * _Nullable credential))completionHandler;

/*! @abstract Invoked when the web view's web content process is terminated.
 @param webView The web view whose underlying web content process was terminated.
 */
- (void)webViewWebContentProcessDidTerminate:(WKWebView *)webView API_AVAILABLE(macosx(10.11), ios(9.0));

@end

下面分类详细的说一下

1. 加载回调状态

上面这些代理方法中有几个是和加载回调状态相关的。

// 页面开始加载时调用
- (void)webView:(WKWebView *)webView didStartProvisionalNavigation:(WKNavigation *)navigation;

// 当内容开始返回时调用
- (void)webView:(WKWebView *)webView didCommitNavigation:(WKNavigation *)navigation;

// 页面加载完成之后调用
- (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation;

// 页面加载失败时调用
- (void)webView:(WKWebView *)webView didFailProvisionalNavigation:(WKNavigation *)navigation;
// 在这里处理失败的逻辑,包括断网逻辑的处理

2. 页面跳转状态

// 接收到服务器跳转请求之后调用
- (void)webView:(WKWebView *)webView didReceiveServerRedirectForProvisionalNavigation:(WKNavigation *)navigation;

// 在收到响应后,决定是否跳转
- (void)webView:(WKWebView *)webView decidePolicyForNavigationResponse:(WKNavigationResponse *)navigationResponse decisionHandler:(void (^)(WKNavigationResponsePolicy))decisionHandler;

// 在发送请求之前,决定是否跳转
- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler;

这里,我们用的比较多的方法就是- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler。一般在这个方法里面可以打印出要跳转的全地址。

//打印请求的全地址
NSLog(@"==== decidePolicyForNavigationAction url = %@", navigationAction.request.URL.absoluteString);

这里需要给个回调WKNavigationActionPolicy是一个枚举,其实就是告诉API要不要跳转。

/*! @enum WKNavigationActionPolicy
 @abstract The policy to pass back to the decision handler from the
 webView:decidePolicyForNavigationAction:decisionHandler: method.
 @constant WKNavigationActionPolicyCancel   Cancel the navigation.
 @constant WKNavigationActionPolicyAllow    Allow the navigation to continue.
 */
typedef NS_ENUM(NSInteger, WKNavigationActionPolicy) {
    WKNavigationActionPolicyCancel,
    WKNavigationActionPolicyAllow,
} API_AVAILABLE(macosx(10.10), ios(8.0));

当我们回调WKNavigationActionPolicyCancel时,就不能进行跳转到相应的url,选择WKNavigationActionPolicyAllow就可以跳转到相应的URL,二者只能返回一个,否则如果连着分别进行返回就会直接Crash。


WKUIDelegate

这个也是WKWebView的代理,其主要的API如下所示:

@class WKFrameInfo;
@class WKNavigationAction;
@class WKOpenPanelParameters;
@class WKPreviewElementInfo;
@class WKWebView;
@class WKWebViewConfiguration;
@class WKWindowFeatures;

/*! A class conforming to the WKUIDelegate protocol provides methods for
 presenting native UI on behalf of a webpage.
 */
@protocol WKUIDelegate <NSObject>

@optional

/*! @abstract Creates a new web view.
 @param webView The web view invoking the delegate method.
 @param configuration The configuration to use when creating the new web
 view.
 @param navigationAction The navigation action causing the new web view to
 be created.
 @param windowFeatures Window features requested by the webpage.
 @result A new web view or nil.
 @discussion The web view returned must be created with the specified configuration. WebKit will load the request in the returned web view.

 If you do not implement this method, the web view will cancel the navigation.
 */
- (nullable WKWebView *)webView:(WKWebView *)webView createWebViewWithConfiguration:(WKWebViewConfiguration *)configuration forNavigationAction:(WKNavigationAction *)navigationAction windowFeatures:(WKWindowFeatures *)windowFeatures;

/*! @abstract Notifies your app that the DOM window object's close() method completed successfully.
  @param webView The web view invoking the delegate method.
  @discussion Your app should remove the web view from the view hierarchy and update
  the UI as needed, such as by closing the containing browser tab or window.
  */
- (void)webViewDidClose:(WKWebView *)webView API_AVAILABLE(macosx(10.11), ios(9.0));

/*! @abstract Displays a JavaScript alert panel.
 @param webView The web view invoking the delegate method.
 @param message The message to display.
 @param frame Information about the frame whose JavaScript initiated this
 call.
 @param completionHandler The completion handler to call after the alert
 panel has been dismissed.
 @discussion For user security, your app should call attention to the fact
 that a specific website controls the content in this panel. A simple forumla
 for identifying the controlling website is frame.request.URL.host.
 The panel should have a single OK button.

 If you do not implement this method, the web view will behave as if the user selected the OK button.
 */
- (void)webView:(WKWebView *)webView runJavaScriptAlertPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(void))completionHandler;

/*! @abstract Displays a JavaScript confirm panel.
 @param webView The web view invoking the delegate method.
 @param message The message to display.
 @param frame Information about the frame whose JavaScript initiated this call.
 @param completionHandler The completion handler to call after the confirm
 panel has been dismissed. Pass YES if the user chose OK, NO if the user
 chose Cancel.
 @discussion For user security, your app should call attention to the fact
 that a specific website controls the content in this panel. A simple forumla
 for identifying the controlling website is frame.request.URL.host.
 The panel should have two buttons, such as OK and Cancel.

 If you do not implement this method, the web view will behave as if the user selected the Cancel button.
 */
- (void)webView:(WKWebView *)webView runJavaScriptConfirmPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(BOOL result))completionHandler;

/*! @abstract Displays a JavaScript text input panel.
 @param webView The web view invoking the delegate method.
 @param message The message to display.
 @param defaultText The initial text to display in the text entry field.
 @param frame Information about the frame whose JavaScript initiated this call.
 @param completionHandler The completion handler to call after the text
 input panel has been dismissed. Pass the entered text if the user chose
 OK, otherwise nil.
 @discussion For user security, your app should call attention to the fact
 that a specific website controls the content in this panel. A simple forumla
 for identifying the controlling website is frame.request.URL.host.
 The panel should have two buttons, such as OK and Cancel, and a field in
 which to enter text.

 If you do not implement this method, the web view will behave as if the user selected the Cancel button.
 */
- (void)webView:(WKWebView *)webView runJavaScriptTextInputPanelWithPrompt:(NSString *)prompt defaultText:(nullable NSString *)defaultText initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(NSString * _Nullable result))completionHandler;

#if TARGET_OS_IPHONE

/*! @abstract Allows your app to determine whether or not the given element should show a preview.
 @param webView The web view invoking the delegate method.
 @param elementInfo The elementInfo for the element the user has started touching.
 @discussion To disable previews entirely for the given element, return NO. Returning NO will prevent 
 webView:previewingViewControllerForElement:defaultActions: and webView:commitPreviewingViewController:
 from being invoked.
 
 This method will only be invoked for elements that have default preview in WebKit, which is
 limited to links. In the future, it could be invoked for additional elements.
 */
- (BOOL)webView:(WKWebView *)webView shouldPreviewElement:(WKPreviewElementInfo *)elementInfo API_AVAILABLE(ios(10.0));

/*! @abstract Allows your app to provide a custom view controller to show when the given element is peeked.
 @param webView The web view invoking the delegate method.
 @param elementInfo The elementInfo for the element the user is peeking.
 @param defaultActions An array of the actions that WebKit would use as previewActionItems for this element by 
 default. These actions would be used if allowsLinkPreview is YES but these delegate methods have not been 
 implemented, or if this delegate method returns nil.
 @discussion Returning a view controller will result in that view controller being displayed as a peek preview.
 To use the defaultActions, your app is responsible for returning whichever of those actions it wants in your 
 view controller's implementation of -previewActionItems.
 
 Returning nil will result in WebKit's default preview behavior. webView:commitPreviewingViewController: will only be invoked
 if a non-nil view controller was returned.
 */
- (nullable UIViewController *)webView:(WKWebView *)webView previewingViewControllerForElement:(WKPreviewElementInfo *)elementInfo defaultActions:(NSArray<id <WKPreviewActionItem>> *)previewActions API_AVAILABLE(ios(10.0));

/*! @abstract Allows your app to pop to the view controller it created.
 @param webView The web view invoking the delegate method.
 @param previewingViewController The view controller that is being popped.
 */
- (void)webView:(WKWebView *)webView commitPreviewingViewController:(UIViewController *)previewingViewController API_AVAILABLE(ios(10.0));

#endif // TARGET_OS_IPHONE

#if !TARGET_OS_IPHONE

/*! @abstract Displays a file upload panel.
 @param webView The web view invoking the delegate method.
 @param parameters Parameters describing the file upload control.
 @param frame Information about the frame whose file upload control initiated this call.
 @param completionHandler The completion handler to call after open panel has been dismissed. Pass the selected URLs if the user chose OK, otherwise nil.

 If you do not implement this method, the web view will behave as if the user selected the Cancel button.
 */
- (void)webView:(WKWebView *)webView runOpenPanelWithParameters:(WKOpenPanelParameters *)parameters initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(NSArray<NSURL *> * _Nullable URLs))completionHandler API_AVAILABLE(macosx(10.12));

#endif

这个协议主要用于WKWebView处理web界面的三种提示框(警告框、确认框、输入框)。


WebView与JS交互

其实就是原生与H5之间的交互。

1. WebView调用JS

这个其实和UIWebView是一样的,都是调用一句话就可以。

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

2. JS调用WebView

与webView调用JS相比,这个就有点复杂了。UIWebView中可以使用JavaScriptCore使用JSExport协议实现H5的JS调用原生,但是对于WKWebView来说不在支持JavaScriptCore了,而是有专门对应的类WKUserContentController中的下面方法进行处理。

- (void)addScriptMessageHandler:(id <WKScriptMessageHandler>)scriptMessageHandler name:(NSString *)name;

这里,name就是调用的H5调用原生的方法名,scriptMessageHandler是代理回调,JS调用name方法后,OC会调用scriptMessageHandler指定的对象。

先看下面一个简单的示例,后面会给一个比较全的调用封装。

//OC注册供JS调用的方法
[[_webView configuration].userContentController addScriptMessageHandler:self name:@"H5CallNative"];

//OC在JS调用方法做的处理
- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message
{
    NSLog(@"JS 调用了 %@ 方法,传回参数 %@",message.name, message.body);
}
//JS调用
 window.webkit.messageHandlers.H5CallNative.postMessage(null);

这里H5CallNative就是H5调用的OC方法名。

3. JS和WKWebView交互引擎的封装

1)webview中处理部分

在实例化webview时要设置引擎对象,一般如下所示:

WKUserContentController *userContentVC = [[WKUserContentController alloc] init];
WKWebViewConfiguration *config = [[WKWebViewConfiguration alloc] init];
config.userContentController = userContentVC;
self.webView = [[WKWebView alloc] initWithFrame:rect configuration:config];
self.webView.navigationDelegate = self;
[userContentVC addScriptMessageHandler:[JJJSEngine shared] name:@"JSCallNative"];
[JJJSEngine shared].webview = self.webView;

这里JJJSEngine是处理交互使用的引擎,@"JSCallNative"就是JS要调用的native的方法名。

注意在dealloc中使用完要移除引擎和清空引擎。

- (void)dealloc
{
    [[JJJSEngine shared] cleanUp];
    [JJJSEngine shared].webview = nil;
    [[self.webView configuration].userContentController removeScriptMessageHandlerForName:@"JSCallNative"];
}

2)JJJSEngine引擎中处理部分

下面就看一下JJJSEngine引擎中需要处理的内容。

#import <Foundation/Foundation.h>
#import <WebKit/WebKit.h>

@interface JJJSEngine : NSObject <WKScriptMessageHandler>

@property (nonatomic, weak) WKWebView *webview;
@property (nonatomic, weak) id <WKScriptMessageHandler> scriptDelegate;

+ (instancetype)shared;

//清理
- (void)cleanUp;

//native向js发送消息
- (void)sendJSMessage:(NSString *)content;

@end
@interface JJJSEngine()

@property (nonatomic, strong) dispatch_queue_t jsQueue;
@property (nonatomic, strong) NSMutableDictionary *jsCallbacksDictM;

@end

@implementation JJJSEngine

#pragma mark -  Override Base Function

- (instancetype)init
{
    self = [super init];
    if (self) {
        _jsQueue = dispatch_queue_create("com.xxxxx.jsbridge", DISPATCH_QUEUE_SERIAL);
        _jsCallbacksDictM = [NSMutableDictionary dictionaryWithCapacity:5];
    }
    return self;
}

#pragma mark -  Object Private Function

//发送JS消息
- (void)sendJSMessageName:(NSString *)callback response:(NSDictionary *)response
{
    if ([callback isNotEmpty] == NO || self.webview == nil) {
        return;
    }
    
    NSString *responseStr = [JSONHelp object2NSString:response];
    responseStr = responseStr ? : @"";
    responseStr = [responseStr stringByReplacingOccurrencesOfString:@"'" withString:@"\'"];
    NSString *jsCallStr = [NSString stringWithFormat: @"%@(\'%@\');", callback, responseStr];
    dispatch_async(dispatch_get_main_queue(), ^{
        @try {
            [self.webview evaluateJavaScript:jsCallStr completionHandler:^(id _Nullable result, NSError * _Nullable error) {
                if (error != nil) {
                    DDLogError(@"evaluate script fail :%@",jsCallStr);
                }
            }];
        } @catch (NSException *exception) {
            DDLogError(@"webview evaluate error :%@",[exception reason]);
        }
    });
}

#pragma mark -  Object Public Function

- (void)cleanUp
{
    [self.jsCallbacksDictM removeAllObjects];
}

//native向js发送消息
- (void)sendJSMessage:(NSString *)content
{
    [self sendJSMessageName:self.jsCallbacksDictM[@"xxxx"] response:response];
}

#pragma mark -  Class Public Function

+ (instancetype)shared
{
    static JJJSEngine *object;
    static dispatch_once_t predicate;
    dispatch_once(&predicate, ^{
        object = [[self alloc] init];
    });
    return object;
}

#pragma mark - WKScriptMessageHandler

- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message
{
    NSLog(@"JS 调用了 %@ 方法,传回参数 %@",message.name, message.body);
    
    if ([message.name isEqualToString:@"JSCallNative"]) {
        //在这里处理Json字符串并根据actionType进行其他逻辑处理
    }
}

@end

3)JS中处理部分

window.webkit.messageHandlers.JSCallNative.postMessage("待传递处理的json字符串");

Cookie存放信息

先说一下一个应用场景,很多时候我们需要将个别几个信息放在cookie里面,然后当我们加载webview的时候,H5会拿我们的信息去服务端去请求数据,然后渲染页面,比如说我的等级页面,需要uid和token,然后请求服务器,这样的显示界面才是属于我们自己的等级。还有比如关于App的界面需要传递一个版本号,那么也可以在cookie里面进行存储。

1. UIWebView存储cookie信息

在UIWebView中我们直接可以使用下面的例子进行cookie的信息存储。

//获取 Version  版本号
NSString *version = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleShortVersionString"];

//获取 Build  构建版本号
NSString *build = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleVersion"];
NSString *versionStr = [NSString stringWithFormat:@"%@.%@", version, build];
NSHTTPCookie *versionCookie = [NSHTTPCookie cookieWithProperties:@{NSHTTPCookieName:@"version",
                                                                   NSHTTPCookieValue:_To_Str(versionStr),
                                                                   NSHTTPCookiePath:@"/",
                                                                   NSHTTPCookieDomain:@".xxxx.com",
                                                                   NSHTTPCookieOriginURL:@".xxxx.com"}];
[[NSHTTPCookieStorage sharedHTTPCookieStorage] setCookie:versionCookie];

所以使用NSHTTPCookieStorage就可以实现cookie中的信息存储和传递。

2. WKWebView存储cookie信息

对于WKWebView来说上面的那一套cookie的NSHTTPCookieStorage机制就不管用了,如果还是按照那种存储方式的话,你打开调试器可以看见cookie里面根本就没有值,也就是说NSHTTPCookieStorageWKWebView中失效了。

真正的解决方法就是用WKUserScript进行注入

//在WKWebView实例化的时候
NSString *cookieStr = [self calculateCookie];
WKUserScript *cookieScript = [[WKUserScript alloc] initWithSource: cookieStr injectionTime:WKUserScriptInjectionTimeAtDocumentStart forMainFrameOnly:NO];
WKUserContentController *userContentVC = [[WKUserContentController alloc] init];
[userContentVC addUserScript:cookieScript];
//获取要注入的cookie字符串
- (NSString *)calculateCookie
{
    NSString *cookieStr = @"";
    //获取 Version  版本号
    NSString *version = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleShortVersionString"];

    //获取 Build  构建版本号
    NSString *build = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleVersion"];
    NSString *versionStr = [NSString stringWithFormat:@"%@.%@", version, build];
    cookieStr = [NSString stringWithFormat:@"document.cookie='token=%@';document.cookie='userId=%@';document.cookie='version=%@'", [JJUserManager token], [JJUserManager userID], versionStr];
    return cookieStr;
}

这样,我们通过网络监察器查看,就可以看见cookie中确实有值了。


动态设置导航栏title

1. UIWebView设置title

UIWebView可以在代理方法- (void)webViewDidFinishLoad:(UIWebView *)webView中,也就是加载完成后设置title,如下所示:

NSString *title = [self.webView stringByEvaluatingJavaScriptFromString:@"document.title"];
self.title = title;

2. WKWebView设置title

对于WKWebView,可以使用KVO监听设置title,如下所示

//webview实例化后就添加KVO
[self.webView addObserver:self forKeyPath:@"title" options:NSKeyValueObservingOptionNew context:nil];
#pragma mark -  KVO

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context
{
    if ([keyPath isEqualToString:@"title"]) {
        if (object == self.webView) {
            self.title = self.webView.title;
        }
        else{
            [super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
        }
    }
}

注意:在dealloc后要移除观察。

[self.webView removeObserver:self forKeyPath:@"title"];

参考文章

1. 使用WKWebView替换UIWebView

后记

本篇主要讲述了WKWebView替换UIWebView,感兴趣的给个赞或者关注~~~~

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,793评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,567评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,342评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,825评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,814评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,680评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,033评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,687评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,175评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,668评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,775评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,419评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,020评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,978评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,206评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,092评论 2 351
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,510评论 2 343

推荐阅读更多精彩内容