KVC和KVO
今天在这里给大家详解一下KVC和KVO的用法, 在这里首先给大家介绍一下KVC的用法,虽然他俩看似只差一个字母但,但其实两种放法的机制相差很大,千万不要被表象所蒙骗哦, 下面分别介绍了两种机制的使用方式, 理解之后你就明白, 他俩一毛钱关系也没有哦 ^- -^
KVC
KVC(键值编码,是KeyValue Coding
的简称),它是一种可以直接通过字符串的名字(key)来访问类属性的机制.使用该机制不需要调取实例变量就可以访问对象属性,并对属性进行赋值.在iOS开发中, KVC经常会被使用,在给Model类进行赋值时,你会经常用到KVC, 如果你还不知道Model,请自行补习.下面的实例清单会涉及Model类.
实例清单1.1 KVC的最基础用法(setValue:forKey:):
//Person类.h文件内容
#import <Foundation/Foundation.h>
@class Student;
@interface Person : NSObject
@property (nonatomic, strong) NSString *name;
@end
//VC中.m内容
Person *per = [[Person alloc] init];
[per setValue:@"火星人" forKey:@"name"];
NSLog(@"%@", per.name);
实例清单1.2 KVC的第二种用法(setValuesForKeysWithDictionary:):
#import <Foundation/Foundation.h>
@interface Person : NSObject
// 自定义Model(Person)类.h文件内容/此.m文件无内容(在真正的开发中.m中是有内容, 在这里为了减少我的工作量,我lazy一下)
@property (nonatomic, strong) NSString *name;
@property (nonatomic, strong) NSString *phoneNumber;
@property (nonatomic, strong) NSString *address;
@end
// VC中的.m文件中内容
// 定义一个字典, 在这里请注意,字典中Key值要和Model类中属性名一致,不然会出现问题,把出错的话,就复制粘贴吧! (^-#-^)
NSDictionary *personDic = @{@"name" :@"你是谁", @"phoneNumber" :@"1314512521", @"address" :@"国际空间站"};
Person *per = [[Person alloc] init];
//注意:这只是KVC中的一种用法(下面会把中的用法都介绍一下)
[per setValuesForKeysWithDictionary:personDic];
实例清单1.3 KVC的第三种用法(可能会失效,原因未知, 有待读者研究)(setValue:forKeyPath:):
// Person.h类文件
#import <Foundation/Foundation.h>
@class Student;
@interface Person : NSObject
@property (nonatomic, strong) Student *stu;
@end
// Student.h类文件
#import <Foundation/Foundation.h>
@interface Student : NSObject
@property (nonatomic, strong) NSString *sex;
@end
//vc.m文件
#import "ViewController.h"
#import "Person.h"
#import "student.h"
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
Person *per = [[Person alloc] init];
//关键路径赋值
[per setValue:@"male" forKeyPath:@"stu.sex"];
Student *stu = [[Student alloc] init];
NSLog(@"%@", stu);
}
KVO
重点来了,KVO的时间到了,KVO的全称叫(Key-value observing),在我理解看来,就是一种监听机制,能够实时的监听关键Key值得变化,现在你可能不明白这个Key到底是神魔玩意, 先不要着急,继续读下去,在这我先举几个例子,比如:UIscrollView中属性contentOffset, 自定义的BOOL属性, UIView及子类的backgroundColor属性等等都可以作为Key,即观察的对象.废话不多说了,实例才能证明一切.
声明:如果想实现下列清单中的案例请粘贴到工程中就可以了,具体的实现可能使你更加的了解KVO -%-
实例清单如下:
//自定义的TitleView(网易新闻的文字横条)
//TitleView.h文件
#import <UIKit/UIKit.h>
@interface TitleView : UIView
@property (nonatomic, retain) NSArray *titles;
@end
//TitleView.m文件
#import "TitleView.h"
@interface TitleView ()
@end
@implementation TitleView
#pragma mark - override
- (instancetype)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
[self createSubviews];
}
return self;
}
- (void)setTitles:(NSArray *)titles {
for (int i = 0; i < 5; i++) {
[self.subviews[i] setTitle:titles[i] forState:UIControlStateNormal];
}
}
- (void)createSubviews {
for (int i = 0; i < 5; i++) {
UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
[button setTitleColor:[UIColor lightGrayColor] forState:UIControlStateNormal];
button.frame = CGRectMake(CGRectGetWidth(self.bounds) / 5 * i, 0, CGRectGetWidth(self.bounds) / 5, CGRectGetHeight(self.bounds));
[self addSubview:button];
}
}
// 当然自定义TitleView不是重点啦, 重点来啦
@interface ViewController () <UICollectionViewDelegate, UICollectionViewDataSource,
UIScrollViewDelegate>
@property (nonatomic, retain) UIView *viewOfRedline;
@property (nonatomic, retain) UICollectionView *collectionView;
@property (nonatomic, retain) TitleView *viewOfTitle;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
[self createViewOfRedLine];
[self createCollectionView];
[self createTitleView];
}
//不是重点哦
- (void)createViewOfRedLine {
self.viewOfRedline = [[UIView alloc] initWithFrame:CGRectMake(0, 64, CGRectGetWidth(self.view.bounds) / 5, 2)];
[self.view addSubview:self.viewOfRedline];
self.viewOfRedline.backgroundColor = [UIColor redColor];
}
// 不是重点哦
- (void)createCollectionView {
UICollectionViewFlowLayout *layout = [[UICollectionViewFlowLayout alloc] init];
layout.scrollDirection = UICollectionViewScrollDirectionHorizontal;
layout.itemSize = CGSizeMake(CGRectGetWidth(self.view.bounds), CGRectGetHeight(self.view.bounds) - 66);
layout.minimumInteritemSpacing = 0;
layout.minimumLineSpacing = 0;
self.collectionView = [[UICollectionView alloc] initWithFrame:CGRectMake(0, 66, CGRectGetWidth(self.view.bounds), CGRectGetHeight(self.view.bounds) - 66) collectionViewLayout:layout];
self.collectionView.dataSource = self;
self.collectionView.delegate = self;
[self.view addSubview:self.collectionView];
// 注册cell
[self.collectionView registerClass:[UICollectionViewCell class] forCellWithReuseIdentifier:@"pool"];
self.collectionView.pagingEnabled = YES;
// KVO 观察collectionView的offset //注意:次步是重点
[self.collectionView addObserver:self forKeyPath:@"contentOffset" options:NSKeyValueObservingOptionNew context:nil];
}
// 不是重点
- (void)createTitleView {
self.viewOfTitle = [[TitleView alloc] initWithFrame:CGRectMake(0, 32, CGRectGetWidth(self.view.bounds), 32)];
self.viewOfTitle.titles = @[@"头条", @"热点", @"财经", @"新闻", @"汽车"];
[self.view addSubview:self.viewOfTitle];
}
// KOV的实现,事件处理都在这里进行处理, 这是重点哦, 好好理解一下吧
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context {
//objectForKey:@"new" 中的只能写@"new"(新的值, 监听的Key的变化的值) 或者写 @"old"(旧的值, 监听Key变化之前的值)
// 在这里我们获取新值(变化后的值)
CGFloat x = [[change objectForKey:@"new"] CGPointValue].x / 5.0f;
self.viewOfRedline.frame = CGRectMake(x, 64, CGRectGetWidth(self.view.bounds) / 5, 2);
}
// UIScrollView的协议方法之一
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section {
return 5;
}
// UIScrollView的协议方法之一
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
UICollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"pool" forIndexPath:indexPath];
cell.backgroundColor = [UIColor colorWithRed:arc4random() % (256) / 255.0f green:arc4random() % (256) / 255.0f blue:arc4random() % (256) / 255.0f alpha:1];
return cell;
}
// 改变标题的颜色
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView {
NSInteger index = scrollView.contentOffset.x / 5.0f / (CGRectGetWidth(self.view.bounds) / 5);
for (UIButton *button in self.viewOfTitle.subviews) {
[button setTitleColor:[UIColor lightGrayColor] forState:UIControlStateNormal];
}
[self.viewOfTitle.subviews[index] setTitleColor:[UIColor redColor] forState:UIControlStateNormal];
}