UITableView下拉刷新的實(shí)現(xiàn)原理是自定義的下拉刷新控件KVO監(jiān)聽(tīng)UITableView(UIScrollView)的 contentOffset 屬性。
#import <UIKit/UIKit.h>
typedef void (^RefreshHeaderBlock)(void);
@interface RefreshHeader : UIView
- (instancetype)initWithTarget:(id)target beginRefreshBlock:(RefreshHeaderBlock)refreshHeaderBlock;
- (void)beginRefreshing;
- (void)endRefreshing;
@end
把UITableView(UIScrollView)傳進(jìn)來(lái)作為目標(biāo),添加監(jiān)聽(tīng)
@implementation RefreshHeader
- (instancetype)initWithTarget:(id)target beginRefreshBlock:(RefreshHeaderBlock)refreshHeaderBlock{
self = [super init];
if (self) {
self.frame = CGRectMake(0, 0, 0, HeaderHeight);
_refreshHeaderBlock = refreshHeaderBlock;
_scrollView = (UIScrollView *)target;
[_scrollView addSubview:self];
NSKeyValueObservingOptions option = NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld;
[_scrollView addObserver:self forKeyPath:RefreshHeaderKeyPathContentOffset options:option context:nil];
}
return self;
}
- (void)layoutSubviews{
[super layoutSubviews];
CGFloat superWidth = self.scrollView.frame.size.width;
self.frame = CGRectMake(0, - HeaderHeight, superWidth, HeaderHeight);
......
}
#pragma mark public
- (void)beginRefreshing{
if (!_isRefresh) {
_isRefresh = YES;
//設(shè)置偏移量,銜接加載的更多數(shù)據(jù)
[UIView animateWithDuration:0.3 animations:^{
//刷新控件停留
[_scrollView setContentInset:UIEdgeInsetsMake(HeaderHeight, 0, 0, 0)];
[self.activityView startAnimating];
self.arrowView.hidden = YES;
self.stateLable.text = @"加載中";
}];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
_refreshHeaderBlock();
});
}
}
- (void)endRefreshing{
_isRefresh = NO;
[UIView animateWithDuration:0.3 animations:^{
[self.activityView stopAnimating];
self.arrowView.hidden = YES;
self.arrowView.transform = CGAffineTransformMakeRotation(M_PI*2);
//還原scrollView
[self.scrollView setContentInset:UIEdgeInsetsMake(0, 0, 0, 0)];
}];
}
#pragma mark KVO
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context{
if ([keyPath isEqualToString:RefreshHeaderKeyPathContentOffset]) {
[self scrollViewContentOffsetDidChange:change];
}else{
[super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
}
}
#pragma mark logicol
//判斷contentOffset<0代表手勢(shì)為往下拖動(dòng),根據(jù)isDragging屬性判斷是否松開(kāi),如果松開(kāi)且偏移量大于(HeaderHeight),啟動(dòng)刷新動(dòng)作
- (void)scrollViewContentOffsetDidChange:(NSDictionary *)change{
self.stateLable.hidden = NO;
CGFloat offset = self.scrollView.contentOffset.y;
if (offset < 0) {
//正在拖拽
if(self.scrollView.isDragging){
//還原scrollView
[self.scrollView setContentInset:UIEdgeInsetsMake(0, 0, 0, 0)];
[UIView animateWithDuration:0.3 animations:^{
self.arrowView.hidden = NO;
if (offset < -HeaderHeight*1.5) {
self.arrowView.transform = CGAffineTransformMakeRotation(M_PI);
self.stateLable.text = @"釋放更新";
}else{
self.arrowView.transform = CGAffineTransformMakeRotation(M_PI*2);
self.stateLable.text = @"載入更多";
}
}];
return;
}
//松開(kāi)且偏移量大于頭部高度,啟動(dòng)刷新
if (offset < -HeaderHeight) {
[self beginRefreshing];
}
}
}
-(void)dealloc{
[self.scrollView removeObserver:self forKeyPath:RefreshHeaderKeyPathContentOffset];
}