iOS-OC仿Boss直聘下拉刷新動畫

??在刷即刻的時候,突然發(fā)現(xiàn)它的首頁刷新動畫還不錯,因此我就仿寫了下boss直聘App的下拉刷新動畫(別問為啥),在這里記錄下來供大家參考討論。最終運行效果如下:
QQ20180620-162220-HD.gif

??廢話不多說開始上代碼了。首先創(chuàng)建一個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;
}

在父視圖改變的時候設置監(jiān)聽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的時候有可能用到這個方法,當本視圖的父類視圖改變的時候,系統(tǒng)會自動的執(zhí)行這個方法.newSuperview是本視圖的新父類視圖.newSuperview有可能是nil.
??在監(jiān)聽偏移量的方法里實現(xiàn)隨著偏移量三個點的變化:

#pragma mark ---Kvo
/*監(jiān)聽偏移量*/
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context {
    if ([keyPath isEqualToString:@"contentOffset"]) {
        self.offSet =  self.scrollView.contentOffset.y;
        [self setOffSetUI];
    }
}
/*設置*/
-(void)setOffSetUI{
    //如果到達臨界點,則執(zhí)行刷新動畫
    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();
        }
    }
}

再達到臨界值的時候執(zhí)行刷新動作并執(zhí)行刷新動畫:

/*執(zhí)行動畫*/
-(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;
}

最后再實現(xiàn)結束刷新的方法即可:

 -(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;
    }];
}

本文借鑒:http://www.itdecent.cn/p/3c51e4896632
demo:https://github.com/SionChen/CYXBossRefreshDemo/tree/master 歡迎討論

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

友情鏈接更多精彩內容