美文网首页
自定义有逼格的刷新控件

自定义有逼格的刷新控件

作者: 宙斯YY | 来源:发表于2018-11-22 10:47 被阅读11次
    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
    
    

    相关文章

      网友评论

          本文标题:自定义有逼格的刷新控件

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