基于MJRefresh模仿美团外卖的下拉刷新动画

相较于弹出加载框式的加载方式,下拉刷新式加载方式所带来的用户体验要好很多,当前主流APP的下拉刷新动画也是越来越绚丽,最近因为项目需要,模仿美团外卖的下拉刷新,对项目中的下拉刷新进行了修改。(其实是万恶的设计师,非要模仿人家的设计,做为程序员只好费尽脑细胞了。)
dome我已经上传到GitHub上了,有需要的可朋友可以下载看看。

refresh.gif

因为在项目中使用MJRefresh做为下拉刷新框架,因此所有的设计都是基于MJRefresh进行的修改。而MJRefresh也算是当前iOS开发使用的比较多的下拉刷新框架。

一、普通动态效果

首先加载动态图片在MJRefresh提供是提供的有现成的方法的,在MJRefreshGifHeader中,提供的就是加载动态图片的方法,使用起来也比较简单。
我一般的做法是继承MJRefreshGifHeader,重新创建一个类,用来设置动态图片加载的一些基本属性。

1、 重写下拉刷新的父类的方法

- (void)prepare{
    [super prepare];
}

2、 更换动画图片

// 设置即将刷新状态的动画图片(一松开就会刷新的状态)
NSMutableArray *refreshingImages = [NSMutableArray array];
for (int i = 1; i<=22; i++) {
    UIImage *image = [UIImage imageNamed:[NSString stringWithFormat:@"gif_header_%d", i]];
    [refreshingImages addObject:image];
}
// 设置正在刷新状态的动画图片
[self setImages:refreshingImages forState:MJRefreshStateRefreshing];

3、设计一些基本属性

// 隐藏时间
self.lastUpdatedTimeLabel.hidden = YES;
// 隐藏状态
self.stateLabel.hidden = YES;
// 下拉试图高度
self.mj_h = 70;

二、模仿美团外卖效果

如果只是实现加载动画,那么上面的做法其实已经能够实现了,可是如何才能实现上面那种随着下拉距离,图片不断放大的效果呢!
仔细分析MJRefreshGifHeader里面的效果不难发现,如果是继承于MJRefreshGifHeader是无法做到这样的效果的,那么很自然的就能想到,从MJRefreshGifHeader的父级来看看能否实现。
MJRefreshGifHeader的父级是MJRefreshStateHeader。
MJRefreshStateHeader里面的方法其实同MJRefreshGifHeader比较相似,比较重要的几个方法分别是:

// 初始化方法
- (void)prepare{
    [super prepare];
}
// 布局子视图 
- (void)placeSubviews {

}
// 拉拽的百分比
- (void)setPullingPercent:(CGFloat)pullingPercent {
    [super setPullingPercent:pullingPercent];
}
// 下拉状态
- (void)setState:(MJRefreshState)state {
    MJRefreshCheckState
}

搞清楚了这几个方法,接下来就比较简单了

1、在初始方法中,设置下拉刷新的基础属性

// 初始化方法
- (void)prepare{
    [super prepare];
    // 隐藏时间
    self.lastUpdatedTimeLabel.hidden = YES;
    // 隐藏状态
    self.stateLabel.hidden = YES;
    // 下拉试图高度
    self.mj_h = 70;
}

2、在布局方法中创建imageView,用来展示动态图片

// 布局子视图
- (void)placeSubviews {
    [super placeSubviews];
    if (self.refreshImg.constraints.count) return;
    // 刷新状态状态图片
    self.refreshImg.frame = self.bounds;
    self.refreshImg.contentMode = UIViewContentModeCenter;
    // 普通状态状态图片
    self.idleImg.x = 0;
    self.idleImg.width = self.width;
    self.idleImg.contentMode = UIViewContentModeCenter;
}

这里我分别创建了两个imageView,分别用在下拉阶段的放大缩小和加载阶段的动画展示,这主要是因为在开发过程中,我发现如果使用一个imageView,就会造成在加载阶段的动画图片尺寸变大的现象,具体是因为什么我也没有找到原因。

3、在拖拽方法中,对普通状态的图片信息缩放

// 拉拽的百分比
- (void)setPullingPercent:(CGFloat)pullingPercent {
    [super setPullingPercent:pullingPercent];
    // 视图正在下拉
    if (0 < pullingPercent && pullingPercent <= 1) {
        // 普通状态图片高度
        self.idleImg.height = self.height - 70.0 * (1 - pullingPercent);
        // 普通状态图片y值
        self.idleImg.y = 70.0 * (1-pullingPercent);
        // 普通状态图片
        self.idleIm = [UIImage imageNamed:@"header"];
        self.idleIm = [self scaleToSize:self.idleIm size:CGSizeMake(self.idleIm.size.width * pullingPercent, self.idleIm.size.height * pullingPercent)];
        self.idleImg.image = self.idleIm;
    }
}

在初始化方法中曾经设置过一个属性

// 下拉试图高度
self.mj_h = 70;

这个属性的意思就是当下拉高度在070之间,拉拽的百分比属性pullingPercent,就在01之间。也就是说我们在pullingPercent为0~1的时候,对图片进行缩放。为了保证无论下拉高度在什么位置,图片都处在下拉区域的正中心,所有需要不停的改变图片的位置

// 普通状态图片高度
self.idleImg.height = self.height - 70.0 * (1 - pullingPercent);
// 普通状态图片y值
self.idleImg.y = 70.0 * (1-pullingPercent);

图片尺寸的修改方法,我进行了一个封装,

// 图片缩放到指定大小尺寸
- (UIImage *)scaleToSize:(UIImage *)img size:(CGSize)size{
    // 创建一个bitmap的context
    // 并把它设置成为当前正在使用的context
    UIGraphicsBeginImageContext(size);
    // 绘制改变大小的图片
    [img drawInRect:CGRectMake(0, 0, size.width, size.height)];
    // 从当前context中创建一个改变大小后的图片
    UIImage* scaledImage = UIGraphicsGetImageFromCurrentImageContext();
    // 使当前的context出堆栈
    UIGraphicsEndImageContext();
    // 返回新的改变大小后的图片
    return scaledImage;
}

4、最后一步,就是在不同的刷新状态进行判断,然后对所需要展示的imageView进行切换。

// 下拉状态 
- (void)setState:(MJRefreshState)state {
    MJRefreshCheckState
    switch (state) {
        case MJRefreshStateIdle: { // 普通闲置状态
            // 停止刷新动画
            [self.refreshImg stopAnimating];
            // 隐藏刷新状态图片
            [self.refreshImg setHidden:YES];
            // 展示普通状态图片
            [self.idleImg setHidden:NO];
            break;
        }
        case MJRefreshStateRefreshing: { // 正在刷新中的状态
            // 隐藏普通状态图片
            [self.idleImg setHidden:YES];
            // 展示刷新状态图片
            [self.refreshImg setHidden:NO];
            // 刷新状态图片
            self.refreshImg.animationImages = @[[UIImage imageNamed:@"gif_header_1"], [UIImage imageNamed:@"gif_header_2"], [UIImage imageNamed:@"gif_header_3"], [UIImage imageNamed:@"gif_header_4"]];
            self.refreshImg.animationDuration = 0.4;
            // 开始刷新动画
            [self.refreshImg startAnimating];
            break;
        }
        default:
            break;
    }
}

这里我只判断了两个状态,在MJRefresh中总共为我们5个不同的状态

/** 普通闲置状态 */
MJRefreshStateIdle = 1,
/** 松开就可以进行刷新的状态 */
MJRefreshStatePulling,
/** 正在刷新中的状态 */
MJRefreshStateRefreshing,
/** 即将刷新的状态 */
MJRefreshStateWillRefresh,
/** 所有数据加载完毕,没有更多的数据了 */
MJRefreshStateNoMoreData

好了,到这里基本上就能实现上面图片的功能了,不过有一个问题,我一直没有解决,在进行图片大小缩放的时候,图片会出现模糊的情况,如果各位有人有解决方案的话,在此先谢谢了。

你可能感兴趣的:(基于MJRefresh模仿美团外卖的下拉刷新动画)