序言
在iOS开发中,苹果提供了许多机制给我们进行回调。KVO(key-value-observing)是一种十分有趣的回调机制,在某个对象注册监听者后,在被监听的对象发生改变时,对象会发送一个通知给监听者,以便监听者执行回调操作。最常见的KVO运用是监听scrollView的contentOffset属性,来完成用户滚动时动态改变某些控件的属性实现效果,包括渐变导航栏、下拉刷新控件等效果。
使用
KVO的使用非常简单,使用KVO的要求是对象必须能支持kvc机制——所有NSObject的子类都支持这个机制。拿上面的渐变导航栏做,我们为tableView添加了一个监听者controller,在我们滑动列表的时候,会计算当前列表的滚动偏移量,然后改变导航栏的背景色透明度。
//添加监听者
[self.tableView addObserver: self forKeyPath: @"contentOffset" options: NSKeyValueObservingOptionNew context: nil];
/**
* 监听属性值发生改变时回调
*/
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context
{
CGFloat offset = self.tableView.contentOffset.y;
CGFloat delta = offset / kNavBarH + 1.f;
delta = MAX(0, delta);
[self bgView].alpha = MIN(1, delta);
}
//移除观察者
- (void)dealloc {
[self.tableView removeObserver:self forKeyPath:@"contentOffset"];
}
//懒加载实现背景View
- (UIView*)bgView {
if (_bgView == nil) {
_bgView = [[UIImageView alloc]initWithFrame:CGRectMake(0, 0, WIDTH, kNavBarH)];
_bgView.backgroundColor = KNavBarColor;
[self.navigationController.navigationBar setValue:_bgView forKey:@"backgroundView"];
}
return _bgView;
}
示例代码
//
// KVOUITableViewVC.h
// KVC-KVO
//
// Created by Jason on 2018/4/11.
// Copyright © 2018年 hzb. All rights reserved.
//
#import <UIKit/UIKit.h>
@interface KVOUITableViewVC : UIViewController
@end
//
// KVOUITableViewVC.m
// KVC-KVO
//
// Created by Jason on 2018/4/11.
// Copyright © 2018年 hzb. All rights reserved.
//
#import "KVOUITableViewVC.h"
@interface KVOUITableViewVC ()<UITableViewDelegate,UITableViewDataSource>
@property (nonatomic,strong) UITableView *tableView;
@property (nonatomic,strong) UIView *bgView;
@end
@implementation KVOUITableViewVC
- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = [UIColor whiteColor];
self.tableView = [[UITableView alloc] initWithFrame:CGRectMake(0, kNavBarH, WIDTH, HEIGHT) style:UITableViewStylePlain];
self.tableView.tableFooterView = [[UIView alloc] init]; //不显示没内容的cell
self.tableView.delegate = self;
self.tableView.dataSource = self;
[self.view addSubview:self.tableView];
//添加监听者
[self.tableView addObserver: self forKeyPath: @"contentOffset" options: NSKeyValueObservingOptionNew context: nil];
}
/**
* 监听属性值发生改变时回调
*/
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context
{
CGFloat offset = self.tableView.contentOffset.y;
CGFloat delta = offset / kNavBarH + 1.f;
delta = MAX(0, delta);
[self bgView].alpha = MIN(1, delta);
}
//移除观察者
- (void)dealloc {
[self.tableView removeObserver:self forKeyPath:@"contentOffset"];
}
//懒加载实现背景View
- (UIView*)bgView {
if (_bgView == nil) {
_bgView = [[UIImageView alloc]initWithFrame:CGRectMake(0, 0, WIDTH, kNavBarH)];
_bgView.backgroundColor = KNavBarColor;
[self.navigationController.navigationBar setValue:_bgView forKey:@"backgroundView"];
}
return _bgView;
}
#pragma mark - UITableViewDelegate、UITableViewDataSource
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return 100;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *ID = @"cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:ID];
if (!cell) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:ID];
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
}
cell.textLabel.text = [NSString stringWithFormat:@"第%ld行",(long)indexPath.row];
return cell;
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
@end