如何來(lái)實(shí)現(xiàn)一個(gè)自定義刷新控件
來(lái)源于raywenderlich
-
前言
在滑動(dòng)視圖上添加下拉刷新是很常見(jiàn)的,現(xiàn)在大多數(shù)人都是使用MJRefresh,今天我簡(jiǎn)單講下如果你要自己寫(xiě)個(gè)刷新控件怎么寫(xiě)秸苗。先看看效果:
首先自己創(chuàng)建一個(gè)繼承UIView的控件
在自定義一個(gè)控件的時(shí)候我們首先考慮的是他要實(shí)現(xiàn)的方法伪煤,以及屬性损离,然后再是這些方法的實(shí)現(xiàn)哼审。
- 屬性有4個(gè)
/** 代理*/
@property(nonatomic,weak)id<AIRefreshViewDelegate> delegate;
/** 填加的滑動(dòng)視圖*/
@property(nonatomic,strong)UIScrollView *scrollView;
/** 是否正在被刷新*/
@property(nonatomic, assign,getter=isRefreshing)BOOL refreshing;
/** 進(jìn)度*/
@property(nonatomic, assign)CGFloat progress;
- 方法我們需要實(shí)現(xiàn)5個(gè)
/**
初始化方法
@param frame 初始frame
@param scrollView 所要添加的滑動(dòng)視圖
@return 實(shí)體
*/
- (instancetype)initWithFrame:(CGRect)frame scrollView:(UIScrollView*)scrollView
;
/**
結(jié)束刷新
*/
- (void)endRefreshing;
/**
開(kāi)始刷新
*/
- (void)beginRefreshing;
還有兩個(gè)是處理Scrollview的拖拽手勢(shì)和將要結(jié)束拖拽代理事件(AIRefreshView這里我并沒(méi)有實(shí)現(xiàn)Scrollview的代理,只是寫(xiě)了方法名一樣的函數(shù)显设,希望把外部的UIScrollview的代理在AIRefreshView控件中處理)
- .m
.h的方法說(shuō)完了框弛,看看.m的屬性,我們需要一個(gè)圓捕捂、飛機(jī)瑟枫、背景圖(這里飛機(jī)用的layer是想盡量使用輕量級(jí)的控件)
/** 圓CAShapeLayer */
@property(nonatomic,strong)CAShapeLayer *ovalShapeLayer;
/** 飛機(jī)layer*/
@property(nonatomic,strong)CALayer *airplaneLayer;
/** 背景圖*/
@property(nonatomic,weak)UIImageView *bgImageView;
首先是初始化方法初始化方法添加圓、飛機(jī)指攒、背景
- (instancetype)initWithFrame:(CGRect)frame scrollView:(UIScrollView*)scrollView
{
self = [super initWithFrame:frame];
if (self) {
self.scrollView = scrollView;
_refreshing = NO;
_progress = 0.;
//add the background Image
UIImageView *imageView = [[UIImageView alloc]initWithImage:[UIImage imageNamed:@"refresh-view-bg"]];
imageView.contentMode = UIViewContentModeScaleAspectFill;
imageView.clipsToBounds = YES;
self.bgImageView = imageView;
[self addSubview:imageView];
//shapeLayer
self.ovalShapeLayer = [CAShapeLayer layer];
self.ovalShapeLayer.strokeColor = [UIColor whiteColor].CGColor;
self.ovalShapeLayer.fillColor = [UIColor clearColor].CGColor;
self.ovalShapeLayer.lineWidth = 4.;
self.ovalShapeLayer.lineDashPattern = @[@2,@3];
CGFloat refreshRadius = frame.size.height/2 *.8;
self.ovalShapeLayer.path = [UIBezierPath bezierPathWithOvalInRect:
CGRectMake(frame.size.width *.5 - refreshRadius,
frame.size.height *.5 - refreshRadius,
2 * refreshRadius,
2 * refreshRadius)].CGPath;
[self.layer addSublayer:self.ovalShapeLayer];
self.airplaneLayer = [CALayer layer];
UIImageView *airplaneImage = [[UIImageView alloc]initWithImage:[UIImage imageNamed:@"airplane"]];
self.airplaneLayer.contents = (__bridge id _Nullable)(airplaneImage.image.CGImage);
self.airplaneLayer.bounds = CGRectMake(0, 0, airplaneImage.frame.size.width, airplaneImage.frame.size.height);
self.airplaneLayer.position = CGPointMake(frame.size.width * .5 + frame.size.height *.5 *.8,
frame.size.height * .5);
[self.layer addSublayer:self.airplaneLayer];
}
return self;
}
- 接下來(lái)重點(diǎn)就在這兩個(gè)代理事件
在拖拽的代理函數(shù)中計(jì)算力奋,內(nèi)容頂部的偏移量,以及拖拽的進(jìn)度(因?yàn)槲覀兊膭?dòng)畫(huà)顯示需要這個(gè)進(jìn)度)
-(void)scrollViewDidScroll:(UIScrollView *)scrollView {
CGFloat offsetY = MAX(-(scrollView.contentOffset.y+scrollView.contentInset.top), 0.);
_progress = MIN(MAX(offsetY / self.frame.size.height, 0.), 1.);
if (!self.isRefreshing) {
[self redrawFromProgress:self.progress];
}
}
/**
通過(guò)進(jìn)度畫(huà)圓幽七,和飛機(jī)
@param progress 進(jìn)度
*/
- (void)redrawFromProgress:(CGFloat)progress {
self.airplaneLayer.opacity = _progress;
self.ovalShapeLayer.strokeEnd = _progress;
}
在拖拽結(jié)束的時(shí)候判斷是否需要進(jìn)行刷新動(dòng)作景殷,以及調(diào)用刷新動(dòng)畫(huà)、實(shí)現(xiàn),刷新控件代理猿挚。
-(void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset {
if (!self.isRefreshing && self.progress >= 1.) {
if (self.delegate && [self.delegate respondsToSelector:@selector(refreshViewDidRefresh:)]) {
[self.delegate refreshViewDidRefresh:self];
[self beginRefreshing];
}
}
}
基本的開(kāi)始結(jié)束方法
- (void)beginRefreshing {
self.refreshing = YES;
[UIView animateWithDuration:.3 animations:^{
UIEdgeInsets newInsets = self.scrollView.contentInset;
newInsets.top += self.frame.size.height;
self.scrollView.contentInset = newInsets;
}];
}
- (void)endRefreshing {
self.refreshing = NO;
[UIView animateWithDuration:.3 delay:0. options:(UIViewAnimationOptionCurveEaseOut) animations:^{
UIEdgeInsets newInsets = self.scrollView.contentInset;
newInsets.top -= self.frame.size.height;
self.scrollView.contentInset = newInsets;
} completion:^(BOOL finished) {
}];
}
- 動(dòng)畫(huà)
上面已經(jīng)實(shí)現(xiàn)了基本的刷新咐旧,但是現(xiàn)在缺少動(dòng)畫(huà)。
你應(yīng)該將開(kāi)始動(dòng)畫(huà)放在獲取數(shù)據(jù)的時(shí)候绩蜻,代碼添加在beginRefreshing
最后
首先是圓圈
CABasicAnimation *strokeStartAnimation = [CABasicAnimation animationWithKeyPath:@"strokeStart"];
strokeStartAnimation.fromValue = @-.5;
strokeStartAnimation.toValue = @1.;
CABasicAnimation *strokeEndAnimation = [CABasicAnimation animationWithKeyPath:@"strokeEnd"];
strokeEndAnimation.fromValue = @0.;
strokeEndAnimation.toValue = @1.;
CAAnimationGroup *strokeAniamtionGroup = [CAAnimationGroup animation];
strokeAniamtionGroup.duration = 1.5;
strokeAniamtionGroup.repeatDuration = 5.;
strokeAniamtionGroup.animations = @[strokeStartAnimation,strokeEndAnimation];
[self.ovalShapeLayer addAnimation:strokeAniamtionGroup forKey:nil];
這段代碼創(chuàng)建了兩個(gè)動(dòng)畫(huà):第一個(gè)strokeStart從-0.5到1.0這是一種投機(jī)取巧的方式铣墨,當(dāng)動(dòng)畫(huà)在-0.5到0.0的時(shí)間不會(huì)做任何事。因?yàn)檫@些值只是代表不可見(jiàn)部分的形狀办绝。
飛機(jī)動(dòng)畫(huà)
//飛機(jī)動(dòng)畫(huà)
CAKeyframeAnimation *flightAnimation = [CAKeyframeAnimation animationWithKeyPath:@"position"];
flightAnimation.path = self.ovalShapeLayer.path;
flightAnimation.calculationMode = kCAAnimationPaced;
//旋轉(zhuǎn)
CABasicAnimation *airplanOrientationAnimation = [CABasicAnimation animationWithKeyPath:@"transform.rotation"];
airplanOrientationAnimation.fromValue = @0.;
airplanOrientationAnimation.toValue = @(M_PI *2);
CAAnimationGroup *flightAnimationGroup = [CAAnimationGroup animation];
flightAnimationGroup.duration = 1.5;
flightAnimationGroup.repeatDuration = 5.;
flightAnimationGroup.animations = @[flightAnimation,airplanOrientationAnimation];
[self.airplaneLayer addAnimation:flightAnimationGroup forKey:nil];
飛機(jī)動(dòng)畫(huà)主要使用CAKeyframeAnimation
動(dòng)畫(huà)使飛機(jī)按照?qǐng)A的路徑走伊约,設(shè)置calculationMode
屬性為kCAAnimationPaced
使動(dòng)畫(huà)以一個(gè)恒定的速度而且確保沿著這個(gè)路徑走。當(dāng)然也要設(shè)置他的旋轉(zhuǎn)角度通過(guò)airplanOrientationAnimation
動(dòng)畫(huà)
最后的效果應(yīng)該是這樣:
點(diǎn)擊這里可以查看源碼第25個(gè)cell,喜歡的給個(gè)star