IOS开发-WKWebView的使用、KVO监听例子

简单使用

1.加载网页

与UIWebview一样,仅需三步:记住导入(#import <WebKit/WebKit.h>)

// 加载网页
- (void)loadWbView {
    // 1.创建webView,并设置大小,“80”为状态栏高度
    self.webVIew = [[WKWebView alloc] initWithFrame:CGRectMake(0, 80, self.view.frame.size.width, self.view.frame.size.height - 80)];
    // 2.创建请求
    NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:@"https://time.geekbang.org/"]];
    // 3.加载页面
    [self.webVIew loadRequest:request];
    [self.view addSubview:self.webVIew];
}

2. 加载本地文件

// 加载本地文件
- (void)loadLocalFile {
    // 1.创建webView,并设置大小,“80”为状态栏高度
    WKWebView *webVIew = [[WKWebView alloc] initWithFrame:CGRectMake(0, 80, self.view.frame.size.width, self.view.frame.size.height - 80)];
    // 2.创建url
    NSURL *url = [NSURL fileURLWithPath:@"/Users/userName/Desktop/test.json/"];
    // 3.加载文件
    [webVIew loadFileURL:url allowingReadAccessToURL:url];
    [self.view addSubview:webVIew];
}

3.三个加载函数

/// 加载网页
- (WKNavigation *)loadRequest:(NSURLRequest *)request;
// 加载HTML文件
- (WKNavigation *)loadHTMLString:(NSString *)string baseURL:(nullable NSURL *)baseURL;
// 加载data数据 
- (WKNavigation *)loadData:(NSData *)data MIMEType:(NSString *)MIMEType characterEncodingName:(NSString *)characterEncodingName baseURL:(NSURL *)baseURL;

4.WKwebView基本方法

// 刷新
- (void)reload;
// 停止加载
- (void)stopLoading;
// 后退函数
- (void)goBack;
// 前进函数
- (void)goForward;
// 是否可以后退
@property (nonatomic, readonly, getter=canGoBack) BOOL canGoBack;
// 是否可以向前
@property (nonatomic, readonly, getter=canGoForward) BOOL canGoForward;
// 是否正在加载
@property (nonatomic, readonly, getter=isLoading) BOOL loading;
//会比较网络数据是否有变化,没有变化则使用缓存,否则从新请求
- (WKNavigation *)reloadFromOrigin; // 增加的函数
// 比向前向后更强大,可以跳转到某个指定历史页面
- (WKNavigation *)goToBackForwardListItem:(WKBackForwardListItem *)item; // 

5.一些常用属性

  • allowsBackForwardNavigationGestures:BOOL类型,是否允许左右划手势导航,默认不允许
  • estimatedProgress:加载进度,取值范围0~1
  • title:页面title
  • .scrollView.scrollEnabled:是否允许上下滚动,默认允许
  • backForwardList:WKBackForwardList类型,访问历史列表,可以通过前进后退按钮访问,或者通过goToBackForwardListItem函数跳到指定页面

WKWebView涉及的代理方法


    // 页面开始加载时调用
- (void)webView:(WKWebView *)webView didStartProvisionalNavigation:(WKNavigation *)navigation {
}
    // 页面加载失败时调用
- (void)webView:(WKWebView *)webView didFailProvisionalNavigation:(null_unspecified WKNavigation *)navigation withError:(NSError *)error {
    
} 
    // 当内容开始返回时调用
- (void)webView:(WKWebView *)webView didCommitNavigation:(WKNavigation *)navigation {
}
    // 页面加载完成之后调用
- (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation {
    [self getCookie];
}
    //提交发生错误时调用
- (void)webView:(WKWebView *)webView didFailNavigation:(WKNavigation *)navigation withError:(NSError *)error {
    
}  
   // 接收到服务器跳转请求即服务重定向时之后调用
- (void)webView:(WKWebView *)webView didReceiveServerRedirectForProvisionalNavigation:(WKNavigation *)navigation {
}
  // 根据WebView对于即将跳转的HTTP请求头信息和相关信息来决定是否跳转
- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler {
    
    NSString * urlStr = navigationAction.request.URL.absoluteString;
    NSLog(@"发送跳转请求:%@",urlStr);
    //自己定义的协议头
    NSString *htmlHeadString = @"github://";
    if([urlStr hasPrefix:htmlHeadString]){
        UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"通过截取URL调用OC" message:@"你想前往我的Github主页?" preferredStyle:UIAlertControllerStyleAlert];
        [alertController addAction:([UIAlertAction actionWithTitle:@"取消" style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) {   
        }])];
        [alertController addAction:([UIAlertAction actionWithTitle:@"打开" 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);
    }
}
    
    // 根据客户端受到的服务器响应头以及response相关信息来决定是否可以跳转
- (void)webView:(WKWebView *)webView decidePolicyForNavigationResponse:(WKNavigationResponse *)navigationResponse decisionHandler:(void (^)(WKNavigationResponsePolicy))decisionHandler{
    NSString * urlStr = navigationResponse.response.URL.absoluteString;
    NSLog(@"当前跳转地址:%@",urlStr);
    //允许跳转
    decisionHandler(WKNavigationResponsePolicyAllow);
    //不允许跳转
    //decisionHandler(WKNavigationResponsePolicyCancel);
} 
    //需要响应身份验证时调用 同样在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];
    //为 challenge 的发送方提供 credential
    [challenge.sender useCredential:newCred forAuthenticationChallenge:challenge];
    completionHandler(NSURLSessionAuthChallengeUseCredential,newCred);
}
    //进程被终止时调用
- (void)webViewWebContentProcessDidTerminate:(WKWebView *)webView{
}

WKWebView中的KVO举例(观察者模式)

1.注册监听者

要让一个对象监听另一个对象的属性的变化,首先需要将这个对象注册为相关属性的观察者,我们可以使用以下方法来实现:


- (void)addObserver:(NSObject *)anObserver
         forKeyPath:(NSString *)keyPath
            options:(NSKeyValueObservingOptions)options
            context:(void *)context
            
anObserver:观察者对象,这个对象必须实现observeValueForKeyPath:ofObject:change:context:方法,以响应属性的修改通知。
keyPath:被监听的属性。这个值不能为nil。
options:监听选项,这个值可以是NSKeyValueObservingOptions选项的组合。关于监听选项,我们会在下面介绍。
context:任意的额外数据,我们可以将这些数据作为上下文数据,它会传递给观察者对象的observeValueForKeyPath:ofObject:change:context:方法。这个参数的意义在于用于区分同一对象监听同一属性(从属于同一对象)的多个不同的监听。我们将在下面看到。

监听的四种类型

typedef NS_OPTIONS(NSUInteger, NSKeyValueObservingOptions) {
    // 提供属性的新值
    NSKeyValueObservingOptionNew = 0x01,
    // 提供属性的旧值
    NSKeyValueObservingOptionOld = 0x02,
    // 如果指定,则在添加观察者的时候立即发送一个通知给观察者,
    // 并且是在注册观察者方法返回之前
    NSKeyValueObservingOptionInitial = 0x04,
    // 如果指定,则在每次修改属性时,会在修改通知被发送之前预先发送一条通知给观察者,
    // 这与-willChangeValueForKey:被触发的时间是相对应的。
    // 这样,在每次修改属性时,实际上是会发送两条通知。
    NSKeyValueObservingOptionPrior = 0x08

在添加观察者时还有两点需要注意的是:

  1. 调用这个方法时,两个对象(即观察者对象及属性所属的对象)都不会被retain。
  2. 可以多次调用这个方法,将同一个对象注册为同一属性的观察者(所有参数可以完全相同)。这时,即便在所有参数一致的情况下,新注册的观察者并不会替换原来观察者,而是会并存。这样,当属性被修改时,两次监听都会响应。

举例:创建WkWebView并注册添加观察者

	self.webVIew = [[WKWebView alloc] initWithFrame:CGRectMake(0, 80, self.view.frame.size.width, 	self.view.frame.size.height - 80)];
    // 2.创建请求
    NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:@"https://time.geekbang.org/"]];
    // 3.加载页面
    [self.webVIew loadRequest:request];
    self.webVIew.navigationDelegate = self;
    // 给webView 添加一个监听者(self),监听的为estimatedProgress属性有新变化的时候(NSKeyValueObservingOptionNew)
//添加监测网页加载进度的观察者
    [self.webView addObserver:self
                   forKeyPath:@"estimatedProgress"
                      options:NSKeyValueObservingOptionNew
                      context:nil];
   //添加监测网页标题title的观察者
    [self.webView addObserver:self
                   forKeyPath:@"title"
                      options:NSKeyValueObservingOptionNew
                      context:nil];

};

2.添加监听的回调方法

当被监听的属性修改时,KVO会发出一个通知以告知所有监听这个属性的观察者对象。而观察者对象必须实现 -observeValueForKeyPath:ofObject:change:context:方法,来对属性修改通知做相应的处理。这个方法的声明如下:

- (void)observeValueForKeyPath:(NSString *)keyPath
                      ofObject:(id)object
                        change:(NSDictionary *)change
                       context:(void *)context

参数说明:

  • keyPath:即被观察的属性,与参数object相关。
  • object:keyPath所属的对象。
  • change:这是一个字典,它包含了属性被修改的一些信息。这个字典中包含的值会根据我们在添加观察者时设置的options参数的不同而有所不同。
  • context:这个值即是添加观察者时提供的上下文信息。

对于第三个参数,我们通常称之为变化字典(Change Dictionary),它记录了被监听属性的变化情况。
与注册时的options 选项的参数一一对应。
我们可以通过以下key来获取我们想要的信息:

// 属性变化的类型,是一个NSNumber对象,包含NSKeyValueChange枚举相关的值
NSString *const NSKeyValueChangeKindKey;
 
// 属性的新值。当NSKeyValueChangeKindKey是 NSKeyValueChangeSetting,
// 且添加观察的方法设置了NSKeyValueObservingOptionNew时,我们能获取到属性的新值。
// 如果NSKeyValueChangeKindKey是NSKeyValueChangeInsertion或者NSKeyValueChangeReplacement,
// 且指定了NSKeyValueObservingOptionNew时,则我们能获取到一个NSArray对象,包含被插入的对象或
// 用于替换其它对象的对象。
NSString *const NSKeyValueChangeNewKey;
 
// 属性的旧值。当NSKeyValueChangeKindKey是 NSKeyValueChangeSetting,
// 且添加观察的方法设置了NSKeyValueObservingOptionOld时,我们能获取到属性的旧值。
// 如果NSKeyValueChangeKindKey是NSKeyValueChangeRemoval或者NSKeyValueChangeReplacement,
// 且指定了NSKeyValueObservingOptionOld时,则我们能获取到一个NSArray对象,包含被移除的对象或
// 被替换的对象。
NSString *const NSKeyValueChangeOldKey;
 
// 如果NSKeyValueChangeKindKey的值是NSKeyValueChangeInsertion、NSKeyValueChangeRemoval
// 或者NSKeyValueChangeReplacement,则这个key对应的值是一个NSIndexSet对象,
// 包含了被插入、移除或替换的对象的索引
NSString *const NSKeyValueChangeIndexesKey;
 
// 当指定了NSKeyValueObservingOptionPrior选项时,在属性被修改的通知发送前,
// 会先发送一条通知给观察者。我们可以使用NSKeyValueChangeNotificationIsPriorKey
// 来获取到通知是否是预先发送的,如果是,获取到的值总是@(YES)
NSString *const NSKeyValueChangeNotificationIsPriorKey;

其中,NSKeyValueChangeKindKey的值取自于NSKeyValueChange,它的值是由以下枚举定义的:

enum {
 
    // 设置一个新值。被监听的属性可以是一个对象,也可以是一对一关系的属性或一对多关系的属性。
    NSKeyValueChangeSetting = 1,
 
    // 表示一个对象被插入到一对多关系的属性。
    NSKeyValueChangeInsertion = 2,
 
    // 表示一个对象被从一对多关系的属性中移除。
    NSKeyValueChangeRemoval = 3,
 
    // 表示一个对象在一对多的关系的属性中被替换
    NSKeyValueChangeReplacement = 4
};
typedef NSUInteger NSKeyValueChange;

以下这个例子中 chang 的内容为:

{
    kind = 1;  // 对应上面 NSKeyValueChangeSetting
    new = "0.5"; // 表示当前的进度为0.5
}

举例:


#pragma mark kvo 监听进度 必须实现此方法 监听的回调
-(void)observeValueForKeyPath:(NSString *)keyPath
                     ofObject:(id)object
                       change:(NSDictionary<NSKeyValueChangeKey,id> *)change
                      context:(void *)context{
    if ([keyPath isEqualToString:NSStringFromSelector(@selector(estimatedProgress))]
        && object == _webView) {
        NSLog(@"网页加载进度 = %f",_webView.estimatedProgress);
        self.progressView.progress = _webView.estimatedProgress;
        if (_webView.estimatedProgress >= 1.0f) {
            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];
    }
}

3.移除监听

移除监听的方法有两种:

- (void)removeObserver:(NSObject *)anObserver
            forKeyPath:(NSString *)keyPath
 
- (void)removeObserver:(NSObject *)observer
            forKeyPath:(NSString *)keyPath
               context:(void *)context

需要在dealloc中调用

- (void)dealloc {
    // 移除监听
    [self.webVIew removeObserver:self forKeyPath:@"estimatedProgress"];
}
  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值