KVO和利用运行时给补丁中添加属性
2016-01-29 本文已影响105人
LennonLin
KVO(Key-Value Observing)
GOF设计模式中观察着模式的应用。键值观察 观察对象的属性 当属性变化时引发事件.
- 添加观察者并且绑定事件:addObserver:forKeyPath:options:context
- 回调方法:observeValueForKeyPath:ofObject:change:context
- 移除观察者:removeObserver:forKeyPath
观察progressDemo
// frantionCompleted是progress的属性,也是想要观察的属性
[progress addObserver:self forKeyPath:NSStringFromSelector(@selector(fractionCompleted)) options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:nil];
// KVO观察者的回调方法
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
if ([keyPath isEqualToString:NSStringFromSelector(@selector(fractionCompleted))]) {
// NSLog(@"%f", [change[@"new"] floatValue]);
dispatch_async(dispatch_get_main_queue(), ^{
_uploadProgressView.progress = [change[@"new"] floatValue];
if (currentProgress >= 1.0) {
// 移除观察者(避免内存泄露的问题)
[object removeObserver:self forKeyPath:NSStringFromSelector(@selector(fractionCompleted))];
}
});
}
}
设置一个tableview里面图片的缩放效果
1.给一个ScrollView打补丁利用运行时添加属性
2.利用了观察者模式
DEMO
1.h文件
#import <UIKit/UIKit.h>
#import <objc/runtime.h>
// 直接写一个集成添加属性
@interface ScaleImage : UIImageView
@property (nonatomic, weak) UIScrollView *scrollView;
@end
// 在类别中不可以添加成员变量
@interface UIScrollView (ScaleImage)
// 1.添加一个成员变量 _imageView
// 2.添加了一个setter方法
// 3.添加了一个getter方法
// 不可以使用属性的 实现部分不会生成_ImageView的属性,也就是说没有实现部分 但是可以用运行时绑定一个属性在里面实现
// @property (nonatomic, weak) UIImageView *imageView;
// 传入一张图片实现缩放效果
@property (nonatomic, weak) ScaleImage *imageView;
- (void)addScaleableImageWithImage:(UIImage *) image height:(CGFloat) height;
- (void) removeSceableImage;
@end
2..m文件
#import "UIScrollView+ScaleImage.h"
#import <objc/runtime.h>
const NSString *keyScaleImage = @"keyScaleImage";
// 不能写错的字符串
NSString *kcontentOffset = @"contentOffset";
@interface ScaleImage ( )
{
CGFloat _height;
}
@end
// 继承的实现
@implementation ScaleImage
- (void)setScrollView:(UIScrollView *)scrollView
{
[_scrollView removeObserver:self forKeyPath:kcontentOffset];
// 监听scrollView的contentOffset的改变
_scrollView = scrollView;
// 记录自己的初始高度
_height = CGRectGetHeight(self.frame);
[scrollView addObserver:self forKeyPath:kcontentOffset options:NSKeyValueObservingOptionNew context:nil];
}
/**NSObject的方法,只要是继承自NSObject的都有这个方法*/
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
// 观察到变化后,回调的方法。
// 让self(ScaleImage)去改变frame
// 注册需要更新视图
[self setNeedsLayout];
// 告诉当前视图 需要去更新 视图会在下一个消息循环是更新
// 在使用setNeedsLayout之后 会调用LayoutSubViews
}
- (void)layoutSubviews
{
[super layoutSubviews];
// 更新视图
CGFloat offsetY = self.scrollView.contentOffset.y;
if (offsetY < 0) {
CGFloat x = 0 - fabs(offsetY);
CGFloat y = 0 - fabs(offsetY);
CGFloat w = CGRectGetWidth(self.scrollView.frame) + fabs(offsetY ) * 2;
CGFloat h = _height + fabs(offsetY);
self.frame = CGRectMake(x, y, w, h);
}
else {
self.frame = CGRectMake(0, 0, CGRectGetWidth(self.scrollView.frame), _height);
}
}
- (void)dealloc
{
[self.scrollView removeObserver:self forKeyPath:kcontentOffset];
}
- (void)removeFromSuperview
{
[self.scrollView removeObserver:self forKeyPath:kcontentOffset];
}
@end
@implementation UIScrollView (ScaleImage)
- (void)setImageView:(ScaleImage *)imageView
{
// 用运行时的方法给当前scrollView绑定一个属性
// 参数1:被绑定的对象,给谁绑定
// 参数2:key,通过key来绑定属性
// 参数3:value,绑定的对象
// 参数4:绑定策略
objc_setAssociatedObject(self, &keyScaleImage, imageView, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (ScaleImage *)imageView
{
return objc_getAssociatedObject(self, &keyScaleImage);
}
- (void)addScaleableImageWithImage:(UIImage *)image
height:(CGFloat)height
{
ScaleImage *imageView = [[ScaleImage alloc] initWithFrame:CGRectMake(0, 0, CGRectGetWidth(self.frame), height)];
imageView.image = image;
[self addSubview:imageView];
imageView.scrollView = self;
self.imageView = imageView;
// 将视图放在底层
[self sendSubviewToBack:imageView];
// UIImageView *imageView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, CGRectGetWidth(self.frame), height)];
// imageView.image = image;
// [self addSubview:imageView];
//
// // self改变时imageView需要改变。所以imageView是观察者
// [self addObserver:imageView forKeyPath:@"contenOffset" options:NSKeyValueObservingOptionNew context:nil];
}
- (void)removeSceableImage {
[self.imageView removeFromSuperview];
self.imageView = nil;
}
@end