前言:之前在實(shí)現(xiàn)下拉刷新,上拉加載功能時(shí)浩村,我一直都是使用MJRefresh進(jìn)行集成做葵。一直想自己寫一個(gè)類似于MJRefresh的刷新控件,方便與自己對(duì)MJRefresh原理的理解心墅,昨天抽出了時(shí)間自己寫了一個(gè)類似的刷新控件酿矢,在這做一下記錄與分享。
Github: 看這里
思路整理與代碼實(shí)現(xiàn):
1. 通過(guò)上/下拉頁(yè)面顯示刷新提示文字我們可以判斷出怎燥,刷新提示文字的顯示與否是根據(jù)當(dāng)前scrollView的偏移值來(lái)確定的瘫筐。在這里我們的一切動(dòng)態(tài)顯示和用戶交互跟當(dāng)前scrollView的偏移值存在著聯(lián)系。所以我們給MyRefreshView提供一個(gè)類方法的接口铐姚,將scrollView傳進(jìn)去:
+ (MyRefreshView *)refreshViewWithScrollView:(UIScrollView *)scrollView{
MyRefreshView *refreshView = [[MyRefreshView alloc]init];
refreshView.frame = CGRectMake(20, -25, KScreenWidth-20, 18);
refreshView.scrollView = scrollView;
[refreshView setupView];
return refreshView;
}
2. 添加控件严肪,這里我們需要一個(gè)提示文本,一個(gè)顯示偏移進(jìn)度的圓形進(jìn)度條谦屑,一個(gè)刷新時(shí)的loading等待圈:
@interface MyRefreshView ()
/**
刷新狀態(tài)值
*/
@property (nonatomic, assign) NSInteger refreshFlag;
/**
刷新菊花圈
*/
@property (nonatomic, strong) UIActivityIndicatorView *activityIndicatorView;
/**
偏移動(dòng)畫
*/
@property (nonatomic, strong) CAShapeLayer *scheduleLayer;
/**
監(jiān)聽視圖
*/
@property (nonatomic, strong) UIScrollView *scrollView;
/**
刷新文字
*/
@property (nonatomic, strong) UILabel *titleLabel;
@end
3. 在setupView方法中構(gòu)建視圖驳糯,將refreshView添加到當(dāng)前的scrollView上。并用KVO將refreshView添加為scrollView的觀察者氢橙,實(shí)時(shí)監(jiān)聽scrollView的偏移值
刷新提示文字:在refreshView上加上一個(gè)Label酝枢;
圓形進(jìn)度條:這里我們用CAShapeLayer實(shí)現(xiàn)。CAShapeLayer有strokeStart和strokeEnd屬性悍手,在繪圖時(shí)帘睦,這兩個(gè)屬性可以改變path的起始位置和結(jié)束位置。設(shè)置strokeStart和strokeEnd初始值都為0坦康,然后根據(jù)scrollView的偏移值來(lái)改變strokeEnd的值竣付,從而達(dá)到改變進(jìn)度的目的;
loading視圖:這里用系統(tǒng)自由的菊花圈UIActivityIndicatorView滞欠,實(shí)際操作中也可以設(shè)置gif動(dòng)畫或者CAAnimation來(lái)實(shí)現(xiàn)古胆;
refreshFlag:刷新狀態(tài)值(根據(jù)偏移值判斷當(dāng)前是否處于可刷新狀態(tài));
- (void)setupView{
self.titleLabel = [[UILabel alloc]initWithFrame:CGRectMake(25, 0, KScreenWidth-20-25, 18)];
[self.titleLabel setAdjustsFontSizeToFitWidth:YES];
[self addSubview:self.titleLabel];
self.scheduleLayer = [[CAShapeLayer alloc]init];
self.scheduleLayer.frame = CGRectMake(0, 0, 18, 18);
self.scheduleLayer.strokeStart = 0;
self.scheduleLayer.fillColor = [UIColor clearColor].CGColor;
self.scheduleLayer.strokeColor = [UIColor whiteColor].CGColor;
self.scheduleLayer.backgroundColor = [UIColor clearColor].CGColor;
self.scheduleLayer.lineWidth = 2.0;
self.scheduleLayer.strokeEnd = 0.0;
[self.scheduleLayer setTransform:CATransform3DMakeRotation(-M_PI_2, 0, 0, 1)];
self.scheduleLayer.path = [UIBezierPath bezierPathWithOvalInRect:CGRectInset(CGRectMake(0, 0, 18, 18), 2, 2)].CGPath;
[self.layer addSublayer:self.scheduleLayer];
self.activityIndicatorView = [[UIActivityIndicatorView alloc]initWithFrame:CGRectMake(0, 0, 18, 18)];
[self addSubview:self.activityIndicatorView];
[self.scrollView addSubview:self];
[self.scrollView addObserver:self forKeyPath:@"contentOffset" options:NSKeyValueObservingOptionOld|NSKeyValueObservingOptionNew context:nil];
}
4. 動(dòng)態(tài)監(jiān)聽
在observeValueForKeyPath方法中獲取到當(dāng)前的偏移值筛璧,并進(jìn)行相關(guān)的邏輯判斷
這里我們?cè)O(shè)置的刷新臨界點(diǎn)為-80逸绎;而strokeStar和strokeEnd的取值范圍為0-1之間。當(dāng)scrollView被拖拽時(shí)夭谤,顯示refreshView棺牧,根據(jù)偏移值動(dòng)態(tài)改變相關(guān)屬性的值。
結(jié)束拖拽時(shí)朗儒,根據(jù)當(dāng)前的偏移值判斷refreshView是否進(jìn)入刷新狀態(tài)颊乘。
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary*)change context:(void *)context{
if (self.refreshing) {
return;
}
if ([keyPath isEqualToString:@"contentOffset"]) {
CGFloat height = [change[@"new"] CGPointValue].y;
if (height > 0) {
height = 0;
}else if (height > -80){
height = height/-80.0;
}else{
height = 1.0;
}
if (self.scrollView.isDragging) {
self.hidden = NO;
if (height == 1.0) {
self.title = @"釋放刷新";
}else{
self.title = @"下拉刷新";
}
[self.activityIndicatorView stopAnimating];
self.scheduleLayer.strokeEnd = height;
self.scheduleLayer.hidden = NO;
self.refreshFlag = height;
}else{
if (self.refreshFlag == 1.0){
self.refreshing = YES;
[self.scrollView setContentOffset:CGPointMake(0, -80) animated:YES];
[self.activityIndicatorView startAnimating];
self.title = @"正在刷新";
self.scheduleLayer.hidden = YES;
self.scheduleLayer.strokeEnd = 0;
}else{
[self.activityIndicatorView stopAnimating];
self.scheduleLayer.hidden = NO;
self.scheduleLayer.strokeEnd = height;
}
}
}
}
在title的set方法中改變當(dāng)前的刷新文字
- (void)setTitle:(NSString *)title{
_title = title;
self.titleLabel.text = title;
}
5. 數(shù)據(jù)請(qǐng)求成功后参淹,設(shè)置結(jié)束刷新接口,結(jié)束刷新動(dòng)畫,并改變刷新狀態(tài)乏悄,將scrollView的偏移值設(shè)置為最初狀態(tài)
- (void)endRefresh{
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)1.0*NSEC_PER_SEC), dispatch_get_main_queue(), ^{
self.refreshing = NO;
self.refreshFlag = 0;
self.scheduleLayer.strokeEnd = 0;
[self.activityIndicatorView stopAnimating];
[self.scrollView setContentOffset:CGPointMake(0, 0) animated:YES];
self.hidden = YES;
});
}