??廢話不多說開始上代碼了。首先創(chuàng)建一個CYXRefreshHeader
@interface CYXRefreshHeader : UIView
/*刷新block*/
@property (nonatomic,strong) void(^refresh)(void);
/*開始刷新*/
-(void)startRefresh;
/*結(jié)束刷新*/
-(void)endRefresh;
@end
初始化三個點(diǎn)的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;
}
在父視圖改變的時候設(shè)置監(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 方法的調(diào)用時機(jī):
??當(dāng)自己重寫一個UIView的時候有可能用到這個方法,當(dāng)本視圖的父類視圖改變的時候,系統(tǒng)會自動的執(zhí)行這個方法.newSuperview是本視圖的新父類視圖.newSuperview有可能是nil.
??在監(jiān)聽偏移量的方法里實(shí)現(xiàn)隨著偏移量三個點(diǎ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];
}
}
/*設(shè)置*/
-(void)setOffSetUI{
//如果到達(dá)臨界點(diǎn)织阅,則執(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坐標(biāo)的變化
self.top = topY;
}
if (-self.offSet >= RefreshFullOffset && !self.isAnimating && !self.scrollView.dragging) {
//刷新
[self startAnimation];
if (self.refresh) {
self.refresh();
}
}
}
再達(dá)到臨界值的時候執(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;
}
最后再實(shí)現(xiàn)結(jié)束刷新的方法即可:
-(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坐標(biāo)*/
-(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.reibang.com/p/3c51e4896632
demo:https://github.com/SionChen/CYXBossRefreshDemo/tree/master 歡迎討論