自定义有逼格的刷新控件

demo.gif

一.下拉刷新控件

要点:利用UIScrollView的分类实现;如何监听下拉刷新控件三种状态;通过分类反向获取控件;不影响UIScrollView代理调用。

1.区分下拉控件的三种状态
typedef NS_ENUM(NSInteger, RefreshState) {
    RefreshState_ready, //NSLog(@"下拉去刷新");
    RefreshState_will,  //NSLog(@"松开去释放");
    RefreshState_loading //NSLog(@"加载中");
};

利用UIScrollView的contentOffset和是否在拖拽的状态,可以区分出三种状态。
比如利用UIScrollView的代理方法(但是不能使用代理,一会解释):

-(void)scrollViewDidScroll:(UIScrollView *)scrollView
{
      //isDragging且>contentOffset.y>临界值->RefreshState_will
      //isDragging且>contentOffset.y<临界值->RefreshState_ready
}

-(void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset
{
      //!isDragging&&RefreshState_will->RefreshState_loading
}

2.通过分类反向获取控件

通过runtime在分类中绑定RefreshView实例,为了直接可以通过分类获取到该RefreshView,调用EndRefresh方法。

#import <objc/runtime.h>
static char UIScrollViewPullToRefreshView;

@implementation UIScrollView (ZSRefresh)

-(void)setZsrefresh:(ZSRefreshView * _Nonnull)zsrefresh
{
    [self willChangeValueForKey:@"ZSRefreshView"];
    objc_setAssociatedObject(self, &UIScrollViewPullToRefreshView,
                             zsrefresh,
                             OBJC_ASSOCIATION_ASSIGN);
    [self didChangeValueForKey:@"ZSRefreshView"];
}

-(ZSRefreshView *)zsrefresh
{
    return objc_getAssociatedObject(self, &UIScrollViewPullToRefreshView);
}

3.不影响UIScrollView代理调用

如果在分类中使用之前的两个代理方法来区分下拉控件三种状态,那么在使用该控件的时候,再去使用这两个代理方法将会失效,因为delegate只能指定一个对象(一对一),所以应该使用KVO的方式监听contentOffset。

4.分类代码和使用方式

UIScrollView+ZSRefreshView.h

@interface ZSRefreshView:UIView

-(void)startRefresh;
-(void)endRefresh;

@end

typedef void(^LoadingBlock)(void);

@interface UIScrollView (ZSRefresh)

@property (nonatomic, strong, readonly) ZSRefreshView *zsrefresh;

-(ZSRefreshView*)addPull:(LoadingBlock)loadingblock;

@end

UIScrollView+ZSRefreshView.m

#import "UIScrollView+ZSRefresh.h"

#define BallWidth 10
#define RefreshHeight 60
#define LoadingTime 1.0

typedef NS_ENUM(NSInteger, RefreshState) {
    RefreshState_ready, //NSLog(@"下拉去刷新");
    RefreshState_will,  //NSLog(@"松开去释放");
    RefreshState_loading //NSLog(@"加载中");
};

@interface ZSRefreshView()<UITableViewDelegate>

@property(nonatomic,copy) LoadingBlock loadingBlock;
@property(nonatomic,assign) RefreshState refreshstate;
@property(nonatomic,assign) float ratio;
@property(nonatomic,weak) UIScrollView *scrollview;
@property(nonatomic,strong) UIView * ballview1;
@property(nonatomic,strong) UIView * ballview2;
@property(nonatomic,strong) UIView * ballview3;

@end


@implementation ZSRefreshView


-(instancetype)initWithFrame:(CGRect)frame
{
    if(self=[super initWithFrame:frame])
    {
        self.ballview1=[[UIView alloc]init];
        self.ballview1.layer.cornerRadius=BallWidth/2;
        self.ballview1.backgroundColor=SDColor(245, 191, 20);
        self.ballview2=[[UIView alloc]init];
        self.ballview2.layer.cornerRadius=BallWidth/2;
        self.ballview2.backgroundColor=ThemeColor;
        self.ballview3=[[UIView alloc]init];
        self.ballview3.layer.cornerRadius=BallWidth/2;
        self.ballview3.backgroundColor=SDColor(235, 1, 6);
        [self addSubview:self.ballview1];
        [self addSubview:self.ballview2];
        [self addSubview:self.ballview3];
    }
    return self;
}

-(void)setScrollview:(UIScrollView *)scrollview
{
    _scrollview=scrollview;
}


- (void)loading{
    CABasicAnimation *anim = [CABasicAnimation animationWithKeyPath:@"opacity"];
    anim.duration = LoadingTime;
    anim.fromValue=@(1.0);
    anim.toValue = @(0.3);
    anim.repeatCount = MAXFLOAT;
    [self.ballview1.layer addAnimation:anim forKey:@"anim1"];
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(LoadingTime/3.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        [self.ballview2.layer addAnimation:anim forKey:@"anim2"];
    });
    
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(LoadingTime*2.0/3.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        [self.ballview3.layer addAnimation:anim forKey:@"anim3"];
    });
}


-(void)startRefresh
{
    self.refreshstate=RefreshState_loading;
    self.ballview1.frame=CGRectMake((self.scrollview.width-BallWidth)/2-20,(RefreshHeight-10)-(RefreshHeight/2-10), BallWidth, BallWidth);
    self.ballview2.frame=CGRectMake((self.scrollview.width-BallWidth)/2, (RefreshHeight-10)-(RefreshHeight/2-10), BallWidth, BallWidth);
    self.ballview3.frame=CGRectMake((self.scrollview.width-BallWidth)/2+20,(RefreshHeight-10)-(RefreshHeight/2-10), BallWidth, BallWidth);
    [self loading];
    [UIView animateWithDuration:0.3 animations:^{
         //自动刷新需要设置setContentOffset才生效
        self.scrollview.contentOffset=CGPointMake(0, -RefreshHeight);
        self.scrollview.contentInset=UIEdgeInsetsMake(RefreshHeight, 0, 0, 0);
    } completion:^(BOOL finished) {
    }];
    if(self.loadingBlock)
    {
        self.loadingBlock();
    }
    
}

-(void)endRefresh
{
    self.refreshstate=RefreshState_ready;
    [UIView animateWithDuration:0.3 animations:^{
        self.scrollview.contentInset=UIEdgeInsetsMake(0, 0, 0, 0);
    } completion:^(BOOL finished) {
        [self.ballview1.layer removeAnimationForKey:@"anim1"];
        [self.ballview2.layer removeAnimationForKey:@"anim2"];
        [self.ballview3.layer removeAnimationForKey:@"anim3"];
    }];
}

-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context
{
    if([keyPath isEqualToString:@"contentOffset"])
    {
        CGPoint point=[[change valueForKey:NSKeyValueChangeNewKey]CGPointValue];
        [self scrollViewScrolling:point];
    }
}

- (void)scrollViewScrolling:(CGPoint)contentOffset{
    if(self.scrollview.contentOffset.y<=0.0)
    {
        if(self.scrollview.isDragging&&self.scrollview.contentOffset.y>=-RefreshHeight)
        {
            self.refreshstate=RefreshState_ready;
            [self.ballview1.layer removeAnimationForKey:@"anim1"];
            [self.ballview2.layer removeAnimationForKey:@"anim2"];
            [self.ballview3.layer removeAnimationForKey:@"anim3"];
            self.ratio=MIN(fabs(self.scrollview.contentOffset.y)/RefreshHeight, 1.0);
            self.ballview1.frame=CGRectMake((self.scrollview.width-BallWidth)/2-20*self.ratio,(RefreshHeight-10)-(RefreshHeight/2-10)*self.ratio, BallWidth, BallWidth);
            self.ballview2.frame=CGRectMake((self.scrollview.width-BallWidth)/2, (RefreshHeight-10)-(RefreshHeight/2-10)*self.ratio, BallWidth, BallWidth);
            self.ballview3.frame=CGRectMake((self.scrollview.width-BallWidth)/2+20*self.ratio,(RefreshHeight-10)-(RefreshHeight/2-10)*self.ratio, BallWidth, BallWidth);
        }else if(self.scrollview.isDragging&&self.scrollview.contentOffset.y<-RefreshHeight)
        {
            self.refreshstate=RefreshState_will;
        }else if(!self.scrollview.isDragging&&self.refreshstate==RefreshState_will)
        {
            self.refreshstate=RefreshState_loading;
            [self loading];
            [UIView animateWithDuration:0.3 animations:^{
                self.scrollview.contentInset=UIEdgeInsetsMake(RefreshHeight, 0, 0, 0);
            } completion:^(BOOL finished) {
            }];
            if(self.loadingBlock)
            {
                self.loadingBlock();
            }
            
        }
    }
}
@end

#import <objc/runtime.h>
static char UIScrollViewPullToRefreshView;

@implementation UIScrollView (ZSRefresh)

-(ZSRefreshView *)addPull:(LoadingBlock)loadingblock
{
    ZSRefreshView * zsrefreshview=[[ZSRefreshView alloc]initWithFrame:CGRectMake(0, -RefreshHeight,[UIScreen mainScreen].bounds.size.width, RefreshHeight)];
    zsrefreshview.refreshstate=RefreshState_ready;
    zsrefreshview.scrollview=self;
    zsrefreshview.loadingBlock=loadingblock;
    zsrefreshview.backgroundColor=[UIColor clearColor];
    [self addSubview:zsrefreshview];
    self.zsrefresh=zsrefreshview;
    [self addObserver:self.zsrefresh forKeyPath:@"contentOffset" options:NSKeyValueObservingOptionNew context:nil];
    return zsrefreshview;
}

-(void)setZsrefresh:(ZSRefreshView * _Nonnull)zsrefresh
{
    [self willChangeValueForKey:@"ZSRefreshView"];
    objc_setAssociatedObject(self, &UIScrollViewPullToRefreshView,
                             zsrefresh,
                             OBJC_ASSOCIATION_ASSIGN);
    [self didChangeValueForKey:@"ZSRefreshView"];
}

-(ZSRefreshView *)zsrefresh
{
    return objc_getAssociatedObject(self, &UIScrollViewPullToRefreshView);
}

@end

使用

[self.tableview addPull:^{
        [self.tableview.zsrefresh endRefresh];
    }];

二.中心加载HUD

要点:直接添加到UIApplication.keywindow上
JSXLoadingView.h

@interface JSXLoadingView : CommonViewFromXib

@property (weak, nonatomic) IBOutlet UIView *bgview;

+(instancetype)shareOnceView;

-(void)show;

-(void)hide;

@end

JSXLoadingView.m

#import "JSXLoadingView.h"

@interface JSXLoadingView()
{
    UIView * ball1;
    UIView * ball2;
    UIView * ball3;
    CAAnimationGroup * group;
    CAAnimationGroup * group2;
    CAAnimationGroup * group3;
}
@end

@implementation JSXLoadingView

#pragma mark - 保证JSXLoadingView单例
// 创建静态对象 防止外部访问
static JSXLoadingView *_instance;

#pragma mark - 初始化JSXLoadingView

+(instancetype)shareOnceView
{
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        if (_instance == nil) {
            _instance = [[[NSBundle mainBundle]loadNibNamed:NSStringFromClass(self) owner:self options:nil]lastObject];
        }
    });
    return _instance;
}


-(void)awakeFromNib
{
    [super awakeFromNib];
    self.bgview.backgroundColor=[UIColor clearColor];
    ball1=[[UIView alloc]initWithFrame:CGRectMake(20, 10, 10, 10)];
    ball1.backgroundColor=SDColor(245, 191, 20);
    ball1.layer.cornerRadius=5;
    [self.bgview addSubview:ball1];
    ball2=[[UIView alloc]initWithFrame:CGRectMake(10, 30, 10, 10)];
    ball2.backgroundColor=ThemeColor;
    ball2.layer.cornerRadius=5;
    [self.bgview addSubview:ball2];
    ball3=[[UIView alloc]initWithFrame:CGRectMake(30, 30, 10, 10)];
    ball3.backgroundColor=SDColor(235, 1, 6);
    ball3.layer.cornerRadius=5;
    [self.bgview addSubview:ball3];
    
    NSValue * point1=[NSValue valueWithCGPoint:CGPointMake(25, 15)];
    NSValue * point2=[NSValue valueWithCGPoint:CGPointMake(35, 35)];
    NSValue * point3=[NSValue valueWithCGPoint:CGPointMake(15, 35)];
    
    CAKeyframeAnimation * opanim=[CAKeyframeAnimation animationWithKeyPath:@"opacity"];
    opanim.values=[NSArray arrayWithObjects:@1.0,@0.5,@1.0,@0.5,@1.0,@0.5,nil];
    opanim.autoreverses = NO;
    
    CAKeyframeAnimation * anim=[CAKeyframeAnimation animationWithKeyPath:@"position"];
    anim.values=[NSArray arrayWithObjects:point1,point2,point3,point1, nil];
    anim.autoreverses = NO;
    anim.timingFunctions = @[[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut]];
    
    CAKeyframeAnimation * anim2=[CAKeyframeAnimation animationWithKeyPath:@"position"];
    anim2.values=[NSArray arrayWithObjects:point2,point3,point1,point2, nil];
    anim2.autoreverses = NO;
    anim2.timingFunctions = @[[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut]];
    
    CAKeyframeAnimation * anim3=[CAKeyframeAnimation animationWithKeyPath:@"position"];
    anim3.values=[NSArray arrayWithObjects:point3,point1,point2,point3,nil];
    anim3.autoreverses = NO;
    anim3.timingFunctions = @[[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut]];
    
    group=[CAAnimationGroup animation];
    group.animations=@[opanim,anim];
    group.duration = 2;
    group.repeatCount = MAXFLOAT;
    
    group2=[CAAnimationGroup animation];
    group2.animations=@[opanim,anim2];
    group2.duration = 2;
    group2.repeatCount = MAXFLOAT;
    
    group3=[CAAnimationGroup animation];
    group3.animations=@[opanim,anim3];
    group3.duration = 2;
    group3.repeatCount = MAXFLOAT;
    
    self.frame=CGRectMake(SDScreenWidth/2-50, SDScreenHeight/2-50, 100, 100);
    [[UIApplication sharedApplication].keyWindow addSubview:self];
}

-(void)show
{
    [ball1.layer addAnimation:group forKey:@"ani1"];
    [ball2.layer addAnimation:group2 forKey:@"ani2"];
    [ball3.layer addAnimation:group3 forKey:@"ani3"];
    self.hidden=NO;
}

-(void)hide
{
    [ball1.layer removeAllAnimations];
    [ball2.layer removeAllAnimations];
    [ball3.layer removeAllAnimations];
    self.hidden=YES;
}
@end

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容