美文网首页
KVO和利用运行时给补丁中添加属性

KVO和利用运行时给补丁中添加属性

作者: LennonLin | 来源:发表于2016-01-29 09:20 被阅读105次

    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
    

    相关文章

      网友评论

          本文标题:KVO和利用运行时给补丁中添加属性

          本文链接:https://www.haomeiwen.com/subject/goejkttx.html