iOS 加载WKWebView

WKWebView是苹果在iOS 8之后推出的框架WebKit中的浏览器控件, 其加载速度比UIWebView快了许多, 但内存占用率却下降很多, 也解决了加载网页时的内存泄露问题.

WKWebView的属性
/// webView的自定义配置

@property (nonatomic,readonly, copy) WKWebViewConfiguration *configuration;

/// 导航代理

@property (nullable, nonatomic, weak)id <WKNavigationDelegate> navigationDelegate;

/// UI代理

@property (nullable, nonatomic, weak)id <WKUIDelegate> UIDelegate;;

/// 访问过网页历史列表

@property (nonatomic,readonly, strong) WKBackForwardList *backForwardList;

/// 自定义初始化

- (instancetype)initWithFrame:(CGRect)frame configuration:(WKWebViewConfiguration *)configuration NS_DESIGNATED_INITIALIZER;- (nullable instancetype)initWithCoder:(NSCoder *)coder NS_DESIGNATED_INITIALIZER;

/// url加载webView视图

- (nullable WKNavigation *)loadRequest:(NSURLRequest *)request;

/// 文件加载webView视图

- (nullable WKNavigation *)loadFileURL:(NSURL *)URL allowingReadAccessToURL:(NSURL *)readAccessURL API_AVAILABLE(macosx(10.11), ios(9.0));

/// HTMLString字符串加载webView视图

- (nullable WKNavigation *)loadHTMLString:(NSString *)stringbaseURL:(nullable NSURL *)baseURL;

/// NSData数据加载webView视图

- (nullable WKNavigation *)loadData:(NSData *)data MIMEType:(NSString *)MIMEType characterEncodingName:(NSString *)characterEncodingName baseURL:(NSURL *)baseURL API_AVAILABLE(macosx(10.11), ios(9.0));

/// 返回上一个网页节点

- (nullable WKNavigation *)goToBackForwardListItem:(WKBackForwardListItem *)item;

/// 网页的标题, 一般使用KVO动态获取

@property (nullable, nonatomic,readonly, copy) NSString *title;

/// 页面加载进度, 一般使用KVO动态获取
@property (nonatomic, readonly) double estimatedProgress;

/// 网页的URL地址

@property (nullable, nonatomic,readonly, copy) NSURL *URL;

/// 网页是否正在加载

@property (nonatomic,readonly, getter=isLoading) BOOL loading;

/// 加载的进度 范围为[0, 1]

@property (nonatomic,readonly)double estimatedProgress;

/// 网页链接是否安全

@property (nonatomic,readonly) BOOL hasOnlySecureContent;

/// 证书服务

@property (nonatomic,readonly, nullable) SecTrustRef serverTrust API_AVAILABLE(macosx(10.12), ios(10.0));

/// 是否可以返回

@property (nonatomic,readonly) BOOL canGoBack;

/// 是否可以前进

@property (nonatomic,readonly) BOOL canGoForward;

/// 返回到上一个网页

- (nullable WKNavigation *)goBack;

/// 前进到下一个网页

- (nullable WKNavigation *)goForward;

/// 重新加载

- (nullable WKNavigation *)reload;

/// 忽略缓存 重新加载

- (nullable WKNavigation *)reloadFromOrigin;

/// 停止加载

- (void)stopLoading;

/// 执行JavaScript

- (void)evaluateJavaScript:(NSString *)javaScriptString completionHandler:(void(^ _Nullable)(_Nullableid, NSError * _Nullable error))completionHandler;

/// 是否允许左右滑动,返回-前进操作  默认是NO

@property (nonatomic) BOOL allowsBackForwardNavigationGestures;

/// 自定义代理字符串

@property (nullable, nonatomic, copy) NSString *customUserAgent API_AVAILABLE(macosx(10.11), ios(9.0));

/// 在iOS上默认为NO,标识不允许链接预览

@property (nonatomic) BOOL allowsLinkPreview API_AVAILABLE(macosx(10.11), ios(9.0));

/// 滚动视图, WKWebView继承自UIView, 所以如果想设置scrollView的一些属性, 需要对此属性进行配置


@property (nonatomic,readonly, strong) UIScrollView *scrollView;

/// 是否支持放大手势,默认为NO

@property (nonatomic) BOOL allowsMagnification;

/// 放大因子,默认为1

@property (nonatomic) CGFloat magnification;

/// 据设置的缩放因子来缩放页面,并居中显示结果在指定的点

- (void)setMagnification:(CGFloat)magnification centeredAtPoint:(CGPoint)point;/// 证书列表@property (nonatomic,readonly, copy) NSArray *certificateChain API_DEPRECATED_WITH_REPLACEMENT("serverTrust", macosx(10.11,10.12), ios(9.0,10.0));

WKWebView的常用方法:
// 带配置信息的初始化方法
// configuration 配置信息
- (instancetype)initWithFrame:(CGRect)frame configuration:(WKWebViewConfiguration *)configuration
// 加载请求
- (nullable WKNavigation *)loadRequest:(NSURLRequest *)request;
// 加载HTML
- (nullable WKNavigation *)loadHTMLString:(NSString *)string baseURL:(nullable NSURL *)baseURL;
// 返回上一级
- (nullable WKNavigation *)goBack;
// 前进下一级, 需要曾经打开过, 才能前进
- (nullable WKNavigation *)goForward;
// 刷新页面
- (nullable WKNavigation *)reload;
// 根据缓存有效期来刷新页面
- (nullable WKNavigation *)reloadFromOrigin;
// 停止加载页面
- (void)stopLoading;
// 执行JavaScript代码
- (void)evaluateJavaScript:(NSString *)javaScriptString completionHandler:(void (^ _Nullable)(_Nullable id, NSError * _Nullable error))completionHandler;

WKWebView的使用
WKWebViewConfiguration *config = [[WKWebViewConfiguration alloc] init];

config.selectionGranularity = WKSelectionGranularityDynamic;

config.allowsInlineMediaPlayback = YES;

WKPreferences *preferences = [WKPreferences new];

//是否支持JavaScript

preferences.javaScriptEnabled = YES;

//不通过用户交互,是否可以打开窗口

preferences.javaScriptCanOpenWindowsAutomatically = YES;

config.preferences = preferences;

// 创建UserContentController(提供JavaScript向webView发送消息的方法)
    WKUserContentController* userContent = [[WKUserContentController alloc] init];
    // 添加消息处理,注意:self指代的对象需要遵守WKScriptMessageHandler协议,结束时需要移除
    [userContent addScriptMessageHandler:self name:@"NativeMethod"];
    // 将UserConttentController设置到配置文件
    config.userContentController = userContent;

WKWebView *webview = [[WKWebView alloc] initWithFrame:CGRectMake(0, 0, KScreenWidth, KScreenHeight - 64) configuration:config];

[self.view addSubview:webview];

/* 加载服务器url的方法*/

NSString *url = @"https://www.baidu.com/";

NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:url]];

[webview loadRequest:request];

webview.navigationDelegate = self;

webview.UIDelegate = self;

#pragma mark - WKNavigationDelegate
/* 页面开始加载 */
- (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{
     
}
/* 在发送请求之前,决定是否跳转 */
- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler{

  NSURL *url =  navigationAction.request.URL;
    
    NSString *urlStr = url.absoluteString;
    NSLog(@"【load url】=== %@", urlStr);
   //不允许跳转
   //decisionHandler(WKNavigationActionPolicyCancel);
   //return;

    //允许跳转
    decisionHandler(WKNavigationActionPolicyAllow);
    
}
/* 在收到响应后,决定是否跳转 */
- (void)webView:(WKWebView *)webView decidePolicyForNavigationResponse:(WKNavigationResponse *)navigationResponse decisionHandler:(void (^)(WKNavigationResponsePolicy))decisionHandler{
     
    NSLog(@"===%@",navigationResponse.response.URL.absoluteString);
    //允许跳转
    decisionHandler(WKNavigationResponsePolicyAllow);
    //不允许跳转
    //decisionHandler(WKNavigationResponsePolicyCancel);
}

// 接收到服务器跳转请求之后调用

- (void)webView:(WKWebView *)webView didReceiveServerRedirectForProvisionalNavigation:(WKNavigation *)navigation {

    NSLog(@"-------接收到服务器跳转请求之后调用");

}

// 数据加载发生错误时调用

- (void)webView:(WKWebView *)webView didFailNavigation:(null_unspecified WKNavigation *)navigation withError:(NSError *)error {

    NSLog(@"----数据加载发生错误时调用");

}

// 需要响应身份验证时调用 同样在block中需要传入用户身份凭证

- (void)webView:(WKWebView *)webView didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void(^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential * _Nullable credential))completionHandler {

    //用户身份信息    NSLog(@"----需要响应身份验证时调用 同样在block中需要传入用户身份凭证");

    NSURLCredential *newCred = [NSURLCredential credentialWithUser:@""                                                          password:@""                                                      persistence:NSURLCredentialPersistenceNone];

    // 为 challenge 的发送方提供 credential    [[challenge sender] useCredential:newCred forAuthenticationChallenge:challenge];

    completionHandler(NSURLSessionAuthChallengeUseCredential,newCred);

}

// 进程被终止时调用

- (void)webViewWebContentProcessDidTerminate:(WKWebView *)webView {

    NSLog(@"----------进程被终止时调用");

}

自定义配置

WKUserContentController 是JavaScript与原生进行交互的桥梁, 主要使用的方法有:

// 注入JavaScript与原生交互协议

// JS 端可通过 window.webkit.messageHandlers.<name>.postMessage(<messageBody>) 发送消息
- (void)addScriptMessageHandler:(id <WKScriptMessageHandler>)scriptMessageHandler name:(NSString *)name;
// 移除注入的协议, 在deinit方法中调用
- (void)removeScriptMessageHandlerForName:(NSString *)name;

实现WKScriptMessageHandler协议方法:

JavaScript调用原生方法

- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message {

  // 判断是否是调用原生的  

    if([@"NativeMethod" isEqualToString:message.name]) { 

        // 判断message的内容,然后做相应的操作   

        if([@"close" isEqualToString:message.body]) {

        }

     }

}
在 message的name和body属性中我们可以获取到与JS调取原生的方法名和所传递的参数。

注意:上面将当前ViewController设置为MessageHandler之后需要在当前ViewController销毁前将其移除,否则会造成内存泄漏。

[self.webView.configuration.userContentController removeScriptMessageHandlerForName:@"NativeMethod"];

原生调用JS方法

// iosImg 为js方法名,后面跟的是参数

NSString *promptCode = [NSString stringWithFormat:@"iosImg('%@','%@')",self.picType,longStr];
    
    [_webView evaluateJavaScript:promptCode completionHandler:^(id object,NSError *error) {
        
        NSLog(@"=======%@",error);
        
    }];
WKUIDelegate代理方法

web界面中有弹出警告框时调用
这个代理方法, 主要是用来处理使用系统的弹框来替换JS中的一些弹框的,比如: 警告框, 选择框, 输入框,

/**
 webView中弹出警告框时调用, 只能有一个按钮

 @param webView webView
 @param message 提示信息
 @param frame 可用于区分哪个窗口调用的
 @param completionHandler 警告框消失的时候调用, 回调给JS
 */

- (void)webView:(WKWebView *)webView runJavaScriptAlertPanelWithMessage:(NSString *)message initiatedByFrame:(void(^)())completionHandler {

    NSLog(@"-------web界面中有弹出警告框时调用");

UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"警告" message:message preferredStyle:(UIAlertControllerStyleAlert)];
    UIAlertAction *ok = [UIAlertAction actionWithTitle:@"我知道了" style:(UIAlertActionStyleDefault) handler:^(UIAlertAction * _Nonnull action) {
        completionHandler();
    }];
    
    [alert addAction:ok];
    [self presentViewController:alert animated:YES completion:nil];


}

下面这些方法是交互JavaScript的方法

// JavaScript调用confirm方法后回调的方法 confirm是js中的确定框,需要在block中把用户选择的情况传递进去
/** 对应js的confirm方法
 webView中弹出选择框时调用, 两个按钮

 @param webView webView description
 @param message 提示信息
 @param frame 可用于区分哪个窗口调用的
 @param completionHandler 确认框消失的时候调用, 回调给JS, 参数为选择结果: YES or NO
 */

-(void)webView:(WKWebView *)webView runJavaScriptConfirmPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void(^)(BOOL))completionHandler {

    NSLog(@"%@",message);

    completionHandler(YES);

/*
UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"请选择" message:message preferredStyle:(UIAlertControllerStyleAlert)];
    UIAlertAction *ok = [UIAlertAction actionWithTitle:@"同意" style:(UIAlertActionStyleDefault) handler:^(UIAlertAction * _Nonnull action) {
        completionHandler(YES);
    }];
    
    UIAlertAction *cancel = [UIAlertAction actionWithTitle:@"不同意" style:(UIAlertActionStyleCancel) handler:^(UIAlertAction * _Nonnull action) {
        completionHandler(NO);
    }];
    
    [alert addAction:ok];
    [alert addAction:cancel];
    [self presentViewController:alert animated:YES completion:nil];

*/

}

// JavaScript调用prompt方法后回调的方法 prompt是js中的输入框 需要在block中把用户输入的信息传入
/** 对应js的prompt方法
 webView中弹出输入框时调用, 两个按钮 和 一个输入框

 @param webView webView description
 @param prompt 提示信息
 @param defaultText 默认提示文本
 @param frame 可用于区分哪个窗口调用的
 @param completionHandler 输入框消失的时候调用, 回调给JS, 参数为输入的内容
 */

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

    NSLog(@"%@",prompt);

    completionHandler(@"123");

/*
UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"请输入" message:prompt preferredStyle:(UIAlertControllerStyleAlert)];

    
    [alert addTextFieldWithConfigurationHandler:^(UITextField * _Nonnull textField) {
        textField.placeholder = @"请输入";
    }];
    
    UIAlertAction *ok = [UIAlertAction actionWithTitle:@"确定" style:(UIAlertActionStyleDefault) handler:^(UIAlertAction * _Nonnull action) {
        
        UITextField *tf = [alert.textFields firstObject];
        
                completionHandler(tf.text);
    }];
    
    UIAlertAction *cancel = [UIAlertAction actionWithTitle:@"取消" style:(UIAlertActionStyleCancel) handler:^(UIAlertAction * _Nonnull action) {
                completionHandler(defaultText);
    }];
    
    [alert addAction:ok];
    [alert addAction:cancel];
    [self presentViewController:alert animated:YES completion:nil];

*/

}

// 默认预览元素调用

- (BOOL)webView:(WKWebView *)webView shouldPreviewElement:(WKPreviewElementInfo *)elementInfo {

    NSLog(@"-----默认预览元素调用");

    return YES;

}

// 返回一个视图控制器将导致视图控制器被显示为一个预览。返回nil将WebKit的默认预览的行为。

- (nullable UIViewController *)webView:(WKWebView *)webView previewingViewControllerForElement:(WKPreviewElementInfo *)elementInfo defaultActions:(NSArray> *)previewActions {

    NSLog(@"----返回一个视图控制器将导致视图控制器被显示为一个预览。返回nil将WebKit的默认预览的行为。");

    return self;

}

// 允许应用程序向它创建的视图控制器弹出

- (void)webView:(WKWebView *)webView commitPreviewingViewController:(UIViewController *)previewingViewController {

    NSLog(@"----允许应用程序向它创建的视图控制器弹出");

}

// 显示一个文件上传面板。completionhandler完成处理程序调用后打开面板已被撤销。通过选择的网址,如果用户选择确定,否则为零。如果不实现此方法,Web视图将表现为如果用户选择了取消按钮。

- (void)webView:(WKWebView *)webView runOpenPanelWithParameters:(WKOpenPanelParameters *)parameters initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void(^)(NSArray * _Nullable URLs))completionHandler {

    NSLog(@"----显示一个文件上传面板");

}

创建新的webView时调用的方法

- (nullable WKWebView *)webView:(WKWebView *)webView createWebViewWithConfiguration:(WKWebViewConfiguration *)configuration forNavigationAction:(WKNavigationAction *)navigationAction windowFeatures:(WKWindowFeatures *)windowFeatures {

    NSLog(@"-----创建新的webView时调用的方法");

    return webView;

}

关闭webView时调用的方法

- (void)webViewDidClose:(WKWebView *)webView {

    NSLog(@"----关闭webView时调用的方法");

}
WKUserScript

WKUserScript用于往加载的页面中添加额外需要执行的JavaScript代码, 主要是一个初始化方法:

/*
source: 需要执行的JavaScript代码
injectionTime: 加入的位置, 是一个枚举
typedef NS_ENUM(NSInteger, WKUserScriptInjectionTime) {
    WKUserScriptInjectionTimeAtDocumentStart,
    WKUserScriptInjectionTimeAtDocumentEnd
} API_AVAILABLE(macosx(10.10), ios(8.0));

forMainFrameOnly: 是加入所有框架, 还是只加入主框架
*/
- (instancetype)initWithSource:(NSString *)source injectionTime:(WKUserScriptInjectionTime)injectionTime forMainFrameOnly:(BOOL)forMainFrameOnly;

// 通过WKUserScript注入需要执行的JavaScript代码
- (void)addUserScript:(WKUserScript *)userScript;
// 移除所有注入的JavaScript代码
- (void)removeAllUserScripts;

WKWebsiteDataStore

iOS7.0只有UIWebView, 而iOS8.0是有WKWebView, 但8.0的WKWebView没有删除缓存方法。iOS9.0之后就开始支持啦。
所以使用时候一定要适配iOS9.0以上

WKWebsiteDataStore 提供了网站所能使用的数据类型,包括 cookies,硬盘缓存,内存缓存活在一些WebSQL的数据持久化和本地持久化。可通过 WKWebViewConfiguration类的属性 websiteDataStore 进行相关的设置。WKWebsiteDataStore 相关的API也比较简单:

// 默认的data store
+ (WKWebsiteDataStore *)defaultDataStore;

// 如果为webView设置了这个data Store,则不会有数据缓存被写入文件
// 当需要实现隐私浏览的时候,可使用这个
+ (WKWebsiteDataStore *)nonPersistentDataStore;

// 是否是可缓存数据的,只读
@property (nonatomic, readonly, getter=isPersistent) BOOL persistent;

// 获取所有可使用的数据类型
+ (NSSet<NSString *> *)allWebsiteDataTypes;

// 查找指定类型的缓存数据
// 回调的值是WKWebsiteDataRecord的集合
- (void)fetchDataRecordsOfTypes:(NSSet<NSString *> *)dataTypes completionHandler:(void (^)(NSArray<WKWebsiteDataRecord *> *))completionHandler;

// 删除指定的纪录
// 这里的参数是通过上面的方法查找到的WKWebsiteDataRecord实例获取的
- (void)removeDataOfTypes:(NSSet<NSString *> *)dataTypes forDataRecords:(NSArray<WKWebsiteDataRecord *> *)dataRecords completionHandler:(void (^)(void))completionHandler;

// 删除某时间后修改的某类型的数据
- (void)removeDataOfTypes:(NSSet<NSString *> *)websiteDataTypes modifiedSince:(NSDate *)date completionHandler:(void (^)(void))completionHandler;

// 保存的HTTP cookies
@property (nonatomic, readonly) WKHTTPCookieStore *httpCookieStore

dataTyle
// 硬盘缓存
WKWebsiteDataTypeDiskCache,

// HTML离线web应用程序缓存
WKWebsiteDataTypeOfflineWebApplicationCache,

// 内存缓存
WKWebsiteDataTypeMemoryCache,

// 本地缓存
WKWebsiteDataTypeLocalStorage,

// cookies
WKWebsiteDataTypeCookies,

// HTML会话存储
WKWebsiteDataTypeSessionStorage,

//  IndexedDB 数据库
WKWebsiteDataTypeIndexedDBDatabases,

// WebSQL 数据库
WKWebsiteDataTypeWebSQLDatabases

WKWebsiteDataRecord
// 展示名称, 通常是域名
@property (nonatomic, readonly, copy) NSString *displayName;

// 包含的数据类型
@property (nonatomic, readonly, copy) NSSet<NSString *> *dataTypes;

简单应用

删除指定时间的所有类型数据
例:删除所有

NSSet *websiteDataTypes = [WKWebsiteDataStore allWebsiteDataTypes];
    NSDate *dateFrom = [NSDate dateWithTimeIntervalSince1970:0];
    [[WKWebsiteDataStore defaultDataStore] removeDataOfTypes:websiteDataTypes modifiedSince:dateFrom completionHandler:^{
        // Done
        NSLog(@"释放");
    }];

查找删除

WKWebsiteDataStore *dataStore = [WKWebsiteDataStore defaultDataStore];
    [dataStore fetchDataRecordsOfTypes:[WKWebsiteDataStore allWebsiteDataTypes] completionHandler:^(NSArray<WKWebsiteDataRecord *> * _Nonnull records) {
        for (WKWebsiteDataRecord *record in records) {
            [dataStore removeDataOfTypes:record.dataTypes forDataRecords:@[record] completionHandler:^{
                // done
            }];
        }
    }];

查找删除特定的内容

WKWebsiteDataStore *dataStore = [WKWebsiteDataStore defaultDataStore];
    [dataStore fetchDataRecordsOfTypes:[WKWebsiteDataStore allWebsiteDataTypes] completionHandler:^(NSArray<WKWebsiteDataRecord *> * _Nonnull records) {
        for (WKWebsiteDataRecord *record in records) {
            if ([record.displayName isEqualToString:@"baidu"]) {
                [dataStore removeDataOfTypes:record.dataTypes forDataRecords:@[record] completionHandler:^{
                    // done
                }];
            }
        }
    }];

因URL中含有中文加载网页白屏显示的解决方法就是将中文符号UTF8转码。
看需要有时可以对url进行两次UTF8编码,第一次编码,中文会被转码成16进制,但是每个字符前面会有一个%号,这个%号会被看成转义字符影响浏览器的解析,所以还需要再编码一次。

[@"中文" stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]

获取h5中的标题 添加进度条

获取h5中的标题和添加进度条放到一起展示看起来更明朗一点,在初始化wenview时,添加两个观察者分别用来监听webview 的estimatedProgress和title属性:

webview.navigationDelegate = self;
webview.UIDelegate = self;
     
[webview addObserver:self forKeyPath:@"estimatedProgress" options:NSKeyValueObservingOptionNew context:nil];
[webview addObserver:self forKeyPath:@"title" options:NSKeyValueObservingOptionNew context:NULL];
@property (nonatomic,weak) CALayer *progressLayer;
-(void)setupProgress{
    UIView *progress = [[UIView alloc]init];
    progress.frame = CGRectMake(0, 0, KScreenWidth, 3);
    progress.backgroundColor = [UIColor  clearColor];
    [self.view addSubview:progress];
     
    CALayer *layer = [CALayer layer];
    layer.frame = CGRectMake(0, 0, 0, 3);
    layer.backgroundColor = [UIColor greenColor].CGColor;
    [progress.layer addSublayer:layer];
    self.progressLayer = layer;
}

实现观察者的回调方法:

#pragma mark - KVO回馈
-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<nskeyvaluechangekey,id> *)change context:(void *)context{
    if ([keyPath isEqualToString:@"estimatedProgress"]) {
        self.progressLayer.opacity = 1;
        if ([change[@"new"] floatValue] <[change[@"old"] floatValue]) {
            return;
        }
        self.progressLayer.frame = CGRectMake(0, 0, KScreenWidth*[change[@"new"] floatValue], 3);
        if ([change[@"new"]floatValue] == 1.0) {
            dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(.5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
                self.progressLayer.opacity = 0;
                self.progressLayer.frame = CGRectMake(0, 0, 0, 3);
            });
        }
    }else if ([keyPath isEqualToString:@"title"]){
        self.title = change[@"new"];
    }
}

本文参考链接来自:https://www.jianshu.com/p/833448c30d70http://www.cocoachina.com/ios/20180314/22589.html

推荐阅读更多精彩内容