iOS-OC仿Boss直聘下拉刷新动画

  在刷即刻的时候,突然发现它的首页刷新动画还不错,因此我就仿写了下boss直聘App的下拉刷新动画(别问为啥),在这里记录下来供大家参考讨论。最终运行效果如下:
QQ20180620-162220-HD.gif

  废话不多说开始上代码了。首先创建一个CYXRefreshHeader

@interface CYXRefreshHeader : UIView
/*刷新block*/
@property (nonatomic,strong) void(^refresh)(void);
/*开始刷新*/
-(void)startRefresh;
/*结束刷新*/
-(void)endRefresh;
@end

初始化三个点的Layer

-(instancetype)init{
    if (self = [super init]) {
        self.backgroundColor = [UIColor whiteColor];
        self.frame = CGRectMake(0, HeaderHeight, HeaderWidth, HeaderHeight);
        [self.layer addSublayer:self.firstPointLayer];
        [self.layer addSublayer:self.secondPointLayer];
        [self.layer addSublayer:self.thirdPointLayer];
        self.firstPointLayer.frame = CGRectMake(HeaderWidth/2-RefreshArcRadius, HeaderHeight/2-RefreshArcRadius, RefreshArcRadius*2, RefreshArcRadius*2);
        self.secondPointLayer.frame = CGRectMake(HeaderWidth/2-RefreshArcRadius, HeaderHeight/2-RefreshArcRadius, RefreshArcRadius*2, RefreshArcRadius*2);
        self.thirdPointLayer.frame = CGRectMake(HeaderWidth/2-RefreshArcRadius, HeaderHeight/2-RefreshArcRadius, RefreshArcRadius*2, RefreshArcRadius*2);
        self.firstPointLayer.path = [self pointPath].CGPath;
        self.secondPointLayer.path = [self pointPath].CGPath;
        self.thirdPointLayer.path = [self pointPath].CGPath;
    }
    return self;
}
#pragma mark ---Path
-(UIBezierPath *)pointPath{
    UIBezierPath *path = [UIBezierPath bezierPathWithArcCenter:CGPointMake(RefreshArcRadius, RefreshArcRadius) radius:RefreshArcRadius startAngle:0 endAngle:M_PI * 2 clockwise:YES];
    return path;
}

在父视图改变的时候设置监听scrollview的滑动偏移量

/*父视图改变的时候*/
- (void)willMoveToSuperview:(UIView *)newSuperview {
    [super willMoveToSuperview:newSuperview];
    if ([newSuperview isKindOfClass:[UIScrollView class]]) {
        self.scrollView = (UIScrollView *)newSuperview;
        self.centerX = self.scrollView.width/2;
        self.bottom = 0;
        [self.scrollView addObserver:self forKeyPath:@"contentOffset" options:NSKeyValueObservingOptionNew context:nil];
    }else {
        [self.superview removeObserver:self forKeyPath:@"contentOffset"];
    }
}

  这里- (void)willMoveToSuperview:(UIView *)newSuperview 方法的调用时机:
  当自己重写一个UIView的时候有可能用到这个方法,当本视图的父类视图改变的时候,系统会自动的执行这个方法.newSuperview是本视图的新父类视图.newSuperview有可能是nil.
  在监听偏移量的方法里实现随着偏移量三个点的变化:

#pragma mark ---Kvo
/*监听偏移量*/
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
    if ([keyPath isEqualToString:@"contentOffset"]) {
        self.offSet =  self.scrollView.contentOffset.y;
        [self setOffSetUI];
    }
}
/*设置*/
-(void)setOffSetUI{
    //如果到达临界点,则执行刷新动画
    if (!self.isAnimating&&self.offSet<=0) {
        CGFloat scale = -self.offSet/RefreshFullOffset;
        if (scale>1) {scale=1;}
        if (scale<0) {scale=0;}
        CGFloat centerX = HeaderWidth/2;
        CGFloat maxLeftX = centerX-RefreshMaxWidth;
        CGFloat maxLeftDistance = centerX - maxLeftX;
        CGFloat maxRightX = centerX+RefreshMaxWidth;
        CGFloat maxRightDistance = maxRightX - centerX;
        
        self.firstPointLayer.frame = CGRectMake(centerX-maxLeftDistance*scale-RefreshArcRadius, HeaderHeight/2-RefreshArcRadius, RefreshArcRadius*2, RefreshArcRadius*2);
        self.thirdPointLayer.frame = CGRectMake(centerX+maxRightDistance*scale-RefreshArcRadius, HeaderHeight/2-RefreshArcRadius, RefreshArcRadius*2, RefreshArcRadius*2);
        CGFloat topY =-(RefreshFullOffset/2-RefreshFullOffset/2.0*(1.0-scale))-RefreshArcRadius*2;//y坐标的变化
        self.top = topY;
    }
    if (-self.offSet >= RefreshFullOffset && !self.isAnimating && !self.scrollView.dragging) {
        //刷新
        [self startAnimation];
        if (self.refresh) {
            self.refresh();
        }
    }
}

再达到临界值的时候执行刷新动作并执行刷新动画:

/*执行动画*/
-(void)startAnimation{
    self.isAnimating = YES;
    [UIView animateWithDuration:0.5 animations:^{
        UIEdgeInsets inset = self.scrollView.contentInset;
        inset.top = RefreshFullOffset;
        self.scrollView.contentInset = inset;
    }];
    CAKeyframeAnimation * animation = [self opacityAnimation];
    [self.firstPointLayer addAnimation:animation forKey:@"opacity"];
    
    animation = [self opacityAnimation];
    animation.beginTime = CACurrentMediaTime()+KeyAnimationDuration/2;
    [self.secondPointLayer addAnimation:animation forKey:@"opacity"];
    
    animation = [self opacityAnimation];
    animation.beginTime = CACurrentMediaTime()+KeyAnimationDuration;
    [self.thirdPointLayer addAnimation:animation forKey:@"opacity"];
}
-(CAKeyframeAnimation *)opacityAnimation{
    CAKeyframeAnimation * animation = [CAKeyframeAnimation animationWithKeyPath:@"opacity"];
    animation.duration = KeyAnimationDuration;
    
    animation.repeatCount = HUGE_VALF;
    animation.fillMode = kCAFillModeForwards;
    animation.duration = KeyAnimationDuration*2;
    animation.values = @[[NSNumber numberWithFloat:1.0f],
                         [NSNumber numberWithFloat:0.0f],
                         [NSNumber numberWithFloat:1.0f],
                         [NSNumber numberWithFloat:1.0f]];
    return animation;
}

最后再实现结束刷新的方法即可:

 -(void)endRefresh{
   [UIView animateWithDuration:0.5 animations:^{
        UIEdgeInsets inset = self.scrollView.contentInset;
        inset.top = 0.f;
        self.scrollView.contentInset = inset;
   } completion:^(BOOL finished) {
        [self stopAnimation];
        [self initLayerFrame];
   }];
}

/*初始化layer坐标*/
-(void)initLayerFrame{
    self.firstPointLayer.frame = CGRectMake(HeaderWidth/2-RefreshArcRadius, HeaderHeight/2-RefreshArcRadius, RefreshArcRadius*2, RefreshArcRadius*2);
    self.secondPointLayer.frame = CGRectMake(HeaderWidth/2-RefreshArcRadius, HeaderHeight/2-RefreshArcRadius, RefreshArcRadius*2, RefreshArcRadius*2);
    self.thirdPointLayer.frame = CGRectMake(HeaderWidth/2-RefreshArcRadius, HeaderHeight/2-RefreshArcRadius, RefreshArcRadius*2, RefreshArcRadius*2);
}
/*停止动画*/
-(void)stopAnimation{
    [UIView animateWithDuration:0.5 animations:^{
        UIEdgeInsets inset = self.scrollView.contentInset;
        inset.top = 0.f;
        self.scrollView.contentInset = inset;
    } completion:^(BOOL finished) {
        [self.thirdPointLayer removeAllAnimations];
        [self.firstPointLayer removeAllAnimations];
        [self.secondPointLayer removeAllAnimations];
        self.isAnimating = NO;
    }];
}

本文借鉴:https://www.jianshu.com/p/3c51e4896632
demo:https://github.com/SionChen/CYXBossRefreshDemo/tree/master 欢迎讨论

你可能感兴趣的:(iOS-OC仿Boss直聘下拉刷新动画)