Runtime 替换全局 MJRefreshHeader

2019-05-29  本文已影响0人  RyanYuan

实现效果

表头替换后效果

需求

思路

MJRefresh继承关系

方案一
创建 MJRefreshNormalHeader 子类,继承并重写 headerWithRefreshingBlock 方法。可以实现所有功能,但需要对 MJRefreshNormalHeader 进行高度自定义且需要在所有调用的地方导入并修改类名

方案二
直接使用 MJRefreshGifHeader ,替换所有 MJRefreshNormalHeader 表头并进行动画配置,或者直接在 MJRefreshGifHeader 基础上进行封装,需要在所有调用的地方导入并修改类名

方案三
继承 MJRefreshGifHeader 重写初始化方法,在初始化方法中配置相关动画,并使用 runtime 拦截并替换 headerWithRefreshingBlock 方法。该方案自定义内容较少,不用修改任何本类以外代码,工程间可以“即拖即用”。如下图:

方案三.png

实现

RYRefreshGifHeader.m


/*
 * 重写 MJRefreshComponent 刷新控件基类中的初始化方法
 */
- (void)prepare {
    [super prepare];
    
    // 隐藏刷新状态及刷新时间显示
    self.lastUpdatedTimeLabel.hidden = YES;
    self.stateLabel.hidden = YES;
    
    // 加载下拉及刷新状态图片组
    NSArray *idleImageArr = [self configImageArrayWithImagePrefixionString:@""];
    NSArray *refreshingImageArr = [self configImageArrayWithImagePrefixionString:@""];
    
    // 调用 MJRefreshGifHeader 公共方法设置对应状态图片
    [self setImages:idleImageArr duration:idleImageArr.count * 0.1 forState:MJRefreshStateIdle];
    [self setImages:refreshingImageArr duration:refreshingImageArr.count * 0.04 forState:MJRefreshStateRefreshing];
}

/*
 * 重写 MJRefreshGifHeader 加载图片方法,修改图片偏移量以适配刘海屏下加载图片被遮挡问题
 */
- (void)setImages:(NSArray *)images duration:(NSTimeInterval)duration forState:(MJRefreshState)state {
    [super setImages:images duration:duration forState:state];
    BOOL isIphonexSerious = ([UIScreen mainScreen].bounds.size.height == 812 || [UIScreen mainScreen].bounds.size.height == 896);
    self.mj_h += (isIphonexSerious ? kRYRefreshGifHeaderIPhoneXAppendHight : kRYRefreshGifHeaderAppendHight);
}

/*
 * 根据图片名前缀配置图片数组
 */
- (NSArray *)configImageArrayWithImagePrefixionString:(NSString *)prefixionString {
    
    NSMutableArray *imageArr = @[].mutableCopy;
    for (NSInteger index = 0; index < kRYRefreshGifHeaderMaxImageLoopCount; index++) {
        NSString *imageName = [NSString stringWithFormat:@"%@%@", prefixionString, @(index)];
        UIImage *image = [UIImage imageNamed:imageName];
        if (image != nil) {
            
            // MJRefreshGifHeader 暂不支持修改loading图片的比例,所以这里使用 Quart2D 对图片进行等比缩放
            CGFloat screenWidth = [UIScreen mainScreen].bounds.size.width;
            CGFloat screenScale = [NSString stringWithFormat:@"%.2f", screenWidth / 375.f].floatValue;
            CGSize imageTempSize = CGSizeMake(kRYRefreshGifHeaderImageWidth * screenScale, kRYRefreshGifHeaderImageWidth * screenScale);
            // 创建一个bitmap的context
            // 并把它设置成为当前正在使用的context
            UIGraphicsBeginImageContextWithOptions(imageTempSize, NO, [[UIScreen mainScreen] scale]);
            // 绘制改变大小的图片
            [image drawInRect:CGRectMake(0, 0, imageTempSize.width, imageTempSize.height)];
            // 从当前context中创建一个改变大小后的图片
            UIImage *scaledImage = UIGraphicsGetImageFromCurrentImageContext();
            // 使当前的context出堆栈
            UIGraphicsEndImageContext();
            [imageArr addObject:scaledImage];
        } else {
            break;
        }
    }
    return imageArr.copy;
}

/*
 * runtime 拦截到 MJRefreshNormalHeader 的 headerWithRefreshingBlock 方法后会调用该方法进行替换。返回 RYRefreshGifHeader 对象并初始化回调block
 */
+ (instancetype)swizzled_headerWithRefreshingBlock:(MJRefreshComponentRefreshingBlock)refreshingBlock {
    RYRefreshGifHeader *cmp = [[self alloc] init];
    cmp.refreshingBlock = refreshingBlock;
    return cmp;
}    

MJRefreshNormalHeader+Gif.m


/*
 * 修改 headerWithRefreshingBlock 方法的IMP指针指向,使其在调用时执行本类中 swizzled_headerWithRefreshingBlock 的实现
 */
+ (void)load {
    Method normalHeaderMethod = class_getClassMethod(self, @selector(headerWithRefreshingBlock:));
    Method gifHeaderMethod = class_getClassMethod(self, @selector(swizzled_headerWithRefreshingBlock:));
    IMP gifHeaderMethodImplementation = method_getImplementation(gifHeaderMethod);
    method_setImplementation(normalHeaderMethod, gifHeaderMethodImplementation);
}

/*
 * 交换后的方法直接调用 RYRefreshGifHeader 的类方法
 */
+ (instancetype)swizzled_headerWithRefreshingBlock:(MJRefreshComponentRefreshingBlock)refreshingBlock {
    return (MJRefreshNormalHeader *)[RYRefreshGifHeader swizzled_headerWithRefreshingBlock:refreshingBlock];
}

Demo下载

已导入 MJRefresh 情况下将 RYRefreshGifHeader 拖入工程中可直接实现替换

上一篇 下一篇

猜你喜欢

热点阅读