说到下拉刷新我想都会想到MJRefresh,虽然它很强大,但是有些需求还是办不到,只能自定义.如下图:
MJRefresh是将动画图片放在scrollview的顶部,使动画图片与scrollview在同一级,并且随着scrollview的滑动而滑动.而上图中的效果是将动画放在视图控制器上,并且在tableview上面,而且根据tableview滑动改变透明度.
我的思路大概是这样的:
1.这个自定义的View能随着scrollview滑动而改变自身的状态,并且它的X需要在scrollview的中间,Y在scrollview下面一点那么说明View需要持有scrollview,.
2.在View里面监听ScrollView的contentOffset属性,来做相应的处理.
3.刷新状态大致分为闲置状态,正在刷新和结束刷新3种.
4.正在刷新的时候需要改变scrollView的contentInset,并在刷新结束后恢复.
好了,沿着这个思路开干吧,代码量非常少,只有一个文件.
MHRefreshHeader.h
#import <UIKit/UIKit.h>
UIKIT_EXTERN NSString *const BaseRefreshViewObserveKeyPath;
typedef enum {
MHRefreshStateNormal,//闲置状态
MHRefreshStateRefreshing,//正在刷新
MHRefreshStateEndRresh,//结束刷新
} MHRefreshViewState;
@interface MHRefreshHeader : UIView
@property (nonatomic, copy) void(^refreshingBlock)(void);
@property(nonatomic,strong) UIImageView *refreshImageView;
@property (nonatomic, strong) UIScrollView *scrollView;
@property (nonatomic, assign) MHRefreshViewState refreshState;
- (void)endRefreshing;
+(instancetype)refreshWithScroll:(UIScrollView *)scrollView;
@end
MHRefreshHeader.m
#import "MHRefreshHeader.h"
#import "UIView+Additions.h"
static const CGFloat RefreshMinY = - 64.f;
NSString *const BaseRefreshViewObserveKeyPath = @"contentOffset";
@implementation MHRefreshHeader
//使点击穿透
-(UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
{
return nil;
}
+(instancetype)refreshWithScroll:(UIScrollView *)scrollView
{
MHRefreshHeader *header = [MHRefreshHeader new];
header.centerX = scrollView.centerX;
header.y = scrollView.y;
header.scrollView = scrollView;
[scrollView.superview addSubview:header];
return header;
}
- (instancetype)initWithFrame:(CGRect)frame
{
if (self = [super initWithFrame:frame]) {
[self setupView];
}
return self;
}
- (void)setScrollView:(UIScrollView *)scrollView
{
_scrollView = scrollView;
//添加监听
[scrollView addObserver:self forKeyPath:BaseRefreshViewObserveKeyPath options:NSKeyValueObservingOptionNew context:nil];
}
- (void)willMoveToSuperview:(UIView *)newSuperview
{
if (!newSuperview) {
[self.scrollView removeObserver:self forKeyPath:BaseRefreshViewObserveKeyPath];
}
}
-(void)endRefreshing
{
self.refreshState = MHRefreshStateEndRresh;
}
//初始化图片
- (void)setupView
{
self.backgroundColor = [UIColor clearColor];
_refreshImageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"loading_1"]];
NSMutableArray *refreshingImages = [NSMutableArray array];
for (NSUInteger i = 1; i < 6; i++) {
UIImage *image = [UIImage imageNamed:[NSString stringWithFormat:@"loading_%zd", i]];
[refreshingImages addObject:image];
}
_refreshImageView.animationImages = refreshingImages;
self.bounds = _refreshImageView.bounds;
[self addSubview:_refreshImageView];
_refreshImageView.alpha = 0;
self.refreshState = MHRefreshStateNormal;
}
//刷新状态改变
- (void)setRefreshState:(MHRefreshViewState)refreshState
{
if (refreshState == MHRefreshStateRefreshing) {
if (self.refreshingBlock) {
self.refreshingBlock();
}
[_refreshImageView startAnimating];
[UIView animateWithDuration:0.3 animations:^{
self.scrollView.contentInset = UIEdgeInsetsMake(64, 0, 0, 0);
}];
} else if (refreshState == MHRefreshStateNormal) {
if (_refreshImageView.isAnimating) {
[_refreshImageView stopAnimating];
self.scrollView.contentInset = UIEdgeInsetsMake(0, 0, 0, 0);
}
}else if (refreshState == MHRefreshStateEndRresh){
if (_refreshImageView.isAnimating) {
[_refreshImageView stopAnimating];
[UIView animateWithDuration:0.3 animations:^{
self.scrollView.contentInset = UIEdgeInsetsMake(0, 0, 0, 0);
}];
}
}
}
//改变透明度
-(void)setPullingPercent:(CGFloat)percent
{
_refreshImageView.alpha = percent;
}
//滑动处理
- (void)updateRefreshHeaderWithOffsetY:(CGFloat)y
{
if (y > 0) {//防止往上划的时候动图显示出来
return;
}
[self setPullingPercent:(fabs(y) - 20) / (fabs(RefreshMinY) - 20)];
if (self.refreshState == MHRefreshStateRefreshing) {
return;
}
if (y < RefreshMinY) {
if(!self.scrollView.isDragging ){
self.refreshState = MHRefreshStateRefreshing;
}
}else{
if (self.refreshState == MHRefreshStateNormal) {
return;
}
self.refreshState = MHRefreshStateNormal;
}
}
//监听滑动
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context
{
if (keyPath != BaseRefreshViewObserveKeyPath) return;
[self updateRefreshHeaderWithOffsetY:self.scrollView.contentOffset.y];
}
@end
是不是很简单呀?
然后再看看使用
_refreshHeader = [MHRefreshHeader refreshWithScroll:_tableView];
__weak typeof(self) weakSelf = self;
[_refreshHeader setRefreshingBlock:^{
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[weakSelf.refreshHeader endRefreshing];
});
}];
只有2步,通过scrollview创建,然后在刷新方法里面结束刷新.给大家提供个思路,有更好的办法欢迎留言交流😁
github地址